@seatlayer/core 0.26.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/{de-73TIXYJH.js → de-UHAOVDS3.js} +3 -2
- package/dist/de-UHAOVDS3.js.map +1 -0
- package/dist/{es-SKAODLTR.js → es-6TJS2L7S.js} +3 -2
- package/dist/es-6TJS2L7S.js.map +1 -0
- package/dist/{fr-J4637T6V.js → fr-MMSY7FPE.js} +3 -2
- package/dist/fr-MMSY7FPE.js.map +1 -0
- package/dist/index.cjs +496 -42
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +79 -2
- package/dist/index.d.ts +79 -2
- package/dist/index.js +493 -42
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
- package/dist/de-73TIXYJH.js.map +0 -1
- package/dist/es-SKAODLTR.js.map +0 -1
- package/dist/fr-J4637T6V.js.map +0 -1
package/dist/index.cjs
CHANGED
|
@@ -48,6 +48,7 @@ var init_es = __esm({
|
|
|
48
48
|
// renderer (drawn on the Konva map — shared by the embed SDK)
|
|
49
49
|
"map.aria": "Mapa de asientos. Usa las flechas para moverte entre asientos, Intro para seleccionar.",
|
|
50
50
|
"map.seatsLeft": "{count} LIBRES",
|
|
51
|
+
"map.soldOut": "AGOTADO",
|
|
51
52
|
"map.statusHeld": "En espera",
|
|
52
53
|
"map.statusTaken": "Ocupado",
|
|
53
54
|
"map.fromPrice": "DESDE {price}",
|
|
@@ -55,7 +56,7 @@ var init_es = __esm({
|
|
|
55
56
|
"picker.floor": "Piso",
|
|
56
57
|
"picker.zoomLevel": "Nivel de zoom",
|
|
57
58
|
"picker.rungTip.zones": "Vista general \u2014 grupos de secciones como Grada norte o VIP",
|
|
58
|
-
"picker.rungTip.sections": "Bloques de secci\xF3n \u2014
|
|
59
|
+
"picker.rungTip.sections": "Bloques de secci\xF3n \u2014 las secciones agotadas y casi llenas se aten\xFAan para ver la disponibilidad de un vistazo",
|
|
59
60
|
"picker.rungTip.seats": "Asientos individuales \u2014 los bloques se convierten en puntos",
|
|
60
61
|
"picker.rungLabel.zones": "ZONAS",
|
|
61
62
|
"picker.rungLabel.sections": "SECCIONES",
|
|
@@ -107,6 +108,7 @@ var init_de = __esm({
|
|
|
107
108
|
// renderer (drawn on the Konva map — shared by the embed SDK)
|
|
108
109
|
"map.aria": "Sitzplan. Mit Pfeiltasten zwischen Pl\xE4tzen navigieren, Eingabe zum Ausw\xE4hlen.",
|
|
109
110
|
"map.seatsLeft": "{count} FREI",
|
|
111
|
+
"map.soldOut": "AUSVERKAUFT",
|
|
110
112
|
"map.statusHeld": "Reserviert",
|
|
111
113
|
"map.statusTaken": "Vergeben",
|
|
112
114
|
"map.fromPrice": "AB {price}",
|
|
@@ -114,7 +116,7 @@ var init_de = __esm({
|
|
|
114
116
|
"picker.floor": "Etage",
|
|
115
117
|
"picker.zoomLevel": "Zoomstufe",
|
|
116
118
|
"picker.rungTip.zones": "\xDCbersicht \u2014 Bereichsgruppen wie Nordtrib\xFCne oder VIP",
|
|
117
|
-
"picker.rungTip.sections": "Bereichs-Bl\xF6cke \u2014
|
|
119
|
+
"picker.rungTip.sections": "Bereichs-Bl\xF6cke \u2014 ausverkaufte und fast volle Bereiche werden abgedunkelt, um die Verf\xFCgbarkeit auf einen Blick zu zeigen",
|
|
118
120
|
"picker.rungTip.seats": "Einzelne Pl\xE4tze \u2014 Bl\xF6cke l\xF6sen sich zu Punkten auf",
|
|
119
121
|
"picker.rungLabel.zones": "ZONEN",
|
|
120
122
|
"picker.rungLabel.sections": "BEREICHE",
|
|
@@ -166,6 +168,7 @@ var init_fr = __esm({
|
|
|
166
168
|
// renderer (drawn on the Konva map — shared by the embed SDK)
|
|
167
169
|
"map.aria": "Plan de salle. Utilisez les fl\xE8ches pour naviguer entre les si\xE8ges, Entr\xE9e pour s\xE9lectionner.",
|
|
168
170
|
"map.seatsLeft": "{count} LIBRES",
|
|
171
|
+
"map.soldOut": "COMPLET",
|
|
169
172
|
"map.statusHeld": "En attente",
|
|
170
173
|
"map.statusTaken": "Pris",
|
|
171
174
|
"map.fromPrice": "D\xC8S {price}",
|
|
@@ -173,7 +176,7 @@ var init_fr = __esm({
|
|
|
173
176
|
"picker.floor": "\xC9tage",
|
|
174
177
|
"picker.zoomLevel": "Niveau de zoom",
|
|
175
178
|
"picker.rungTip.zones": "Vue g\xE9n\xE9rale \u2014 groupes de sections comme Tribune nord ou VIP",
|
|
176
|
-
"picker.rungTip.sections": "Blocs de sections \u2014
|
|
179
|
+
"picker.rungTip.sections": "Blocs de sections \u2014 les sections compl\xE8tes ou presque pleines sont estomp\xE9es pour voir la disponibilit\xE9 en un coup d\u2019\u0153il",
|
|
177
180
|
"picker.rungTip.seats": "Si\xE8ges individuels \u2014 les blocs se transforment en points",
|
|
178
181
|
"picker.rungLabel.zones": "ZONES",
|
|
179
182
|
"picker.rungLabel.sections": "SECTIONS",
|
|
@@ -1310,7 +1313,30 @@ function isBookableLabelLegibleAtScale(fontSize, effectiveScale) {
|
|
|
1310
1313
|
return fontSize * effectiveScale >= MIN_VISIBLE_BOOKABLE_LABEL_PX;
|
|
1311
1314
|
}
|
|
1312
1315
|
var ACCESS_GLYPH_VIEWBOX = 24;
|
|
1313
|
-
var
|
|
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
|
+
}
|
|
1314
1340
|
function bookableMarkerLabel(publicLabel) {
|
|
1315
1341
|
return /-(\d{1,5})$/.exec(publicLabel)?.[1] ?? publicLabel;
|
|
1316
1342
|
}
|
|
@@ -1823,6 +1849,187 @@ function buyerBackgroundImage(owner) {
|
|
|
1823
1849
|
return background?.url && !background.assetId ? background : void 0;
|
|
1824
1850
|
}
|
|
1825
1851
|
|
|
1852
|
+
// src/core/icons.ts
|
|
1853
|
+
var VENUE_ICON_VIEWBOX = 24;
|
|
1854
|
+
var VENUE_ICON_STROKE = 2;
|
|
1855
|
+
var WHEELCHAIR = "M13 4.6a1.6 1.6 0 1 0-3.2 0 1.6 1.6 0 0 0 3.2 0 M11 6.5V13h5l2.4 6 M15 16.4A5 5 0 1 1 9 11.3";
|
|
1856
|
+
var VENUE_ICONS = [
|
|
1857
|
+
// ---- Facilities --------------------------------------------------------
|
|
1858
|
+
{
|
|
1859
|
+
key: "restroom-men",
|
|
1860
|
+
label: "Men's restroom",
|
|
1861
|
+
group: "facilities",
|
|
1862
|
+
path: "M16 7a4 4 0 1 0-8 0 4 4 0 0 0 8 0 M20 21v-2a4 4 0 0 0-4-4H8a4 4 0 0 0-4 4v2"
|
|
1863
|
+
},
|
|
1864
|
+
{
|
|
1865
|
+
key: "restroom-women",
|
|
1866
|
+
label: "Women's restroom",
|
|
1867
|
+
group: "facilities",
|
|
1868
|
+
path: "M15 5a3 3 0 1 0-6 0 3 3 0 0 0 6 0 M12 8l-4 10h8l-4-10 M10 18v4 M14 18v4"
|
|
1869
|
+
},
|
|
1870
|
+
{
|
|
1871
|
+
key: "restroom-accessible",
|
|
1872
|
+
label: "Accessible restroom",
|
|
1873
|
+
group: "facilities",
|
|
1874
|
+
path: WHEELCHAIR
|
|
1875
|
+
},
|
|
1876
|
+
{
|
|
1877
|
+
key: "first-aid",
|
|
1878
|
+
label: "First aid",
|
|
1879
|
+
group: "facilities",
|
|
1880
|
+
path: "M5 4h14a1 1 0 0 1 1 1v14a1 1 0 0 1-1 1H5a1 1 0 0 1-1-1V5a1 1 0 0 1 1-1Z M12 8v8 M8 12h8"
|
|
1881
|
+
},
|
|
1882
|
+
{
|
|
1883
|
+
key: "coat-check",
|
|
1884
|
+
label: "Coat check",
|
|
1885
|
+
group: "facilities",
|
|
1886
|
+
path: "M12 6a2 2 0 0 1 0-4 2 2 0 0 1 1.6 3.2L21 13H3l8.4-7.8 M3 13h18"
|
|
1887
|
+
},
|
|
1888
|
+
{
|
|
1889
|
+
key: "atm",
|
|
1890
|
+
label: "ATM / cash",
|
|
1891
|
+
group: "facilities",
|
|
1892
|
+
path: "M2 6h20v12H2V6Z M15 12a3 3 0 1 1-6 0 3 3 0 0 1 6 0 M6 9h.01 M18 15h.01"
|
|
1893
|
+
},
|
|
1894
|
+
{
|
|
1895
|
+
key: "info",
|
|
1896
|
+
label: "Info point",
|
|
1897
|
+
group: "facilities",
|
|
1898
|
+
path: "M12 3a9 9 0 1 0 0 18 9 9 0 0 0 0-18 M12 11v5 M12 8h.01"
|
|
1899
|
+
},
|
|
1900
|
+
{
|
|
1901
|
+
key: "lost-found",
|
|
1902
|
+
label: "Lost & found",
|
|
1903
|
+
group: "facilities",
|
|
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"
|
|
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
|
+
},
|
|
1912
|
+
{
|
|
1913
|
+
key: "charging",
|
|
1914
|
+
label: "Charging point",
|
|
1915
|
+
group: "facilities",
|
|
1916
|
+
path: "M3 8h13v8H3V8Z M16 10h3v4h-3 M9.6 9l-2 3.5h2.5l-1.5 3"
|
|
1917
|
+
},
|
|
1918
|
+
{
|
|
1919
|
+
key: "smoking",
|
|
1920
|
+
label: "Smoking area",
|
|
1921
|
+
group: "facilities",
|
|
1922
|
+
path: "M2 15h13v3H2v-3Z M18 16h2 M19 8c0 1 1 1.5 1 2.5S19 12 19 13"
|
|
1923
|
+
},
|
|
1924
|
+
{
|
|
1925
|
+
key: "no-smoking",
|
|
1926
|
+
label: "No smoking",
|
|
1927
|
+
group: "facilities",
|
|
1928
|
+
path: "M12 3a9 9 0 1 0 0 18 9 9 0 0 0 0-18 M5.6 5.6l12.8 12.8 M7 13h6v2H7v-2Z"
|
|
1929
|
+
},
|
|
1930
|
+
// ---- Food & drink ------------------------------------------------------
|
|
1931
|
+
{
|
|
1932
|
+
key: "food",
|
|
1933
|
+
label: "Food",
|
|
1934
|
+
group: "food-drink",
|
|
1935
|
+
path: "M7 3v6a2 2 0 0 0 4 0V3 M9 11v10 M17 3c-1.6 1-2.2 3-2.2 6 0 2 .9 3.2 2.2 3.6V21"
|
|
1936
|
+
},
|
|
1937
|
+
{
|
|
1938
|
+
key: "bar",
|
|
1939
|
+
label: "Bar",
|
|
1940
|
+
group: "food-drink",
|
|
1941
|
+
path: "M4 5h16l-8 8-8-8Z M12 13v6 M8 20h8"
|
|
1942
|
+
},
|
|
1943
|
+
{
|
|
1944
|
+
key: "coffee",
|
|
1945
|
+
label: "Caf\xE9 / coffee",
|
|
1946
|
+
group: "food-drink",
|
|
1947
|
+
path: "M4 8h13v4a5 5 0 0 1-5 5H9a5 5 0 0 1-5-5V8Z M17 9h2a2 2 0 0 1 0 4h-2 M8 2v2 M11 2v2 M4 21h14"
|
|
1948
|
+
},
|
|
1949
|
+
{
|
|
1950
|
+
key: "water",
|
|
1951
|
+
label: "Water / drinks",
|
|
1952
|
+
group: "food-drink",
|
|
1953
|
+
path: "M12 22a7 7 0 0 1-7-7c0-5 7-12 7-12s7 7 7 12a7 7 0 0 1-7 7Z"
|
|
1954
|
+
},
|
|
1955
|
+
{
|
|
1956
|
+
key: "merch",
|
|
1957
|
+
label: "Merch / shop",
|
|
1958
|
+
group: "food-drink",
|
|
1959
|
+
path: "M6 8h12l-1 12H7L6 8Z M9 8V6a3 3 0 0 1 6 0v2"
|
|
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
|
+
},
|
|
1973
|
+
// ---- Getting around ----------------------------------------------------
|
|
1974
|
+
{
|
|
1975
|
+
key: "entrance",
|
|
1976
|
+
label: "Entrance",
|
|
1977
|
+
group: "navigation",
|
|
1978
|
+
path: "M14 3h5a1 1 0 0 1 1 1v16a1 1 0 0 1-1 1h-5 M3 12h11 M10 8l4 4-4 4"
|
|
1979
|
+
},
|
|
1980
|
+
{
|
|
1981
|
+
key: "exit",
|
|
1982
|
+
label: "Exit",
|
|
1983
|
+
group: "navigation",
|
|
1984
|
+
path: "M10 3H5a1 1 0 0 0-1 1v16a1 1 0 0 0 1 1h5 M14 12h7 M17 8l4 4-4 4"
|
|
1985
|
+
},
|
|
1986
|
+
{
|
|
1987
|
+
key: "emergency-exit",
|
|
1988
|
+
label: "Emergency exit",
|
|
1989
|
+
group: "navigation",
|
|
1990
|
+
path: "M14 4.6a1.5 1.5 0 1 0-3 0 1.5 1.5 0 0 0 3 0 M12.5 8l-3.5 2 1.6 3.6-2 4.4 M12.5 10l3.5 1.5 M9 10.5l-3.5 1 M15 12h6 M18 9l3 3-3 3"
|
|
1991
|
+
},
|
|
1992
|
+
{
|
|
1993
|
+
key: "stairs",
|
|
1994
|
+
label: "Stairs",
|
|
1995
|
+
group: "navigation",
|
|
1996
|
+
path: "M3 20v-4h4v-4h4v-4h4V4h5"
|
|
1997
|
+
},
|
|
1998
|
+
{
|
|
1999
|
+
key: "elevator",
|
|
2000
|
+
label: "Elevator",
|
|
2001
|
+
group: "navigation",
|
|
2002
|
+
path: "M5 3h14a1 1 0 0 1 1 1v16a1 1 0 0 1-1 1H5a1 1 0 0 1-1-1V4a1 1 0 0 1 1-1Z M8 11l1.5-2 1.5 2 M8 14l1.5 2 1.5-2 M15 8v8"
|
|
2003
|
+
},
|
|
2004
|
+
{
|
|
2005
|
+
key: "parking",
|
|
2006
|
+
label: "Parking",
|
|
2007
|
+
group: "navigation",
|
|
2008
|
+
path: "M5 3h14a1 1 0 0 1 1 1v16a1 1 0 0 1-1 1H5a1 1 0 0 1-1-1V4a1 1 0 0 1 1-1Z M9 17V7h4a3 3 0 0 1 0 6H9"
|
|
2009
|
+
},
|
|
2010
|
+
// ---- Accessibility -----------------------------------------------------
|
|
2011
|
+
{
|
|
2012
|
+
key: "wheelchair",
|
|
2013
|
+
label: "Wheelchair access",
|
|
2014
|
+
group: "accessibility",
|
|
2015
|
+
path: WHEELCHAIR
|
|
2016
|
+
},
|
|
2017
|
+
{
|
|
2018
|
+
key: "hearing",
|
|
2019
|
+
label: "Hearing assistance",
|
|
2020
|
+
group: "accessibility",
|
|
2021
|
+
path: "M8 20a4 4 0 0 1-2-3c0-1 .5-2 .5-3a4.5 4.5 0 1 1 9 0c0 1.4-1 2-2 2.4-1.2.5-1.5 1-1.5 2.2 M17 6a5 5 0 0 1 1 6 M19.5 4a8 8 0 0 1 1.2 9"
|
|
2022
|
+
}
|
|
2023
|
+
];
|
|
2024
|
+
var BY_KEY = new Map(VENUE_ICONS.map((icon) => [icon.key, icon]));
|
|
2025
|
+
function venueIcon(key) {
|
|
2026
|
+
return key ? BY_KEY.get(key) : void 0;
|
|
2027
|
+
}
|
|
2028
|
+
function venueIconPath(key) {
|
|
2029
|
+
return venueIcon(key)?.path;
|
|
2030
|
+
}
|
|
2031
|
+
var VENUE_ICON_KEYS = VENUE_ICONS.map((icon) => icon.key);
|
|
2032
|
+
|
|
1826
2033
|
// src/core/spatialIndex.ts
|
|
1827
2034
|
var DEFAULT_SEAT_RADIUS = 9;
|
|
1828
2035
|
function halfExtents(shape) {
|
|
@@ -2160,6 +2367,7 @@ var en = {
|
|
|
2160
2367
|
// renderer (drawn on the Konva map — shared by the embed SDK)
|
|
2161
2368
|
"map.aria": "Seating map. Use arrow keys to move between seats, Enter to select.",
|
|
2162
2369
|
"map.seatsLeft": "{count} LEFT",
|
|
2370
|
+
"map.soldOut": "SOLD OUT",
|
|
2163
2371
|
"map.fromPrice": "FROM {price}",
|
|
2164
2372
|
"map.statusHeld": "On hold",
|
|
2165
2373
|
"map.statusTaken": "Taken",
|
|
@@ -2167,7 +2375,7 @@ var en = {
|
|
|
2167
2375
|
"picker.floor": "Floor",
|
|
2168
2376
|
"picker.zoomLevel": "Zoom level",
|
|
2169
2377
|
"picker.rungTip.zones": "Venue overview \u2014 groups of sections such as North Stand or VIP",
|
|
2170
|
-
"picker.rungTip.sections": "Section blocks \u2014
|
|
2378
|
+
"picker.rungTip.sections": "Section blocks \u2014 sold-out and nearly-gone sections shade to show availability at a glance",
|
|
2171
2379
|
"picker.rungTip.seats": "Individual seats \u2014 blocks melt into dots",
|
|
2172
2380
|
"picker.rungLabel.zones": "ZONES",
|
|
2173
2381
|
"picker.rungLabel.sections": "SECTIONS",
|
|
@@ -2368,6 +2576,22 @@ function overviewPalette(canvasBackground) {
|
|
|
2368
2576
|
focalStroke: DARK_OVERVIEW_FOCAL_STROKE
|
|
2369
2577
|
};
|
|
2370
2578
|
}
|
|
2579
|
+
var SECTION_NEARLY_GONE_RATIO = 0.1;
|
|
2580
|
+
function overviewStateFill(palette, state) {
|
|
2581
|
+
const base = palette.sectionFill;
|
|
2582
|
+
const soldOut = desaturate(darken(base, 0.2), 0.85);
|
|
2583
|
+
switch (state) {
|
|
2584
|
+
case "closed":
|
|
2585
|
+
return darken(base, 0.12);
|
|
2586
|
+
case "sold-out":
|
|
2587
|
+
return soldOut;
|
|
2588
|
+
case "nearly-gone":
|
|
2589
|
+
return lerpColor(base, soldOut, 0.4);
|
|
2590
|
+
case "normal":
|
|
2591
|
+
default:
|
|
2592
|
+
return base;
|
|
2593
|
+
}
|
|
2594
|
+
}
|
|
2371
2595
|
var clamp = (v, lo, hi) => Math.max(lo, Math.min(hi, v));
|
|
2372
2596
|
function seatIdOf(target) {
|
|
2373
2597
|
const n = target;
|
|
@@ -2390,6 +2614,13 @@ function darken(hex, amt) {
|
|
|
2390
2614
|
const mix = (c) => Math.round(c * (1 - amt));
|
|
2391
2615
|
return `#${(1 << 24 | mix(n >> 16 & 255) << 16 | mix(n >> 8 & 255) << 8 | mix(n & 255)).toString(16).slice(1)}`;
|
|
2392
2616
|
}
|
|
2617
|
+
function desaturate(hex, amt) {
|
|
2618
|
+
const rgb2 = hexToRgb(hex);
|
|
2619
|
+
if (!rgb2) return hex;
|
|
2620
|
+
const [r, g, b] = rgb2;
|
|
2621
|
+
const grey = 0.299 * r + 0.587 * g + 0.114 * b;
|
|
2622
|
+
return toHex(r + (grey - r) * amt, g + (grey - g) * amt, b + (grey - b) * amt);
|
|
2623
|
+
}
|
|
2393
2624
|
function polyBounds(pts) {
|
|
2394
2625
|
let minX = Infinity;
|
|
2395
2626
|
let minY = Infinity;
|
|
@@ -2453,18 +2684,36 @@ function polygonLabelCandidates(outer, holes, preferred) {
|
|
|
2453
2684
|
}
|
|
2454
2685
|
return points.sort((left, right) => Math.hypot(left.x - centre.x, left.y - centre.y) - Math.hypot(right.x - centre.x, right.y - centre.y)).filter((point, index, all) => index === all.findIndex((other) => Math.abs(other.x - point.x) < 1e-6 && Math.abs(other.y - point.y) < 1e-6));
|
|
2455
2686
|
}
|
|
2687
|
+
function roundedPolygonSubpath(context, points, radius) {
|
|
2688
|
+
const n = points.length;
|
|
2689
|
+
const start = { x: (points[n - 1].x + points[0].x) / 2, y: (points[n - 1].y + points[0].y) / 2 };
|
|
2690
|
+
context.moveTo(start.x, start.y);
|
|
2691
|
+
for (let i = 0; i < n; i += 1) {
|
|
2692
|
+
const curr = points[i];
|
|
2693
|
+
const next = points[(i + 1) % n];
|
|
2694
|
+
const prev = points[(i - 1 + n) % n];
|
|
2695
|
+
const rMax = Math.min(
|
|
2696
|
+
Math.hypot(curr.x - prev.x, curr.y - prev.y) / 2,
|
|
2697
|
+
Math.hypot(next.x - curr.x, next.y - curr.y) / 2
|
|
2698
|
+
);
|
|
2699
|
+
context.arcTo(curr.x, curr.y, next.x, next.y, Math.max(0, Math.min(radius, rMax)));
|
|
2700
|
+
}
|
|
2701
|
+
context.closePath();
|
|
2702
|
+
}
|
|
2456
2703
|
function polygonWithHolesShape(outer, holes, attrs, outerPath) {
|
|
2457
2704
|
const signedArea = (points) => points.reduce((sum, point, index) => {
|
|
2458
2705
|
const next = points[(index + 1) % points.length];
|
|
2459
2706
|
return sum + point.x * next.y - next.x * point.y;
|
|
2460
2707
|
}, 0);
|
|
2461
2708
|
const outerClockwise = signedArea(outer) > 0;
|
|
2709
|
+
const cornerRadius = attrs.cornerRadius && attrs.cornerRadius > 0 ? attrs.cornerRadius : 0;
|
|
2462
2710
|
return new import_Shape.Shape({
|
|
2463
2711
|
...attrs,
|
|
2464
2712
|
sceneFunc(context, shape) {
|
|
2465
2713
|
context.beginPath();
|
|
2466
2714
|
const polygonPath = (points) => {
|
|
2467
2715
|
if (!points.length) return;
|
|
2716
|
+
if (cornerRadius > 0 && points.length >= 3) return roundedPolygonSubpath(context, points, cornerRadius);
|
|
2468
2717
|
context.moveTo(points[0].x, points[0].y);
|
|
2469
2718
|
for (let index = 1; index < points.length; index += 1) context.lineTo(points[index].x, points[index].y);
|
|
2470
2719
|
context.closePath();
|
|
@@ -2599,6 +2848,8 @@ var _SeatmapRenderer = class _SeatmapRenderer {
|
|
|
2599
2848
|
this.accessGlyphById = /* @__PURE__ */ new Map();
|
|
2600
2849
|
/** Authored free-text nodes obey the same rendered-size visibility floor. */
|
|
2601
2850
|
this.freeTextById = /* @__PURE__ */ new Map();
|
|
2851
|
+
/** Vector venue-icon Path nodes + authored size; obey the free-text size floor. */
|
|
2852
|
+
this.iconNodeById = /* @__PURE__ */ new Map();
|
|
2602
2853
|
/** Stage/rink landmarks retain a readable screen-space caption at overview. */
|
|
2603
2854
|
this.primaryFocalLabels = /* @__PURE__ */ new Map();
|
|
2604
2855
|
/** GA paint and text share price/highlight filter state. */
|
|
@@ -2612,6 +2863,12 @@ var _SeatmapRenderer = class _SeatmapRenderer {
|
|
|
2612
2863
|
this.effSelection = DEF_SELECTION;
|
|
2613
2864
|
/** Colorblind-safe mode (Okabe-Ito hues + hollow booked seats). */
|
|
2614
2865
|
this.colorblind = false;
|
|
2866
|
+
/** Stable per-category-KEY colorblind hue (OV-46) — order-independent. */
|
|
2867
|
+
this.cbHueByKey = /* @__PURE__ */ new Map();
|
|
2868
|
+
/** OV-45(a): authored row-letter labels for the buyer/preview map, resolved in
|
|
2869
|
+
* world space (matching seat labels) and honouring each row's labelPresentation
|
|
2870
|
+
* (position/rotation/visibility/style). Painted at the seats LOD rung. */
|
|
2871
|
+
this.rowLabelPlan = [];
|
|
2615
2872
|
/** Category order from the doc — the stable index into the CB palette. */
|
|
2616
2873
|
this.catOrder = [];
|
|
2617
2874
|
this.selection = /* @__PURE__ */ new Set();
|
|
@@ -2981,6 +3238,7 @@ var _SeatmapRenderer = class _SeatmapRenderer {
|
|
|
2981
3238
|
this.accessRingById.clear();
|
|
2982
3239
|
this.accessGlyphById.clear();
|
|
2983
3240
|
this.freeTextById.clear();
|
|
3241
|
+
this.iconNodeById.clear();
|
|
2984
3242
|
this.primaryFocalLabels.clear();
|
|
2985
3243
|
this.gaById.clear();
|
|
2986
3244
|
for (const marker of this.selectionMarkers.values()) marker.destroy();
|
|
@@ -3035,13 +3293,22 @@ var _SeatmapRenderer = class _SeatmapRenderer {
|
|
|
3035
3293
|
this.catColor.clear();
|
|
3036
3294
|
this.catPrice.clear();
|
|
3037
3295
|
this.catOrder = doc.categories.map((c) => c.key);
|
|
3296
|
+
this.cbHueByKey.clear();
|
|
3297
|
+
[...this.catOrder].sort().forEach((key, i) => {
|
|
3298
|
+
this.cbHueByKey.set(key, CB_PALETTE[i % CB_PALETTE.length]);
|
|
3299
|
+
});
|
|
3038
3300
|
for (const c of doc.categories) {
|
|
3039
3301
|
this.catColor.set(c.key, c.color);
|
|
3040
3302
|
if (typeof c.price === "number") this.catPrice.set(c.key, c.price);
|
|
3041
3303
|
}
|
|
3042
3304
|
for (const obj of view.objects) {
|
|
3043
3305
|
if (obj.type === "booth") {
|
|
3044
|
-
this.boothDims.set(obj.id, {
|
|
3306
|
+
this.boothDims.set(obj.id, {
|
|
3307
|
+
width: obj.width,
|
|
3308
|
+
height: obj.height,
|
|
3309
|
+
rotation: obj.rotation,
|
|
3310
|
+
...obj.points && obj.points.length >= 3 ? { points: obj.points.map((p) => ({ x: p.x, y: p.y })) } : {}
|
|
3311
|
+
});
|
|
3045
3312
|
}
|
|
3046
3313
|
}
|
|
3047
3314
|
this.seats = expandChart(view, {
|
|
@@ -3061,6 +3328,7 @@ var _SeatmapRenderer = class _SeatmapRenderer {
|
|
|
3061
3328
|
this.unsectionedSeatGroup = new import_Group.Group({ listening: true });
|
|
3062
3329
|
this.seatLayer.add(this.unsectionedSeatGroup);
|
|
3063
3330
|
this.renderSeats();
|
|
3331
|
+
this.buildRowLabelPlan(view);
|
|
3064
3332
|
this.overlayLayer.add(this.labelGroup);
|
|
3065
3333
|
this.overlayLayer.add(this.fgDecorGroup);
|
|
3066
3334
|
this.overlayLayer.add(this.hoverRing);
|
|
@@ -4360,7 +4628,7 @@ var _SeatmapRenderer = class _SeatmapRenderer {
|
|
|
4360
4628
|
this.accessRingById.set(seat.id, ring);
|
|
4361
4629
|
target.add(ring);
|
|
4362
4630
|
}
|
|
4363
|
-
if (seat.accessibility
|
|
4631
|
+
if (accessGlyphPath(seat.accessibility ?? [])) {
|
|
4364
4632
|
const glyph = this.buildAccessGlyph(seat, typeof c.fill() === "string" ? c.fill() : "");
|
|
4365
4633
|
this.accessGlyphById.set(seat.id, glyph);
|
|
4366
4634
|
target.add(glyph);
|
|
@@ -4372,25 +4640,42 @@ var _SeatmapRenderer = class _SeatmapRenderer {
|
|
|
4372
4640
|
renderBoothUnit(seat) {
|
|
4373
4641
|
const target = this.seatContainer(seat.id);
|
|
4374
4642
|
const dims = this.boothDims.get(seat.rowId) ?? { width: 40, height: 30, rotation: 0 };
|
|
4375
|
-
|
|
4376
|
-
|
|
4377
|
-
|
|
4378
|
-
|
|
4379
|
-
|
|
4380
|
-
|
|
4381
|
-
|
|
4382
|
-
|
|
4383
|
-
|
|
4384
|
-
|
|
4385
|
-
|
|
4386
|
-
|
|
4387
|
-
|
|
4388
|
-
|
|
4389
|
-
|
|
4390
|
-
|
|
4643
|
+
let labelX = seat.x;
|
|
4644
|
+
let labelY = seat.y;
|
|
4645
|
+
let node;
|
|
4646
|
+
if (dims.points && dims.points.length >= 3) {
|
|
4647
|
+
node = new import_Line.Line({
|
|
4648
|
+
x: seat.x,
|
|
4649
|
+
y: seat.y,
|
|
4650
|
+
points: dims.points.flatMap((p) => [p.x, p.y]),
|
|
4651
|
+
closed: true,
|
|
4652
|
+
perfectDrawEnabled: false,
|
|
4653
|
+
shadowForStrokeEnabled: false,
|
|
4654
|
+
hitStrokeWidth: 0
|
|
4655
|
+
});
|
|
4656
|
+
labelX = seat.x + dims.points.reduce((a, p) => a + p.x, 0) / dims.points.length;
|
|
4657
|
+
labelY = seat.y + dims.points.reduce((a, p) => a + p.y, 0) / dims.points.length;
|
|
4658
|
+
} else {
|
|
4659
|
+
node = new import_Rect.Rect({
|
|
4660
|
+
x: seat.x,
|
|
4661
|
+
y: seat.y,
|
|
4662
|
+
width: dims.width,
|
|
4663
|
+
height: dims.height,
|
|
4664
|
+
offsetX: dims.width / 2,
|
|
4665
|
+
offsetY: dims.height / 2,
|
|
4666
|
+
rotation: dims.rotation,
|
|
4667
|
+
cornerRadius: 4,
|
|
4668
|
+
perfectDrawEnabled: false,
|
|
4669
|
+
shadowForStrokeEnabled: false,
|
|
4670
|
+
hitStrokeWidth: 0
|
|
4671
|
+
});
|
|
4672
|
+
}
|
|
4673
|
+
node.setAttr("seatId", seat.id);
|
|
4674
|
+
this.circleById.set(seat.id, node);
|
|
4675
|
+
target.add(node);
|
|
4391
4676
|
const t2 = new import_Text.Text({
|
|
4392
|
-
x:
|
|
4393
|
-
y:
|
|
4677
|
+
x: labelX,
|
|
4678
|
+
y: labelY,
|
|
4394
4679
|
text: seat.displayLabel ?? seat.label,
|
|
4395
4680
|
fontSize: BOOTH_LABEL_FONT_SIZE,
|
|
4396
4681
|
fontStyle: "600",
|
|
@@ -4405,7 +4690,7 @@ var _SeatmapRenderer = class _SeatmapRenderer {
|
|
|
4405
4690
|
this.hasBoothText = true;
|
|
4406
4691
|
this.boothLabelById.set(seat.id, t2);
|
|
4407
4692
|
target.add(t2);
|
|
4408
|
-
this.paintSeat(
|
|
4693
|
+
this.paintSeat(node, seat.id);
|
|
4409
4694
|
}
|
|
4410
4695
|
/**
|
|
4411
4696
|
* Build the centred accessibility glyph for a seat. Sized relative to the seat
|
|
@@ -4417,7 +4702,9 @@ var _SeatmapRenderer = class _SeatmapRenderer {
|
|
|
4417
4702
|
return new import_Path.Path({
|
|
4418
4703
|
x: seat.x,
|
|
4419
4704
|
y: seat.y,
|
|
4420
|
-
|
|
4705
|
+
// Non-null: buildAccessGlyph is only called for seats the gate confirmed
|
|
4706
|
+
// resolve to a primary-accommodation glyph.
|
|
4707
|
+
data: accessGlyphPath(seat.accessibility ?? []) ?? "",
|
|
4421
4708
|
offsetX: ACCESS_GLYPH_VIEWBOX / 2,
|
|
4422
4709
|
offsetY: ACCESS_GLYPH_VIEWBOX / 2,
|
|
4423
4710
|
scaleX: k,
|
|
@@ -4473,8 +4760,7 @@ var _SeatmapRenderer = class _SeatmapRenderer {
|
|
|
4473
4760
|
/** The category's display color — Okabe-Ito hue when colorblind-safe is on. */
|
|
4474
4761
|
seatBaseColor(categoryKey) {
|
|
4475
4762
|
if (!this.colorblind) return this.catColor.get(categoryKey) ?? "#6e7bff";
|
|
4476
|
-
|
|
4477
|
-
return CB_PALETTE[(idx >= 0 ? idx : 0) % CB_PALETTE.length];
|
|
4763
|
+
return this.cbHueByKey.get(categoryKey) ?? CB_PALETTE[0];
|
|
4478
4764
|
}
|
|
4479
4765
|
/** Per-seat authored label size relative to the neutral default (1 = default),
|
|
4480
4766
|
* mirroring the section convention (labelStyle.size ?? 18) / 18. Only real
|
|
@@ -4945,6 +5231,28 @@ var _SeatmapRenderer = class _SeatmapRenderer {
|
|
|
4945
5231
|
renderText(obj) {
|
|
4946
5232
|
const background = this.canvasBackground;
|
|
4947
5233
|
const preferredInk = obj.color ?? this.theme.textColor ?? DEF_TEXT;
|
|
5234
|
+
const iconPath = obj.semanticKind === "icon" ? venueIconPath(obj.iconKey) : void 0;
|
|
5235
|
+
if (iconPath) {
|
|
5236
|
+
const scale = obj.fontSize / VENUE_ICON_VIEWBOX;
|
|
5237
|
+
const iconNode = new import_Path.Path({
|
|
5238
|
+
x: obj.position.x,
|
|
5239
|
+
y: obj.position.y,
|
|
5240
|
+
data: iconPath,
|
|
5241
|
+
rotation: obj.rotation,
|
|
5242
|
+
scaleX: scale,
|
|
5243
|
+
scaleY: scale,
|
|
5244
|
+
stroke: stateAwareBookableLabelInk(background, preferredInk),
|
|
5245
|
+
strokeWidth: VENUE_ICON_STROKE,
|
|
5246
|
+
lineCap: "round",
|
|
5247
|
+
lineJoin: "round",
|
|
5248
|
+
fillEnabled: false,
|
|
5249
|
+
listening: false,
|
|
5250
|
+
perfectDrawEnabled: false
|
|
5251
|
+
});
|
|
5252
|
+
this.iconNodeById.set(obj.id, { node: iconNode, fontSize: obj.fontSize });
|
|
5253
|
+
this.bgLayer.add(iconNode);
|
|
5254
|
+
return;
|
|
5255
|
+
}
|
|
4948
5256
|
const node = new import_Text.Text({
|
|
4949
5257
|
x: obj.position.x,
|
|
4950
5258
|
y: obj.position.y,
|
|
@@ -5112,7 +5420,8 @@ var _SeatmapRenderer = class _SeatmapRenderer {
|
|
|
5112
5420
|
fill: color,
|
|
5113
5421
|
opacity: GA_FILL_OPACITY,
|
|
5114
5422
|
stroke: color,
|
|
5115
|
-
strokeWidth: 1.5
|
|
5423
|
+
strokeWidth: 1.5,
|
|
5424
|
+
cornerRadius: obj.cornerRadius
|
|
5116
5425
|
});
|
|
5117
5426
|
poly.setAttr("gaId", obj.id);
|
|
5118
5427
|
poly.on("click tap", (e) => {
|
|
@@ -5324,20 +5633,33 @@ var _SeatmapRenderer = class _SeatmapRenderer {
|
|
|
5324
5633
|
sec.outlinePoly.shadowBlur(4 + raw * 12);
|
|
5325
5634
|
sec.outlinePoly.shadowOpacity(0.25 + raw * 0.45);
|
|
5326
5635
|
}
|
|
5327
|
-
/** Recompute a section's
|
|
5636
|
+
/** Recompute a section's availability shading + retained availability text. */
|
|
5328
5637
|
refreshSectionFill(sec) {
|
|
5329
5638
|
sec.blockPoly.fill(this.sectionBlockFill(sec));
|
|
5330
|
-
sec.subLabel.text(
|
|
5639
|
+
sec.subLabel.text(
|
|
5640
|
+
this.sectionAvailabilityState(sec) === "sold-out" ? t("map.soldOut") : t("map.seatsLeft", { count: sec.free })
|
|
5641
|
+
);
|
|
5331
5642
|
sec.subLabel.offsetX(sec.subLabel.width() / 2);
|
|
5332
5643
|
}
|
|
5333
5644
|
/** True when a section/zone is currently in the `closed` event-state. */
|
|
5334
5645
|
isSectionClosed(sec) {
|
|
5335
5646
|
return this.closedSections.has(sec.id) || this.closedSections.has(sec.logicalId) || sec.zone != null && this.closedSections.has(sec.zone);
|
|
5336
5647
|
}
|
|
5337
|
-
/**
|
|
5648
|
+
/** Resolve the four-bucket overview availability state for a section (OV-69).
|
|
5649
|
+
* `closed` (operator) outranks live availability; the availability buckets
|
|
5650
|
+
* only apply to seated sections (GA-only shells have no seat capacity here). */
|
|
5651
|
+
sectionAvailabilityState(sec) {
|
|
5652
|
+
if (this.isSectionClosed(sec)) return "closed";
|
|
5653
|
+
if (sec.total <= 0) return "normal";
|
|
5654
|
+
if (sec.free <= 0) return "sold-out";
|
|
5655
|
+
if (sec.free <= sec.total * SECTION_NEARLY_GONE_RATIO) return "nearly-gone";
|
|
5656
|
+
return "normal";
|
|
5657
|
+
}
|
|
5658
|
+
/** Overview shells stay neutral except for the bounded availability shading
|
|
5659
|
+
* (nearly-gone / sold-out) and the operator `closed` slab — never category
|
|
5660
|
+
* or price paint, which is deferred to section focus/seat detail. */
|
|
5338
5661
|
sectionBlockFill(sec) {
|
|
5339
|
-
|
|
5340
|
-
return this.isSectionClosed(sec) ? darken(fill, 0.12) : fill;
|
|
5662
|
+
return overviewStateFill(overviewPalette(this.canvasBackground), this.sectionAvailabilityState(sec));
|
|
5341
5663
|
}
|
|
5342
5664
|
/**
|
|
5343
5665
|
* Zone rung: one giant screen-constant label per zone (+ optional "FROM $n"),
|
|
@@ -6343,10 +6665,9 @@ var _SeatmapRenderer = class _SeatmapRenderer {
|
|
|
6343
6665
|
};
|
|
6344
6666
|
});
|
|
6345
6667
|
const palette = overviewPalette(this.canvasBackground);
|
|
6346
|
-
const neutralSectionFills =
|
|
6347
|
-
palette.
|
|
6348
|
-
|
|
6349
|
-
]);
|
|
6668
|
+
const neutralSectionFills = new Set(
|
|
6669
|
+
["normal", "nearly-gone", "sold-out", "closed"].map((state) => overviewStateFill(palette, state).toLowerCase())
|
|
6670
|
+
);
|
|
6350
6671
|
const visibleSectionShells = this.sections.filter((section) => section.blockPoly.opacity() > 0.05);
|
|
6351
6672
|
return {
|
|
6352
6673
|
viewport,
|
|
@@ -6516,7 +6837,7 @@ var _SeatmapRenderer = class _SeatmapRenderer {
|
|
|
6516
6837
|
if (shouldCache && !this.cached) {
|
|
6517
6838
|
this.cacheSeatLayer();
|
|
6518
6839
|
} else if (!shouldCache && (this.cached || !this.seatLayer.listening())) {
|
|
6519
|
-
if (this.cached) this.
|
|
6840
|
+
if (this.cached) this.releaseSeatLayerBitmap();
|
|
6520
6841
|
this.seatLayer.listening(true);
|
|
6521
6842
|
this.cached = false;
|
|
6522
6843
|
this.seatLayer.batchDraw();
|
|
@@ -6536,6 +6857,28 @@ var _SeatmapRenderer = class _SeatmapRenderer {
|
|
|
6536
6857
|
this.rebuildSeatCache();
|
|
6537
6858
|
this.seatLayer.batchDraw();
|
|
6538
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
|
+
}
|
|
6539
6882
|
/**
|
|
6540
6883
|
* SYNCHRONOUS repaint that bypasses requestAnimationFrame — see the
|
|
6541
6884
|
* ISeatmapRenderer.forceDraw() contract. Chrome pauses rAF (and therefore
|
|
@@ -6554,7 +6897,7 @@ var _SeatmapRenderer = class _SeatmapRenderer {
|
|
|
6554
6897
|
if (this.effScale() < CACHE_THRESHOLD) {
|
|
6555
6898
|
this.rebuildSeatCache();
|
|
6556
6899
|
} else if (this.cached) {
|
|
6557
|
-
this.
|
|
6900
|
+
this.releaseSeatLayerBitmap();
|
|
6558
6901
|
this.seatLayer.listening(true);
|
|
6559
6902
|
this.cached = false;
|
|
6560
6903
|
}
|
|
@@ -6570,6 +6913,63 @@ var _SeatmapRenderer = class _SeatmapRenderer {
|
|
|
6570
6913
|
const gaOverviewHidden = objectId != null && (kind === "ga-label" || kind === "ga-capacity") && this.gaById.get(objectId)?.sectionId != null && effectiveScale < CACHE_THRESHOLD;
|
|
6571
6914
|
node.visible(!gaDimmed && !gaOverviewHidden && isBookableLabelLegibleAtScale(node.fontSize(), effectiveScale));
|
|
6572
6915
|
}
|
|
6916
|
+
for (const { node, fontSize } of this.iconNodeById.values()) {
|
|
6917
|
+
node.visible(isBookableLabelLegibleAtScale(fontSize, effectiveScale));
|
|
6918
|
+
}
|
|
6919
|
+
}
|
|
6920
|
+
/** OV-45(a): resolve authored row-letter labels for the buyer/preview map, in
|
|
6921
|
+
* world space, matching the Designer's placement contract (start/end/both/none
|
|
6922
|
+
* preset, a free-drag `position` that wins over the preset, rotation, and
|
|
6923
|
+
* labelStyle colour/size fed through the same auto-contrast guard). Segmented
|
|
6924
|
+
* rows keep their own logical-label handling and are resolved by their owner. */
|
|
6925
|
+
buildRowLabelPlan(view) {
|
|
6926
|
+
this.rowLabelPlan = [];
|
|
6927
|
+
const background = this.theme.background ?? "#0e1117";
|
|
6928
|
+
const seatsByRow = /* @__PURE__ */ new Map();
|
|
6929
|
+
for (const seat of this.seats) {
|
|
6930
|
+
if (seat.kind === "booth") continue;
|
|
6931
|
+
const key = seat.logicalRowId ?? seat.rowId;
|
|
6932
|
+
(seatsByRow.get(key) ?? seatsByRow.set(key, []).get(key)).push(seat);
|
|
6933
|
+
}
|
|
6934
|
+
const seatOrder = (seat) => seat.logicalSeatIndex ?? Number(seat.id.split(":").pop() ?? 0);
|
|
6935
|
+
for (const obj of view.objects) {
|
|
6936
|
+
if (obj.type !== "row") continue;
|
|
6937
|
+
if (obj.segmentedRow && obj.segmentedRow.componentIndex !== 0) continue;
|
|
6938
|
+
const presentation = obj.segmentedRow?.labelPresentation ?? obj.labelPresentation;
|
|
6939
|
+
if (presentation?.visible === false || presentation?.positionPreset === "none") continue;
|
|
6940
|
+
const key = obj.segmentedRow ? obj.segmentedRow.groupId : obj.id;
|
|
6941
|
+
const seats = (seatsByRow.get(key) ?? []).slice().sort((a, b) => seatOrder(a) - seatOrder(b));
|
|
6942
|
+
if (!seats.length) continue;
|
|
6943
|
+
const first = seats[0];
|
|
6944
|
+
const last = seats[seats.length - 1];
|
|
6945
|
+
const labelStyle = presentation?.labelStyle;
|
|
6946
|
+
const ink = stateAwareBookableLabelInk(
|
|
6947
|
+
background,
|
|
6948
|
+
labelStyle?.color ?? this.theme.rowLabelColor ?? this.theme.textColor ?? "#e6e9f0"
|
|
6949
|
+
);
|
|
6950
|
+
const fontSize = labelStyle?.size ?? 12;
|
|
6951
|
+
const rotation = presentation?.rotation ?? 0;
|
|
6952
|
+
const text = obj.segmentedRow?.displayLabel ?? obj.displayLabel ?? obj.label;
|
|
6953
|
+
const opacity = this.objectFilteredOut(obj) ? 0.15 : 1;
|
|
6954
|
+
const push = (x, y) => this.rowLabelPlan.push({ text, x, y, rotation, fontSize, ink, opacity });
|
|
6955
|
+
if (presentation?.position) {
|
|
6956
|
+
push(presentation.position.x, presentation.position.y);
|
|
6957
|
+
continue;
|
|
6958
|
+
}
|
|
6959
|
+
const radians = obj.rotation * Math.PI / 180;
|
|
6960
|
+
const along = { x: Math.cos(radians), y: Math.sin(radians) };
|
|
6961
|
+
const gap = this.seatR + 12;
|
|
6962
|
+
const preset = presentation?.positionPreset ?? "start";
|
|
6963
|
+
if (preset === "start" || preset === "both") push(first.x - along.x * gap, first.y - along.y * gap);
|
|
6964
|
+
if (preset === "end" || preset === "both") push(last.x + along.x * gap, last.y + along.y * gap);
|
|
6965
|
+
}
|
|
6966
|
+
}
|
|
6967
|
+
/** Row labels never carry status; the buyer just dims them under the same
|
|
6968
|
+
* category/price filters that dim their seats. */
|
|
6969
|
+
objectFilteredOut(obj) {
|
|
6970
|
+
const key = obj.categoryKey;
|
|
6971
|
+
if (!key) return false;
|
|
6972
|
+
return Boolean(this.categoryHighlight && key !== this.categoryHighlight || this.categoryFilter && !this.categoryFilter.has(key));
|
|
6573
6973
|
}
|
|
6574
6974
|
updateLabels() {
|
|
6575
6975
|
const effectiveScale = this.effScale();
|
|
@@ -6663,6 +7063,30 @@ var _SeatmapRenderer = class _SeatmapRenderer {
|
|
|
6663
7063
|
this.seatLabelById.set(seat.id, t2);
|
|
6664
7064
|
if (++count >= MAX_LABELS) break;
|
|
6665
7065
|
}
|
|
7066
|
+
for (const rl of this.rowLabelPlan) {
|
|
7067
|
+
const screen = this.worldToScreen({ x: rl.x, y: rl.y });
|
|
7068
|
+
if (screen.x < -40 || screen.x > this.stage.width() + 40 || screen.y < -40 || screen.y > this.stage.height() + 40) continue;
|
|
7069
|
+
const t2 = new import_Text.Text({
|
|
7070
|
+
x: rl.x,
|
|
7071
|
+
y: rl.y,
|
|
7072
|
+
text: rl.text,
|
|
7073
|
+
fontSize: rl.fontSize,
|
|
7074
|
+
fontStyle: "700",
|
|
7075
|
+
fontFamily: this.labelFont(),
|
|
7076
|
+
fill: rl.ink,
|
|
7077
|
+
rotation: rl.rotation,
|
|
7078
|
+
opacity: rl.opacity,
|
|
7079
|
+
shadowColor: "#05070c",
|
|
7080
|
+
shadowBlur: rl.ink.toLowerCase() === "#ffffff" ? 5 : 0,
|
|
7081
|
+
shadowOpacity: rl.ink.toLowerCase() === "#ffffff" ? 0.8 : 0,
|
|
7082
|
+
shadowForStrokeEnabled: false,
|
|
7083
|
+
listening: false,
|
|
7084
|
+
perfectDrawEnabled: false
|
|
7085
|
+
});
|
|
7086
|
+
t2.offsetX(t2.width() / 2);
|
|
7087
|
+
t2.offsetY(t2.height() / 2);
|
|
7088
|
+
this.labelGroup.add(t2);
|
|
7089
|
+
}
|
|
6666
7090
|
if (this.isoT > 0 && this.viewMode !== "perspective") this.applyUprightLabels();
|
|
6667
7091
|
this.overlayLayer.batchDraw();
|
|
6668
7092
|
}
|
|
@@ -6994,6 +7418,36 @@ var PickerController = class {
|
|
|
6994
7418
|
const group = this.groupedTableBySeatId.get(seatIdOrLabel) ?? this.groupedTablesByLabel.get(seatIdOrLabel);
|
|
6995
7419
|
return group ? this.toTableSeat(group) : null;
|
|
6996
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
|
+
}
|
|
6997
7451
|
/** Fetch chart, build label maps, mount the renderer, seed statuses, go live. */
|
|
6998
7452
|
async render(host) {
|
|
6999
7453
|
if (this.renderer) return null;
|
|
@@ -7909,7 +8363,7 @@ var PickerController = class {
|
|
|
7909
8363
|
const color = sec.color ?? zone?.color ?? categories[0]?.color ?? "#6e7bff";
|
|
7910
8364
|
return {
|
|
7911
8365
|
id,
|
|
7912
|
-
label: sec.label,
|
|
8366
|
+
label: sec.displayLabel ?? sec.label,
|
|
7913
8367
|
zoneLabel: zone?.label ?? "",
|
|
7914
8368
|
...sec.entrance && sec.entrance.trim() ? { entrance: sec.entrance.trim() } : {},
|
|
7915
8369
|
color,
|