@nex125/seatmap-viewer 0.1.0 → 0.1.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.d.cts CHANGED
@@ -1,17 +1,41 @@
1
1
  import * as react_jsx_runtime from 'react/jsx-runtime';
2
+ import { ReactNode } from 'react';
2
3
  import { Venue, SeatStatus } from '@nex125/seatmap-core';
3
4
  import { TooltipData } from '@nex125/seatmap-react';
4
5
  export { useSeatStatus } from '@nex125/seatmap-react';
5
6
 
7
+ interface SeatmapCartSeat {
8
+ seatId: string;
9
+ seatLabel: string;
10
+ rowLabel: string | null;
11
+ sectionId: string | null;
12
+ sectionLabel: string | null;
13
+ categoryId: string;
14
+ categoryName: string;
15
+ unitPrice: number;
16
+ }
17
+ interface SeatmapCartPayload {
18
+ venueId: string;
19
+ venueName: string;
20
+ seats: SeatmapCartSeat[];
21
+ totalSelectedSeats: number;
22
+ totalCost: number;
23
+ }
24
+ interface SeatmapCartEvent {
25
+ type: "cart-proceed-clicked";
26
+ payload: SeatmapCartPayload;
27
+ }
6
28
  interface SeatmapViewerProps {
7
29
  venue: Venue;
8
30
  onSeatClick?: (seatId: string, sectionId: string) => void;
9
31
  onSeatHover?: (seatId: string | null, sectionId: string | null) => void;
10
32
  onSelectionChange?: (seatIds: string[]) => void;
11
33
  onStatusUpdate?: (seatId: string, status: SeatStatus) => void;
12
- renderTooltip?: (data: TooltipData) => React.ReactNode;
34
+ renderTooltip?: (data: TooltipData) => ReactNode;
35
+ showLabels?: boolean;
13
36
  className?: string;
37
+ onCartEvent?: (event: SeatmapCartEvent) => void;
14
38
  }
15
- declare function SeatmapViewer({ venue, onSeatClick, onSeatHover, renderTooltip, className, }: SeatmapViewerProps): react_jsx_runtime.JSX.Element;
39
+ declare function SeatmapViewer({ venue, onSeatClick, onSeatHover, onSelectionChange, renderTooltip, showLabels, className, onCartEvent, }: SeatmapViewerProps): react_jsx_runtime.JSX.Element;
16
40
 
17
- export { SeatmapViewer, type SeatmapViewerProps };
41
+ export { type SeatmapCartEvent, type SeatmapCartPayload, type SeatmapCartSeat, SeatmapViewer, type SeatmapViewerProps };
package/dist/index.d.ts CHANGED
@@ -1,17 +1,41 @@
1
1
  import * as react_jsx_runtime from 'react/jsx-runtime';
2
+ import { ReactNode } from 'react';
2
3
  import { Venue, SeatStatus } from '@nex125/seatmap-core';
3
4
  import { TooltipData } from '@nex125/seatmap-react';
4
5
  export { useSeatStatus } from '@nex125/seatmap-react';
5
6
 
7
+ interface SeatmapCartSeat {
8
+ seatId: string;
9
+ seatLabel: string;
10
+ rowLabel: string | null;
11
+ sectionId: string | null;
12
+ sectionLabel: string | null;
13
+ categoryId: string;
14
+ categoryName: string;
15
+ unitPrice: number;
16
+ }
17
+ interface SeatmapCartPayload {
18
+ venueId: string;
19
+ venueName: string;
20
+ seats: SeatmapCartSeat[];
21
+ totalSelectedSeats: number;
22
+ totalCost: number;
23
+ }
24
+ interface SeatmapCartEvent {
25
+ type: "cart-proceed-clicked";
26
+ payload: SeatmapCartPayload;
27
+ }
6
28
  interface SeatmapViewerProps {
7
29
  venue: Venue;
8
30
  onSeatClick?: (seatId: string, sectionId: string) => void;
9
31
  onSeatHover?: (seatId: string | null, sectionId: string | null) => void;
10
32
  onSelectionChange?: (seatIds: string[]) => void;
11
33
  onStatusUpdate?: (seatId: string, status: SeatStatus) => void;
12
- renderTooltip?: (data: TooltipData) => React.ReactNode;
34
+ renderTooltip?: (data: TooltipData) => ReactNode;
35
+ showLabels?: boolean;
13
36
  className?: string;
37
+ onCartEvent?: (event: SeatmapCartEvent) => void;
14
38
  }
