@zonetrix/viewer 2.0.0 → 2.1.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +102 -4
- package/dist/components/SeatMapViewer.d.ts +2 -1
- package/dist/hooks/useConfigFetcher.d.ts +0 -1
- package/dist/index.js +1 -1
- package/dist/index.mjs +141 -129
- package/dist/types/index.d.ts +4 -1
- package/package.json +11 -10
package/README.md
CHANGED
|
@@ -14,8 +14,10 @@ Lightweight React component for rendering interactive seat maps in your booking
|
|
|
14
14
|
- 🌐 **Flexible Config Loading** - Load from JSON files or API endpoints
|
|
15
15
|
- 🔍 **Mouse Wheel Zoom** - Smooth zoom with configurable limits
|
|
16
16
|
- 🎨 **Customizable Colors** - Override default colors to match your brand
|
|
17
|
+
- 🏢 **Multi-floor Support** - Filter and display seats by floor
|
|
18
|
+
- 👁️ **Hidden Seats** - Automatically filters out hidden seats
|
|
17
19
|
- 📱 **Responsive** - Works seamlessly across all screen sizes
|
|
18
|
-
- ⚡ **Lightweight** - Minimal dependencies
|
|
20
|
+
- ⚡ **Lightweight** - Minimal dependencies
|
|
19
21
|
- 🔒 **Type-safe** - Full TypeScript support
|
|
20
22
|
|
|
21
23
|
## Installation
|
|
@@ -31,7 +33,30 @@ pnpm add @zonetrix/viewer
|
|
|
31
33
|
### Peer Dependencies
|
|
32
34
|
|
|
33
35
|
```bash
|
|
34
|
-
npm install react react-dom
|
|
36
|
+
npm install react react-dom konva react-konva
|
|
37
|
+
```
|
|
38
|
+
|
|
39
|
+
## Exports
|
|
40
|
+
|
|
41
|
+
```typescript
|
|
42
|
+
// Main component
|
|
43
|
+
import { SeatMapViewer } from '@zonetrix/viewer';
|
|
44
|
+
import type { SeatMapViewerProps } from '@zonetrix/viewer';
|
|
45
|
+
|
|
46
|
+
// Types
|
|
47
|
+
import type {
|
|
48
|
+
SeatState,
|
|
49
|
+
SeatShape,
|
|
50
|
+
SeatData,
|
|
51
|
+
SeatMapConfig,
|
|
52
|
+
ColorSettings
|
|
53
|
+
} from '@zonetrix/viewer';
|
|
54
|
+
|
|
55
|
+
// Hooks
|
|
56
|
+
import { useConfigFetcher } from '@zonetrix/viewer';
|
|
57
|
+
|
|
58
|
+
// Constants
|
|
59
|
+
import { DEFAULT_COLORS } from '@zonetrix/viewer';
|
|
35
60
|
```
|
|
36
61
|
|
|
37
62
|
## Quick Start
|
|
@@ -40,6 +65,7 @@ npm install react react-dom
|
|
|
40
65
|
|
|
41
66
|
```tsx
|
|
42
67
|
import { SeatMapViewer } from '@zonetrix/viewer';
|
|
68
|
+
import type { SeatData, SeatMapConfig } from '@zonetrix/viewer';
|
|
43
69
|
import venueConfig from './venue-config.json';
|
|
44
70
|
|
|
45
71
|
function BookingApp() {
|
|
@@ -77,14 +103,19 @@ function BookingApp() {
|
|
|
77
103
|
|------|------|----------|-------------|
|
|
78
104
|
| `config` | `SeatMapConfig` | No* | Seat map configuration object |
|
|
79
105
|
| `configUrl` | `string` | No* | URL to fetch configuration from |
|
|
106
|
+
| `floorId` | `string` | No | Filter seats/stages by floor ID |
|
|
107
|
+
| `onFloorChange` | `(floorId: string) => void` | No | Callback when floor changes |
|
|
80
108
|
| `reservedSeats` | `string[]` | No | Array of seat IDs/numbers to mark as reserved |
|
|
81
109
|
| `unavailableSeats` | `string[]` | No | Array of seat IDs/numbers to mark as unavailable |
|
|
82
110
|
| `onSeatSelect` | `(seat: SeatData) => void` | No | Callback when a seat is selected |
|
|
83
111
|
| `onSeatDeselect` | `(seat: SeatData) => void` | No | Callback when a seat is deselected |
|
|
84
112
|
| `onSelectionChange` | `(seats: SeatData[]) => void` | No | Callback when selection changes |
|
|
85
113
|
| `colorOverrides` | `Partial<ColorSettings>` | No | Custom colors for seat states |
|
|
114
|
+
| `showTooltip` | `boolean` | No | Show tooltips on hover (default: true) |
|
|
86
115
|
| `zoomEnabled` | `boolean` | No | Enable/disable mouse wheel zoom (default: true) |
|
|
87
|
-
| `
|
|
116
|
+
| `className` | `string` | No | Custom CSS class for the container |
|
|
117
|
+
| `onConfigLoad` | `(config: SeatMapConfig) => void` | No | Callback when config is loaded |
|
|
118
|
+
| `onError` | `(error: Error) => void` | No | Callback when an error occurs |
|
|
88
119
|
|
|
89
120
|
*Note: Either `config` or `configUrl` must be provided.
|
|
90
121
|
|
|
@@ -226,6 +257,68 @@ function MobileOptimized() {
|
|
|
226
257
|
}
|
|
227
258
|
```
|
|
228
259
|
|
|
260
|
+
### 6. Multi-floor Venue
|
|
261
|
+
|
|
262
|
+
```tsx
|
|
263
|
+
import { useState } from 'react';
|
|
264
|
+
import { SeatMapViewer } from '@zonetrix/viewer';
|
|
265
|
+
|
|
266
|
+
function MultiFloorVenue() {
|
|
267
|
+
const [currentFloor, setCurrentFloor] = useState('floor_1');
|
|
268
|
+
|
|
269
|
+
return (
|
|
270
|
+
<div>
|
|
271
|
+
{/* Floor selector */}
|
|
272
|
+
<div className="floor-tabs">
|
|
273
|
+
{venueConfig.floors?.map((floor) => (
|
|
274
|
+
<button
|
|
275
|
+
key={floor.id}
|
|
276
|
+
onClick={() => setCurrentFloor(floor.id)}
|
|
277
|
+
className={currentFloor === floor.id ? 'active' : ''}
|
|
278
|
+
>
|
|
279
|
+
{floor.name}
|
|
280
|
+
</button>
|
|
281
|
+
))}
|
|
282
|
+
</div>
|
|
283
|
+
|
|
284
|
+
<SeatMapViewer
|
|
285
|
+
config={venueConfig}
|
|
286
|
+
floorId={currentFloor}
|
|
287
|
+
onFloorChange={setCurrentFloor}
|
|
288
|
+
onSeatSelect={(seat) => handleSelection(seat)}
|
|
289
|
+
/>
|
|
290
|
+
</div>
|
|
291
|
+
);
|
|
292
|
+
}
|
|
293
|
+
```
|
|
294
|
+
|
|
295
|
+
### 7. Error Handling
|
|
296
|
+
|
|
297
|
+
```tsx
|
|
298
|
+
import { SeatMapViewer } from '@zonetrix/viewer';
|
|
299
|
+
|
|
300
|
+
function RobustViewer() {
|
|
301
|
+
const handleConfigLoad = (config) => {
|
|
302
|
+
console.log('Config loaded:', config.metadata.name);
|
|
303
|
+
console.log('Total seats:', config.seats.length);
|
|
304
|
+
};
|
|
305
|
+
|
|
306
|
+
const handleError = (error) => {
|
|
307
|
+
console.error('Failed to load seat map:', error.message);
|
|
308
|
+
// Show error notification to user
|
|
309
|
+
};
|
|
310
|
+
|
|
311
|
+
return (
|
|
312
|
+
<SeatMapViewer
|
|
313
|
+
configUrl="https://api.example.com/venues/123/config"
|
|
314
|
+
onConfigLoad={handleConfigLoad}
|
|
315
|
+
onError={handleError}
|
|
316
|
+
onSeatSelect={(seat) => console.log('Selected:', seat)}
|
|
317
|
+
/>
|
|
318
|
+
);
|
|
319
|
+
}
|
|
320
|
+
```
|
|
321
|
+
|
|
229
322
|
## Configuration Format
|
|
230
323
|
|
|
231
324
|
The viewer accepts a `SeatMapConfig` object. You can create these configurations using our creator studio or build them programmatically.
|
|
@@ -289,15 +382,18 @@ interface SeatData {
|
|
|
289
382
|
columnLabel?: string;
|
|
290
383
|
price?: number;
|
|
291
384
|
seatNumber?: string;
|
|
385
|
+
floorId?: string;
|
|
292
386
|
}
|
|
293
387
|
```
|
|
294
388
|
|
|
295
389
|
### SeatState
|
|
296
390
|
|
|
297
391
|
```typescript
|
|
298
|
-
type SeatState = 'available' | 'reserved' | 'selected' | 'unavailable';
|
|
392
|
+
type SeatState = 'available' | 'reserved' | 'selected' | 'unavailable' | 'hidden';
|
|
299
393
|
```
|
|
300
394
|
|
|
395
|
+
Note: Hidden seats are automatically filtered out from the viewer.
|
|
396
|
+
|
|
301
397
|
### SeatShape
|
|
302
398
|
|
|
303
399
|
```typescript
|
|
@@ -314,6 +410,7 @@ interface ColorSettings {
|
|
|
314
410
|
seatReserved: string;
|
|
315
411
|
seatSelected: string;
|
|
316
412
|
seatUnavailable: string;
|
|
413
|
+
seatHidden: string;
|
|
317
414
|
gridLines: string;
|
|
318
415
|
currency: string;
|
|
319
416
|
}
|
|
@@ -327,6 +424,7 @@ interface ColorSettings {
|
|
|
327
424
|
| `reserved` | Seat is booked by another user | ❌ No | Yellow/Warning color |
|
|
328
425
|
| `selected` | Seat is selected by current user | ✅ Yes (to deselect) | Primary/Blue color |
|
|
329
426
|
| `unavailable` | Seat is blocked (maintenance, etc.) | ❌ No | Gray color |
|
|
427
|
+
| `hidden` | Seat exists but is not displayed | ❌ No | Not rendered |
|
|
330
428
|
|
|
331
429
|
## Events & Callbacks
|
|
332
430
|
|
|
@@ -1,9 +1,10 @@
|
|
|
1
1
|
import { default as React } from 'react';
|
|
2
2
|
import { SeatMapConfig, SeatData, ColorSettings } from '../types';
|
|
3
|
-
|
|
4
3
|
export interface SeatMapViewerProps {
|
|
5
4
|
config?: SeatMapConfig;
|
|
6
5
|
configUrl?: string;
|
|
6
|
+
floorId?: string;
|
|
7
|
+
onFloorChange?: (floorId: string) => void;
|
|
7
8
|
reservedSeats?: string[];
|
|
8
9
|
unavailableSeats?: string[];
|
|
9
10
|
onSeatSelect?: (seat: SeatData) => void;
|
package/dist/index.js
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
"use strict";Object.defineProperty(exports,Symbol.toStringTag,{value:"Module"});const
|
|
1
|
+
"use strict";Object.defineProperty(exports,Symbol.toStringTag,{value:"Module"});const s=require("react/jsx-runtime"),t=require("react"),h=require("react-konva");function $(i){const[f,n]=t.useState(null),[v,x]=t.useState(!1),[p,d]=t.useState(null),u=async()=>{if(i){x(!0),d(null);try{const o=await fetch(i);if(!o.ok)throw new Error(`Failed to fetch config: ${o.statusText}`);const c=await o.json();n(c)}catch(o){const c=o instanceof Error?o:new Error("Unknown error occurred");d(c),console.error("Failed to fetch seat map config:",c)}finally{x(!1)}}};return t.useEffect(()=>{u()},[i]),{config:f,loading:v,error:p,refetch:u}}const z={canvasBackground:"#1a1a1a",stageColor:"#808080",seatAvailable:"#2C2B30",seatReserved:"#FCEA00",seatSelected:"#3A7DE5",seatUnavailable:"#6b7280",seatHidden:"#4a4a4a",gridLines:"#404040",currency:"KD"},V=t.memo(({seat:i,state:f,colors:n,onClick:v})=>{const d={available:n.seatAvailable,reserved:n.seatReserved,selected:n.seatSelected,unavailable:n.seatUnavailable,hidden:n.seatHidden}[f],u=f==="available"||f==="selected",o=t.useCallback(()=>{u&&v(i)},[i,v,u]),c={x:i.position.x,y:i.position.y,fill:d,stroke:"#ffffff",strokeWidth:1,onClick:o,onTap:o};return i.shape==="circle"?s.jsx(h.Circle,{...c,radius:12}):s.jsx(h.Rect,{...c,width:24,height:24,offsetX:12,offsetY:12,cornerRadius:i.shape==="square"?0:4})});V.displayName="ViewerSeat";const _=t.memo(({stage:i,stageColor:f})=>s.jsxs(h.Group,{x:i.position.x,y:i.position.y,children:[s.jsx(h.Rect,{width:i.config.width,height:i.config.height,fill:f+"80",stroke:"#ffffff",strokeWidth:2,cornerRadius:10}),s.jsx(h.Text,{text:i.config.label,x:0,y:0,width:i.config.width,height:i.config.height,fontSize:24,fontStyle:"bold",fill:"#ffffff",align:"center",verticalAlign:"middle"})]}));_.displayName="ViewerStage";const X=({config:i,configUrl:f,floorId:n,onFloorChange:v,reservedSeats:x=[],unavailableSeats:p=[],onSeatSelect:d,onSeatDeselect:u,onSelectionChange:o,colorOverrides:c,showTooltip:G=!0,zoomEnabled:A=!0,className:k="",onConfigLoad:N,onError:R})=>{const L=t.useRef(null),[S,q]=t.useState(new Set),[M,D]=t.useState(1),[y,O]=t.useState({x:0,y:0}),{config:W,loading:U,error:b}=$(f),r=i||W,E=t.useMemo(()=>r?{...r.colors,...c}:{...z,...c},[r,c]),F=t.useMemo(()=>{if(!r)return[];let e=r.seats.filter(a=>a.state!=="hidden");return n&&(e=e.filter(a=>a.floorId===n||!a.floorId&&n==="floor_default")),e},[r,n]),Y=t.useMemo(()=>r?.stages?n?r.stages.filter(e=>e.floorId===n||!e.floorId&&n==="floor_default"):r.stages:[],[r,n]),w=t.useMemo(()=>{const e=new Set(x),a=new Set(p);return{reserved:e,unavailable:a}},[x,p]),T=t.useCallback(e=>{const a=e.id,l=e.seatNumber||"";return w.unavailable.has(a)||w.unavailable.has(l)?"unavailable":w.reserved.has(a)||w.reserved.has(l)?"reserved":S.has(a)?"selected":e.state},[w,S]);t.useEffect(()=>{r&&N&&N(r)},[r,N]),t.useEffect(()=>{b&&R&&R(b)},[b,R]);const B=t.useCallback(e=>{const a=T(e);if(a!=="available"&&a!=="selected")return;const l=S.has(e.id);q(g=>{const m=new Set(g);return l?m.delete(e.id):m.add(e.id),m}),l?u?.(e):(d?.(e),d||console.log("Seat selected:",e))},[T,S,d,u]),j=t.useMemo(()=>r?F.filter(e=>S.has(e.id)):[],[F,S]);t.useEffect(()=>{o?.(j)},[j,o]);const H=t.useCallback(e=>{if(!A)return;e.evt.preventDefault();const a=L.current;if(!a)return;const l=M,g=a.getPointerPosition();if(!g)return;const m={x:(g.x-y.x)/l,y:(g.y-y.y)/l},K=e.evt.deltaY>0?-1:1,P=1.05;let C=K>0?l*P:l/P;C=Math.max(.5,Math.min(5,C)),D(C),O({x:g.x-m.x*C,y:g.y-m.y*C})},[A,M,y]);return U?s.jsx("div",{className:`flex items-center justify-center h-full ${k}`,children:s.jsx("p",{children:"Loading seat map..."})}):b?s.jsx("div",{className:`flex items-center justify-center h-full ${k}`,children:s.jsxs("p",{className:"text-red-500",children:["Error loading seat map: ",b.message]})}):r?s.jsxs("div",{className:`relative ${k}`,children:[s.jsxs(h.Stage,{ref:L,width:r.canvas.width,height:r.canvas.height,scaleX:M,scaleY:M,x:y.x,y:y.y,onWheel:H,style:{backgroundColor:r.canvas.backgroundColor},children:[s.jsx(h.Layer,{listening:!1,children:Y.map(e=>s.jsx(_,{stage:e,stageColor:E.stageColor},e.id))}),s.jsx(h.Layer,{children:F.map(e=>s.jsx(V,{seat:e,state:T(e),colors:E,onClick:B},e.id))})]}),j.length>0&&s.jsxs("div",{className:"absolute top-4 right-4 bg-white dark:bg-gray-800 p-4 rounded shadow-lg",children:[s.jsxs("h3",{className:"font-semibold mb-2",children:["Selected Seats (",j.length,")"]}),s.jsx("div",{className:"max-h-48 overflow-y-auto space-y-1",children:j.map(e=>s.jsxs("div",{className:"text-sm",children:[e.seatNumber,e.price&&` - ${E.currency} ${e.price.toFixed(2)}`]},e.id))})]})]}):s.jsx("div",{className:`flex items-center justify-center h-full ${k}`,children:s.jsx("p",{children:"No configuration provided"})})};exports.DEFAULT_COLORS=z;exports.SeatMapViewer=X;exports.useConfigFetcher=$;
|
package/dist/index.mjs
CHANGED
|
@@ -1,71 +1,74 @@
|
|
|
1
|
-
import { jsx as
|
|
2
|
-
import { useState as
|
|
3
|
-
import { Stage as
|
|
4
|
-
function
|
|
5
|
-
const [
|
|
1
|
+
import { jsx as s, jsxs as u } from "react/jsx-runtime";
|
|
2
|
+
import { useState as v, useEffect as T, useRef as Q, useMemo as N, useCallback as $, memo as W } from "react";
|
|
3
|
+
import { Stage as Z, Layer as L, Group as ee, Rect as D, Text as te, Circle as ie } from "react-konva";
|
|
4
|
+
function re(t) {
|
|
5
|
+
const [c, r] = v(null), [S, g] = v(!1), [y, d] = v(null), f = async () => {
|
|
6
6
|
if (t) {
|
|
7
|
-
|
|
7
|
+
g(!0), d(null);
|
|
8
8
|
try {
|
|
9
|
-
const
|
|
10
|
-
if (!
|
|
11
|
-
throw new Error(`Failed to fetch config: ${
|
|
12
|
-
const
|
|
13
|
-
|
|
14
|
-
} catch (
|
|
15
|
-
const
|
|
16
|
-
|
|
9
|
+
const a = await fetch(t);
|
|
10
|
+
if (!a.ok)
|
|
11
|
+
throw new Error(`Failed to fetch config: ${a.statusText}`);
|
|
12
|
+
const o = await a.json();
|
|
13
|
+
r(o);
|
|
14
|
+
} catch (a) {
|
|
15
|
+
const o = a instanceof Error ? a : new Error("Unknown error occurred");
|
|
16
|
+
d(o), console.error("Failed to fetch seat map config:", o);
|
|
17
17
|
} finally {
|
|
18
|
-
|
|
18
|
+
g(!1);
|
|
19
19
|
}
|
|
20
20
|
}
|
|
21
21
|
};
|
|
22
|
-
return
|
|
23
|
-
|
|
22
|
+
return T(() => {
|
|
23
|
+
f();
|
|
24
24
|
}, [t]), {
|
|
25
|
-
config:
|
|
26
|
-
loading:
|
|
27
|
-
error:
|
|
28
|
-
refetch:
|
|
25
|
+
config: c,
|
|
26
|
+
loading: S,
|
|
27
|
+
error: y,
|
|
28
|
+
refetch: f
|
|
29
29
|
};
|
|
30
30
|
}
|
|
31
|
-
const
|
|
31
|
+
const ne = {
|
|
32
32
|
canvasBackground: "#1a1a1a",
|
|
33
33
|
stageColor: "#808080",
|
|
34
34
|
seatAvailable: "#2C2B30",
|
|
35
35
|
seatReserved: "#FCEA00",
|
|
36
36
|
seatSelected: "#3A7DE5",
|
|
37
37
|
seatUnavailable: "#6b7280",
|
|
38
|
+
seatHidden: "#4a4a4a",
|
|
38
39
|
gridLines: "#404040",
|
|
39
40
|
currency: "KD"
|
|
40
|
-
}, Y = W(({ seat: t, state:
|
|
41
|
-
const
|
|
42
|
-
available:
|
|
43
|
-
reserved:
|
|
44
|
-
selected:
|
|
45
|
-
unavailable:
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
}
|
|
41
|
+
}, Y = W(({ seat: t, state: c, colors: r, onClick: S }) => {
|
|
42
|
+
const d = {
|
|
43
|
+
available: r.seatAvailable,
|
|
44
|
+
reserved: r.seatReserved,
|
|
45
|
+
selected: r.seatSelected,
|
|
46
|
+
unavailable: r.seatUnavailable,
|
|
47
|
+
hidden: r.seatHidden
|
|
48
|
+
// Hidden seats are filtered out, but included for type safety
|
|
49
|
+
}[c], f = c === "available" || c === "selected", a = $(() => {
|
|
50
|
+
f && S(t);
|
|
51
|
+
}, [t, S, f]), o = {
|
|
49
52
|
x: t.position.x,
|
|
50
53
|
y: t.position.y,
|
|
51
|
-
fill:
|
|
54
|
+
fill: d,
|
|
52
55
|
stroke: "#ffffff",
|
|
53
56
|
strokeWidth: 1,
|
|
54
|
-
onClick:
|
|
55
|
-
onTap:
|
|
57
|
+
onClick: a,
|
|
58
|
+
onTap: a
|
|
56
59
|
};
|
|
57
|
-
return t.shape === "circle" ? /* @__PURE__ */
|
|
58
|
-
|
|
60
|
+
return t.shape === "circle" ? /* @__PURE__ */ s(
|
|
61
|
+
ie,
|
|
59
62
|
{
|
|
60
|
-
...
|
|
63
|
+
...o,
|
|
61
64
|
radius: 12
|
|
62
65
|
}
|
|
63
|
-
) : /* @__PURE__ */
|
|
64
|
-
|
|
66
|
+
) : /* @__PURE__ */ s(
|
|
67
|
+
D,
|
|
65
68
|
{
|
|
66
|
-
...
|
|
67
|
-
width:
|
|
68
|
-
height:
|
|
69
|
+
...o,
|
|
70
|
+
width: 24,
|
|
71
|
+
height: 24,
|
|
69
72
|
offsetX: 12,
|
|
70
73
|
offsetY: 12,
|
|
71
74
|
cornerRadius: t.shape === "square" ? 0 : 4
|
|
@@ -73,20 +76,20 @@ const re = {
|
|
|
73
76
|
);
|
|
74
77
|
});
|
|
75
78
|
Y.displayName = "ViewerSeat";
|
|
76
|
-
const B = W(({ stage: t, stageColor:
|
|
77
|
-
/* @__PURE__ */
|
|
78
|
-
|
|
79
|
+
const B = W(({ stage: t, stageColor: c }) => /* @__PURE__ */ u(ee, { x: t.position.x, y: t.position.y, children: [
|
|
80
|
+
/* @__PURE__ */ s(
|
|
81
|
+
D,
|
|
79
82
|
{
|
|
80
83
|
width: t.config.width,
|
|
81
84
|
height: t.config.height,
|
|
82
|
-
fill:
|
|
85
|
+
fill: c + "80",
|
|
83
86
|
stroke: "#ffffff",
|
|
84
87
|
strokeWidth: 2,
|
|
85
88
|
cornerRadius: 10
|
|
86
89
|
}
|
|
87
90
|
),
|
|
88
|
-
/* @__PURE__ */
|
|
89
|
-
|
|
91
|
+
/* @__PURE__ */ s(
|
|
92
|
+
te,
|
|
90
93
|
{
|
|
91
94
|
text: t.config.label,
|
|
92
95
|
x: 0,
|
|
@@ -102,98 +105,107 @@ const B = W(({ stage: t, stageColor: o }) => /* @__PURE__ */ p(Z, { x: t.positio
|
|
|
102
105
|
)
|
|
103
106
|
] }));
|
|
104
107
|
B.displayName = "ViewerStage";
|
|
105
|
-
const
|
|
108
|
+
const ce = ({
|
|
106
109
|
config: t,
|
|
107
|
-
configUrl:
|
|
108
|
-
|
|
109
|
-
|
|
110
|
+
configUrl: c,
|
|
111
|
+
floorId: r,
|
|
112
|
+
onFloorChange: S,
|
|
113
|
+
reservedSeats: g = [],
|
|
114
|
+
unavailableSeats: y = [],
|
|
110
115
|
onSeatSelect: d,
|
|
111
|
-
onSeatDeselect:
|
|
112
|
-
onSelectionChange:
|
|
113
|
-
colorOverrides:
|
|
114
|
-
showTooltip:
|
|
115
|
-
zoomEnabled:
|
|
116
|
-
className:
|
|
117
|
-
onConfigLoad:
|
|
118
|
-
onError:
|
|
116
|
+
onSeatDeselect: f,
|
|
117
|
+
onSelectionChange: a,
|
|
118
|
+
colorOverrides: o,
|
|
119
|
+
showTooltip: se = !0,
|
|
120
|
+
zoomEnabled: P = !0,
|
|
121
|
+
className: F = "",
|
|
122
|
+
onConfigLoad: j,
|
|
123
|
+
onError: z
|
|
119
124
|
}) => {
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
return
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
125
|
+
const V = Q(null), [m, H] = v(/* @__PURE__ */ new Set()), [R, U] = v(1), [w, X] = v({ x: 0, y: 0 }), { config: q, loading: G, error: x } = re(c), i = t || q, A = N(
|
|
126
|
+
() => i ? { ...i.colors, ...o } : { ...ne, ...o },
|
|
127
|
+
[i, o]
|
|
128
|
+
), E = N(() => {
|
|
129
|
+
if (!i) return [];
|
|
130
|
+
let e = i.seats.filter((n) => n.state !== "hidden");
|
|
131
|
+
return r && (e = e.filter(
|
|
132
|
+
(n) => n.floorId === r || !n.floorId && r === "floor_default"
|
|
133
|
+
)), e;
|
|
134
|
+
}, [i, r]), K = N(() => i?.stages ? r ? i.stages.filter(
|
|
135
|
+
(e) => e.floorId === r || !e.floorId && r === "floor_default"
|
|
136
|
+
) : i.stages : [], [i, r]), b = N(() => {
|
|
137
|
+
const e = new Set(g), n = new Set(y);
|
|
138
|
+
return { reserved: e, unavailable: n };
|
|
139
|
+
}, [g, y]), M = $((e) => {
|
|
140
|
+
const n = e.id, l = e.seatNumber || "";
|
|
141
|
+
return b.unavailable.has(n) || b.unavailable.has(l) ? "unavailable" : b.reserved.has(n) || b.reserved.has(l) ? "reserved" : m.has(n) ? "selected" : e.state;
|
|
142
|
+
}, [b, m]);
|
|
143
|
+
T(() => {
|
|
144
|
+
i && j && j(i);
|
|
145
|
+
}, [i, j]), T(() => {
|
|
146
|
+
x && z && z(x);
|
|
147
|
+
}, [x, z]);
|
|
148
|
+
const O = $((e) => {
|
|
149
|
+
const n = M(e);
|
|
150
|
+
if (n !== "available" && n !== "selected")
|
|
139
151
|
return;
|
|
140
|
-
const
|
|
141
|
-
|
|
142
|
-
const
|
|
143
|
-
return
|
|
144
|
-
}),
|
|
145
|
-
}, [
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
}, [
|
|
149
|
-
const
|
|
150
|
-
if (!
|
|
152
|
+
const l = m.has(e.id);
|
|
153
|
+
H((h) => {
|
|
154
|
+
const p = new Set(h);
|
|
155
|
+
return l ? p.delete(e.id) : p.add(e.id), p;
|
|
156
|
+
}), l ? f?.(e) : (d?.(e), d || console.log("Seat selected:", e));
|
|
157
|
+
}, [M, m, d, f]), C = N(() => i ? E.filter((e) => m.has(e.id)) : [], [E, m]);
|
|
158
|
+
T(() => {
|
|
159
|
+
a?.(C);
|
|
160
|
+
}, [C, a]);
|
|
161
|
+
const I = $((e) => {
|
|
162
|
+
if (!P) return;
|
|
151
163
|
e.evt.preventDefault();
|
|
152
|
-
const
|
|
153
|
-
if (!
|
|
154
|
-
const
|
|
155
|
-
if (!
|
|
156
|
-
const
|
|
157
|
-
x: (
|
|
158
|
-
y: (
|
|
159
|
-
},
|
|
160
|
-
let
|
|
161
|
-
|
|
162
|
-
x:
|
|
163
|
-
y:
|
|
164
|
+
const n = V.current;
|
|
165
|
+
if (!n) return;
|
|
166
|
+
const l = R, h = n.getPointerPosition();
|
|
167
|
+
if (!h) return;
|
|
168
|
+
const p = {
|
|
169
|
+
x: (h.x - w.x) / l,
|
|
170
|
+
y: (h.y - w.y) / l
|
|
171
|
+
}, J = e.evt.deltaY > 0 ? -1 : 1, _ = 1.05;
|
|
172
|
+
let k = J > 0 ? l * _ : l / _;
|
|
173
|
+
k = Math.max(0.5, Math.min(5, k)), U(k), X({
|
|
174
|
+
x: h.x - p.x * k,
|
|
175
|
+
y: h.y - p.y * k
|
|
164
176
|
});
|
|
165
|
-
}, [
|
|
166
|
-
return
|
|
177
|
+
}, [P, R, w]);
|
|
178
|
+
return G ? /* @__PURE__ */ s("div", { className: `flex items-center justify-center h-full ${F}`, children: /* @__PURE__ */ s("p", { children: "Loading seat map..." }) }) : x ? /* @__PURE__ */ s("div", { className: `flex items-center justify-center h-full ${F}`, children: /* @__PURE__ */ u("p", { className: "text-red-500", children: [
|
|
167
179
|
"Error loading seat map: ",
|
|
168
|
-
|
|
169
|
-
] }) }) :
|
|
170
|
-
/* @__PURE__ */
|
|
171
|
-
|
|
180
|
+
x.message
|
|
181
|
+
] }) }) : i ? /* @__PURE__ */ u("div", { className: `relative ${F}`, children: [
|
|
182
|
+
/* @__PURE__ */ u(
|
|
183
|
+
Z,
|
|
172
184
|
{
|
|
173
|
-
ref:
|
|
174
|
-
width:
|
|
175
|
-
height:
|
|
185
|
+
ref: V,
|
|
186
|
+
width: i.canvas.width,
|
|
187
|
+
height: i.canvas.height,
|
|
176
188
|
scaleX: R,
|
|
177
189
|
scaleY: R,
|
|
178
|
-
x:
|
|
179
|
-
y:
|
|
180
|
-
onWheel:
|
|
181
|
-
style: { backgroundColor:
|
|
190
|
+
x: w.x,
|
|
191
|
+
y: w.y,
|
|
192
|
+
onWheel: I,
|
|
193
|
+
style: { backgroundColor: i.canvas.backgroundColor },
|
|
182
194
|
children: [
|
|
183
|
-
/* @__PURE__ */
|
|
195
|
+
/* @__PURE__ */ s(L, { listening: !1, children: K.map((e) => /* @__PURE__ */ s(
|
|
184
196
|
B,
|
|
185
197
|
{
|
|
186
198
|
stage: e,
|
|
187
|
-
stageColor:
|
|
199
|
+
stageColor: A.stageColor
|
|
188
200
|
},
|
|
189
201
|
e.id
|
|
190
202
|
)) }),
|
|
191
|
-
/* @__PURE__ */
|
|
203
|
+
/* @__PURE__ */ s(L, { children: E.map((e) => /* @__PURE__ */ s(
|
|
192
204
|
Y,
|
|
193
205
|
{
|
|
194
206
|
seat: e,
|
|
195
|
-
state:
|
|
196
|
-
colors:
|
|
207
|
+
state: M(e),
|
|
208
|
+
colors: A,
|
|
197
209
|
onClick: O
|
|
198
210
|
},
|
|
199
211
|
e.id
|
|
@@ -201,21 +213,21 @@ const ae = ({
|
|
|
201
213
|
]
|
|
202
214
|
}
|
|
203
215
|
),
|
|
204
|
-
|
|
205
|
-
/* @__PURE__ */
|
|
216
|
+
C.length > 0 && /* @__PURE__ */ u("div", { className: "absolute top-4 right-4 bg-white dark:bg-gray-800 p-4 rounded shadow-lg", children: [
|
|
217
|
+
/* @__PURE__ */ u("h3", { className: "font-semibold mb-2", children: [
|
|
206
218
|
"Selected Seats (",
|
|
207
|
-
|
|
219
|
+
C.length,
|
|
208
220
|
")"
|
|
209
221
|
] }),
|
|
210
|
-
/* @__PURE__ */
|
|
222
|
+
/* @__PURE__ */ s("div", { className: "max-h-48 overflow-y-auto space-y-1", children: C.map((e) => /* @__PURE__ */ u("div", { className: "text-sm", children: [
|
|
211
223
|
e.seatNumber,
|
|
212
|
-
e.price && ` - ${
|
|
224
|
+
e.price && ` - ${A.currency} ${e.price.toFixed(2)}`
|
|
213
225
|
] }, e.id)) })
|
|
214
226
|
] })
|
|
215
|
-
] }) : /* @__PURE__ */
|
|
227
|
+
] }) : /* @__PURE__ */ s("div", { className: `flex items-center justify-center h-full ${F}`, children: /* @__PURE__ */ s("p", { children: "No configuration provided" }) });
|
|
216
228
|
};
|
|
217
229
|
export {
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
230
|
+
ne as DEFAULT_COLORS,
|
|
231
|
+
ce as SeatMapViewer,
|
|
232
|
+
re as useConfigFetcher
|
|
221
233
|
};
|
package/dist/types/index.d.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
export type SeatState = "available" | "reserved" | "selected" | "unavailable";
|
|
1
|
+
export type SeatState = "available" | "reserved" | "selected" | "unavailable" | "hidden";
|
|
2
2
|
export type SeatShape = "circle" | "square" | "rounded-square";
|
|
3
3
|
export interface SeatData {
|
|
4
4
|
id: string;
|
|
@@ -23,6 +23,7 @@ export interface SerializedSeat {
|
|
|
23
23
|
columnLabel?: string;
|
|
24
24
|
seatNumber?: string;
|
|
25
25
|
price?: number;
|
|
26
|
+
floorId?: string;
|
|
26
27
|
}
|
|
27
28
|
export interface SerializedSection {
|
|
28
29
|
id: string;
|
|
@@ -41,6 +42,7 @@ export interface SerializedStage {
|
|
|
41
42
|
y: number;
|
|
42
43
|
};
|
|
43
44
|
config: any;
|
|
45
|
+
floorId?: string;
|
|
44
46
|
}
|
|
45
47
|
export interface ColorSettings {
|
|
46
48
|
canvasBackground: string;
|
|
@@ -49,6 +51,7 @@ export interface ColorSettings {
|
|
|
49
51
|
seatReserved: string;
|
|
50
52
|
seatSelected: string;
|
|
51
53
|
seatUnavailable: string;
|
|
54
|
+
seatHidden: string;
|
|
52
55
|
gridLines: string;
|
|
53
56
|
currency: string;
|
|
54
57
|
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@zonetrix/viewer",
|
|
3
|
-
"version": "2.
|
|
3
|
+
"version": "2.1.1",
|
|
4
|
+
"type": "module",
|
|
4
5
|
"description": "Lightweight React component for rendering interactive seat maps",
|
|
5
6
|
"main": "./dist/index.js",
|
|
6
7
|
"module": "./dist/index.mjs",
|
|
@@ -8,9 +9,9 @@
|
|
|
8
9
|
"sideEffects": false,
|
|
9
10
|
"exports": {
|
|
10
11
|
".": {
|
|
12
|
+
"types": "./dist/index.d.ts",
|
|
11
13
|
"import": "./dist/index.mjs",
|
|
12
|
-
"require": "./dist/index.js"
|
|
13
|
-
"types": "./dist/index.d.ts"
|
|
14
|
+
"require": "./dist/index.js"
|
|
14
15
|
},
|
|
15
16
|
"./styles.css": "./dist/style.css"
|
|
16
17
|
},
|
|
@@ -34,19 +35,19 @@
|
|
|
34
35
|
"author": "",
|
|
35
36
|
"license": "MIT",
|
|
36
37
|
"peerDependencies": {
|
|
38
|
+
"konva": "^10.0.12",
|
|
37
39
|
"react": "^18.0.0 || ^19.0.0",
|
|
38
40
|
"react-dom": "^18.0.0 || ^19.0.0",
|
|
39
|
-
"konva": "^
|
|
40
|
-
"react-konva": "^18.0.0"
|
|
41
|
+
"react-konva": "^19.2.1"
|
|
41
42
|
},
|
|
42
43
|
"devDependencies": {
|
|
43
44
|
"@types/react": "^19.2.0",
|
|
44
45
|
"@types/react-dom": "^19.2.0",
|
|
45
|
-
"@vitejs/plugin-react-swc": "^4.
|
|
46
|
-
"konva": "^
|
|
47
|
-
"react-konva": "^
|
|
46
|
+
"@vitejs/plugin-react-swc": "^4.2.2",
|
|
47
|
+
"konva": "^10.0.12",
|
|
48
|
+
"react-konva": "^19.2.1",
|
|
48
49
|
"typescript": "^5.8.3",
|
|
49
|
-
"vite": "^
|
|
50
|
-
"vite-plugin-dts": "^
|
|
50
|
+
"vite": "^7.3.0",
|
|
51
|
+
"vite-plugin-dts": "^4.5.4"
|
|
51
52
|
}
|
|
52
53
|
}
|