@seatlayer/core 0.1.2 → 0.1.3
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-Y3E6YTGP.js +70 -0
- package/dist/de-Y3E6YTGP.js.map +1 -0
- package/dist/es-UM4ZS357.js +70 -0
- package/dist/es-UM4ZS357.js.map +1 -0
- package/dist/fr-IGRD2M4W.js +70 -0
- package/dist/fr-IGRD2M4W.js.map +1 -0
- package/dist/index.cjs +1140 -91
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +432 -8
- package/dist/index.d.ts +432 -8
- package/dist/index.js +883 -90
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
package/dist/index.cjs
CHANGED
|
@@ -3,6 +3,9 @@ var __defProp = Object.defineProperty;
|
|
|
3
3
|
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
|
4
4
|
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
5
5
|
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
6
|
+
var __esm = (fn, res) => function __init() {
|
|
7
|
+
return fn && (res = (0, fn[__getOwnPropNames(fn)[0]])(fn = 0)), res;
|
|
8
|
+
};
|
|
6
9
|
var __export = (target, all) => {
|
|
7
10
|
for (var name in all)
|
|
8
11
|
__defProp(target, name, { get: all[name], enumerable: true });
|
|
@@ -17,24 +20,277 @@ var __copyProps = (to, from, except, desc) => {
|
|
|
17
20
|
};
|
|
18
21
|
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
|
|
19
22
|
|
|
23
|
+
// src/i18n/locales/es.ts
|
|
24
|
+
var es_exports = {};
|
|
25
|
+
__export(es_exports, {
|
|
26
|
+
es: () => es
|
|
27
|
+
});
|
|
28
|
+
var es;
|
|
29
|
+
var init_es = __esm({
|
|
30
|
+
"src/i18n/locales/es.ts"() {
|
|
31
|
+
"use strict";
|
|
32
|
+
es = {
|
|
33
|
+
// shared
|
|
34
|
+
"common.cancel": "Cancelar",
|
|
35
|
+
"common.close": "Cerrar",
|
|
36
|
+
"common.done": "Listo",
|
|
37
|
+
"common.copied": "\u2713 Copiado",
|
|
38
|
+
// buyer picker (extraction in progress — P4 of the port plan)
|
|
39
|
+
"picker.holdSeats": "Retener asientos y comprar",
|
|
40
|
+
"picker.completeBooking": "Completar reserva",
|
|
41
|
+
"picker.seatsHeld": "Asientos retenidos \u2014 {time}",
|
|
42
|
+
"picker.holdExpired": "Tu retenci\xF3n expir\xF3 \u2014 los asientos fueron liberados. Elige de nuevo.",
|
|
43
|
+
"picker.seatTaken": "El asiento {label} fue tomado por otro comprador.",
|
|
44
|
+
"picker.poweredBy": "Con la tecnolog\xEDa de SeatMap",
|
|
45
|
+
"picker.seats.one": "{count} asiento",
|
|
46
|
+
"picker.seats.other": "{count} asientos",
|
|
47
|
+
// renderer (drawn on the Konva map — shared by the embed SDK)
|
|
48
|
+
"map.aria": "Mapa de asientos. Usa las flechas para moverte entre asientos, Intro para seleccionar.",
|
|
49
|
+
"map.seatsLeft": "{count} LIBRES",
|
|
50
|
+
"map.fromPrice": "DESDE {price}",
|
|
51
|
+
// buyer picker page (src/pages/PickerPage.tsx)
|
|
52
|
+
"picker.language": "Idioma",
|
|
53
|
+
"picker.zoomToFit": "Ajustar zoom",
|
|
54
|
+
"picker.viewMode": "Modo de vista",
|
|
55
|
+
"picker.floor": "Piso",
|
|
56
|
+
"picker.zoomLevel": "Nivel de zoom",
|
|
57
|
+
"picker.rungTip.zones": "Nivel m\xE1s lejano \u2014 bloques de zona (Piso / Patio Bajo / Patio Alto)",
|
|
58
|
+
"picker.rungTip.sections": "Bloques de secci\xF3n \u2014 mezcla de categor\xEDas + matiz de disponibilidad",
|
|
59
|
+
"picker.rungTip.seats": "Asientos individuales \u2014 los bloques se convierten en puntos",
|
|
60
|
+
"picker.rungLabel.zones": "ZONAS",
|
|
61
|
+
"picker.rungLabel.sections": "SECCIONES",
|
|
62
|
+
"picker.rungLabel.seats": "ASIENTOS",
|
|
63
|
+
"picker.sectionSummaryAria": "Resumen de secci\xF3n {label}",
|
|
64
|
+
"picker.closeSectionSummary": "Cerrar resumen de secci\xF3n",
|
|
65
|
+
"picker.seatsLeftInSection.one": "{count} asiento disponible",
|
|
66
|
+
"picker.seatsLeftInSection.other": "{count} asientos disponibles",
|
|
67
|
+
"picker.overview": "Descripci\xF3n general",
|
|
68
|
+
"picker.tapSeatHint": "Toca cualquier asiento para ver su vista",
|
|
69
|
+
"picker.chartSize": "Tama\xF1o del gr\xE1fico",
|
|
70
|
+
"picker.custom": "Personalizado",
|
|
71
|
+
"picker.categories": "Categor\xEDas",
|
|
72
|
+
"picker.accessibility": "Accesibilidad",
|
|
73
|
+
"picker.showAnyAccessible": "Mostrar cualquier asiento accesible",
|
|
74
|
+
"picker.any": "Cualquiera",
|
|
75
|
+
"picker.yourSeats": "Tus asientos",
|
|
76
|
+
"picker.emptySeats": "Toca asientos en el mapa",
|
|
77
|
+
"picker.emptySeatsWithGa": "Toca asientos en el mapa \xB7 toca un \xE1rea de pie para entradas de entrada general",
|
|
78
|
+
"picker.oneFewer": "Uno menos",
|
|
79
|
+
"picker.oneMore": "Uno m\xE1s",
|
|
80
|
+
"picker.remove": "Eliminar {label}",
|
|
81
|
+
"picker.ticketTierFor": "Categor\xEDa de entrada para {label}",
|
|
82
|
+
"picker.total": "Total: {amount}",
|
|
83
|
+
"picker.viewFromSeat": "Vista desde el asiento {label}",
|
|
84
|
+
"picker.real360": "REAL 360\xB0",
|
|
85
|
+
"picker.preview": "VISTA PREVIA",
|
|
86
|
+
"picker.open360": "Abrir vista de 360\xB0",
|
|
87
|
+
"picker.sightline": "\u2248 {rows} filas del escenario \xB7 {angle}\xB0 fuera del centro",
|
|
88
|
+
"picker.bookedDemo": "\xA1Reservado! (demostraci\xF3n)",
|
|
89
|
+
"picker.bookButton.one": "Reservar {count} entrada \u2014 {amount}",
|
|
90
|
+
"picker.bookButton.other": "Reservar {count} entradas \u2014 {amount}",
|
|
91
|
+
"picker.simulateCrowd": "Simular multitud: {state}",
|
|
92
|
+
"picker.on": "ACTIVADO",
|
|
93
|
+
"picker.off": "DESACTIVADO",
|
|
94
|
+
"picker.panorama360": "Foto de 360\xB0 del lugar",
|
|
95
|
+
"picker.illustrationCaption": "ilustraci\xF3n \xB7 \u2248 {m} m del escenario"
|
|
96
|
+
};
|
|
97
|
+
}
|
|
98
|
+
});
|
|
99
|
+
|
|
100
|
+
// src/i18n/locales/de.ts
|
|
101
|
+
var de_exports = {};
|
|
102
|
+
__export(de_exports, {
|
|
103
|
+
de: () => de
|
|
104
|
+
});
|
|
105
|
+
var de;
|
|
106
|
+
var init_de = __esm({
|
|
107
|
+
"src/i18n/locales/de.ts"() {
|
|
108
|
+
"use strict";
|
|
109
|
+
de = {
|
|
110
|
+
// shared
|
|
111
|
+
"common.cancel": "Abbrechen",
|
|
112
|
+
"common.close": "Schlie\xDFen",
|
|
113
|
+
"common.done": "Fertig",
|
|
114
|
+
"common.copied": "\u2713 Kopiert",
|
|
115
|
+
// buyer picker (extraction in progress — P4 of the port plan)
|
|
116
|
+
"picker.holdSeats": "Pl\xE4tze reservieren & Kasse",
|
|
117
|
+
"picker.completeBooking": "Buchung abschlie\xDFen",
|
|
118
|
+
"picker.seatsHeld": "Pl\xE4tze reserviert \u2014 {time}",
|
|
119
|
+
"picker.holdExpired": "Ihre Reservierung ist abgelaufen \u2014 die Pl\xE4tze wurden freigegeben. Bitte erneut ausw\xE4hlen.",
|
|
120
|
+
"picker.seatTaken": "Platz {label} wurde gerade von einem anderen K\xE4ufer gebucht.",
|
|
121
|
+
"picker.poweredBy": "Bereitgestellt von SeatMap",
|
|
122
|
+
"picker.seats.one": "{count} Platz",
|
|
123
|
+
"picker.seats.other": "{count} Pl\xE4tze",
|
|
124
|
+
// renderer (drawn on the Konva map — shared by the embed SDK)
|
|
125
|
+
"map.aria": "Sitzplan. Mit Pfeiltasten zwischen Pl\xE4tzen navigieren, Eingabe zum Ausw\xE4hlen.",
|
|
126
|
+
"map.seatsLeft": "{count} FREI",
|
|
127
|
+
"map.fromPrice": "AB {price}",
|
|
128
|
+
// buyer picker page (src/pages/PickerPage.tsx)
|
|
129
|
+
"picker.language": "Sprache",
|
|
130
|
+
"picker.zoomToFit": "Auf Gr\xF6\xDFe anpassen",
|
|
131
|
+
"picker.viewMode": "Ansichtsmodus",
|
|
132
|
+
"picker.floor": "Etage",
|
|
133
|
+
"picker.zoomLevel": "Zoomstufe",
|
|
134
|
+
"picker.rungTip.zones": "Fernste Stufe \u2014 Zonen-Bl\xF6cke (Etage / Unteres Parterre / Oberes Parterre)",
|
|
135
|
+
"picker.rungTip.sections": "Bereichs-Bl\xF6cke \u2014 Kategoriesortierung + Verf\xFCgbarkeitst\xF6nung",
|
|
136
|
+
"picker.rungTip.seats": "Einzelne Pl\xE4tze \u2014 Bl\xF6cke l\xF6sen sich zu Punkten auf",
|
|
137
|
+
"picker.rungLabel.zones": "ZONEN",
|
|
138
|
+
"picker.rungLabel.sections": "BEREICHE",
|
|
139
|
+
"picker.rungLabel.seats": "PL\xC4TZE",
|
|
140
|
+
"picker.sectionSummaryAria": "Bereichszusammenfassung {label}",
|
|
141
|
+
"picker.closeSectionSummary": "Bereichszusammenfassung schlie\xDFen",
|
|
142
|
+
"picker.seatsLeftInSection.one": "{count} Platz verf\xFCgbar",
|
|
143
|
+
"picker.seatsLeftInSection.other": "{count} Pl\xE4tze verf\xFCgbar",
|
|
144
|
+
"picker.overview": "\xDCbersicht",
|
|
145
|
+
"picker.tapSeatHint": "Tippen Sie auf einen Platz, um die Ansicht zu pr\xFCfen",
|
|
146
|
+
"picker.chartSize": "Kartengr\xF6\xDFe",
|
|
147
|
+
"picker.custom": "Benutzerdefiniert",
|
|
148
|
+
"picker.categories": "Kategorien",
|
|
149
|
+
"picker.accessibility": "Barrierefreiheit",
|
|
150
|
+
"picker.showAnyAccessible": "Jeden barrierefreien Platz anzeigen",
|
|
151
|
+
"picker.any": "Beliebig",
|
|
152
|
+
"picker.yourSeats": "Ihre Pl\xE4tze",
|
|
153
|
+
"picker.emptySeats": "Tippen Sie auf die Pl\xE4tze auf der Karte",
|
|
154
|
+
"picker.emptySeatsWithGa": "Tippen Sie auf die Pl\xE4tze auf der Karte \xB7 tippen Sie auf einen Stehbereich f\xFCr Stehplatztickets",
|
|
155
|
+
"picker.oneFewer": "Einen weniger",
|
|
156
|
+
"picker.oneMore": "Einen mehr",
|
|
157
|
+
"picker.remove": "{label} entfernen",
|
|
158
|
+
"picker.ticketTierFor": "Ticketklasse f\xFCr {label}",
|
|
159
|
+
"picker.total": "Gesamt: {amount}",
|
|
160
|
+
"picker.viewFromSeat": "Ansicht von Platz {label}",
|
|
161
|
+
"picker.real360": "REAL 360\xB0",
|
|
162
|
+
"picker.preview": "VORSCHAU",
|
|
163
|
+
"picker.open360": "\xD6ffnen Sie die 360\xB0-Ansicht",
|
|
164
|
+
"picker.sightline": "\u2248 {rows} Reihen von der B\xFChne \xB7 {angle}\xB0 seitlich versetzt",
|
|
165
|
+
"picker.bookedDemo": "Gebucht! (Demo)",
|
|
166
|
+
"picker.bookButton.one": "{count} Ticket buchen \u2014 {amount}",
|
|
167
|
+
"picker.bookButton.other": "{count} Tickets buchen \u2014 {amount}",
|
|
168
|
+
"picker.simulateCrowd": "Menschenmenge simulieren: {state}",
|
|
169
|
+
"picker.on": "AN",
|
|
170
|
+
"picker.off": "AUS",
|
|
171
|
+
"picker.panorama360": "360\xB0-Veranstaltungsfotos",
|
|
172
|
+
"picker.illustrationCaption": "Illustration \xB7 \u2248 {m} m von der B\xFChne"
|
|
173
|
+
};
|
|
174
|
+
}
|
|
175
|
+
});
|
|
176
|
+
|
|
177
|
+
// src/i18n/locales/fr.ts
|
|
178
|
+
var fr_exports = {};
|
|
179
|
+
__export(fr_exports, {
|
|
180
|
+
fr: () => fr
|
|
181
|
+
});
|
|
182
|
+
var fr;
|
|
183
|
+
var init_fr = __esm({
|
|
184
|
+
"src/i18n/locales/fr.ts"() {
|
|
185
|
+
"use strict";
|
|
186
|
+
fr = {
|
|
187
|
+
// shared
|
|
188
|
+
"common.cancel": "Annuler",
|
|
189
|
+
"common.close": "Fermer",
|
|
190
|
+
"common.done": "Termin\xE9",
|
|
191
|
+
"common.copied": "\u2713 Copi\xE9",
|
|
192
|
+
// buyer picker (extraction in progress — P4 of the port plan)
|
|
193
|
+
"picker.holdSeats": "R\xE9server les si\xE8ges et valider",
|
|
194
|
+
"picker.completeBooking": "Terminer la r\xE9servation",
|
|
195
|
+
"picker.seatsHeld": "Si\xE8ges r\xE9serv\xE9s \u2014 {time}",
|
|
196
|
+
"picker.holdExpired": "Votre r\xE9servation a expir\xE9 \u2014 les si\xE8ges ont \xE9t\xE9 lib\xE9r\xE9s. S\xE9lectionnez \xE0 nouveau.",
|
|
197
|
+
"picker.seatTaken": "Le si\xE8ge {label} vient d'\xEAtre r\xE9serv\xE9 par un autre acheteur.",
|
|
198
|
+
"picker.poweredBy": "Propuls\xE9 par SeatMap",
|
|
199
|
+
"picker.seats.one": "{count} si\xE8ge",
|
|
200
|
+
"picker.seats.other": "{count} si\xE8ges",
|
|
201
|
+
// renderer (drawn on the Konva map — shared by the embed SDK)
|
|
202
|
+
"map.aria": "Plan de salle. Utilisez les fl\xE8ches pour naviguer entre les si\xE8ges, Entr\xE9e pour s\xE9lectionner.",
|
|
203
|
+
"map.seatsLeft": "{count} LIBRES",
|
|
204
|
+
"map.fromPrice": "D\xC8S {price}",
|
|
205
|
+
// buyer picker page (src/pages/PickerPage.tsx)
|
|
206
|
+
"picker.language": "Langue",
|
|
207
|
+
"picker.zoomToFit": "Ajuster le zoom",
|
|
208
|
+
"picker.viewMode": "Mode d'affichage",
|
|
209
|
+
"picker.floor": "\xC9tage",
|
|
210
|
+
"picker.zoomLevel": "Niveau de zoom",
|
|
211
|
+
"picker.rungTip.zones": "Vue la plus large \u2014 blocs de zones (\xC9tage / Parquet inf\xE9rieur / Parquet sup\xE9rieur)",
|
|
212
|
+
"picker.rungTip.sections": "Blocs de sections \u2014 m\xE9lange de cat\xE9gories + teinte de disponibilit\xE9",
|
|
213
|
+
"picker.rungTip.seats": "Si\xE8ges individuels \u2014 les blocs se transforment en points",
|
|
214
|
+
"picker.rungLabel.zones": "ZONES",
|
|
215
|
+
"picker.rungLabel.sections": "SECTIONS",
|
|
216
|
+
"picker.rungLabel.seats": "SI\xC8GES",
|
|
217
|
+
"picker.sectionSummaryAria": "R\xE9sum\xE9 de la section {label}",
|
|
218
|
+
"picker.closeSectionSummary": "Fermer le r\xE9sum\xE9 de la section",
|
|
219
|
+
"picker.seatsLeftInSection.one": "{count} si\xE8ge restant",
|
|
220
|
+
"picker.seatsLeftInSection.other": "{count} si\xE8ges restants",
|
|
221
|
+
"picker.overview": "Vue d'ensemble",
|
|
222
|
+
"picker.tapSeatHint": "Appuyez sur un si\xE8ge pour v\xE9rifier sa vue",
|
|
223
|
+
"picker.chartSize": "Taille du plan",
|
|
224
|
+
"picker.custom": "Personnalis\xE9",
|
|
225
|
+
"picker.categories": "Cat\xE9gories",
|
|
226
|
+
"picker.accessibility": "Accessibilit\xE9",
|
|
227
|
+
"picker.showAnyAccessible": "Afficher un si\xE8ge accessible",
|
|
228
|
+
"picker.any": "N'importe quel",
|
|
229
|
+
"picker.yourSeats": "Vos si\xE8ges",
|
|
230
|
+
"picker.emptySeats": "Appuyez sur les si\xE8ges sur le plan",
|
|
231
|
+
"picker.emptySeatsWithGa": "Appuyez sur les si\xE8ges sur le plan \xB7 appuyez sur une zone debout pour les billets GA",
|
|
232
|
+
"picker.oneFewer": "Un de moins",
|
|
233
|
+
"picker.oneMore": "Un de plus",
|
|
234
|
+
"picker.remove": "Supprimer {label}",
|
|
235
|
+
"picker.ticketTierFor": "Cat\xE9gorie de billet pour {label}",
|
|
236
|
+
"picker.total": "Total : {amount}",
|
|
237
|
+
"picker.viewFromSeat": "Vue depuis le si\xE8ge {label}",
|
|
238
|
+
"picker.real360": "VRAI 360\xB0",
|
|
239
|
+
"picker.preview": "APER\xC7U",
|
|
240
|
+
"picker.open360": "Ouvrir la vue \xE0 360\xB0",
|
|
241
|
+
"picker.sightline": "\u2248 {rows} rang\xE9es de la sc\xE8ne \xB7 {angle}\xB0 d\xE9cal\xE9 du centre",
|
|
242
|
+
"picker.bookedDemo": "R\xE9serv\xE9 ! (d\xE9mo)",
|
|
243
|
+
"picker.bookButton.one": "R\xE9server {count} billet \u2014 {amount}",
|
|
244
|
+
"picker.bookButton.other": "R\xE9server {count} billets \u2014 {amount}",
|
|
245
|
+
"picker.simulateCrowd": "Simuler la foule : {state}",
|
|
246
|
+
"picker.on": "ACTIV\xC9",
|
|
247
|
+
"picker.off": "D\xC9SACTIV\xC9",
|
|
248
|
+
"picker.panorama360": "Photo panoramique 360\xB0 du lieu",
|
|
249
|
+
"picker.illustrationCaption": "illustration \xB7 \u2248 {m} m de la sc\xE8ne"
|
|
250
|
+
};
|
|
251
|
+
}
|
|
252
|
+
});
|
|
253
|
+
|
|
20
254
|
// src/index.ts
|
|
21
255
|
var index_exports = {};
|
|
22
256
|
__export(index_exports, {
|
|
23
257
|
ACCESSIBILITY_TYPES: () => ACCESSIBILITY_TYPES,
|
|
24
258
|
CHART_STORAGE_KEY: () => CHART_STORAGE_KEY,
|
|
259
|
+
DEFAULT_CURRENCY: () => DEFAULT_CURRENCY,
|
|
25
260
|
PickerController: () => PickerController,
|
|
261
|
+
SUPPORTED_LOCALES: () => SUPPORTED_LOCALES,
|
|
26
262
|
SeatmapRenderer: () => SeatmapRenderer,
|
|
263
|
+
UNGROUPED_ID: () => UNGROUPED_ID,
|
|
27
264
|
accessibilityMeta: () => accessibilityMeta,
|
|
265
|
+
allObjects: () => allObjects,
|
|
266
|
+
applyHidden: () => applyHidden,
|
|
28
267
|
chartBounds: () => chartBounds,
|
|
268
|
+
computeSections: () => computeSections,
|
|
29
269
|
createRenderer: () => createRenderer,
|
|
270
|
+
currencySymbol: () => currencySymbol,
|
|
30
271
|
expandBooth: () => expandBooth,
|
|
31
272
|
expandChart: () => expandChart,
|
|
32
273
|
expandRow: () => expandRow,
|
|
33
274
|
expandRowSlots: () => expandRowSlots,
|
|
34
275
|
expandTable: () => expandTable,
|
|
276
|
+
floorObjects: () => floorObjects,
|
|
277
|
+
floorsOf: () => floorsOf,
|
|
278
|
+
formatDate: () => formatDate,
|
|
279
|
+
formatMoney: () => formatMoney,
|
|
280
|
+
getLocale: () => getLocale,
|
|
281
|
+
hiddenObjectIds: () => hiddenObjectIds,
|
|
282
|
+
isSectionHidden: () => isSectionHidden,
|
|
35
283
|
layerOf: () => layerOf,
|
|
284
|
+
loadLocale: () => loadLocale,
|
|
36
285
|
objectCenter: () => objectCenter,
|
|
37
|
-
pointInPolygon: () => pointInPolygon
|
|
286
|
+
pointInPolygon: () => pointInPolygon,
|
|
287
|
+
resolveLocale: () => resolveLocale,
|
|
288
|
+
setLocale: () => setLocale,
|
|
289
|
+
setMoneyLocale: () => setMoneyLocale,
|
|
290
|
+
setStringOverrides: () => setStringOverrides,
|
|
291
|
+
stackFloors: () => stackFloors,
|
|
292
|
+
t: () => t,
|
|
293
|
+
tCount: () => tCount
|
|
38
294
|
});
|
|
39
295
|
module.exports = __toCommonJS(index_exports);
|
|
40
296
|
|
|
@@ -118,7 +374,15 @@ function expandRowSlots(row) {
|
|
|
118
374
|
const start = row.seatLabelStart ?? 1;
|
|
119
375
|
const dir = row.seatNumbering?.direction ?? "ltr";
|
|
120
376
|
const step = row.seatNumbering?.step ?? 1;
|
|
121
|
-
const
|
|
377
|
+
const n = row.seatCount;
|
|
378
|
+
let seatNumber;
|
|
379
|
+
if (dir === "center") {
|
|
380
|
+
const rank = new Array(n);
|
|
381
|
+
Array.from({ length: n }, (_, i) => i).sort((a, b) => Math.abs(2 * a - (n - 1)) - Math.abs(2 * b - (n - 1)) || a - b).forEach((idx, k) => rank[idx] = k);
|
|
382
|
+
seatNumber = (i) => start + rank[i] * step;
|
|
383
|
+
} else {
|
|
384
|
+
seatNumber = (i) => start + (dir === "rtl" ? n - 1 - i : i) * step;
|
|
385
|
+
}
|
|
122
386
|
const ov = overrideMap(row);
|
|
123
387
|
return rowSeatPositions(row).map((p, i) => {
|
|
124
388
|
const o = ov.get(i);
|
|
@@ -153,30 +417,41 @@ function expandRow(row) {
|
|
|
153
417
|
}
|
|
154
418
|
return seats;
|
|
155
419
|
}
|
|
156
|
-
function expandTable(
|
|
420
|
+
function expandTable(t2) {
|
|
157
421
|
const seats = [];
|
|
158
|
-
const n = Math.max(0, Math.round(
|
|
422
|
+
const n = Math.max(0, Math.round(t2.seatCount));
|
|
159
423
|
if (n === 0) return seats;
|
|
160
424
|
const mk = (i, x, y) => ({
|
|
161
|
-
id: `${
|
|
162
|
-
label: `${
|
|
425
|
+
id: `${t2.id}:${i}`,
|
|
426
|
+
label: `${t2.label}-${i + 1}`,
|
|
163
427
|
x,
|
|
164
428
|
y,
|
|
165
|
-
rowId:
|
|
166
|
-
categoryKey:
|
|
429
|
+
rowId: t2.id,
|
|
430
|
+
categoryKey: t2.categoryKey
|
|
167
431
|
});
|
|
168
|
-
if (
|
|
169
|
-
const R = (
|
|
170
|
-
const base =
|
|
432
|
+
if (t2.shape === "round") {
|
|
433
|
+
const R = (t2.radius ?? 40) + TABLE_SEAT_OFFSET;
|
|
434
|
+
const base = t2.rotation * DEG;
|
|
435
|
+
const arc = Math.max(0, Math.min(360, t2.seatArc ?? 360));
|
|
436
|
+
if (arc >= 360 || n === 1) {
|
|
437
|
+
for (let i = 0; i < n; i++) {
|
|
438
|
+
const a = base + i / n * 2 * Math.PI;
|
|
439
|
+
seats.push(mk(i, t2.center.x + R * Math.cos(a), t2.center.y + R * Math.sin(a)));
|
|
440
|
+
}
|
|
441
|
+
return seats;
|
|
442
|
+
}
|
|
443
|
+
const arcRad = arc * DEG;
|
|
444
|
+
const halfGap = (2 * Math.PI - arcRad) / 2;
|
|
445
|
+
const start = base + halfGap;
|
|
171
446
|
for (let i = 0; i < n; i++) {
|
|
172
|
-
const a =
|
|
173
|
-
seats.push(mk(i,
|
|
447
|
+
const a = start + i / (n - 1) * arcRad;
|
|
448
|
+
seats.push(mk(i, t2.center.x + R * Math.cos(a), t2.center.y + R * Math.sin(a)));
|
|
174
449
|
}
|
|
175
450
|
return seats;
|
|
176
451
|
}
|
|
177
|
-
const w =
|
|
178
|
-
const h =
|
|
179
|
-
const enabled =
|
|
452
|
+
const w = t2.width ?? 80;
|
|
453
|
+
const h = t2.height ?? 50;
|
|
454
|
+
const enabled = t2.sides && t2.sides.length ? t2.sides : ["top", "bottom"];
|
|
180
455
|
const order = ["top", "bottom", "left", "right"].filter((s) => enabled.includes(s));
|
|
181
456
|
if (!order.length) return seats;
|
|
182
457
|
const counts = new Map(order.map((s) => [s, 0]));
|
|
@@ -203,7 +478,7 @@ function expandTable(t) {
|
|
|
203
478
|
localX = w / 2 + TABLE_SEAT_OFFSET;
|
|
204
479
|
localY = -h / 2 + (j + 0.5) * h / count;
|
|
205
480
|
}
|
|
206
|
-
const p = place(localX, localY,
|
|
481
|
+
const p = place(localX, localY, t2.rotation, t2.center);
|
|
207
482
|
seats.push(mk(idx++, p.x, p.y));
|
|
208
483
|
}
|
|
209
484
|
}
|
|
@@ -274,9 +549,53 @@ function objectCenter(o) {
|
|
|
274
549
|
return { x: o.x ?? 0, y: o.y ?? 0 };
|
|
275
550
|
}
|
|
276
551
|
}
|
|
552
|
+
function floorsOf(doc) {
|
|
553
|
+
if (doc.floors && doc.floors.length) return doc.floors;
|
|
554
|
+
return [{ id: "floor-0", name: "Main", objects: doc.objects, focalPoint: doc.focalPoint, backgroundImage: doc.backgroundImage }];
|
|
555
|
+
}
|
|
556
|
+
function floorObjects(doc, floorId) {
|
|
557
|
+
const floors = floorsOf(doc);
|
|
558
|
+
return (floorId ? floors.find((f) => f.id === floorId) : floors[0])?.objects ?? [];
|
|
559
|
+
}
|
|
560
|
+
function allObjects(doc) {
|
|
561
|
+
return doc.floors && doc.floors.length ? doc.floors.flatMap((f) => f.objects) : doc.objects;
|
|
562
|
+
}
|
|
563
|
+
function translateObject(o, dx, dy) {
|
|
564
|
+
const p = (pt) => ({ x: pt.x + dx, y: pt.y + dy });
|
|
565
|
+
const pts = (a) => a.map(p);
|
|
566
|
+
switch (o.type) {
|
|
567
|
+
case "row":
|
|
568
|
+
return { ...o, origin: p(o.origin) };
|
|
569
|
+
case "table":
|
|
570
|
+
case "booth":
|
|
571
|
+
return { ...o, center: p(o.center) };
|
|
572
|
+
case "gaArea":
|
|
573
|
+
return { ...o, points: pts(o.points) };
|
|
574
|
+
case "section":
|
|
575
|
+
return { ...o, outline: pts(o.outline) };
|
|
576
|
+
case "text":
|
|
577
|
+
return { ...o, position: p(o.position) };
|
|
578
|
+
case "shape":
|
|
579
|
+
return {
|
|
580
|
+
...o,
|
|
581
|
+
...o.points ? { points: pts(o.points) } : {},
|
|
582
|
+
...o.x != null ? { x: o.x + dx } : {},
|
|
583
|
+
...o.y != null ? { y: o.y + dy } : {}
|
|
584
|
+
};
|
|
585
|
+
}
|
|
586
|
+
}
|
|
587
|
+
function stackFloors(doc, spread = 900) {
|
|
588
|
+
if (!doc.floors || doc.floors.length < 2) return doc;
|
|
589
|
+
const objects = [];
|
|
590
|
+
doc.floors.forEach((f, i) => {
|
|
591
|
+
const dy = -i * spread;
|
|
592
|
+
for (const o of f.objects) objects.push(dy === 0 ? o : translateObject(o, 0, dy));
|
|
593
|
+
});
|
|
594
|
+
return { ...doc, objects, floors: void 0 };
|
|
595
|
+
}
|
|
277
596
|
function expandChart(doc) {
|
|
278
597
|
const out = [];
|
|
279
|
-
for (const obj of doc
|
|
598
|
+
for (const obj of allObjects(doc)) {
|
|
280
599
|
if (obj.type === "row") out.push(...expandRow(obj));
|
|
281
600
|
else if (obj.type === "table") out.push(...expandTable(obj));
|
|
282
601
|
else if (obj.type === "booth") out.push(...expandBooth(obj));
|
|
@@ -296,7 +615,7 @@ function chartBounds(doc) {
|
|
|
296
615
|
if (y > maxY) maxY = y;
|
|
297
616
|
};
|
|
298
617
|
for (const s of expandChart(doc)) acc(s.x, s.y);
|
|
299
|
-
for (const obj of doc
|
|
618
|
+
for (const obj of allObjects(doc)) {
|
|
300
619
|
if (obj.type === "gaArea") {
|
|
301
620
|
for (const p of obj.points) acc(p.x, p.y);
|
|
302
621
|
} else if (obj.type === "section") {
|
|
@@ -341,6 +660,87 @@ function chartBounds(doc) {
|
|
|
341
660
|
};
|
|
342
661
|
}
|
|
343
662
|
|
|
663
|
+
// src/core/sections.ts
|
|
664
|
+
var UNGROUPED_ID = "__ungrouped__";
|
|
665
|
+
function objectSeatLabels(o) {
|
|
666
|
+
if (o.type === "row") return expandRow(o).map((s) => s.label);
|
|
667
|
+
if (o.type === "table") return expandTable(o).map((s) => s.label);
|
|
668
|
+
if (o.type === "booth") return expandBooth(o).map((s) => s.label);
|
|
669
|
+
return [];
|
|
670
|
+
}
|
|
671
|
+
function isSeatObject(o) {
|
|
672
|
+
return o.type === "row" || o.type === "table" || o.type === "booth";
|
|
673
|
+
}
|
|
674
|
+
function computeSections(doc) {
|
|
675
|
+
const objs = allObjects(doc);
|
|
676
|
+
const sectionObjs = objs.filter((o) => o.type === "section");
|
|
677
|
+
const nodes = /* @__PURE__ */ new Map();
|
|
678
|
+
for (const s of sectionObjs) {
|
|
679
|
+
nodes.set(s.id, { id: s.id, label: s.label || "Section", zone: s.zone, seatCount: 0, objectIds: [], seatLabels: [] });
|
|
680
|
+
}
|
|
681
|
+
const ungrouped = { id: UNGROUPED_ID, label: "Other seats", seatCount: 0, objectIds: [], seatLabels: [] };
|
|
682
|
+
const objectToSection = /* @__PURE__ */ new Map();
|
|
683
|
+
for (const obj of objs) {
|
|
684
|
+
if (!isSeatObject(obj)) continue;
|
|
685
|
+
const labels = objectSeatLabels(obj);
|
|
686
|
+
if (labels.length === 0) continue;
|
|
687
|
+
const c = objectCenter(obj);
|
|
688
|
+
const owner = sectionObjs.find((s) => pointInPolygon(c, s.outline));
|
|
689
|
+
const node = owner ? nodes.get(owner.id) : ungrouped;
|
|
690
|
+
node.seatCount += labels.length;
|
|
691
|
+
node.objectIds.push(obj.id);
|
|
692
|
+
node.seatLabels.push(...labels);
|
|
693
|
+
objectToSection.set(obj.id, node.id);
|
|
694
|
+
}
|
|
695
|
+
return {
|
|
696
|
+
sections: sectionObjs.map((s) => nodes.get(s.id)),
|
|
697
|
+
ungrouped: ungrouped.objectIds.length ? ungrouped : null,
|
|
698
|
+
objectToSection
|
|
699
|
+
};
|
|
700
|
+
}
|
|
701
|
+
function isSectionHidden(s, hidden) {
|
|
702
|
+
return hidden.has(s.id) || !!s.zone && hidden.has(s.zone);
|
|
703
|
+
}
|
|
704
|
+
function hiddenObjectIds(doc, hidden) {
|
|
705
|
+
const out = /* @__PURE__ */ new Set();
|
|
706
|
+
if (!hidden.size) return out;
|
|
707
|
+
const { sections, ungrouped, objectToSection } = computeSections(doc);
|
|
708
|
+
const hiddenSectionIds = /* @__PURE__ */ new Set();
|
|
709
|
+
const zoneById = new Map(sections.map((s) => [s.id, s.zone]));
|
|
710
|
+
for (const s of sections) {
|
|
711
|
+
const zone = zoneById.get(s.id);
|
|
712
|
+
if (hidden.has(s.id) || zone && hidden.has(zone)) hiddenSectionIds.add(s.id);
|
|
713
|
+
}
|
|
714
|
+
const ungroupedHidden = !!ungrouped && hidden.has(UNGROUPED_ID);
|
|
715
|
+
for (const [objId, secId] of objectToSection) {
|
|
716
|
+
if (hiddenSectionIds.has(secId) || ungroupedHidden && secId === UNGROUPED_ID) out.add(objId);
|
|
717
|
+
}
|
|
718
|
+
return out;
|
|
719
|
+
}
|
|
720
|
+
function applyHidden(doc, hidden) {
|
|
721
|
+
if (!hidden.size) return doc;
|
|
722
|
+
const hide = hiddenObjectIds(doc, hidden);
|
|
723
|
+
const hiddenSet = hidden instanceof Set ? hidden : new Set(hidden);
|
|
724
|
+
const keep = (o) => {
|
|
725
|
+
if (hide.has(o.id)) return false;
|
|
726
|
+
if (o.type === "section" && isSectionHidden(o, hiddenSet)) return false;
|
|
727
|
+
return true;
|
|
728
|
+
};
|
|
729
|
+
if (doc.floors && doc.floors.length) {
|
|
730
|
+
let changed = false;
|
|
731
|
+
const floors = doc.floors.map((f) => {
|
|
732
|
+
const objects2 = f.objects.filter(keep);
|
|
733
|
+
if (objects2.length !== f.objects.length) changed = true;
|
|
734
|
+
return objects2.length === f.objects.length ? f : { ...f, objects: objects2 };
|
|
735
|
+
});
|
|
736
|
+
if (!changed) return doc;
|
|
737
|
+
return { ...doc, floors, objects: floors[0].objects };
|
|
738
|
+
}
|
|
739
|
+
const objects = doc.objects.filter(keep);
|
|
740
|
+
if (objects.length === doc.objects.length) return doc;
|
|
741
|
+
return { ...doc, objects };
|
|
742
|
+
}
|
|
743
|
+
|
|
344
744
|
// src/engine/SeatmapRenderer.ts
|
|
345
745
|
var import_Core = require("konva/lib/Core");
|
|
346
746
|
var import_Stage = require("konva/lib/Stage");
|
|
@@ -352,21 +752,162 @@ var import_Ellipse = require("konva/lib/shapes/Ellipse");
|
|
|
352
752
|
var import_Line = require("konva/lib/shapes/Line");
|
|
353
753
|
var import_Text = require("konva/lib/shapes/Text");
|
|
354
754
|
var import_Image = require("konva/lib/shapes/Image");
|
|
755
|
+
|
|
756
|
+
// src/lib/money.ts
|
|
757
|
+
var DEFAULT_CURRENCY = "USD";
|
|
758
|
+
var displayLocale;
|
|
759
|
+
function setMoneyLocale(locale) {
|
|
760
|
+
displayLocale = locale;
|
|
761
|
+
}
|
|
762
|
+
var formatterCache = /* @__PURE__ */ new Map();
|
|
763
|
+
function formatter(currency, digits) {
|
|
764
|
+
const key = `${displayLocale ?? ""}|${currency}|${digits ?? "auto"}`;
|
|
765
|
+
let fmt = formatterCache.get(key);
|
|
766
|
+
if (!fmt) {
|
|
767
|
+
fmt = new Intl.NumberFormat(displayLocale, {
|
|
768
|
+
style: "currency",
|
|
769
|
+
currency,
|
|
770
|
+
minimumFractionDigits: digits,
|
|
771
|
+
maximumFractionDigits: digits
|
|
772
|
+
});
|
|
773
|
+
formatterCache.set(key, fmt);
|
|
774
|
+
}
|
|
775
|
+
return fmt;
|
|
776
|
+
}
|
|
777
|
+
function formatMoney(amount, currency = DEFAULT_CURRENCY, fractionDigits) {
|
|
778
|
+
const digits = fractionDigits ?? (Number.isInteger(amount) ? 0 : void 0);
|
|
779
|
+
return formatter(currency, digits).format(amount);
|
|
780
|
+
}
|
|
781
|
+
function currencySymbol(currency = DEFAULT_CURRENCY) {
|
|
782
|
+
const part = formatter(currency, 0).formatToParts(0).find((p) => p.type === "currency");
|
|
783
|
+
return part?.value ?? currency;
|
|
784
|
+
}
|
|
785
|
+
|
|
786
|
+
// src/i18n/locales/en.ts
|
|
787
|
+
var en = {
|
|
788
|
+
// shared
|
|
789
|
+
"common.cancel": "Cancel",
|
|
790
|
+
"common.close": "Close",
|
|
791
|
+
"common.done": "Done",
|
|
792
|
+
"common.copied": "\u2713 Copied",
|
|
793
|
+
// buyer picker (extraction in progress — P4 of the port plan)
|
|
794
|
+
"picker.holdSeats": "Hold seats & checkout",
|
|
795
|
+
"picker.completeBooking": "Complete booking",
|
|
796
|
+
"picker.seatsHeld": "Seats held \u2014 {time}",
|
|
797
|
+
"picker.holdExpired": "Your hold expired \u2014 the seats were released. Pick again.",
|
|
798
|
+
"picker.seatTaken": "Seat {label} was just taken by another buyer.",
|
|
799
|
+
"picker.poweredBy": "Powered by SeatMap",
|
|
800
|
+
"picker.seats.one": "{count} seat",
|
|
801
|
+
"picker.seats.other": "{count} seats",
|
|
802
|
+
// renderer (drawn on the Konva map — shared by the embed SDK)
|
|
803
|
+
"map.aria": "Seating map. Use arrow keys to move between seats, Enter to select.",
|
|
804
|
+
"map.seatsLeft": "{count} LEFT",
|
|
805
|
+
"map.fromPrice": "FROM {price}",
|
|
806
|
+
// buyer picker page (src/pages/PickerPage.tsx)
|
|
807
|
+
"picker.language": "Language",
|
|
808
|
+
"picker.zoomToFit": "Zoom to fit",
|
|
809
|
+
"picker.viewMode": "View mode",
|
|
810
|
+
"picker.floor": "Floor",
|
|
811
|
+
"picker.zoomLevel": "Zoom level",
|
|
812
|
+
"picker.rungTip.zones": "Farthest rung \u2014 zone blocks (Floor / Lower Bowl / Upper Bowl)",
|
|
813
|
+
"picker.rungTip.sections": "Section blocks \u2014 category-mix fill + availability tint",
|
|
814
|
+
"picker.rungTip.seats": "Individual seats \u2014 blocks melt into dots",
|
|
815
|
+
"picker.rungLabel.zones": "ZONES",
|
|
816
|
+
"picker.rungLabel.sections": "SECTIONS",
|
|
817
|
+
"picker.rungLabel.seats": "SEATS",
|
|
818
|
+
"picker.sectionSummaryAria": "{label} section summary",
|
|
819
|
+
"picker.closeSectionSummary": "Close section summary",
|
|
820
|
+
"picker.seatsLeftInSection.one": "{count} seat left",
|
|
821
|
+
"picker.seatsLeftInSection.other": "{count} seats left",
|
|
822
|
+
"picker.overview": "Overview",
|
|
823
|
+
"picker.tapSeatHint": "Tap any seat to check its view",
|
|
824
|
+
"picker.chartSize": "Chart size",
|
|
825
|
+
"picker.custom": "Custom",
|
|
826
|
+
"picker.categories": "Categories",
|
|
827
|
+
"picker.accessibility": "Accessibility",
|
|
828
|
+
"picker.showAnyAccessible": "Show any accessible seat",
|
|
829
|
+
"picker.any": "Any",
|
|
830
|
+
"picker.yourSeats": "Your seats",
|
|
831
|
+
"picker.emptySeats": "Tap seats on the map",
|
|
832
|
+
"picker.emptySeatsWithGa": "Tap seats on the map \xB7 tap a standing area for GA tickets",
|
|
833
|
+
"picker.oneFewer": "One fewer",
|
|
834
|
+
"picker.oneMore": "One more",
|
|
835
|
+
"picker.remove": "Remove {label}",
|
|
836
|
+
"picker.ticketTierFor": "Ticket tier for {label}",
|
|
837
|
+
"picker.total": "Total: {amount}",
|
|
838
|
+
"picker.viewFromSeat": "View from seat {label}",
|
|
839
|
+
"picker.real360": "REAL 360\xB0",
|
|
840
|
+
"picker.preview": "PREVIEW",
|
|
841
|
+
"picker.open360": "Open 360\xB0 view",
|
|
842
|
+
"picker.sightline": "\u2248 {rows} rows from stage \xB7 {angle}\xB0 off-center",
|
|
843
|
+
"picker.bookedDemo": "Booked! (demo)",
|
|
844
|
+
"picker.bookButton.one": "Book {count} ticket \u2014 {amount}",
|
|
845
|
+
"picker.bookButton.other": "Book {count} tickets \u2014 {amount}",
|
|
846
|
+
"picker.simulateCrowd": "Simulate crowd: {state}",
|
|
847
|
+
"picker.on": "ON",
|
|
848
|
+
"picker.off": "OFF",
|
|
849
|
+
"picker.panorama360": "360\xB0 venue photo",
|
|
850
|
+
"picker.illustrationCaption": "illustration \xB7 \u2248 {m} m from stage"
|
|
851
|
+
};
|
|
852
|
+
|
|
853
|
+
// src/i18n/index.ts
|
|
854
|
+
var SUPPORTED_LOCALES = ["en", "es", "de", "fr"];
|
|
855
|
+
var bundles = { en };
|
|
856
|
+
var overrides = {};
|
|
857
|
+
var active = "en";
|
|
858
|
+
function resolveLocale(explicit, stored) {
|
|
859
|
+
for (const candidate of [explicit, stored, typeof navigator !== "undefined" ? navigator.language : null]) {
|
|
860
|
+
if (!candidate) continue;
|
|
861
|
+
const base = candidate.toLowerCase().split("-")[0];
|
|
862
|
+
if (SUPPORTED_LOCALES.includes(base)) return base;
|
|
863
|
+
}
|
|
864
|
+
return "en";
|
|
865
|
+
}
|
|
866
|
+
function getLocale() {
|
|
867
|
+
return active;
|
|
868
|
+
}
|
|
869
|
+
function setLocale(locale, bundle) {
|
|
870
|
+
if (bundle) bundles[locale] = { ...bundles[locale], ...bundle };
|
|
871
|
+
active = bundles[locale] ? locale : "en";
|
|
872
|
+
setMoneyLocale(active);
|
|
873
|
+
if (typeof document !== "undefined") document.documentElement.lang = active;
|
|
874
|
+
}
|
|
875
|
+
function setStringOverrides(next) {
|
|
876
|
+
overrides = next;
|
|
877
|
+
}
|
|
878
|
+
var PLACEHOLDER = /\{(\w+)\}/g;
|
|
879
|
+
function t(key, vars) {
|
|
880
|
+
const raw = overrides[key] ?? bundles[active]?.[key] ?? en[key] ?? key;
|
|
881
|
+
if (!vars) return raw;
|
|
882
|
+
return raw.replace(PLACEHOLDER, (_, name) => String(vars[name] ?? `{${name}}`));
|
|
883
|
+
}
|
|
884
|
+
function tCount(key, count, vars) {
|
|
885
|
+
const rule = new Intl.PluralRules(active).select(count);
|
|
886
|
+
const exact = overrides[`${key}.${rule}`] ?? bundles[active]?.[`${key}.${rule}`] ?? en[`${key}.${rule}`];
|
|
887
|
+
const raw = exact ?? overrides[`${key}.other`] ?? bundles[active]?.[`${key}.other`] ?? en[`${key}.other`] ?? key;
|
|
888
|
+
return raw.replace(PLACEHOLDER, (_, name) => String({ count, ...vars }[name] ?? `{${name}}`));
|
|
889
|
+
}
|
|
890
|
+
function formatDate(value, opts) {
|
|
891
|
+
return new Intl.DateTimeFormat(active, opts ?? { dateStyle: "medium", timeStyle: "short" }).format(value);
|
|
892
|
+
}
|
|
893
|
+
|
|
894
|
+
// src/engine/SeatmapRenderer.ts
|
|
355
895
|
var SEAT_RADIUS = 9;
|
|
356
896
|
var SEAT_LEGIBLE_SCALE = 0.9;
|
|
357
897
|
var CACHE_THRESHOLD = 0.55 * SEAT_LEGIBLE_SCALE;
|
|
358
898
|
var LABEL_SCALE = 1;
|
|
359
|
-
var SECTION_PROMINENT_SCALE = 0.
|
|
899
|
+
var SECTION_PROMINENT_SCALE = 0.45 * SEAT_LEGIBLE_SCALE;
|
|
900
|
+
var BLOCK_MELT_TOP = 0.9 * SEAT_LEGIBLE_SCALE;
|
|
360
901
|
var ZONE_PROMINENT_SCALE = 0.55 * SECTION_PROMINENT_SCALE;
|
|
361
902
|
var MAX_LABELS = 700;
|
|
362
903
|
var ISO_ANGLE_DEG = -11.5;
|
|
363
904
|
var ISO_SQUASH = 0.58;
|
|
364
|
-
var LIFT_PER_STEP =
|
|
905
|
+
var LIFT_PER_STEP = 58;
|
|
365
906
|
var ISO_TWEEN_MS = 320;
|
|
366
907
|
var BLOCK_FILL_ALPHA = 0.85;
|
|
367
908
|
var SOLD_DARKEN = 0.5;
|
|
368
|
-
var SECTION_LABEL_PX =
|
|
369
|
-
var SECTION_SUB_PX =
|
|
909
|
+
var SECTION_LABEL_PX = 20;
|
|
910
|
+
var SECTION_SUB_PX = 12.5;
|
|
370
911
|
var ZONE_LABEL_PX = 30;
|
|
371
912
|
var ZONE_SUB_PX = 12;
|
|
372
913
|
var HELD_FILL = "#6b7280";
|
|
@@ -383,7 +924,7 @@ var ACCESS_RING = {
|
|
|
383
924
|
};
|
|
384
925
|
function seatMatchesAccess(seat, filter) {
|
|
385
926
|
if (filter.length === 0) return !!seat.accessible;
|
|
386
|
-
return !!seat.accessibility?.some((
|
|
927
|
+
return !!seat.accessibility?.some((t2) => filter.includes(t2));
|
|
387
928
|
}
|
|
388
929
|
var DEF_SEAT_LABEL = "#0b1220";
|
|
389
930
|
var DEF_SELECTION = "#ffffff";
|
|
@@ -452,16 +993,21 @@ function mixColors(parts, fallback) {
|
|
|
452
993
|
}
|
|
453
994
|
return tw > 0 ? toHex(r / tw, g / tw, b / tw) : fallback;
|
|
454
995
|
}
|
|
455
|
-
function lerpColor(a, b,
|
|
996
|
+
function lerpColor(a, b, t2) {
|
|
456
997
|
const ca = hexToRgb(a);
|
|
457
998
|
const cb = hexToRgb(b);
|
|
458
999
|
if (!ca || !cb) return a;
|
|
459
|
-
return toHex(ca[0] + (cb[0] - ca[0]) *
|
|
1000
|
+
return toHex(ca[0] + (cb[0] - ca[0]) * t2, ca[1] + (cb[1] - ca[1]) * t2, ca[2] + (cb[2] - ca[2]) * t2);
|
|
460
1001
|
}
|
|
461
1002
|
var _SeatmapRenderer = class _SeatmapRenderer {
|
|
462
1003
|
constructor(container, options = {}) {
|
|
463
1004
|
this.seats = [];
|
|
464
1005
|
this.seatById = /* @__PURE__ */ new Map();
|
|
1006
|
+
/** Multi-floor (Batch 5): the last-set chart + which floor we're rendering. */
|
|
1007
|
+
this.chartDoc = null;
|
|
1008
|
+
this.activeFloorId = "";
|
|
1009
|
+
/** When true on a multi-floor chart, render ALL floors stacked (3D overview). */
|
|
1010
|
+
this.stacked = false;
|
|
465
1011
|
/** Interactive node per seat/booth — a Circle for seats, a Rect for booths. */
|
|
466
1012
|
this.circleById = /* @__PURE__ */ new Map();
|
|
467
1013
|
/** Booth block geometry, keyed by booth id (= the unit's rowId). */
|
|
@@ -485,6 +1031,10 @@ var _SeatmapRenderer = class _SeatmapRenderer {
|
|
|
485
1031
|
this.zones = [];
|
|
486
1032
|
this.seatSection = /* @__PURE__ */ new Map();
|
|
487
1033
|
this.catPrice = /* @__PURE__ */ new Map();
|
|
1034
|
+
/** Section/zone ids to render dimmed (organizer manager: held-back inventory). */
|
|
1035
|
+
this.dimmedSections = /* @__PURE__ */ new Set();
|
|
1036
|
+
/** Object id → floor id (multi-floor only) — resolves a deck tap in the 3D stack. */
|
|
1037
|
+
this.objectFloor = /* @__PURE__ */ new Map();
|
|
488
1038
|
/** Zone id → colour (drives extruded side faces in iso view). */
|
|
489
1039
|
this.zoneColor = /* @__PURE__ */ new Map();
|
|
490
1040
|
this.hasSections = false;
|
|
@@ -497,6 +1047,8 @@ var _SeatmapRenderer = class _SeatmapRenderer {
|
|
|
497
1047
|
this.isoRaf = 0;
|
|
498
1048
|
/** Chart centre the iso projection pivots about (bounds centre). */
|
|
499
1049
|
this.isoCentre = { x: 0, y: 0 };
|
|
1050
|
+
/** rAF for an in-flight camera glide (focusRegion / setRung); 0 = none. */
|
|
1051
|
+
this.glideRaf = 0;
|
|
500
1052
|
/** Set in destroy() so an in-flight iso tween bails. */
|
|
501
1053
|
this.destroyed = false;
|
|
502
1054
|
/** Cached scale the section/zone labels were last sized for (scale-compensation). */
|
|
@@ -544,6 +1096,7 @@ var _SeatmapRenderer = class _SeatmapRenderer {
|
|
|
544
1096
|
// pointer events away from Konva's canvas, killing its click/tap/hover
|
|
545
1097
|
// pipeline. We rely on bubbling instead.
|
|
546
1098
|
this.onPointerDown = (e) => {
|
|
1099
|
+
this.cancelGlide();
|
|
547
1100
|
this.pointers.set(e.pointerId, this.toLocal(e));
|
|
548
1101
|
if (this.pointers.size === 1) {
|
|
549
1102
|
this.moved = 0;
|
|
@@ -605,6 +1158,7 @@ var _SeatmapRenderer = class _SeatmapRenderer {
|
|
|
605
1158
|
};
|
|
606
1159
|
this.container = container;
|
|
607
1160
|
this.opts = { maxSelection: 10, selectableStatuses: ["free"], ...options };
|
|
1161
|
+
this.currency = options.currency;
|
|
608
1162
|
import_Core.Konva.pixelRatio = this.dpr;
|
|
609
1163
|
this.stage = new import_Stage.Stage({
|
|
610
1164
|
container,
|
|
@@ -621,7 +1175,7 @@ var _SeatmapRenderer = class _SeatmapRenderer {
|
|
|
621
1175
|
if (container.tabIndex < 0) container.tabIndex = 0;
|
|
622
1176
|
container.setAttribute("role", "application");
|
|
623
1177
|
if (!container.getAttribute("aria-label")) {
|
|
624
|
-
container.setAttribute("aria-label", "
|
|
1178
|
+
container.setAttribute("aria-label", t("map.aria"));
|
|
625
1179
|
}
|
|
626
1180
|
container.addEventListener("keydown", this.onKeyDown);
|
|
627
1181
|
this.bgLayer = new import_Layer.Layer({ listening: true });
|
|
@@ -664,7 +1218,15 @@ var _SeatmapRenderer = class _SeatmapRenderer {
|
|
|
664
1218
|
}
|
|
665
1219
|
}
|
|
666
1220
|
// ---- ISeatmapRenderer -----------------------------------------------------
|
|
667
|
-
setChart(doc) {
|
|
1221
|
+
setChart(doc, opts) {
|
|
1222
|
+
if (doc !== this.chartDoc) this.stacked = false;
|
|
1223
|
+
this.chartDoc = doc;
|
|
1224
|
+
this.activeFloorId = opts?.floorId ?? floorsOf(doc)[0].id;
|
|
1225
|
+
this.objectFloor.clear();
|
|
1226
|
+
if (doc.floors && doc.floors.length > 1) {
|
|
1227
|
+
for (const f of doc.floors) for (const o of f.objects) this.objectFloor.set(o.id, f.id);
|
|
1228
|
+
}
|
|
1229
|
+
const view = this.floorView(doc);
|
|
668
1230
|
this.focusedId = null;
|
|
669
1231
|
this.focusRing.visible(false);
|
|
670
1232
|
this.bgLayer.destroyChildren();
|
|
@@ -692,7 +1254,7 @@ var _SeatmapRenderer = class _SeatmapRenderer {
|
|
|
692
1254
|
this.isoT = 0;
|
|
693
1255
|
this.isoTarget = 0;
|
|
694
1256
|
this.resetLayerTransforms();
|
|
695
|
-
this.hasSections =
|
|
1257
|
+
this.hasSections = view.objects.some((o) => o.type === "section");
|
|
696
1258
|
for (const z of doc.zones ?? []) if (z.color) this.zoneColor.set(z.id, z.color);
|
|
697
1259
|
this.seatLayer.opacity(1);
|
|
698
1260
|
this.hoverRing.visible(false);
|
|
@@ -707,24 +1269,56 @@ var _SeatmapRenderer = class _SeatmapRenderer {
|
|
|
707
1269
|
this.catColor.set(c.key, c.color);
|
|
708
1270
|
if (typeof c.price === "number") this.catPrice.set(c.key, c.price);
|
|
709
1271
|
}
|
|
710
|
-
for (const obj of
|
|
1272
|
+
for (const obj of view.objects) {
|
|
711
1273
|
if (obj.type === "booth") {
|
|
712
1274
|
this.boothDims.set(obj.id, { width: obj.width, height: obj.height, rotation: obj.rotation });
|
|
713
1275
|
}
|
|
714
1276
|
}
|
|
715
|
-
this.seats = expandChart(
|
|
1277
|
+
this.seats = expandChart(view);
|
|
716
1278
|
for (const s of this.seats) {
|
|
717
1279
|
this.seatById.set(s.id, s);
|
|
718
1280
|
this.statusById.set(s.id, "free");
|
|
719
1281
|
}
|
|
720
|
-
this.renderBackground(
|
|
1282
|
+
this.renderBackground(view);
|
|
721
1283
|
this.renderSeats();
|
|
722
1284
|
this.overlayLayer.add(this.labelGroup);
|
|
723
1285
|
this.overlayLayer.add(this.hoverRing);
|
|
724
|
-
this.bounds = chartBounds(
|
|
1286
|
+
this.bounds = chartBounds(view);
|
|
725
1287
|
this.isoCentre = { x: this.bounds.x + this.bounds.width / 2, y: this.bounds.y + this.bounds.height / 2 };
|
|
726
1288
|
this.zoomToFit();
|
|
727
1289
|
}
|
|
1290
|
+
/** The chart to render: all floors stacked (3D overview), the active floor, or
|
|
1291
|
+
* the whole chart for single-floor charts. */
|
|
1292
|
+
floorView(doc) {
|
|
1293
|
+
if (!doc.floors || !doc.floors.length) return doc;
|
|
1294
|
+
if (this.stacked && doc.floors.length >= 2) return stackFloors(doc);
|
|
1295
|
+
const floor = doc.floors.find((f) => f.id === this.activeFloorId) ?? doc.floors[0];
|
|
1296
|
+
return { ...doc, objects: floor.objects, focalPoint: floor.focalPoint, backgroundImage: floor.backgroundImage, floors: void 0 };
|
|
1297
|
+
}
|
|
1298
|
+
/** Toggle the 3D all-floors stacked overview (Batch 5). Re-renders; no-op on
|
|
1299
|
+
* single-floor charts. The caller re-applies statuses + animates the iso view. */
|
|
1300
|
+
setStacked(on) {
|
|
1301
|
+
if (!this.chartDoc || on === this.stacked) return;
|
|
1302
|
+
const multi = !!this.chartDoc.floors && this.chartDoc.floors.length >= 2;
|
|
1303
|
+
if (!multi) return;
|
|
1304
|
+
this.stacked = on;
|
|
1305
|
+
this.setChart(this.chartDoc, { floorId: this.activeFloorId });
|
|
1306
|
+
}
|
|
1307
|
+
isStacked() {
|
|
1308
|
+
return this.stacked;
|
|
1309
|
+
}
|
|
1310
|
+
/** Switch which floor is shown (2D). Re-renders + re-fits; no-op if unchanged. */
|
|
1311
|
+
setActiveFloor(floorId) {
|
|
1312
|
+
if (!this.chartDoc || floorId === this.activeFloorId) return;
|
|
1313
|
+
this.setChart(this.chartDoc, { floorId });
|
|
1314
|
+
}
|
|
1315
|
+
/** Floors for the host's switcher (single-floor charts return one synthetic floor). */
|
|
1316
|
+
getFloors() {
|
|
1317
|
+
return this.chartDoc ? floorsOf(this.chartDoc).map((f) => ({ id: f.id, name: f.name })) : [];
|
|
1318
|
+
}
|
|
1319
|
+
getActiveFloorId() {
|
|
1320
|
+
return this.activeFloorId;
|
|
1321
|
+
}
|
|
728
1322
|
setStatus(seatIds, status) {
|
|
729
1323
|
let touched = false;
|
|
730
1324
|
const affected = this.hasSections ? /* @__PURE__ */ new Set() : null;
|
|
@@ -804,11 +1398,11 @@ var _SeatmapRenderer = class _SeatmapRenderer {
|
|
|
804
1398
|
const dur = 620;
|
|
805
1399
|
const step = (now) => {
|
|
806
1400
|
if (!ring.getLayer()) return;
|
|
807
|
-
const
|
|
808
|
-
ring.radius(this.seatR * (1 +
|
|
809
|
-
ring.opacity(0.9 * (1 -
|
|
1401
|
+
const t2 = Math.min(1, (now - start) / dur);
|
|
1402
|
+
ring.radius(this.seatR * (1 + t2 * 1.8));
|
|
1403
|
+
ring.opacity(0.9 * (1 - t2));
|
|
810
1404
|
this.overlayLayer.batchDraw();
|
|
811
|
-
if (
|
|
1405
|
+
if (t2 < 1) requestAnimationFrame(step);
|
|
812
1406
|
else {
|
|
813
1407
|
ring.destroy();
|
|
814
1408
|
this.overlayLayer.batchDraw();
|
|
@@ -915,7 +1509,7 @@ var _SeatmapRenderer = class _SeatmapRenderer {
|
|
|
915
1509
|
sameAccessFilter(next) {
|
|
916
1510
|
const cur = this.accessFilter;
|
|
917
1511
|
if (cur === null || next === null) return cur === next;
|
|
918
|
-
return cur.length === next.length && cur.every((
|
|
1512
|
+
return cur.length === next.length && cur.every((t2, i) => t2 === next[i]);
|
|
919
1513
|
}
|
|
920
1514
|
// ---- isometric ("3D") view mode -------------------------------------------
|
|
921
1515
|
/**
|
|
@@ -963,6 +1557,10 @@ var _SeatmapRenderer = class _SeatmapRenderer {
|
|
|
963
1557
|
};
|
|
964
1558
|
this.isoRaf = requestAnimationFrame(step);
|
|
965
1559
|
}
|
|
1560
|
+
/** Current projection — reflects the tween target, not the mid-tween isoT. */
|
|
1561
|
+
getViewMode() {
|
|
1562
|
+
return this.isoTarget === 1 ? "isometric" : "flat";
|
|
1563
|
+
}
|
|
966
1564
|
/** Iso angle (rad) + y-squash for the current isoT. */
|
|
967
1565
|
isoParams() {
|
|
968
1566
|
return { th: ISO_ANGLE_DEG * Math.PI / 180 * this.isoT, sg: 1 - (1 - ISO_SQUASH) * this.isoT };
|
|
@@ -1020,8 +1618,8 @@ var _SeatmapRenderer = class _SeatmapRenderer {
|
|
|
1020
1618
|
* lift + extruded side faces on elevated sections.
|
|
1021
1619
|
*/
|
|
1022
1620
|
applyIso() {
|
|
1023
|
-
const
|
|
1024
|
-
if (
|
|
1621
|
+
const t2 = this.isoT;
|
|
1622
|
+
if (t2 === 0) {
|
|
1025
1623
|
this.resetLayerTransforms();
|
|
1026
1624
|
} else {
|
|
1027
1625
|
const { th, sg } = this.isoParams();
|
|
@@ -1074,13 +1672,13 @@ var _SeatmapRenderer = class _SeatmapRenderer {
|
|
|
1074
1672
|
}
|
|
1075
1673
|
/** Lift elevated sections (+ their members) and update their extruded side faces. */
|
|
1076
1674
|
applyElevation() {
|
|
1077
|
-
const
|
|
1675
|
+
const t2 = this.isoT;
|
|
1078
1676
|
for (const sec of this.sections) {
|
|
1079
1677
|
if (sec.elevation <= 0) continue;
|
|
1080
1678
|
const off = this.isoLiftLocal(sec.elevation);
|
|
1081
1679
|
sec.liftGroupBg?.position(off);
|
|
1082
1680
|
sec.liftGroupSeat?.position(off);
|
|
1083
|
-
const alpha = 0.9 *
|
|
1681
|
+
const alpha = 0.9 * t2;
|
|
1084
1682
|
for (let i = 0; i < sec.sideFaces.length; i++) {
|
|
1085
1683
|
const face = sec.sideFaces[i];
|
|
1086
1684
|
const p0 = sec.outline[i];
|
|
@@ -1094,6 +1692,7 @@ var _SeatmapRenderer = class _SeatmapRenderer {
|
|
|
1094
1692
|
this.destroyed = true;
|
|
1095
1693
|
if (this.rafId) cancelAnimationFrame(this.rafId);
|
|
1096
1694
|
if (this.isoRaf) cancelAnimationFrame(this.isoRaf);
|
|
1695
|
+
if (this.glideRaf) cancelAnimationFrame(this.glideRaf);
|
|
1097
1696
|
if (this.viewChangeRaf) cancelAnimationFrame(this.viewChangeRaf);
|
|
1098
1697
|
if (this.recacheTimer) clearTimeout(this.recacheTimer);
|
|
1099
1698
|
this.resizeObs?.disconnect();
|
|
@@ -1173,7 +1772,7 @@ var _SeatmapRenderer = class _SeatmapRenderer {
|
|
|
1173
1772
|
this.circleById.set(seat.id, rect);
|
|
1174
1773
|
this.paintSeat(rect, seat.id);
|
|
1175
1774
|
target.add(rect);
|
|
1176
|
-
const
|
|
1775
|
+
const t2 = new import_Text.Text({
|
|
1177
1776
|
x: seat.x,
|
|
1178
1777
|
y: seat.y,
|
|
1179
1778
|
text: seat.label,
|
|
@@ -1184,10 +1783,10 @@ var _SeatmapRenderer = class _SeatmapRenderer {
|
|
|
1184
1783
|
listening: false,
|
|
1185
1784
|
perfectDrawEnabled: false
|
|
1186
1785
|
});
|
|
1187
|
-
|
|
1188
|
-
|
|
1786
|
+
t2.offsetX(t2.width() / 2);
|
|
1787
|
+
t2.offsetY(t2.height() / 2);
|
|
1189
1788
|
this.hasBoothText = true;
|
|
1190
|
-
target.add(
|
|
1789
|
+
target.add(t2);
|
|
1191
1790
|
}
|
|
1192
1791
|
/** Apply fill/stroke/opacity for a seat's current status + selection. */
|
|
1193
1792
|
paintSeat(c, id) {
|
|
@@ -1223,6 +1822,32 @@ var _SeatmapRenderer = class _SeatmapRenderer {
|
|
|
1223
1822
|
if (this.categoryHighlight && status === "free" && !selected && seat.categoryKey !== this.categoryHighlight) {
|
|
1224
1823
|
c.opacity(0.25);
|
|
1225
1824
|
}
|
|
1825
|
+
if (this.dimmedSections.size) {
|
|
1826
|
+
const sec = this.seatSection.get(id);
|
|
1827
|
+
if (sec && (this.dimmedSections.has(sec.id) || sec.zone != null && this.dimmedSections.has(sec.zone))) {
|
|
1828
|
+
c.opacity(0.18);
|
|
1829
|
+
}
|
|
1830
|
+
}
|
|
1831
|
+
}
|
|
1832
|
+
/**
|
|
1833
|
+
* Dim the seats of these section/zone ids (organizer manager use) so held-back
|
|
1834
|
+
* inventory is visually distinct on the canvas without hiding it. `null`/empty
|
|
1835
|
+
* clears. Unlike the buyer's applyHidden, the sections stay rendered.
|
|
1836
|
+
*/
|
|
1837
|
+
setDimmedSections(ids) {
|
|
1838
|
+
const next = new Set(ids ?? []);
|
|
1839
|
+
if (next.size === this.dimmedSections.size && [...next].every((i) => this.dimmedSections.has(i))) return;
|
|
1840
|
+
this.dimmedSections = next;
|
|
1841
|
+
for (const seat of this.seats) {
|
|
1842
|
+
const c = this.circleById.get(seat.id);
|
|
1843
|
+
if (c) this.paintSeat(c, seat.id);
|
|
1844
|
+
}
|
|
1845
|
+
if (this.cached) {
|
|
1846
|
+
this.seatLayer.clearCache();
|
|
1847
|
+
this.cacheSeatLayer();
|
|
1848
|
+
} else {
|
|
1849
|
+
this.seatLayer.batchDraw();
|
|
1850
|
+
}
|
|
1226
1851
|
}
|
|
1227
1852
|
/** Legend hover: highlight one category (dim the rest), or null to clear. */
|
|
1228
1853
|
setCategoryHighlight(key) {
|
|
@@ -1405,7 +2030,7 @@ var _SeatmapRenderer = class _SeatmapRenderer {
|
|
|
1405
2030
|
}
|
|
1406
2031
|
/** Prominent stage caption: uppercase, letter-spaced, larger, softly dimmed. */
|
|
1407
2032
|
addStageLabel(x, y, text) {
|
|
1408
|
-
const
|
|
2033
|
+
const t2 = new import_Text.Text({
|
|
1409
2034
|
x,
|
|
1410
2035
|
y,
|
|
1411
2036
|
text: text.toUpperCase(),
|
|
@@ -1417,9 +2042,9 @@ var _SeatmapRenderer = class _SeatmapRenderer {
|
|
|
1417
2042
|
listening: false,
|
|
1418
2043
|
perfectDrawEnabled: false
|
|
1419
2044
|
});
|
|
1420
|
-
|
|
1421
|
-
|
|
1422
|
-
this.bgLayer.add(
|
|
2045
|
+
t2.offsetX(t2.width() / 2);
|
|
2046
|
+
t2.offsetY(t2.height() / 2);
|
|
2047
|
+
this.bgLayer.add(t2);
|
|
1423
2048
|
}
|
|
1424
2049
|
renderGA(obj) {
|
|
1425
2050
|
const color = this.catColor.get(obj.categoryKey) ?? "#6e7bff";
|
|
@@ -1500,12 +2125,14 @@ var _SeatmapRenderer = class _SeatmapRenderer {
|
|
|
1500
2125
|
this.seatLayer.add(liftGroupSeat);
|
|
1501
2126
|
}
|
|
1502
2127
|
const bgTarget = liftGroupBg ?? this.bgLayer;
|
|
2128
|
+
const outlineTint = obj.color ?? this.zoneColor.get(obj.zone ?? "") ?? "#3a4358";
|
|
1503
2129
|
const outlinePoly = new import_Line.Line({
|
|
1504
2130
|
points: pts,
|
|
1505
2131
|
closed: true,
|
|
1506
|
-
stroke:
|
|
1507
|
-
strokeWidth: 1.
|
|
1508
|
-
fill: rgba(
|
|
2132
|
+
stroke: rgba(outlineTint, 0.5),
|
|
2133
|
+
strokeWidth: 1.75,
|
|
2134
|
+
fill: rgba(outlineTint, 0.08),
|
|
2135
|
+
lineJoin: "round",
|
|
1509
2136
|
listening: false,
|
|
1510
2137
|
perfectDrawEnabled: false
|
|
1511
2138
|
});
|
|
@@ -1521,6 +2148,30 @@ var _SeatmapRenderer = class _SeatmapRenderer {
|
|
|
1521
2148
|
perfectDrawEnabled: false
|
|
1522
2149
|
});
|
|
1523
2150
|
bgTarget.add(blockPoly);
|
|
2151
|
+
const rowSeats = /* @__PURE__ */ new Map();
|
|
2152
|
+
for (const id of memberIds) {
|
|
2153
|
+
const s = this.seatById.get(id);
|
|
2154
|
+
if (!s) continue;
|
|
2155
|
+
const i = Number(id.slice(id.lastIndexOf(":") + 1)) || 0;
|
|
2156
|
+
(rowSeats.get(s.rowId) ?? rowSeats.set(s.rowId, []).get(s.rowId)).push({ i, x: s.x, y: s.y });
|
|
2157
|
+
}
|
|
2158
|
+
const rowLines = [];
|
|
2159
|
+
for (const arr of rowSeats.values()) {
|
|
2160
|
+
if (arr.length < 2) continue;
|
|
2161
|
+
arr.sort((a, b) => a.i - b.i);
|
|
2162
|
+
const line = new import_Line.Line({
|
|
2163
|
+
points: arr.flatMap((p) => [p.x, p.y]),
|
|
2164
|
+
stroke: rgba("#ffffff", 0.34),
|
|
2165
|
+
strokeWidth: SEAT_RADIUS * 0.55,
|
|
2166
|
+
lineCap: "round",
|
|
2167
|
+
lineJoin: "round",
|
|
2168
|
+
opacity: 0,
|
|
2169
|
+
listening: false,
|
|
2170
|
+
perfectDrawEnabled: false
|
|
2171
|
+
});
|
|
2172
|
+
rowLines.push(line);
|
|
2173
|
+
bgTarget.add(line);
|
|
2174
|
+
}
|
|
1524
2175
|
const nameLabel = new import_Text.Text({
|
|
1525
2176
|
x: centroid.x,
|
|
1526
2177
|
y: centroid.y,
|
|
@@ -1529,6 +2180,11 @@ var _SeatmapRenderer = class _SeatmapRenderer {
|
|
|
1529
2180
|
fontStyle: "700",
|
|
1530
2181
|
fontFamily: this.labelFont(),
|
|
1531
2182
|
fill: "#8b93a7",
|
|
2183
|
+
// Dark halo so the label reads over the seat dots at any zoom.
|
|
2184
|
+
shadowColor: "#05070c",
|
|
2185
|
+
shadowBlur: 6,
|
|
2186
|
+
shadowOpacity: 0.9,
|
|
2187
|
+
shadowForStrokeEnabled: false,
|
|
1532
2188
|
listening: false,
|
|
1533
2189
|
perfectDrawEnabled: false
|
|
1534
2190
|
});
|
|
@@ -1538,11 +2194,15 @@ var _SeatmapRenderer = class _SeatmapRenderer {
|
|
|
1538
2194
|
const subLabel = new import_Text.Text({
|
|
1539
2195
|
x: centroid.x,
|
|
1540
2196
|
y: centroid.y,
|
|
1541
|
-
text:
|
|
2197
|
+
text: t("map.seatsLeft", { count: free }),
|
|
1542
2198
|
fontSize: 12,
|
|
1543
|
-
fontStyle: "
|
|
2199
|
+
fontStyle: "700",
|
|
1544
2200
|
fontFamily: "JetBrains Mono, ui-monospace, monospace",
|
|
1545
|
-
fill:
|
|
2201
|
+
fill: "#f4f6fb",
|
|
2202
|
+
shadowColor: "#05070c",
|
|
2203
|
+
shadowBlur: 5,
|
|
2204
|
+
shadowOpacity: 0.9,
|
|
2205
|
+
shadowForStrokeEnabled: false,
|
|
1546
2206
|
opacity: 0,
|
|
1547
2207
|
listening: false,
|
|
1548
2208
|
perfectDrawEnabled: false
|
|
@@ -1561,6 +2221,7 @@ var _SeatmapRenderer = class _SeatmapRenderer {
|
|
|
1561
2221
|
baseFill,
|
|
1562
2222
|
outlinePoly,
|
|
1563
2223
|
blockPoly,
|
|
2224
|
+
rowLines,
|
|
1564
2225
|
nameLabel,
|
|
1565
2226
|
subLabel,
|
|
1566
2227
|
elevation,
|
|
@@ -1576,7 +2237,7 @@ var _SeatmapRenderer = class _SeatmapRenderer {
|
|
|
1576
2237
|
refreshSectionFill(sec) {
|
|
1577
2238
|
const sold = sec.total > 0 ? (sec.total - sec.free) / sec.total : 0;
|
|
1578
2239
|
sec.blockPoly.fill(darken(sec.baseFill, sold * SOLD_DARKEN));
|
|
1579
|
-
sec.subLabel.text(
|
|
2240
|
+
sec.subLabel.text(t("map.seatsLeft", { count: sec.free }));
|
|
1580
2241
|
sec.subLabel.offsetX(sec.subLabel.width() / 2);
|
|
1581
2242
|
}
|
|
1582
2243
|
/**
|
|
@@ -1594,8 +2255,10 @@ var _SeatmapRenderer = class _SeatmapRenderer {
|
|
|
1594
2255
|
for (const z of doc.zones) {
|
|
1595
2256
|
const members = byZone.get(z.id);
|
|
1596
2257
|
if (!members || !members.length) continue;
|
|
1597
|
-
|
|
1598
|
-
const
|
|
2258
|
+
let anchor = members[0];
|
|
2259
|
+
for (const s of members) if (s.centroid.y < anchor.centroid.y) anchor = s;
|
|
2260
|
+
const cx = anchor.centroid.x;
|
|
2261
|
+
const cy = anchor.centroid.y;
|
|
1599
2262
|
let minPrice = Infinity;
|
|
1600
2263
|
for (const sec of members) {
|
|
1601
2264
|
for (const id of sec.memberIds) {
|
|
@@ -1613,6 +2276,10 @@ var _SeatmapRenderer = class _SeatmapRenderer {
|
|
|
1613
2276
|
letterSpacing: 2,
|
|
1614
2277
|
fontFamily: this.labelFont(),
|
|
1615
2278
|
fill: z.color ?? "#f2f4f8",
|
|
2279
|
+
shadowColor: "#05070c",
|
|
2280
|
+
shadowBlur: 10,
|
|
2281
|
+
shadowOpacity: 0.95,
|
|
2282
|
+
shadowForStrokeEnabled: false,
|
|
1616
2283
|
opacity: 0,
|
|
1617
2284
|
listening: false,
|
|
1618
2285
|
perfectDrawEnabled: false
|
|
@@ -1625,7 +2292,7 @@ var _SeatmapRenderer = class _SeatmapRenderer {
|
|
|
1625
2292
|
sub = new import_Text.Text({
|
|
1626
2293
|
x: cx,
|
|
1627
2294
|
y: cy,
|
|
1628
|
-
text:
|
|
2295
|
+
text: t("map.fromPrice", { price: formatMoney(minPrice, this.currency) }),
|
|
1629
2296
|
fontSize: 14,
|
|
1630
2297
|
fontStyle: "600",
|
|
1631
2298
|
fontFamily: "JetBrains Mono, ui-monospace, monospace",
|
|
@@ -1656,7 +2323,7 @@ var _SeatmapRenderer = class _SeatmapRenderer {
|
|
|
1656
2323
|
blockT = scale <= SECTION_PROMINENT_SCALE ? 1 : 0;
|
|
1657
2324
|
zoneT = scale <= ZONE_PROMINENT_SCALE ? 1 : 0;
|
|
1658
2325
|
} else {
|
|
1659
|
-
blockT = clamp((
|
|
2326
|
+
blockT = clamp((BLOCK_MELT_TOP - scale) / (BLOCK_MELT_TOP - SECTION_PROMINENT_SCALE), 0, 1);
|
|
1660
2327
|
zoneT = clamp((SECTION_PROMINENT_SCALE - scale) / (SECTION_PROMINENT_SCALE - ZONE_PROMINENT_SCALE), 0, 1);
|
|
1661
2328
|
}
|
|
1662
2329
|
if (!this.zones.length) zoneT = 0;
|
|
@@ -1666,7 +2333,8 @@ var _SeatmapRenderer = class _SeatmapRenderer {
|
|
|
1666
2333
|
if (rescale) this.lodScale = scale;
|
|
1667
2334
|
for (const sec of this.sections) {
|
|
1668
2335
|
sec.blockPoly.opacity(BLOCK_FILL_ALPHA * blockT);
|
|
1669
|
-
sec.
|
|
2336
|
+
for (const line of sec.rowLines) line.opacity(blockT * (1 - zoneT));
|
|
2337
|
+
sec.nameLabel.fill(lerpColor("#aab3c5", "#ffffff", blockT));
|
|
1670
2338
|
sec.nameLabel.opacity(1 - zoneT);
|
|
1671
2339
|
sec.subLabel.opacity(blockT * (1 - zoneT));
|
|
1672
2340
|
if (rescale) {
|
|
@@ -1674,9 +2342,10 @@ var _SeatmapRenderer = class _SeatmapRenderer {
|
|
|
1674
2342
|
this.sizeLabel(sec.subLabel, SECTION_SUB_PX / sx, sec.centroid.y + SECTION_LABEL_PX / sx);
|
|
1675
2343
|
}
|
|
1676
2344
|
}
|
|
2345
|
+
const zoneOpacity = zoneT * (1 - this.isoT);
|
|
1677
2346
|
for (const zone of this.zones) {
|
|
1678
|
-
zone.label.opacity(
|
|
1679
|
-
if (zone.sub) zone.sub.opacity(
|
|
2347
|
+
zone.label.opacity(zoneOpacity);
|
|
2348
|
+
if (zone.sub) zone.sub.opacity(zoneOpacity);
|
|
1680
2349
|
if (rescale) {
|
|
1681
2350
|
const cy = zone.label.y();
|
|
1682
2351
|
this.sizeLabel(zone.label, ZONE_LABEL_PX / sx, cy);
|
|
@@ -1686,11 +2355,11 @@ var _SeatmapRenderer = class _SeatmapRenderer {
|
|
|
1686
2355
|
this.bgLayer.batchDraw();
|
|
1687
2356
|
}
|
|
1688
2357
|
/** Set a centred label's world fontSize (for a target screen px) and re-anchor it. */
|
|
1689
|
-
sizeLabel(
|
|
1690
|
-
|
|
1691
|
-
|
|
1692
|
-
|
|
1693
|
-
|
|
2358
|
+
sizeLabel(t2, fontSize, y) {
|
|
2359
|
+
t2.fontSize(Math.max(1, fontSize));
|
|
2360
|
+
t2.offsetX(t2.width() / 2);
|
|
2361
|
+
t2.offsetY(t2.height() / 2);
|
|
2362
|
+
t2.y(y);
|
|
1694
2363
|
}
|
|
1695
2364
|
/**
|
|
1696
2365
|
* Map a container-relative screen point back to world coords. Inverts the
|
|
@@ -1714,7 +2383,7 @@ var _SeatmapRenderer = class _SeatmapRenderer {
|
|
|
1714
2383
|
return this.sections.find((s) => s.id === id)?.memberIds.slice() ?? [];
|
|
1715
2384
|
}
|
|
1716
2385
|
addCentredLabel(layer, text, x, y, fill, fontSize, bold) {
|
|
1717
|
-
const
|
|
2386
|
+
const t2 = new import_Text.Text({
|
|
1718
2387
|
x,
|
|
1719
2388
|
y,
|
|
1720
2389
|
text,
|
|
@@ -1725,9 +2394,9 @@ var _SeatmapRenderer = class _SeatmapRenderer {
|
|
|
1725
2394
|
listening: false,
|
|
1726
2395
|
perfectDrawEnabled: false
|
|
1727
2396
|
});
|
|
1728
|
-
|
|
1729
|
-
|
|
1730
|
-
layer.add(
|
|
2397
|
+
t2.offsetX(t2.width() / 2);
|
|
2398
|
+
t2.offsetY(t2.height() / 2);
|
|
2399
|
+
layer.add(t2);
|
|
1731
2400
|
}
|
|
1732
2401
|
// ---- selection ------------------------------------------------------------
|
|
1733
2402
|
isSelectable(id) {
|
|
@@ -1781,7 +2450,15 @@ var _SeatmapRenderer = class _SeatmapRenderer {
|
|
|
1781
2450
|
this.seatLayer.on("click tap", (e) => {
|
|
1782
2451
|
if (this.moved > 8) return;
|
|
1783
2452
|
const id = seatIdOf(e.target);
|
|
1784
|
-
if (id)
|
|
2453
|
+
if (!id) return;
|
|
2454
|
+
if (this.stacked && this.opts.onDeckTap) {
|
|
2455
|
+
const floorId = this.objectFloor.get(this.seatById.get(id)?.rowId ?? "");
|
|
2456
|
+
if (floorId) {
|
|
2457
|
+
this.opts.onDeckTap(floorId);
|
|
2458
|
+
return;
|
|
2459
|
+
}
|
|
2460
|
+
}
|
|
2461
|
+
this.toggleSeat(id);
|
|
1785
2462
|
});
|
|
1786
2463
|
this.seatLayer.on("mouseover", (e) => {
|
|
1787
2464
|
const id = seatIdOf(e.target);
|
|
@@ -1811,11 +2488,19 @@ var _SeatmapRenderer = class _SeatmapRenderer {
|
|
|
1811
2488
|
if (!this.cached || this.moved > 8) return;
|
|
1812
2489
|
const pointer = this.stage.getPointerPosition();
|
|
1813
2490
|
if (!pointer) return;
|
|
2491
|
+
if (this.stacked && this.opts.onDeckTap) {
|
|
2492
|
+
const floorId = this.deckFloorAt();
|
|
2493
|
+
if (floorId) {
|
|
2494
|
+
this.opts.onDeckTap(floorId);
|
|
2495
|
+
return;
|
|
2496
|
+
}
|
|
2497
|
+
}
|
|
1814
2498
|
if (this.sections.length) {
|
|
1815
2499
|
const world = this.screenToWorld(pointer);
|
|
1816
2500
|
const hit = this.sections.find((sn) => pointInPolygon(world, sn.outline));
|
|
1817
2501
|
if (hit) {
|
|
1818
|
-
this.
|
|
2502
|
+
if (this.opts.onSectionTap) this.opts.onSectionTap(hit.id);
|
|
2503
|
+
else this.focusRegion(hit.id);
|
|
1819
2504
|
return;
|
|
1820
2505
|
}
|
|
1821
2506
|
}
|
|
@@ -1823,6 +2508,30 @@ var _SeatmapRenderer = class _SeatmapRenderer {
|
|
|
1823
2508
|
this.zoomAbout(target, pointer);
|
|
1824
2509
|
});
|
|
1825
2510
|
}
|
|
2511
|
+
/**
|
|
2512
|
+
* Which deck (floor) a screen tap landed on in the 3D stacked overview. The
|
|
2513
|
+
* seat layer is melt-cached at that zoom so individual seat nodes aren't
|
|
2514
|
+
* hit-testable; instead we invert the iso+stage transform via the layer's
|
|
2515
|
+
* relative pointer and take the nearest seat's floor. Only the floor matters,
|
|
2516
|
+
* so within-floor elevation offsets don't affect the result. Null if none.
|
|
2517
|
+
*/
|
|
2518
|
+
deckFloorAt() {
|
|
2519
|
+
if (!this.objectFloor.size) return null;
|
|
2520
|
+
const p = this.seatLayer.getRelativePointerPosition();
|
|
2521
|
+
if (!p) return null;
|
|
2522
|
+
let best = null;
|
|
2523
|
+
let bestD = Infinity;
|
|
2524
|
+
for (const s of this.seats) {
|
|
2525
|
+
const dx = s.x - p.x;
|
|
2526
|
+
const dy = s.y - p.y;
|
|
2527
|
+
const d = dx * dx + dy * dy;
|
|
2528
|
+
if (d < bestD) {
|
|
2529
|
+
bestD = d;
|
|
2530
|
+
best = s.rowId;
|
|
2531
|
+
}
|
|
2532
|
+
}
|
|
2533
|
+
return best ? this.objectFloor.get(best) ?? null : null;
|
|
2534
|
+
}
|
|
1826
2535
|
// ---- Pan & pinch (raw pointer events — mouse, touch and pen alike) --------
|
|
1827
2536
|
toLocal(e) {
|
|
1828
2537
|
const r = this.container.getBoundingClientRect();
|
|
@@ -1833,7 +2542,9 @@ var _SeatmapRenderer = class _SeatmapRenderer {
|
|
|
1833
2542
|
* reach a readable size on any chart, however large the venue.
|
|
1834
2543
|
*/
|
|
1835
2544
|
zoomBounds() {
|
|
1836
|
-
|
|
2545
|
+
let min = 0.5 * this.fitScale;
|
|
2546
|
+
if (this.zones.length) min = Math.min(min, ZONE_PROMINENT_SCALE * 0.9);
|
|
2547
|
+
return { min, max: Math.max(10 * this.fitScale, 4) };
|
|
1837
2548
|
}
|
|
1838
2549
|
/** Zoom so `clientPoint` (stage-relative px) stays fixed under the cursor. */
|
|
1839
2550
|
zoomAbout(nextScale, clientPoint) {
|
|
@@ -1866,6 +2577,92 @@ var _SeatmapRenderer = class _SeatmapRenderer {
|
|
|
1866
2577
|
this.afterViewChange();
|
|
1867
2578
|
this.stage.batchDraw();
|
|
1868
2579
|
}
|
|
2580
|
+
/**
|
|
2581
|
+
* Smoothly glide the camera to frame a section (by id) or a world-space bounds
|
|
2582
|
+
* rect — the Slice 5 "glide in". Pan+zoom tween over ~450ms easeInOutCubic; the
|
|
2583
|
+
* melt/LOD rides the camera every frame. `prefers-reduced-motion` (or
|
|
2584
|
+
* `opts.animate === false`) snaps via zoomToBounds. A grab (pointer-down) or a
|
|
2585
|
+
* newer glide cancels an in-flight one.
|
|
2586
|
+
*/
|
|
2587
|
+
focusRegion(target, opts) {
|
|
2588
|
+
const b = typeof target === "string" ? (() => {
|
|
2589
|
+
const sec = this.sections.find((s) => s.id === target);
|
|
2590
|
+
return sec ? polyBounds(sec.outline) : null;
|
|
2591
|
+
})() : target;
|
|
2592
|
+
if (!b) return;
|
|
2593
|
+
this.cancelGlide();
|
|
2594
|
+
if (opts?.animate === false || this.reducedMotion) {
|
|
2595
|
+
this.zoomToBounds(b);
|
|
2596
|
+
return;
|
|
2597
|
+
}
|
|
2598
|
+
const w = this.stage.width();
|
|
2599
|
+
const h = this.stage.height();
|
|
2600
|
+
const { min, max } = this.zoomBounds();
|
|
2601
|
+
const margin = 1.12;
|
|
2602
|
+
const toScale = clamp(Math.min(w / (b.width * margin), h / (b.height * margin)), min, max);
|
|
2603
|
+
const toX = w / 2 - (b.x + b.width / 2) * toScale;
|
|
2604
|
+
const toY = h / 2 - (b.y + b.height / 2) * toScale;
|
|
2605
|
+
const fromScale = this.stage.scaleX();
|
|
2606
|
+
const fromX = this.stage.x();
|
|
2607
|
+
const fromY = this.stage.y();
|
|
2608
|
+
if (Math.abs(toScale - fromScale) < 1e-4 && Math.abs(toX - fromX) < 0.5 && Math.abs(toY - fromY) < 0.5) {
|
|
2609
|
+
this.afterViewChange();
|
|
2610
|
+
return;
|
|
2611
|
+
}
|
|
2612
|
+
const start = performance.now();
|
|
2613
|
+
const DUR = 450;
|
|
2614
|
+
const step = (now) => {
|
|
2615
|
+
if (this.destroyed) {
|
|
2616
|
+
this.glideRaf = 0;
|
|
2617
|
+
return;
|
|
2618
|
+
}
|
|
2619
|
+
const raw = Math.min(1, (now - start) / DUR);
|
|
2620
|
+
const e = raw < 0.5 ? 4 * raw * raw * raw : 1 - Math.pow(-2 * raw + 2, 3) / 2;
|
|
2621
|
+
const sc = fromScale + (toScale - fromScale) * e;
|
|
2622
|
+
this.stage.scale({ x: sc, y: sc });
|
|
2623
|
+
this.stage.position({ x: fromX + (toX - fromX) * e, y: fromY + (toY - fromY) * e });
|
|
2624
|
+
this.updateLOD();
|
|
2625
|
+
this.scheduleViewChange();
|
|
2626
|
+
this.stage.batchDraw();
|
|
2627
|
+
if (raw < 1) {
|
|
2628
|
+
this.glideRaf = requestAnimationFrame(step);
|
|
2629
|
+
} else {
|
|
2630
|
+
this.glideRaf = 0;
|
|
2631
|
+
this.afterViewChange();
|
|
2632
|
+
}
|
|
2633
|
+
};
|
|
2634
|
+
this.glideRaf = requestAnimationFrame(step);
|
|
2635
|
+
}
|
|
2636
|
+
/** Cancel an in-flight camera glide (a grab or a newer glide interrupts it). */
|
|
2637
|
+
cancelGlide() {
|
|
2638
|
+
if (this.glideRaf) {
|
|
2639
|
+
cancelAnimationFrame(this.glideRaf);
|
|
2640
|
+
this.glideRaf = 0;
|
|
2641
|
+
}
|
|
2642
|
+
}
|
|
2643
|
+
/** Current LOD rung derived from effective zoom — drives the ZONES/SECTIONS/SEATS pill. */
|
|
2644
|
+
getRung() {
|
|
2645
|
+
const scale = this.effScale();
|
|
2646
|
+
if (scale >= CACHE_THRESHOLD) return "seats";
|
|
2647
|
+
if (this.zones.length && scale < ZONE_PROMINENT_SCALE) return "zones";
|
|
2648
|
+
return "sections";
|
|
2649
|
+
}
|
|
2650
|
+
/** Jump the camera to a rung's zoom band, centred on the chart (glided). */
|
|
2651
|
+
setRung(rung) {
|
|
2652
|
+
if (rung === "zones") {
|
|
2653
|
+
this.cancelGlide();
|
|
2654
|
+
this.zoomToFit();
|
|
2655
|
+
return;
|
|
2656
|
+
}
|
|
2657
|
+
const target = rung === "sections" ? (SECTION_PROMINENT_SCALE + CACHE_THRESHOLD) / 2 : Math.max(SEAT_LEGIBLE_SCALE * 1.1, CACHE_THRESHOLD * 1.3);
|
|
2658
|
+
const w = this.stage.width();
|
|
2659
|
+
const h = this.stage.height();
|
|
2660
|
+
const cx = this.bounds.x + this.bounds.width / 2;
|
|
2661
|
+
const cy = this.bounds.y + this.bounds.height / 2;
|
|
2662
|
+
const bw = w / (target * 1.12);
|
|
2663
|
+
const bh = h / (target * 1.12);
|
|
2664
|
+
this.focusRegion({ x: cx - bw / 2, y: cy - bh / 2, width: bw, height: bh });
|
|
2665
|
+
}
|
|
1869
2666
|
/** Recompute LOD (cache/labels) after any pan/zoom settles. */
|
|
1870
2667
|
afterViewChange() {
|
|
1871
2668
|
this.updateLOD();
|
|
@@ -1916,7 +2713,7 @@ var _SeatmapRenderer = class _SeatmapRenderer {
|
|
|
1916
2713
|
let count = 0;
|
|
1917
2714
|
for (const seat of this.seats) {
|
|
1918
2715
|
if (seat.x < x0 || seat.x > x1 || seat.y < y0 || seat.y > y1) continue;
|
|
1919
|
-
const
|
|
2716
|
+
const t2 = new import_Text.Text({
|
|
1920
2717
|
x: seat.x,
|
|
1921
2718
|
y: seat.y,
|
|
1922
2719
|
text: seat.label,
|
|
@@ -1928,14 +2725,14 @@ var _SeatmapRenderer = class _SeatmapRenderer {
|
|
|
1928
2725
|
perfectDrawEnabled: false
|
|
1929
2726
|
});
|
|
1930
2727
|
const maxW = this.seatR * 2 - 3;
|
|
1931
|
-
if (
|
|
1932
|
-
if (
|
|
1933
|
-
|
|
2728
|
+
if (t2.width() > maxW) t2.fontSize(Math.max(4, 7 * maxW / t2.width()));
|
|
2729
|
+
if (t2.fontSize() < 4.2) {
|
|
2730
|
+
t2.destroy();
|
|
1934
2731
|
continue;
|
|
1935
2732
|
}
|
|
1936
|
-
|
|
1937
|
-
|
|
1938
|
-
this.labelGroup.add(
|
|
2733
|
+
t2.offsetX(t2.width() / 2);
|
|
2734
|
+
t2.offsetY(t2.height() / 2);
|
|
2735
|
+
this.labelGroup.add(t2);
|
|
1939
2736
|
if (++count >= MAX_LABELS) break;
|
|
1940
2737
|
}
|
|
1941
2738
|
if (this.isoT > 0) this.applyUprightLabels();
|
|
@@ -1986,9 +2783,15 @@ var PickerController = class {
|
|
|
1986
2783
|
constructor(options) {
|
|
1987
2784
|
this.renderer = null;
|
|
1988
2785
|
this._doc = null;
|
|
2786
|
+
/** Section/zone ids hidden from buyers this event (3.3) — seats vanish, not grey. */
|
|
2787
|
+
this.hidden = /* @__PURE__ */ new Set();
|
|
1989
2788
|
/** label ⇄ id maps — backend speaks labels, the engine speaks ids. */
|
|
1990
2789
|
this.labelToId = /* @__PURE__ */ new Map();
|
|
1991
2790
|
this.labelToSeat = /* @__PURE__ */ new Map();
|
|
2791
|
+
/** seatId → chosen ticket-tier id (absent ⇒ the category's first/default tier). */
|
|
2792
|
+
this.seatTiers = /* @__PURE__ */ new Map();
|
|
2793
|
+
/** id → seat, for the section-summary breakdown (renderer members are ids). */
|
|
2794
|
+
this.seatById = /* @__PURE__ */ new Map();
|
|
1992
2795
|
this.allIds = [];
|
|
1993
2796
|
// realtime socket
|
|
1994
2797
|
this.ws = null;
|
|
@@ -2006,6 +2809,18 @@ var PickerController = class {
|
|
|
2006
2809
|
get doc() {
|
|
2007
2810
|
return this._doc;
|
|
2008
2811
|
}
|
|
2812
|
+
/** The chart with hidden sections' seats removed — what buyers actually see. */
|
|
2813
|
+
visibleDoc() {
|
|
2814
|
+
return this._doc ? applyHidden(this._doc, this.hidden) : { objects: [] };
|
|
2815
|
+
}
|
|
2816
|
+
/** Adopt a new hidden set; rebuild the visible chart only if it differs. */
|
|
2817
|
+
syncHidden(ids) {
|
|
2818
|
+
const next = ids.filter((x) => typeof x === "string");
|
|
2819
|
+
if (next.length === this.hidden.size && next.every((id) => this.hidden.has(id))) return false;
|
|
2820
|
+
this.hidden = new Set(next);
|
|
2821
|
+
this.renderer?.setChart(this.visibleDoc());
|
|
2822
|
+
return true;
|
|
2823
|
+
}
|
|
2009
2824
|
currentHold() {
|
|
2010
2825
|
return this.hold_;
|
|
2011
2826
|
}
|
|
@@ -2033,15 +2848,19 @@ var PickerController = class {
|
|
|
2033
2848
|
this._doc = res.doc;
|
|
2034
2849
|
this.labelToId = /* @__PURE__ */ new Map();
|
|
2035
2850
|
this.labelToSeat = /* @__PURE__ */ new Map();
|
|
2851
|
+
this.seatById = /* @__PURE__ */ new Map();
|
|
2036
2852
|
this.allIds = [];
|
|
2037
2853
|
for (const s of expandChart(res.doc)) {
|
|
2038
2854
|
this.labelToId.set(s.label, s.id);
|
|
2039
2855
|
this.labelToSeat.set(s.label, s);
|
|
2856
|
+
this.seatById.set(s.id, s);
|
|
2040
2857
|
this.allIds.push(s.id);
|
|
2041
2858
|
}
|
|
2859
|
+
const currency = res.event.currency ?? this.opts.currency;
|
|
2042
2860
|
const renderer = createRenderer(host, {
|
|
2043
2861
|
maxSelection: this.maxSelection,
|
|
2044
2862
|
confirmSelection: this.opts.confirmSelection,
|
|
2863
|
+
currency,
|
|
2045
2864
|
onSelect: (seat) => {
|
|
2046
2865
|
this.opts.onSelect?.(seat);
|
|
2047
2866
|
this.emitSelectionChange();
|
|
@@ -2054,6 +2873,9 @@ var PickerController = class {
|
|
|
2054
2873
|
onFocusSeat: this.opts.onFocusSeat,
|
|
2055
2874
|
onViewChange: this.opts.onViewChange,
|
|
2056
2875
|
onGAClick: this.opts.onGAClick,
|
|
2876
|
+
// Section tap at the far/zone rung → glide in + surface the summary card.
|
|
2877
|
+
onSectionTap: (id) => this.handleSectionTap(id),
|
|
2878
|
+
onDeckTap: (floorId) => this.handleDeckTap(floorId),
|
|
2057
2879
|
onFps: this.opts.onFps
|
|
2058
2880
|
});
|
|
2059
2881
|
if (this.closed) {
|
|
@@ -2061,15 +2883,28 @@ var PickerController = class {
|
|
|
2061
2883
|
return null;
|
|
2062
2884
|
}
|
|
2063
2885
|
this.renderer = renderer;
|
|
2064
|
-
|
|
2065
|
-
|
|
2886
|
+
let seats = null;
|
|
2887
|
+
try {
|
|
2888
|
+
const objs = await this.api.objects(this.key);
|
|
2889
|
+
this.hidden = new Set(objs.hidden ?? []);
|
|
2890
|
+
seats = objs.seats;
|
|
2891
|
+
} catch {
|
|
2892
|
+
}
|
|
2893
|
+
if (this.closed) {
|
|
2894
|
+
renderer.destroy();
|
|
2895
|
+
this.renderer = null;
|
|
2896
|
+
return null;
|
|
2897
|
+
}
|
|
2898
|
+
renderer.setChart(this.visibleDoc());
|
|
2899
|
+
if (seats) this.applySeatsMap(seats);
|
|
2066
2900
|
this.connect();
|
|
2067
2901
|
return {
|
|
2068
2902
|
doc: res.doc,
|
|
2069
2903
|
salesClosed: !!res.event.salesClosed,
|
|
2070
2904
|
eventName: res.event.name,
|
|
2071
2905
|
venue: res.event.venue,
|
|
2072
|
-
startsAt: res.event.startsAt
|
|
2906
|
+
startsAt: res.event.startsAt,
|
|
2907
|
+
currency
|
|
2073
2908
|
};
|
|
2074
2909
|
}
|
|
2075
2910
|
// ---- selection ------------------------------------------------------------
|
|
@@ -2241,6 +3076,126 @@ var PickerController = class {
|
|
|
2241
3076
|
setAccessibilityFilter(types) {
|
|
2242
3077
|
this.renderer?.setAccessibilityFilter?.(types);
|
|
2243
3078
|
}
|
|
3079
|
+
// ---- multi-floor (Batch 5) ------------------------------------------------
|
|
3080
|
+
/** Floors for the buyer's switcher (>1 ⇒ show it). Single-floor ⇒ one entry. */
|
|
3081
|
+
getFloors() {
|
|
3082
|
+
return this.renderer?.getFloors?.() ?? [];
|
|
3083
|
+
}
|
|
3084
|
+
getActiveFloorId() {
|
|
3085
|
+
return this.renderer?.getActiveFloorId?.() ?? "";
|
|
3086
|
+
}
|
|
3087
|
+
/** Switch the shown floor (2D), then re-apply live seat statuses onto it. */
|
|
3088
|
+
setFloor(id) {
|
|
3089
|
+
const r = this.renderer;
|
|
3090
|
+
if (!r?.setActiveFloor || id === r.getActiveFloorId?.()) return;
|
|
3091
|
+
r.setStacked?.(false);
|
|
3092
|
+
r.setActiveFloor(id);
|
|
3093
|
+
void this.resnapshot();
|
|
3094
|
+
this.emitSelectionChange();
|
|
3095
|
+
}
|
|
3096
|
+
/** 3D all-floors stacked overview ⇄ active floor. Re-applies statuses after. */
|
|
3097
|
+
setStacked(on) {
|
|
3098
|
+
const r = this.renderer;
|
|
3099
|
+
if (!r?.setStacked || on === r.isStacked?.()) return;
|
|
3100
|
+
r.setStacked(on);
|
|
3101
|
+
void this.resnapshot();
|
|
3102
|
+
}
|
|
3103
|
+
isMultiFloor() {
|
|
3104
|
+
return (this._doc?.floors?.length ?? 0) > 1;
|
|
3105
|
+
}
|
|
3106
|
+
/** Tap-a-deck-to-enter: leave the 3D stack, drop to flat 2D on `floorId`, and
|
|
3107
|
+
* tell the host so it can sync its 2D/3D toggle + floor-switcher state. */
|
|
3108
|
+
handleDeckTap(floorId) {
|
|
3109
|
+
const r = this.renderer;
|
|
3110
|
+
if (!r) return;
|
|
3111
|
+
r.setViewMode?.("flat");
|
|
3112
|
+
r.setStacked?.(false);
|
|
3113
|
+
r.setActiveFloor?.(floorId);
|
|
3114
|
+
void this.resnapshot();
|
|
3115
|
+
this.emitSelectionChange();
|
|
3116
|
+
this.opts.onDeckTap?.(floorId);
|
|
3117
|
+
}
|
|
3118
|
+
// ---- big-venue: sections / rungs / projection (Slice 5) -------------------
|
|
3119
|
+
/** Switch the map projection (2D flat ⇄ 3D isometric). No-op on a flat renderer. */
|
|
3120
|
+
setViewMode(mode) {
|
|
3121
|
+
this.renderer?.setViewMode?.(mode);
|
|
3122
|
+
}
|
|
3123
|
+
getViewMode() {
|
|
3124
|
+
return this.renderer?.getViewMode?.() ?? "flat";
|
|
3125
|
+
}
|
|
3126
|
+
/** Current LOD rung (for the ZONES/SECTIONS/SEATS pill). */
|
|
3127
|
+
getRung() {
|
|
3128
|
+
return this.renderer?.getRung?.() ?? "seats";
|
|
3129
|
+
}
|
|
3130
|
+
/** Jump to a rung; ZONES clears any focused summary (back to overview). */
|
|
3131
|
+
setRung(rung) {
|
|
3132
|
+
if (rung === "zones") {
|
|
3133
|
+
this.overview();
|
|
3134
|
+
return;
|
|
3135
|
+
}
|
|
3136
|
+
this.renderer?.setRung?.(rung);
|
|
3137
|
+
}
|
|
3138
|
+
/** Glide in on a section and surface its summary (same path as a section tap). */
|
|
3139
|
+
focusSection(id) {
|
|
3140
|
+
this.handleSectionTap(id);
|
|
3141
|
+
}
|
|
3142
|
+
/** Zoom back out to the whole chart and clear the section-summary card. */
|
|
3143
|
+
overview() {
|
|
3144
|
+
this.renderer?.zoomToFit();
|
|
3145
|
+
this.opts.onSectionFocus?.(null);
|
|
3146
|
+
}
|
|
3147
|
+
/** Glide the camera into a tapped section and emit its computed summary. */
|
|
3148
|
+
handleSectionTap(id) {
|
|
3149
|
+
const r = this.renderer;
|
|
3150
|
+
if (!r) return;
|
|
3151
|
+
r.focusRegion?.(id);
|
|
3152
|
+
this.opts.onSectionFocus?.(this.sectionSummary(id));
|
|
3153
|
+
}
|
|
3154
|
+
/**
|
|
3155
|
+
* Build a section summary from the renderer's spatial membership: section +
|
|
3156
|
+
* zone labels, live seats-left, price range, and the per-category breakdown.
|
|
3157
|
+
*/
|
|
3158
|
+
sectionSummary(id) {
|
|
3159
|
+
const r = this.renderer;
|
|
3160
|
+
const doc = this._doc;
|
|
3161
|
+
if (!r || !doc) return null;
|
|
3162
|
+
const sec = doc.objects.find(
|
|
3163
|
+
(o) => o.type === "section" && o.id === id
|
|
3164
|
+
);
|
|
3165
|
+
if (!sec) return null;
|
|
3166
|
+
const memberIds = r.sectionMembers?.(id) ?? [];
|
|
3167
|
+
const byCat = /* @__PURE__ */ new Map();
|
|
3168
|
+
let seatsLeft = 0;
|
|
3169
|
+
for (const sid of memberIds) {
|
|
3170
|
+
const seat = this.seatById.get(sid);
|
|
3171
|
+
if (!seat) continue;
|
|
3172
|
+
byCat.set(seat.categoryKey, (byCat.get(seat.categoryKey) ?? 0) + 1);
|
|
3173
|
+
if (r.getStatus(sid) === "free") seatsLeft++;
|
|
3174
|
+
}
|
|
3175
|
+
const categories = [...byCat.entries()].map(([key, count]) => {
|
|
3176
|
+
const cat = doc.categories.find((c) => c.key === key);
|
|
3177
|
+
return {
|
|
3178
|
+
key,
|
|
3179
|
+
count,
|
|
3180
|
+
label: cat?.label ?? key,
|
|
3181
|
+
color: cat?.color ?? "#6e7bff",
|
|
3182
|
+
price: cat?.price ?? 0
|
|
3183
|
+
};
|
|
3184
|
+
}).sort((a, b) => a.price - b.price);
|
|
3185
|
+
const prices = categories.map((c) => c.price);
|
|
3186
|
+
const zone = sec.zone ? doc.zones?.find((z) => z.id === sec.zone) : void 0;
|
|
3187
|
+
const color = sec.color ?? zone?.color ?? categories[0]?.color ?? "#6e7bff";
|
|
3188
|
+
return {
|
|
3189
|
+
id,
|
|
3190
|
+
label: sec.label,
|
|
3191
|
+
zoneLabel: zone?.label ?? "",
|
|
3192
|
+
color,
|
|
3193
|
+
seatsLeft,
|
|
3194
|
+
priceMin: prices.length ? prices[0] : 0,
|
|
3195
|
+
priceMax: prices.length ? prices[prices.length - 1] : 0,
|
|
3196
|
+
categories
|
|
3197
|
+
};
|
|
3198
|
+
}
|
|
2244
3199
|
destroy() {
|
|
2245
3200
|
this.closed = true;
|
|
2246
3201
|
if (this.reconnectTimer) {
|
|
@@ -2263,11 +3218,53 @@ var PickerController = class {
|
|
|
2263
3218
|
}
|
|
2264
3219
|
// ---- internals ------------------------------------------------------------
|
|
2265
3220
|
toSeat(s) {
|
|
2266
|
-
|
|
3221
|
+
const tiers = this.tiersFor(s.categoryKey);
|
|
3222
|
+
if (!tiers) {
|
|
3223
|
+
return { id: s.id, label: s.label, categoryKey: s.categoryKey, price: this.priceFor(s.categoryKey) };
|
|
3224
|
+
}
|
|
3225
|
+
const chosen = tiers.find((t2) => t2.id === this.seatTiers.get(s.id)) ?? tiers[0];
|
|
3226
|
+
return {
|
|
3227
|
+
id: s.id,
|
|
3228
|
+
label: s.label,
|
|
3229
|
+
categoryKey: s.categoryKey,
|
|
3230
|
+
price: chosen.price,
|
|
3231
|
+
tiers,
|
|
3232
|
+
tierId: chosen.id
|
|
3233
|
+
};
|
|
2267
3234
|
}
|
|
2268
3235
|
priceFor(categoryKey) {
|
|
2269
3236
|
return this._doc?.categories.find((c) => c.key === categoryKey)?.price ?? 0;
|
|
2270
3237
|
}
|
|
3238
|
+
tiersFor(categoryKey) {
|
|
3239
|
+
const t2 = this._doc?.categories.find((c) => c.key === categoryKey)?.tiers;
|
|
3240
|
+
return t2 && t2.length ? t2 : void 0;
|
|
3241
|
+
}
|
|
3242
|
+
/**
|
|
3243
|
+
* Choose a ticket tier for a selected seat (e.g. Adult → Child). Re-emits the
|
|
3244
|
+
* selection so the host's cart + the eventual hold carry the new tier + price.
|
|
3245
|
+
* `tierId = null` reverts to the category's first (default) tier. No-op if the
|
|
3246
|
+
* seat's category has no tiers or the id isn't one of them.
|
|
3247
|
+
*/
|
|
3248
|
+
setSeatTier(seatId, tierId) {
|
|
3249
|
+
const seat = this.seatById.get(seatId);
|
|
3250
|
+
if (!seat) return;
|
|
3251
|
+
const tiers = this.tiersFor(seat.categoryKey);
|
|
3252
|
+
if (!tiers) return;
|
|
3253
|
+
if (tierId == null) this.seatTiers.delete(seatId);
|
|
3254
|
+
else if (tiers.some((t2) => t2.id === tierId)) this.seatTiers.set(seatId, tierId);
|
|
3255
|
+
else return;
|
|
3256
|
+
this.emitSelectionChange();
|
|
3257
|
+
if (this.hold_) this.hold_ = { ...this.hold_, seats: this.seatsForLabels(this.hold_.labels) };
|
|
3258
|
+
}
|
|
3259
|
+
/** PickerSeats (with chosen tier) for a set of held labels. */
|
|
3260
|
+
seatsForLabels(labels) {
|
|
3261
|
+
const out = [];
|
|
3262
|
+
for (const l of labels) {
|
|
3263
|
+
const s = this.labelToSeat.get(l);
|
|
3264
|
+
if (s) out.push(this.toSeat(s));
|
|
3265
|
+
}
|
|
3266
|
+
return out;
|
|
3267
|
+
}
|
|
2271
3268
|
emitSelectionChange() {
|
|
2272
3269
|
this.opts.onSelectionChange?.(this.getSelection());
|
|
2273
3270
|
}
|
|
@@ -2293,11 +3290,12 @@ var PickerController = class {
|
|
|
2293
3290
|
}
|
|
2294
3291
|
/** Set the open hold + (re)arm the server-authoritative expiry timer. */
|
|
2295
3292
|
setHold(hold) {
|
|
2296
|
-
|
|
3293
|
+
const full = { ...hold, seats: this.seatsForLabels(hold.labels) };
|
|
3294
|
+
this.hold_ = full;
|
|
2297
3295
|
if (this.expiryTimer) clearTimeout(this.expiryTimer);
|
|
2298
|
-
const ms = Math.max(0,
|
|
3296
|
+
const ms = Math.max(0, full.expiresAt - Date.now());
|
|
2299
3297
|
this.expiryTimer = setTimeout(() => this.onHoldExpired(), ms);
|
|
2300
|
-
this.opts.onHold?.(
|
|
3298
|
+
this.opts.onHold?.(full);
|
|
2301
3299
|
}
|
|
2302
3300
|
clearHold() {
|
|
2303
3301
|
this.hold_ = null;
|
|
@@ -2361,6 +3359,11 @@ var PickerController = class {
|
|
|
2361
3359
|
const r = this.renderer;
|
|
2362
3360
|
if (!r || !msg || typeof msg !== "object") return;
|
|
2363
3361
|
const m = msg;
|
|
3362
|
+
if (Array.isArray(m.hidden) && this.syncHidden(m.hidden)) {
|
|
3363
|
+
void this.resnapshot();
|
|
3364
|
+
this.opts.onStatusChange?.();
|
|
3365
|
+
}
|
|
3366
|
+
if (m.type === "hidden") return;
|
|
2364
3367
|
if (m.seats && typeof m.seats === "object") {
|
|
2365
3368
|
this.applySeatsMap(m.seats);
|
|
2366
3369
|
} else if (Array.isArray(m.changes)) {
|
|
@@ -2397,22 +3400,68 @@ var PickerController = class {
|
|
|
2397
3400
|
}, delay);
|
|
2398
3401
|
}
|
|
2399
3402
|
};
|
|
3403
|
+
|
|
3404
|
+
// src/i18n/bundles.ts
|
|
3405
|
+
var LOADERS = {
|
|
3406
|
+
es: () => Promise.resolve().then(() => (init_es(), es_exports)).then((m) => ({ default: m.es })),
|
|
3407
|
+
de: () => Promise.resolve().then(() => (init_de(), de_exports)).then((m) => ({ default: m.de })),
|
|
3408
|
+
fr: () => Promise.resolve().then(() => (init_fr(), fr_exports)).then((m) => ({ default: m.fr }))
|
|
3409
|
+
};
|
|
3410
|
+
var loaded = /* @__PURE__ */ new Set(["en"]);
|
|
3411
|
+
async function loadLocale(code) {
|
|
3412
|
+
const locale = resolveLocale(code);
|
|
3413
|
+
if (locale === "en" || loaded.has(locale)) {
|
|
3414
|
+
setLocale(locale);
|
|
3415
|
+
return locale;
|
|
3416
|
+
}
|
|
3417
|
+
try {
|
|
3418
|
+
const mod = await LOADERS[locale]();
|
|
3419
|
+
setLocale(locale, mod.default);
|
|
3420
|
+
loaded.add(locale);
|
|
3421
|
+
return locale;
|
|
3422
|
+
} catch {
|
|
3423
|
+
setLocale("en");
|
|
3424
|
+
return "en";
|
|
3425
|
+
}
|
|
3426
|
+
}
|
|
2400
3427
|
// Annotate the CommonJS export names for ESM import in node:
|
|
2401
3428
|
0 && (module.exports = {
|
|
2402
3429
|
ACCESSIBILITY_TYPES,
|
|
2403
3430
|
CHART_STORAGE_KEY,
|
|
3431
|
+
DEFAULT_CURRENCY,
|
|
2404
3432
|
PickerController,
|
|
3433
|
+
SUPPORTED_LOCALES,
|
|
2405
3434
|
SeatmapRenderer,
|
|
3435
|
+
UNGROUPED_ID,
|
|
2406
3436
|
accessibilityMeta,
|
|
3437
|
+
allObjects,
|
|
3438
|
+
applyHidden,
|
|
2407
3439
|
chartBounds,
|
|
3440
|
+
computeSections,
|
|
2408
3441
|
createRenderer,
|
|
3442
|
+
currencySymbol,
|
|
2409
3443
|
expandBooth,
|
|
2410
3444
|
expandChart,
|
|
2411
3445
|
expandRow,
|
|
2412
3446
|
expandRowSlots,
|
|
2413
3447
|
expandTable,
|
|
3448
|
+
floorObjects,
|
|
3449
|
+
floorsOf,
|
|
3450
|
+
formatDate,
|
|
3451
|
+
formatMoney,
|
|
3452
|
+
getLocale,
|
|
3453
|
+
hiddenObjectIds,
|
|
3454
|
+
isSectionHidden,
|
|
2414
3455
|
layerOf,
|
|
3456
|
+
loadLocale,
|
|
2415
3457
|
objectCenter,
|
|
2416
|
-
pointInPolygon
|
|
3458
|
+
pointInPolygon,
|
|
3459
|
+
resolveLocale,
|
|
3460
|
+
setLocale,
|
|
3461
|
+
setMoneyLocale,
|
|
3462
|
+
setStringOverrides,
|
|
3463
|
+
stackFloors,
|
|
3464
|
+
t,
|
|
3465
|
+
tCount
|
|
2417
3466
|
});
|
|
2418
3467
|
//# sourceMappingURL=index.cjs.map
|