15
- declare function SeatmapViewer({ venue, onSeatClick, onSeatHover, renderTooltip, className, }: SeatmapViewerProps): react_jsx_runtime.JSX.Element;
39
+ declare function SeatmapViewer({ venue, onSeatClick, onSeatHover, onSelectionChange, renderTooltip, showLabels, className, onCartEvent, }: SeatmapViewerProps): react_jsx_runtime.JSX.Element;
16
40
 
17
- export { SeatmapViewer, type SeatmapViewerProps };
41
+ export { type SeatmapCartEvent, type SeatmapCartPayload, type SeatmapCartSeat, SeatmapViewer, type SeatmapViewerProps };
package/dist/index.js CHANGED
@@ -1,25 +1,553 @@
1
- import { SeatmapProvider, SeatmapCanvas, TooltipOverlay } from '@nex125/seatmap-react';
1
+ import { useState, useRef, useMemo, useEffect, useCallback } from 'react';
2
+ import { AVAILABLE_STATUS_ID } from '@nex125/seatmap-core';
3
+ import { SeatmapProvider, useSelection, SeatmapCanvas, TooltipOverlay } from '@nex125/seatmap-react';
2
4
  export { useSeatStatus } from '@nex125/seatmap-react';
3
5
  import { jsx, jsxs } from 'react/jsx-runtime';
4
6
 
5
7
  // src/SeatmapViewer.tsx
6
- function SeatmapViewer({
8
+ var legendContainerStyle = {
9
+ position: "absolute",
10
+ top: 12,
11
+ left: 12,
12
+ zIndex: 10,
13
+ pointerEvents: "none",
14
+ background: "rgba(18, 18, 34, 0.88)",
15
+ border: "1px solid rgba(110, 110, 150, 0.45)",
16
+ borderRadius: 8,
17
+ padding: "10px 12px",
18
+ minWidth: 150,
19
+ color: "#e0e0e0",
20
+ fontFamily: "system-ui",
21
+ fontSize: 12,
22
+ backdropFilter: "blur(2px)"
23
+ };
24
+ var legendHeadingStyle = {
25
+ margin: 0,
26
+ fontSize: 11,
27
+ fontWeight: 600,
28
+ letterSpacing: 0.2,
29
+ color: "#b9b9d6"
30
+ };
31
+ var legendListStyle = {
32
+ listStyle: "none",
33
+ margin: "6px 0 0",
34
+ padding: 0,
35
+ display: "flex",
36
+ flexDirection: "column",
37
+ gap: 4
38
+ };
39
+ var legendItemStyle = {
40
+ display: "flex",
41
+ alignItems: "center",
42
+ gap: 6,
43
+ minWidth: 0
44
+ };
45
+ var legendSwatchStyle = {
46
+ width: 10,
47
+ height: 10,
48
+ borderRadius: 2,
49
+ flexShrink: 0,
50
+ border: "1px solid rgba(255, 255, 255, 0.25)"
51
+ };
52
+ var cartChipStyle = {
53
+ position: "absolute",
54
+ right: 12,
55
+ bottom: 12,
56
+ zIndex: 20,
57
+ border: "1px solid rgba(110, 110, 150, 0.65)",
58
+ background: "rgba(24, 24, 42, 0.95)",
59
+ color: "#ececff",
60
+ borderRadius: 999,
61
+ padding: "8px 14px",
62
+ fontSize: 12,
63
+ fontWeight: 600,
64
+ fontFamily: "system-ui",
65
+ cursor: "pointer",
66
+ boxShadow: "0 8px 18px rgba(0, 0, 0, 0.35)"
67
+ };
68
+ var cartPopupStyle = {
69
+ position: "absolute",
70
+ right: 0,
71
+ bottom: 0,
72
+ zIndex: 30,
73
+ maxHeight: "72vh",
74
+ display: "flex",
75
+ flexDirection: "column",
76
+ background: "rgba(17, 17, 34, 0.98)",
77
+ borderTop: "1px solid rgba(110, 110, 150, 0.65)",
78
+ borderLeft: "1px solid rgba(110, 110, 150, 0.65)",
79
+ borderTopLeftRadius: 12,
80
+ overflow: "hidden",
81
+ boxShadow: "-8px -8px 20px rgba(0, 0, 0, 0.35)"
82
+ };
83
+ var cartIconButtonStyle = {
84
+ width: 24,
85
+ height: 24,
86
+ display: "inline-flex",
87
+ alignItems: "center",
88
+ justifyContent: "center",
89
+ borderRadius: 5,
90
+ border: "1px solid #4a4a6a",
91
+ background: "#262648",
92
+ color: "#ececff",
93
+ fontSize: 14,
94
+ cursor: "pointer"
95
+ };
96
+ function getEffectiveCategoryPrice(category) {
97
+ if (!category) return 0;
98
+ if (category.isPriceOverridden && Number.isFinite(category.overriddenPrice)) {
99
+ return category.overriddenPrice ?? 0;
100
+ }
101
+ return Number.isFinite(category.backendPrice) ? category.backendPrice : 0;
102
+ }
103
+ function formatMoney(value) {
104
+ return `$${value.toFixed(2)}`;
105
+ }
106
+ function TrashIcon() {
107
+ return /* @__PURE__ */ jsx("svg", { width: "12", height: "12", viewBox: "0 0 24 24", fill: "none", "aria-hidden": "true", children: /* @__PURE__ */ jsx(
108
+ "path",
109
+ {
110
+ d: "M9 3h6l1 2h4v2H4V5h4l1-2Zm1 7h2v8h-2v-8Zm4 0h2v8h-2v-8ZM7 10h2v8H7v-8Zm-1 12h12a2 2 0 0 0 2-2V8H4v12a2 2 0 0 0 2 2Z",
111
+ fill: "currentColor"
112
+ }
113
+ ) });
114
+ }
115
+ function ViewerContent({
7
116
  venue,
8
117
  onSeatClick,
9
118
  onSeatHover,
119
+ onSelectionChange,
10
120
  renderTooltip,
11
- className
121
+ showLabels,
122
+ onCartEvent
12
123
  }) {
13
- return /* @__PURE__ */ jsx(SeatmapProvider, { venue, children: /* @__PURE__ */ jsxs("div", { className, style: { width: "100%", height: "100%", position: "relative" }, children: [
124
+ const { selectedSeatIds, setSelection } = useSelection();
125
+ const [isCartOpen, setIsCartOpen] = useState(false);
126
+ const [lastUserAnchorByCategory, setLastUserAnchorByCategory] = useState({});
127
+ const [viewportWidth, setViewportWidth] = useState(
128
+ () => typeof window === "undefined" ? 1280 : window.innerWidth
129
+ );
130
+ const previousSelectedRef = useRef([]);
131
+ const pendingSeatClickRef = useRef(null);
132
+ const showStatuses = venue.seatStatuses.length > 0;
133
+ const showCategories = venue.categories.length > 0;
134
+ const selectedSeatIdArray = useMemo(() => Array.from(selectedSeatIds), [selectedSeatIds]);
135
+ const seatDetailsById = useMemo(() => {
136
+ const categoryMap = new Map(venue.categories.map((category) => [category.id, category]));
137
+ const next = /* @__PURE__ */ new Map();
138
+ for (const section of venue.sections) {
139
+ const cos = Math.cos(section.rotation);
140
+ const sin = Math.sin(section.rotation);
141
+ for (const row of section.rows) {
142
+ for (const seat of row.seats) {
143
+ const category = categoryMap.get(seat.categoryId);
144
+ next.set(seat.id, {
145
+ seatId: seat.id,
146
+ seatLabel: seat.label,
147
+ rowLabel: row.label,
148
+ sectionId: section.id,
149
+ sectionLabel: section.label,
150
+ categoryId: seat.categoryId,
151
+ categoryName: category?.name ?? "Unknown",
152
+ unitPrice: getEffectiveCategoryPrice(category),
153
+ status: seat.status,
154
+ x: section.position.x + seat.position.x * cos - seat.position.y * sin,
155
+ y: section.position.y + seat.position.x * sin + seat.position.y * cos
156
+ });
157
+ }
158
+ }
159
+ }
160
+ for (const table of venue.tables) {
161
+ for (const seat of table.seats) {
162
+ const category = categoryMap.get(seat.categoryId);
163
+ next.set(seat.id, {
164
+ seatId: seat.id,
165
+ seatLabel: seat.label,
166
+ rowLabel: null,
167
+ sectionId: table.id,
168
+ sectionLabel: `Table ${table.label}`,
169
+ categoryId: seat.categoryId,
170
+ categoryName: category?.name ?? "Unknown",
171
+ unitPrice: getEffectiveCategoryPrice(category),
172
+ status: seat.status,
173
+ x: table.position.x + seat.position.x,
174
+ y: table.position.y + seat.position.y
175
+ });
176
+ }
177
+ }
178
+ return next;
179
+ }, [venue]);
180
+ const selectedSeats = useMemo(() => {
181
+ const ordered = [];
182
+ for (const selectedSeatId of selectedSeatIdArray) {
183
+ const seat = seatDetailsById.get(selectedSeatId);
184
+ if (seat) ordered.push(seat);
185
+ }
186
+ return ordered;
187
+ }, [seatDetailsById, selectedSeatIdArray]);
188
+ useEffect(() => {
189
+ onSelectionChange?.(selectedSeatIdArray);
190
+ }, [onSelectionChange, selectedSeatIdArray]);
191
+ useEffect(() => {
192
+ const previous = new Set(previousSelectedRef.current);
193
+ const added = selectedSeatIdArray.filter((seatId) => !previous.has(seatId));
194
+ const clickedSeatId = pendingSeatClickRef.current;
195
+ if (clickedSeatId && added.includes(clickedSeatId)) {
196
+ const clickedSeat = seatDetailsById.get(clickedSeatId);
197
+ if (clickedSeat) {
198
+ setLastUserAnchorByCategory((current) => ({
199
+ ...current,
200
+ [clickedSeat.categoryId]: clickedSeatId
201
+ }));
202
+ }
203
+ }
204
+ previousSelectedRef.current = selectedSeatIdArray;
205
+ pendingSeatClickRef.current = null;
206
+ }, [seatDetailsById, selectedSeatIdArray]);
207
+ useEffect(() => {
208
+ if (typeof window === "undefined") return;
209
+ const onResize = () => setViewportWidth(window.innerWidth);
210
+ window.addEventListener("resize", onResize);
211
+ return () => window.removeEventListener("resize", onResize);
212
+ }, []);
213
+ const cartSeats = useMemo(
214
+ () => selectedSeats.map((seat) => ({
215
+ seatId: seat.seatId,
216
+ seatLabel: seat.seatLabel,
217
+ rowLabel: seat.rowLabel,
218
+ sectionId: seat.sectionId,
219
+ sectionLabel: seat.sectionLabel,
220
+ categoryId: seat.categoryId,
221
+ categoryName: seat.categoryName,
222
+ unitPrice: seat.unitPrice
223
+ })),
224
+ [selectedSeats]
225
+ );
226
+ const totalSelectedSeats = useMemo(() => cartSeats.length, [cartSeats]);
227
+ const totalCost = useMemo(() => cartSeats.reduce((sum, seat) => sum + seat.unitPrice, 0), [cartSeats]);
228
+ const groupedSeatsList = useMemo(() => {
229
+ const grouped = /* @__PURE__ */ new Map();
230
+ for (const seat of cartSeats) {
231
+ const existing = grouped.get(seat.categoryId);
232
+ if (existing) {
233
+ existing.seats.push(seat);
234
+ } else {
235
+ grouped.set(seat.categoryId, {
236
+ categoryName: seat.categoryName,
237
+ unitPrice: seat.unitPrice,
238
+ seats: [seat]
239
+ });
240
+ }
241
+ }
242
+ const ordered = [];
243
+ const selectedSet = new Set(selectedSeatIdArray);
244
+ const availableToAddByCategory = /* @__PURE__ */ new Map();
245
+ for (const seat of seatDetailsById.values()) {
246
+ if (seat.status !== AVAILABLE_STATUS_ID || selectedSet.has(seat.seatId)) continue;
247
+ availableToAddByCategory.set(
248
+ seat.categoryId,
249
+ (availableToAddByCategory.get(seat.categoryId) ?? 0) + 1
250
+ );
251
+ }
252
+ for (const category of venue.categories) {
253
+ const group = grouped.get(category.id);
254
+ if (!group) continue;
255
+ ordered.push({
256
+ categoryId: category.id,
257
+ categoryName: group.categoryName,
258
+ unitPrice: group.unitPrice,
259
+ seats: group.seats,
260
+ availableToAdd: availableToAddByCategory.get(category.id) ?? 0
261
+ });
262
+ }
263
+ for (const [categoryId, group] of grouped.entries()) {
264
+ if (venue.categories.some((category) => category.id === categoryId)) continue;
265
+ ordered.push({
266
+ categoryId,
267
+ categoryName: group.categoryName,
268
+ unitPrice: group.unitPrice,
269
+ seats: group.seats,
270
+ availableToAdd: availableToAddByCategory.get(categoryId) ?? 0
271
+ });
272
+ }
273
+ return ordered;
274
+ }, [cartSeats, seatDetailsById, selectedSeatIdArray, venue.categories]);
275
+ const cartPayload = useMemo(
276
+ () => ({
277
+ venueId: venue.id,
278
+ venueName: venue.name,
279
+ seats: cartSeats,
280
+ totalSelectedSeats,
281
+ totalCost
282
+ }),
283
+ [venue.id, venue.name, cartSeats, totalSelectedSeats, totalCost]
284
+ );
285
+ const emitCartEvent = useCallback(
286
+ (event) => {
287
+ onCartEvent?.(event);
288
+ },
289
+ [onCartEvent]
290
+ );
291
+ const removeSeatFromSelection = useCallback(
292
+ (seatId) => {
293
+ const next = selectedSeatIdArray.filter((id) => id !== seatId);
294
+ setSelection(next);
295
+ },
296
+ [selectedSeatIdArray, setSelection]
297
+ );
298
+ const handleSeatCanvasClick = useCallback(
299
+ (seatId, sectionId) => {
300
+ pendingSeatClickRef.current = seatId;
301
+ onSeatClick?.(seatId, sectionId);
302
+ },
303
+ [onSeatClick]
304
+ );
305
+ const handleAddSeatInCategory = useCallback(
306
+ (categoryId) => {
307
+ const selectedSet = new Set(selectedSeatIdArray);
308
+ const selectedInCategory = selectedSeatIdArray.filter(
309
+ (selectedSeatId) => seatDetailsById.get(selectedSeatId)?.categoryId === categoryId
310
+ );
311
+ const anchorSeatId = (lastUserAnchorByCategory[categoryId] && seatDetailsById.has(lastUserAnchorByCategory[categoryId]) ? lastUserAnchorByCategory[categoryId] : null) ?? selectedInCategory[selectedInCategory.length - 1] ?? null;
312
+ const anchorSeat = anchorSeatId ? seatDetailsById.get(anchorSeatId) : null;
313
+ const candidates = Array.from(seatDetailsById.values()).filter(
314
+ (seat) => seat.categoryId === categoryId && seat.status === AVAILABLE_STATUS_ID && !selectedSet.has(seat.seatId)
315
+ );
316
+ if (candidates.length === 0) return;
317
+ let nextSeat = candidates[0];
318
+ if (anchorSeat) {
319
+ nextSeat = candidates.reduce((best, candidate) => {
320
+ const bestDistance = (best.x - anchorSeat.x) ** 2 + (best.y - anchorSeat.y) ** 2;
321
+ const candidateDistance = (candidate.x - anchorSeat.x) ** 2 + (candidate.y - anchorSeat.y) ** 2;
322
+ return candidateDistance < bestDistance ? candidate : best;
323
+ }, candidates[0]);
324
+ }
325
+ setSelection([...selectedSeatIdArray, nextSeat.seatId]);
326
+ },
327
+ [lastUserAnchorByCategory, seatDetailsById, selectedSeatIdArray, setSelection]
328
+ );
329
+ const handleRemoveLastInCategory = useCallback(
330
+ (categoryId) => {
331
+ const selectedInCategory = selectedSeatIdArray.filter(
332
+ (selectedSeatId) => seatDetailsById.get(selectedSeatId)?.categoryId === categoryId
333
+ );
334
+ const seatIdToRemove = selectedInCategory[selectedInCategory.length - 1];
335
+ if (!seatIdToRemove) return;
336
+ setSelection(selectedSeatIdArray.filter((selectedSeatId) => selectedSeatId !== seatIdToRemove));
337
+ },
338
+ [seatDetailsById, selectedSeatIdArray, setSelection]
339
+ );
340
+ const handleProceed = useCallback(() => {
341
+ if (cartPayload.seats.length === 0) return;
342
+ emitCartEvent({ type: "cart-proceed-clicked", payload: cartPayload });
343
+ }, [cartPayload, emitCartEvent]);
344
+ const cartWidth = viewportWidth > 1200 ? Math.floor(viewportWidth / 5) : viewportWidth;
345
+ return /* @__PURE__ */ jsxs("div", { style: { width: "100%", height: "100%", position: "relative" }, children: [
14
346
  /* @__PURE__ */ jsx(
15
347
  SeatmapCanvas,
16
348
  {
17
- onSeatClick,
18
- onSeatHover
349
+ onSeatClick: handleSeatCanvasClick,
350
+ onSeatHover,
351
+ showSectionLabels: showLabels,
352
+ enableSeatHover: showLabels
19
353
  }
20
354
  ),
21
- /* @__PURE__ */ jsx(TooltipOverlay, { renderTooltip })
22
- ] }) });
355
+ (showStatuses || showCategories) && /* @__PURE__ */ jsxs("aside", { "aria-label": "Seatmap legend", style: legendContainerStyle, children: [
356
+ showStatuses && /* @__PURE__ */ jsxs("section", { children: [
357
+ /* @__PURE__ */ jsx("p", { style: legendHeadingStyle, children: "Seat Status" }),
358
+ /* @__PURE__ */ jsx("ul", { style: legendListStyle, children: venue.seatStatuses.map((status) => /* @__PURE__ */ jsxs("li", { style: legendItemStyle, children: [
359
+ /* @__PURE__ */ jsx("span", { style: { ...legendSwatchStyle, background: status.color } }),
360
+ /* @__PURE__ */ jsx("span", { children: status.name })
361
+ ] }, status.id)) })
362
+ ] }),
363
+ showStatuses && showCategories && /* @__PURE__ */ jsx("div", { style: { height: 1, background: "rgba(123, 123, 165, 0.45)", margin: "8px 0" } }),
364
+ showCategories && /* @__PURE__ */ jsxs("section", { children: [
365
+ /* @__PURE__ */ jsx("p", { style: legendHeadingStyle, children: "Pricing" }),
366
+ /* @__PURE__ */ jsx("ul", { style: legendListStyle, children: venue.categories.map((category) => /* @__PURE__ */ jsxs("li", { style: legendItemStyle, children: [
367
+ /* @__PURE__ */ jsx("span", { style: { ...legendSwatchStyle, background: category.color } }),
368
+ /* @__PURE__ */ jsx("span", { children: category.name })
369
+ ] }, category.id)) })
370
+ ] })
371
+ ] }),
372
+ !isCartOpen && /* @__PURE__ */ jsxs("button", { type: "button", onClick: () => setIsCartOpen(true), style: cartChipStyle, children: [
373
+ "Cart (",
374
+ totalSelectedSeats,
375
+ ")"
376
+ ] }),
377
+ isCartOpen && /* @__PURE__ */ jsxs("aside", { "aria-label": "Selected seats cart", style: { ...cartPopupStyle, width: cartWidth, maxWidth: "100%" }, children: [
378
+ /* @__PURE__ */ jsxs(
379
+ "div",
380
+ {
381
+ style: {
382
+ display: "flex",
383
+ alignItems: "center",
384
+ justifyContent: "space-between",
385
+ gap: 8,
386
+ padding: "10px 12px",
387
+ borderBottom: "1px solid rgba(110, 110, 150, 0.35)"
388
+ },
389
+ children: [
390
+ /* @__PURE__ */ jsx("strong", { style: { color: "#ececff", fontSize: 13, fontFamily: "system-ui" }, children: "Selected Seats Cart" }),
391
+ /* @__PURE__ */ jsx(
392
+ "button",
393
+ {
394
+ type: "button",
395
+ onClick: () => setIsCartOpen(false),
396
+ style: { ...cartIconButtonStyle, width: 22, height: 22 },
397
+ title: "Close cart",
398
+ "aria-label": "Close cart",
399
+ children: "x"
400
+ }
401
+ )
402
+ ]
403
+ }
404
+ ),
405
+ /* @__PURE__ */ jsx("div", { style: { display: "flex", flexDirection: "column", gap: 8, overflow: "auto", padding: 12, flex: 1 }, children: groupedSeatsList.length === 0 ? /* @__PURE__ */ jsx("div", { style: { color: "#9e9eb8", fontSize: 12, fontFamily: "system-ui" }, children: "No seats selected yet." }) : groupedSeatsList.map((group) => /* @__PURE__ */ jsxs(
406
+ "article",
407
+ {
408
+ style: {
409
+ background: "#242447",
410
+ border: "1px solid #3b3b61",
411
+ borderRadius: 8,
412
+ padding: 8,
413
+ display: "flex",
414
+ flexDirection: "column",
415
+ gap: 6
416
+ },
417
+ children: [
418
+ /* @__PURE__ */ jsx("div", { style: { color: "#ececff", fontSize: 12, fontFamily: "system-ui", fontWeight: 600 }, children: group.categoryName }),
419
+ /* @__PURE__ */ jsxs("div", { style: { color: "#adb0d2", fontSize: 11, fontFamily: "system-ui" }, children: [
420
+ group.seats.length,
421
+ " ticket",
422
+ group.seats.length === 1 ? "" : "s",
423
+ " - ",
424
+ formatMoney(group.unitPrice),
425
+ " each"
426
+ ] }),
427
+ /* @__PURE__ */ jsxs("div", { style: { display: "flex", alignItems: "center", justifyContent: "space-between", gap: 8 }, children: [
428
+ /* @__PURE__ */ jsxs("div", { style: { display: "inline-flex", alignItems: "center", gap: 6 }, children: [
429
+ /* @__PURE__ */ jsx(
430
+ "button",
431
+ {
432
+ type: "button",
433
+ style: cartIconButtonStyle,
434
+ onClick: () => handleRemoveLastInCategory(group.categoryId),
435
+ disabled: group.seats.length === 0,
436
+ "aria-label": `Remove one ${group.categoryName} seat`,
437
+ children: "-"
438
+ }
439
+ ),
440
+ /* @__PURE__ */ jsx("span", { style: { minWidth: 20, textAlign: "center", color: "#ececff", fontSize: 12 }, children: group.seats.length }),
441
+ /* @__PURE__ */ jsx(
442
+ "button",
443
+ {
444
+ type: "button",
445
+ style: cartIconButtonStyle,
446
+ onClick: () => handleAddSeatInCategory(group.categoryId),
447
+ disabled: group.availableToAdd === 0,
448
+ "aria-label": `Add one ${group.categoryName} seat`,
449
+ children: "+"
450
+ }
451
+ )
452
+ ] }),
453
+ /* @__PURE__ */ jsx("span", { style: { color: "#ececff", fontSize: 12, fontFamily: "system-ui", fontWeight: 600 }, children: formatMoney(group.seats.reduce((sum, seat) => sum + seat.unitPrice, 0)) })
454
+ ] }),
455
+ /* @__PURE__ */ jsx("div", { style: { display: "flex", flexDirection: "column", gap: 4 }, children: group.seats.map((seat) => /* @__PURE__ */ jsxs(
456
+ "div",
457
+ {
458
+ style: { display: "flex", alignItems: "center", justifyContent: "space-between", gap: 8 },
459
+ children: [
460
+ /* @__PURE__ */ jsxs("span", { style: { color: "#c8c8df", fontSize: 11, fontFamily: "system-ui" }, children: [
461
+ seat.sectionLabel ?? "Section",
462
+ " ",
463
+ seat.rowLabel ? `- ${seat.rowLabel}${seat.seatLabel}` : `- ${seat.seatLabel}`
464
+ ] }),
465
+ /* @__PURE__ */ jsx(
466
+ "button",
467
+ {
468
+ type: "button",
469
+ style: cartIconButtonStyle,
470
+ onClick: () => removeSeatFromSelection(seat.seatId),
471
+ title: "Remove seat",
472
+ "aria-label": `Remove seat ${seat.seatLabel}`,
473
+ children: /* @__PURE__ */ jsx(TrashIcon, {})
474
+ }
475
+ )
476
+ ]
477
+ },
478
+ seat.seatId
479
+ )) })
480
+ ]
481
+ },
482
+ group.categoryId
483
+ )) }),
484
+ /* @__PURE__ */ jsxs(
485
+ "div",
486
+ {
487
+ style: {
488
+ borderTop: "1px solid rgba(110, 110, 150, 0.35)",
489
+ padding: 12,
490
+ display: "flex",
491
+ flexDirection: "column",
492
+ gap: 8
493
+ },
494
+ children: [
495
+ /* @__PURE__ */ jsxs("div", { style: { color: "#d4d4ee", fontSize: 12, fontFamily: "system-ui" }, children: [
496
+ totalSelectedSeats,
497
+ " seat",
498
+ totalSelectedSeats === 1 ? "" : "s",
499
+ " - Total ",
500
+ formatMoney(totalCost)
501
+ ] }),
502
+ /* @__PURE__ */ jsx(
503
+ "button",
504
+ {
505
+ type: "button",
506
+ disabled: cartSeats.length === 0,
507
+ onClick: handleProceed,
508
+ style: {
509
+ border: "1px solid #2f7b44",
510
+ background: cartSeats.length === 0 ? "#30543d" : "#2f7b44",
511
+ color: "#ecfff2",
512
+ borderRadius: 8,
513
+ padding: "8px 12px",
514
+ fontSize: 13,
515
+ fontWeight: 600,
516
+ fontFamily: "system-ui",
517
+ cursor: cartSeats.length === 0 ? "not-allowed" : "pointer"
518
+ },
519
+ children: "Proceed"
520
+ }
521
+ )
522
+ ]
523
+ }
524
+ )
525
+ ] }),
526
+ showLabels && /* @__PURE__ */ jsx(TooltipOverlay, { renderTooltip })
527
+ ] });
528
+ }
529
+ function SeatmapViewer({
530
+ venue,
531
+ onSeatClick,
532
+ onSeatHover,
533
+ onSelectionChange,
534
+ renderTooltip,
535
+ showLabels = true,
536
+ className,
537
+ onCartEvent
538
+ }) {
539
+ return /* @__PURE__ */ jsx(SeatmapProvider, { venue, children: /* @__PURE__ */ jsx("div", { className, style: { width: "100%", height: "100%", position: "relative" }, children: /* @__PURE__ */ jsx(
540
+ ViewerContent,
541
+ {
542
+ venue,
543
+ onSeatClick,
544
+ onSeatHover,
545
+ onSelectionChange,
546
+ renderTooltip,
547
+ showLabels,
548
+ onCartEvent
549
+ }
550
+ ) }) });
23
551
  }
24
552
 
25
553
  export { SeatmapViewer };