@zonetrix/viewer 2.1.1 → 2.3.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +54 -8
- package/dist/components/SeatMapViewer.d.ts +7 -0
- package/dist/index.d.ts +1 -1
- package/dist/index.js +1 -1
- package/dist/index.mjs +261 -155
- package/dist/types/index.d.ts +7 -0
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -49,7 +49,8 @@ import type {
|
|
|
49
49
|
SeatShape,
|
|
50
50
|
SeatData,
|
|
51
51
|
SeatMapConfig,
|
|
52
|
-
ColorSettings
|
|
52
|
+
ColorSettings,
|
|
53
|
+
FloorConfig
|
|
53
54
|
} from '@zonetrix/viewer';
|
|
54
55
|
|
|
55
56
|
// Hooks
|
|
@@ -103,7 +104,7 @@ function BookingApp() {
|
|
|
103
104
|
|------|------|----------|-------------|
|
|
104
105
|
| `config` | `SeatMapConfig` | No* | Seat map configuration object |
|
|
105
106
|
| `configUrl` | `string` | No* | URL to fetch configuration from |
|
|
106
|
-
| `floorId` | `string` | No | Filter seats/stages by floor ID |
|
|
107
|
+
| `floorId` | `string` | No | Filter seats/stages by floor ID (controlled mode) |
|
|
107
108
|
| `onFloorChange` | `(floorId: string) => void` | No | Callback when floor changes |
|
|
108
109
|
| `reservedSeats` | `string[]` | No | Array of seat IDs/numbers to mark as reserved |
|
|
109
110
|
| `unavailableSeats` | `string[]` | No | Array of seat IDs/numbers to mark as unavailable |
|
|
@@ -116,6 +117,11 @@ function BookingApp() {
|
|
|
116
117
|
| `className` | `string` | No | Custom CSS class for the container |
|
|
117
118
|
| `onConfigLoad` | `(config: SeatMapConfig) => void` | No | Callback when config is loaded |
|
|
118
119
|
| `onError` | `(error: Error) => void` | No | Callback when an error occurs |
|
|
120
|
+
| `showFloorSelector` | `boolean` | No | Show/hide built-in floor selector (default: true when floors > 1) |
|
|
121
|
+
| `floorSelectorPosition` | `string` | No | Position: 'top-left' \| 'top-right' \| 'bottom-left' \| 'bottom-right' |
|
|
122
|
+
| `floorSelectorClassName` | `string` | No | Custom CSS class for floor selector |
|
|
123
|
+
| `showAllFloorsOption` | `boolean` | No | Show "All" button in floor selector (default: true) |
|
|
124
|
+
| `allFloorsLabel` | `string` | No | Custom label for "All" button (default: 'All') |
|
|
119
125
|
|
|
120
126
|
*Note: Either `config` or `configUrl` must be provided.
|
|
121
127
|
|
|
@@ -257,19 +263,43 @@ function MobileOptimized() {
|
|
|
257
263
|
}
|
|
258
264
|
```
|
|
259
265
|
|
|
260
|
-
### 6. Multi-floor Venue
|
|
266
|
+
### 6. Multi-floor Venue (Built-in Floor Selector)
|
|
267
|
+
|
|
268
|
+
The viewer includes a built-in floor selector that automatically appears when your config has multiple floors.
|
|
261
269
|
|
|
262
270
|
```tsx
|
|
263
|
-
import { useState } from 'react';
|
|
264
271
|
import { SeatMapViewer } from '@zonetrix/viewer';
|
|
265
272
|
|
|
266
273
|
function MultiFloorVenue() {
|
|
267
|
-
|
|
274
|
+
return (
|
|
275
|
+
<SeatMapViewer
|
|
276
|
+
config={venueConfig}
|
|
277
|
+
onSeatSelect={(seat) => handleSelection(seat)}
|
|
278
|
+
// Floor selector auto-shows when config.floors.length > 1
|
|
279
|
+
// Customize position and labels:
|
|
280
|
+
floorSelectorPosition="top-right"
|
|
281
|
+
allFloorsLabel="All Floors"
|
|
282
|
+
/>
|
|
283
|
+
);
|
|
284
|
+
}
|
|
285
|
+
```
|
|
286
|
+
|
|
287
|
+
#### Custom Floor Selector (Controlled Mode)
|
|
288
|
+
|
|
289
|
+
For full control over the floor selector UI, use controlled mode:
|
|
290
|
+
|
|
291
|
+
```tsx
|
|
292
|
+
import { useState } from 'react';
|
|
293
|
+
import { SeatMapViewer } from '@zonetrix/viewer';
|
|
294
|
+
|
|
295
|
+
function CustomFloorSelector() {
|
|
296
|
+
const [currentFloor, setCurrentFloor] = useState<string | null>(null);
|
|
268
297
|
|
|
269
298
|
return (
|
|
270
299
|
<div>
|
|
271
|
-
{/*
|
|
300
|
+
{/* Your custom floor selector */}
|
|
272
301
|
<div className="floor-tabs">
|
|
302
|
+
<button onClick={() => setCurrentFloor(null)}>All</button>
|
|
273
303
|
{venueConfig.floors?.map((floor) => (
|
|
274
304
|
<button
|
|
275
305
|
key={floor.id}
|
|
@@ -283,7 +313,8 @@ function MultiFloorVenue() {
|
|
|
283
313
|
|
|
284
314
|
<SeatMapViewer
|
|
285
315
|
config={venueConfig}
|
|
286
|
-
|
|
316
|
+
showFloorSelector={false} // Hide built-in selector
|
|
317
|
+
floorId={currentFloor || undefined}
|
|
287
318
|
onFloorChange={setCurrentFloor}
|
|
288
319
|
onSeatSelect={(seat) => handleSelection(seat)}
|
|
289
320
|
/>
|
|
@@ -364,7 +395,11 @@ The viewer accepts a `SeatMapConfig` object. You can create these configurations
|
|
|
364
395
|
}
|
|
365
396
|
],
|
|
366
397
|
"sections": [],
|
|
367
|
-
"stages": []
|
|
398
|
+
"stages": [],
|
|
399
|
+
"floors": [
|
|
400
|
+
{ "id": "floor_1", "name": "Ground Floor", "order": 0 },
|
|
401
|
+
{ "id": "floor_2", "name": "First Floor", "order": 1 }
|
|
402
|
+
]
|
|
368
403
|
}
|
|
369
404
|
```
|
|
370
405
|
|
|
@@ -416,6 +451,17 @@ interface ColorSettings {
|
|
|
416
451
|
}
|
|
417
452
|
```
|
|
418
453
|
|
|
454
|
+
### FloorConfig
|
|
455
|
+
|
|
456
|
+
```typescript
|
|
457
|
+
interface FloorConfig {
|
|
458
|
+
id: string; // Unique identifier (e.g., "floor_1")
|
|
459
|
+
name: string; // Display name (e.g., "Ground Floor")
|
|
460
|
+
order: number; // Sort order (0 = first)
|
|
461
|
+
color?: string; // Optional floor color
|
|
462
|
+
}
|
|
463
|
+
```
|
|
464
|
+
|
|
419
465
|
## Seat States Explained
|
|
420
466
|
|
|
421
467
|
| State | Description | User Can Select? | Visual |
|
|
@@ -16,5 +16,12 @@ export interface SeatMapViewerProps {
|
|
|
16
16
|
className?: string;
|
|
17
17
|
onConfigLoad?: (config: SeatMapConfig) => void;
|
|
18
18
|
onError?: (error: Error) => void;
|
|
19
|
+
showFloorSelector?: boolean;
|
|
20
|
+
floorSelectorPosition?: 'top-left' | 'top-right' | 'bottom-left' | 'bottom-right';
|
|
21
|
+
floorSelectorClassName?: string;
|
|
22
|
+
showAllFloorsOption?: boolean;
|
|
23
|
+
allFloorsLabel?: string;
|
|
24
|
+
fitToView?: boolean;
|
|
25
|
+
fitPadding?: number;
|
|
19
26
|
}
|
|
20
27
|
export declare const SeatMapViewer: React.FC<SeatMapViewerProps>;
|
package/dist/index.d.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
export { SeatMapViewer } from './components/SeatMapViewer';
|
|
2
2
|
export type { SeatMapViewerProps } from './components/SeatMapViewer';
|
|
3
|
-
export type { SeatState, SeatShape, SeatData, SerializedSeat, SerializedSection, SerializedStage, ColorSettings, SeatMapConfig, } from './types';
|
|
3
|
+
export type { SeatState, SeatShape, SeatData, SerializedSeat, SerializedSection, SerializedStage, ColorSettings, SeatMapConfig, FloorConfig, } from './types';
|
|
4
4
|
export { DEFAULT_COLORS } from './types';
|
|
5
5
|
export { useConfigFetcher } from './hooks/useConfigFetcher';
|
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 o=require("react/jsx-runtime"),t=require("react"),v=require("react-konva");function K(i){const[h,d]=t.useState(null),[S,y]=t.useState(!1),[w,x]=t.useState(null),g=async()=>{if(i){y(!0),x(null);try{const f=await fetch(i);if(!f.ok)throw new Error(`Failed to fetch config: ${f.statusText}`);const u=await f.json();d(u)}catch(f){const u=f instanceof Error?f:new Error("Unknown error occurred");x(u),console.error("Failed to fetch seat map config:",u)}finally{y(!1)}}};return t.useEffect(()=>{g()},[i]),{config:h,loading:S,error:w,refetch:g}}const G={canvasBackground:"#1a1a1a",stageColor:"#808080",seatAvailable:"#2C2B30",seatReserved:"#FCEA00",seatSelected:"#3A7DE5",seatUnavailable:"#6b7280",seatHidden:"#4a4a4a",gridLines:"#404040",currency:"KD"},J=t.memo(({seat:i,state:h,colors:d,onClick:S})=>{const x={available:d.seatAvailable,reserved:d.seatReserved,selected:d.seatSelected,unavailable:d.seatUnavailable,hidden:d.seatHidden}[h],g=h==="available"||h==="selected",f=t.useCallback(()=>{g&&S(i)},[i,S,g]),u={x:i.position.x,y:i.position.y,fill:x,stroke:"#ffffff",strokeWidth:1,onClick:f,onTap:f};return i.shape==="circle"?o.jsx(v.Circle,{...u,radius:12}):o.jsx(v.Rect,{...u,width:24,height:24,offsetX:12,offsetY:12,cornerRadius:i.shape==="square"?0:4})});J.displayName="ViewerSeat";const Q=t.memo(({stage:i,stageColor:h})=>o.jsxs(v.Group,{x:i.position.x,y:i.position.y,children:[o.jsx(v.Rect,{width:i.config.width,height:i.config.height,fill:h+"80",stroke:"#ffffff",strokeWidth:2,cornerRadius:10}),o.jsx(v.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"})]}));Q.displayName="ViewerStage";const Z=t.memo(({floors:i,currentFloorId:h,onFloorChange:d,showAllOption:S,allLabel:y,position:w,className:x})=>{const g=t.useMemo(()=>[...i].sort((p,I)=>p.order-I.order),[i]),u={position:"absolute",display:"flex",alignItems:"center",gap:"8px",padding:"8px 12px",backgroundColor:"rgba(26, 26, 26, 0.95)",borderRadius:"8px",margin:"12px",zIndex:10,...{"top-left":{top:0,left:0},"top-right":{top:0,right:0},"bottom-left":{bottom:0,left:0},"bottom-right":{bottom:0,right:0}}[w]},Y={padding:"6px 14px",fontSize:"14px",fontWeight:500,border:"1px solid #444",borderRadius:"6px",backgroundColor:"transparent",color:"#fff",cursor:"pointer",transition:"all 0.2s ease"},E={...Y,backgroundColor:"#3A7DE5",borderColor:"#3A7DE5"};return o.jsxs("div",{className:x,style:u,children:[S&&o.jsx("button",{type:"button",onClick:()=>d(null),style:h===null?E:Y,children:y}),g.map(p=>o.jsx("button",{type:"button",onClick:()=>d(p.id),style:h===p.id?E:Y,children:p.name},p.id))]})});Z.displayName="FloorSelectorBar";const Se=({config:i,configUrl:h,floorId:d,onFloorChange:S,reservedSeats:y=[],unavailableSeats:w=[],onSeatSelect:x,onSeatDeselect:g,onSelectionChange:f,colorOverrides:u,showTooltip:Y=!0,zoomEnabled:E=!0,className:p="",onConfigLoad:I,onError:T,showFloorSelector:O,floorSelectorPosition:ee="top-left",floorSelectorClassName:te,showAllFloorsOption:ne=!0,allFloorsLabel:se="All",fitToView:z=!0,fitPadding:W=40})=>{const P=t.useRef(null),[j,oe]=t.useState(new Set),[X,V]=t.useState(1),[F,_]=t.useState({x:0,y:0}),[ie,re]=t.useState(null),[q,U]=t.useState(!1),{config:ae,loading:le,error:N}=K(h),s=i||ae,$=d!==void 0,m=$?d||null:ie,ce=t.useCallback(e=>{$||re(e),S?.(e)},[$,S]),B=s?.floors||[],de=O!==void 0?O:B.length>1,D=t.useMemo(()=>s?{...s.colors,...u}:{...G,...u},[s,u]),k=t.useMemo(()=>{if(!s)return[];let e=s.seats.filter(n=>n.state!=="hidden");return m&&(e=e.filter(n=>n.floorId===m||!n.floorId&&m==="floor_default")),e},[s,m]),L=t.useMemo(()=>s?.stages?m?s.stages.filter(e=>e.floorId===m||!e.floorId&&m==="floor_default"):s.stages:[],[s,m]),b=t.useMemo(()=>{if(!s||k.length===0&&L.length===0)return null;const e=12;let n=1/0,r=1/0,a=-1/0,l=-1/0;return k.forEach(c=>{n=Math.min(n,c.position.x-e),r=Math.min(r,c.position.y-e),a=Math.max(a,c.position.x+e),l=Math.max(l,c.position.y+e)}),L.forEach(c=>{n=Math.min(n,c.position.x),r=Math.min(r,c.position.y),a=Math.max(a,c.position.x+(c.config?.width||200)),l=Math.max(l,c.position.y+(c.config?.height||100))}),{minX:n,minY:r,maxX:a,maxY:l,width:a-n,height:l-r}},[s,k,L]);t.useEffect(()=>{if(!z||q||!s||!b)return;const e=s.canvas.width,n=s.canvas.height,r=e-W*2,a=n-W*2,l=r/b.width,c=a/b.height,M=Math.min(l,c,2),C=b.minX+b.width/2,he=b.minY+b.height/2,pe=e/2,xe=n/2,ge=pe-C*M,me=xe-he*M;V(M),_({x:ge,y:me}),U(!0)},[z,q,s,b,W]),t.useEffect(()=>{z&&U(!1)},[m,z]);const R=t.useMemo(()=>{const e=new Set(y),n=new Set(w);return{reserved:e,unavailable:n}},[y,w]),H=t.useCallback(e=>{const n=e.id,r=e.seatNumber||"";return R.unavailable.has(n)||R.unavailable.has(r)?"unavailable":R.reserved.has(n)||R.reserved.has(r)?"reserved":j.has(n)?"selected":e.state},[R,j]);t.useEffect(()=>{s&&I&&I(s)},[s,I]),t.useEffect(()=>{N&&T&&T(N)},[N,T]);const fe=t.useCallback(e=>{const n=H(e);if(n!=="available"&&n!=="selected")return;const r=j.has(e.id);oe(a=>{const l=new Set(a);return r?l.delete(e.id):l.add(e.id),l}),r?g?.(e):(x?.(e),x||console.log("Seat selected:",e))},[H,j,x,g]),A=t.useMemo(()=>s?k.filter(e=>j.has(e.id)):[],[k,j]);t.useEffect(()=>{f?.(A)},[A,f]);const ue=t.useCallback(e=>{if(!E)return;e.evt.preventDefault();const n=P.current;if(!n)return;const r=X,a=n.getPointerPosition();if(!a)return;const l={x:(a.x-F.x)/r,y:(a.y-F.y)/r},c=e.evt.deltaY>0?-1:1,M=1.05;let C=c>0?r*M:r/M;C=Math.max(.5,Math.min(5,C)),V(C),_({x:a.x-l.x*C,y:a.y-l.y*C})},[E,X,F]);return le?o.jsx("div",{className:`flex items-center justify-center h-full ${p}`,children:o.jsx("p",{children:"Loading seat map..."})}):N?o.jsx("div",{className:`flex items-center justify-center h-full ${p}`,children:o.jsxs("p",{className:"text-red-500",children:["Error loading seat map: ",N.message]})}):s?o.jsxs("div",{className:`relative ${p}`,children:[de&&B.length>0&&o.jsx(Z,{floors:B,currentFloorId:m,onFloorChange:ce,showAllOption:ne,allLabel:se,position:ee,className:te}),o.jsxs(v.Stage,{ref:P,width:s.canvas.width,height:s.canvas.height,scaleX:X,scaleY:X,x:F.x,y:F.y,onWheel:ue,style:{backgroundColor:s.canvas.backgroundColor},children:[o.jsx(v.Layer,{listening:!1,children:L.map(e=>o.jsx(Q,{stage:e,stageColor:D.stageColor},e.id))}),o.jsx(v.Layer,{children:k.map(e=>o.jsx(J,{seat:e,state:H(e),colors:D,onClick:fe},e.id))})]}),A.length>0&&o.jsxs("div",{className:"absolute top-4 right-4 bg-white dark:bg-gray-800 p-4 rounded shadow-lg",children:[o.jsxs("h3",{className:"font-semibold mb-2",children:["Selected Seats (",A.length,")"]}),o.jsx("div",{className:"max-h-48 overflow-y-auto space-y-1",children:A.map(e=>o.jsxs("div",{className:"text-sm",children:[e.seatNumber,e.price&&` - ${D.currency} ${e.price.toFixed(2)}`]},e.id))})]})]}):o.jsx("div",{className:`flex items-center justify-center h-full ${p}`,children:o.jsx("p",{children:"No configuration provided"})})};exports.DEFAULT_COLORS=G;exports.SeatMapViewer=Se;exports.useConfigFetcher=K;
|
package/dist/index.mjs
CHANGED
|
@@ -1,34 +1,34 @@
|
|
|
1
|
-
import { jsx as
|
|
2
|
-
import { useState as
|
|
3
|
-
import { Stage as
|
|
4
|
-
function
|
|
5
|
-
const [
|
|
6
|
-
if (
|
|
7
|
-
|
|
1
|
+
import { jsx as r, jsxs as v } from "react/jsx-runtime";
|
|
2
|
+
import { useState as S, useEffect as F, useRef as we, useCallback as W, useMemo as k, memo as O } from "react";
|
|
3
|
+
import { Stage as Ce, Layer as Z, Group as ke, Rect as ee, Text as Ie, Circle as Me } from "react-konva";
|
|
4
|
+
function Ne(i) {
|
|
5
|
+
const [h, c] = S(null), [y, x] = S(!1), [w, p] = S(null), g = async () => {
|
|
6
|
+
if (i) {
|
|
7
|
+
x(!0), p(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 d = await fetch(i);
|
|
10
|
+
if (!d.ok)
|
|
11
|
+
throw new Error(`Failed to fetch config: ${d.statusText}`);
|
|
12
|
+
const f = await d.json();
|
|
13
|
+
c(f);
|
|
14
|
+
} catch (d) {
|
|
15
|
+
const f = d instanceof Error ? d : new Error("Unknown error occurred");
|
|
16
|
+
p(f), console.error("Failed to fetch seat map config:", f);
|
|
17
17
|
} finally {
|
|
18
|
-
|
|
18
|
+
x(!1);
|
|
19
19
|
}
|
|
20
20
|
}
|
|
21
21
|
};
|
|
22
|
-
return
|
|
23
|
-
|
|
24
|
-
}, [
|
|
25
|
-
config:
|
|
26
|
-
loading:
|
|
27
|
-
error:
|
|
28
|
-
refetch:
|
|
22
|
+
return F(() => {
|
|
23
|
+
g();
|
|
24
|
+
}, [i]), {
|
|
25
|
+
config: h,
|
|
26
|
+
loading: y,
|
|
27
|
+
error: w,
|
|
28
|
+
refetch: g
|
|
29
29
|
};
|
|
30
30
|
}
|
|
31
|
-
const
|
|
31
|
+
const Fe = {
|
|
32
32
|
canvasBackground: "#1a1a1a",
|
|
33
33
|
stageColor: "#808080",
|
|
34
34
|
seatAvailable: "#2C2B30",
|
|
@@ -38,64 +38,64 @@ const ne = {
|
|
|
38
38
|
seatHidden: "#4a4a4a",
|
|
39
39
|
gridLines: "#404040",
|
|
40
40
|
currency: "KD"
|
|
41
|
-
},
|
|
42
|
-
const
|
|
43
|
-
available:
|
|
44
|
-
reserved:
|
|
45
|
-
selected:
|
|
46
|
-
unavailable:
|
|
47
|
-
hidden:
|
|
41
|
+
}, te = O(({ seat: i, state: h, colors: c, onClick: y }) => {
|
|
42
|
+
const p = {
|
|
43
|
+
available: c.seatAvailable,
|
|
44
|
+
reserved: c.seatReserved,
|
|
45
|
+
selected: c.seatSelected,
|
|
46
|
+
unavailable: c.seatUnavailable,
|
|
47
|
+
hidden: c.seatHidden
|
|
48
48
|
// Hidden seats are filtered out, but included for type safety
|
|
49
|
-
}[
|
|
50
|
-
|
|
51
|
-
}, [
|
|
52
|
-
x:
|
|
53
|
-
y:
|
|
54
|
-
fill:
|
|
49
|
+
}[h], g = h === "available" || h === "selected", d = W(() => {
|
|
50
|
+
g && y(i);
|
|
51
|
+
}, [i, y, g]), f = {
|
|
52
|
+
x: i.position.x,
|
|
53
|
+
y: i.position.y,
|
|
54
|
+
fill: p,
|
|
55
55
|
stroke: "#ffffff",
|
|
56
56
|
strokeWidth: 1,
|
|
57
|
-
onClick:
|
|
58
|
-
onTap:
|
|
57
|
+
onClick: d,
|
|
58
|
+
onTap: d
|
|
59
59
|
};
|
|
60
|
-
return
|
|
61
|
-
|
|
60
|
+
return i.shape === "circle" ? /* @__PURE__ */ r(
|
|
61
|
+
Me,
|
|
62
62
|
{
|
|
63
|
-
...
|
|
63
|
+
...f,
|
|
64
64
|
radius: 12
|
|
65
65
|
}
|
|
66
|
-
) : /* @__PURE__ */
|
|
67
|
-
|
|
66
|
+
) : /* @__PURE__ */ r(
|
|
67
|
+
ee,
|
|
68
68
|
{
|
|
69
|
-
...
|
|
69
|
+
...f,
|
|
70
70
|
width: 24,
|
|
71
71
|
height: 24,
|
|
72
72
|
offsetX: 12,
|
|
73
73
|
offsetY: 12,
|
|
74
|
-
cornerRadius:
|
|
74
|
+
cornerRadius: i.shape === "square" ? 0 : 4
|
|
75
75
|
}
|
|
76
76
|
);
|
|
77
77
|
});
|
|
78
|
-
|
|
79
|
-
const
|
|
80
|
-
/* @__PURE__ */
|
|
81
|
-
|
|
78
|
+
te.displayName = "ViewerSeat";
|
|
79
|
+
const ne = O(({ stage: i, stageColor: h }) => /* @__PURE__ */ v(ke, { x: i.position.x, y: i.position.y, children: [
|
|
80
|
+
/* @__PURE__ */ r(
|
|
81
|
+
ee,
|
|
82
82
|
{
|
|
83
|
-
width:
|
|
84
|
-
height:
|
|
85
|
-
fill:
|
|
83
|
+
width: i.config.width,
|
|
84
|
+
height: i.config.height,
|
|
85
|
+
fill: h + "80",
|
|
86
86
|
stroke: "#ffffff",
|
|
87
87
|
strokeWidth: 2,
|
|
88
88
|
cornerRadius: 10
|
|
89
89
|
}
|
|
90
90
|
),
|
|
91
|
-
/* @__PURE__ */
|
|
92
|
-
|
|
91
|
+
/* @__PURE__ */ r(
|
|
92
|
+
Ie,
|
|
93
93
|
{
|
|
94
|
-
text:
|
|
94
|
+
text: i.config.label,
|
|
95
95
|
x: 0,
|
|
96
96
|
y: 0,
|
|
97
|
-
width:
|
|
98
|
-
height:
|
|
97
|
+
width: i.config.width,
|
|
98
|
+
height: i.config.height,
|
|
99
99
|
fontSize: 24,
|
|
100
100
|
fontStyle: "bold",
|
|
101
101
|
fill: "#ffffff",
|
|
@@ -104,130 +104,236 @@ const B = W(({ stage: t, stageColor: c }) => /* @__PURE__ */ u(ee, { x: t.positi
|
|
|
104
104
|
}
|
|
105
105
|
)
|
|
106
106
|
] }));
|
|
107
|
-
|
|
108
|
-
const
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
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
|
|
107
|
+
ne.displayName = "ViewerStage";
|
|
108
|
+
const ie = O(({
|
|
109
|
+
floors: i,
|
|
110
|
+
currentFloorId: h,
|
|
111
|
+
onFloorChange: c,
|
|
112
|
+
showAllOption: y,
|
|
113
|
+
allLabel: x,
|
|
114
|
+
position: w,
|
|
115
|
+
className: p
|
|
124
116
|
}) => {
|
|
125
|
-
const
|
|
126
|
-
() =>
|
|
127
|
-
[i
|
|
128
|
-
),
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
117
|
+
const g = k(
|
|
118
|
+
() => [...i].sort((u, E) => u.order - E.order),
|
|
119
|
+
[i]
|
|
120
|
+
), f = {
|
|
121
|
+
position: "absolute",
|
|
122
|
+
display: "flex",
|
|
123
|
+
alignItems: "center",
|
|
124
|
+
gap: "8px",
|
|
125
|
+
padding: "8px 12px",
|
|
126
|
+
backgroundColor: "rgba(26, 26, 26, 0.95)",
|
|
127
|
+
borderRadius: "8px",
|
|
128
|
+
margin: "12px",
|
|
129
|
+
zIndex: 10,
|
|
130
|
+
...{
|
|
131
|
+
"top-left": { top: 0, left: 0 },
|
|
132
|
+
"top-right": { top: 0, right: 0 },
|
|
133
|
+
"bottom-left": { bottom: 0, left: 0 },
|
|
134
|
+
"bottom-right": { bottom: 0, right: 0 }
|
|
135
|
+
}[w]
|
|
136
|
+
}, $ = {
|
|
137
|
+
padding: "6px 14px",
|
|
138
|
+
fontSize: "14px",
|
|
139
|
+
fontWeight: 500,
|
|
140
|
+
border: "1px solid #444",
|
|
141
|
+
borderRadius: "6px",
|
|
142
|
+
backgroundColor: "transparent",
|
|
143
|
+
color: "#fff",
|
|
144
|
+
cursor: "pointer",
|
|
145
|
+
transition: "all 0.2s ease"
|
|
146
|
+
}, A = {
|
|
147
|
+
...$,
|
|
148
|
+
backgroundColor: "#3A7DE5",
|
|
149
|
+
borderColor: "#3A7DE5"
|
|
150
|
+
};
|
|
151
|
+
return /* @__PURE__ */ v("div", { className: p, style: f, children: [
|
|
152
|
+
y && /* @__PURE__ */ r(
|
|
153
|
+
"button",
|
|
154
|
+
{
|
|
155
|
+
type: "button",
|
|
156
|
+
onClick: () => c(null),
|
|
157
|
+
style: h === null ? A : $,
|
|
158
|
+
children: x
|
|
159
|
+
}
|
|
160
|
+
),
|
|
161
|
+
g.map((u) => /* @__PURE__ */ r(
|
|
162
|
+
"button",
|
|
163
|
+
{
|
|
164
|
+
type: "button",
|
|
165
|
+
onClick: () => c(u.id),
|
|
166
|
+
style: h === u.id ? A : $,
|
|
167
|
+
children: u.name
|
|
168
|
+
},
|
|
169
|
+
u.id
|
|
170
|
+
))
|
|
171
|
+
] });
|
|
172
|
+
});
|
|
173
|
+
ie.displayName = "FloorSelectorBar";
|
|
174
|
+
const Ye = ({
|
|
175
|
+
config: i,
|
|
176
|
+
configUrl: h,
|
|
177
|
+
floorId: c,
|
|
178
|
+
onFloorChange: y,
|
|
179
|
+
reservedSeats: x = [],
|
|
180
|
+
unavailableSeats: w = [],
|
|
181
|
+
onSeatSelect: p,
|
|
182
|
+
onSeatDeselect: g,
|
|
183
|
+
onSelectionChange: d,
|
|
184
|
+
colorOverrides: f,
|
|
185
|
+
showTooltip: $ = !0,
|
|
186
|
+
zoomEnabled: A = !0,
|
|
187
|
+
className: u = "",
|
|
188
|
+
onConfigLoad: E,
|
|
189
|
+
onError: D,
|
|
190
|
+
// Floor selector props
|
|
191
|
+
showFloorSelector: U,
|
|
192
|
+
floorSelectorPosition: oe = "top-left",
|
|
193
|
+
floorSelectorClassName: re,
|
|
194
|
+
showAllFloorsOption: se = !0,
|
|
195
|
+
allFloorsLabel: ae = "All",
|
|
196
|
+
fitToView: j = !0,
|
|
197
|
+
fitPadding: H = 40
|
|
198
|
+
}) => {
|
|
199
|
+
const q = we(null), [I, le] = S(/* @__PURE__ */ new Set()), [B, G] = S(1), [R, K] = S({ x: 0, y: 0 }), [ce, de] = S(null), [J, Q] = S(!1), { config: fe, loading: he, error: Y } = Ne(h), n = i || fe, L = c !== void 0, m = L ? c || null : ce, ue = W((e) => {
|
|
200
|
+
L || de(e), y?.(e);
|
|
201
|
+
}, [L, y]), P = n?.floors || [], pe = U !== void 0 ? U : P.length > 1, V = k(
|
|
202
|
+
() => n ? { ...n.colors, ...f } : { ...Fe, ...f },
|
|
203
|
+
[n, f]
|
|
204
|
+
), M = k(() => {
|
|
205
|
+
if (!n) return [];
|
|
206
|
+
let e = n.seats.filter((t) => t.state !== "hidden");
|
|
207
|
+
return m && (e = e.filter(
|
|
208
|
+
(t) => t.floorId === m || !t.floorId && m === "floor_default"
|
|
133
209
|
)), e;
|
|
134
|
-
}, [
|
|
135
|
-
(e) => e.floorId ===
|
|
136
|
-
) :
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
return
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
const n =
|
|
150
|
-
|
|
210
|
+
}, [n, m]), T = k(() => n?.stages ? m ? n.stages.filter(
|
|
211
|
+
(e) => e.floorId === m || !e.floorId && m === "floor_default"
|
|
212
|
+
) : n.stages : [], [n, m]), b = k(() => {
|
|
213
|
+
if (!n || M.length === 0 && T.length === 0)
|
|
214
|
+
return null;
|
|
215
|
+
const e = 12;
|
|
216
|
+
let t = 1 / 0, o = 1 / 0, s = -1 / 0, a = -1 / 0;
|
|
217
|
+
return M.forEach((l) => {
|
|
218
|
+
t = Math.min(t, l.position.x - e), o = Math.min(o, l.position.y - e), s = Math.max(s, l.position.x + e), a = Math.max(a, l.position.y + e);
|
|
219
|
+
}), T.forEach((l) => {
|
|
220
|
+
t = Math.min(t, l.position.x), o = Math.min(o, l.position.y), s = Math.max(s, l.position.x + (l.config?.width || 200)), a = Math.max(a, l.position.y + (l.config?.height || 100));
|
|
221
|
+
}), { minX: t, minY: o, maxX: s, maxY: a, width: s - t, height: a - o };
|
|
222
|
+
}, [n, M, T]);
|
|
223
|
+
F(() => {
|
|
224
|
+
if (!j || J || !n || !b) return;
|
|
225
|
+
const e = n.canvas.width, t = n.canvas.height, o = e - H * 2, s = t - H * 2, a = o / b.width, l = s / b.height, N = Math.min(a, l, 2), C = b.minX + b.width / 2, ye = b.minY + b.height / 2, xe = e / 2, be = t / 2, ve = xe - C * N, Se = be - ye * N;
|
|
226
|
+
G(N), K({ x: ve, y: Se }), Q(!0);
|
|
227
|
+
}, [j, J, n, b, H]), F(() => {
|
|
228
|
+
j && Q(!1);
|
|
229
|
+
}, [m, j]);
|
|
230
|
+
const z = k(() => {
|
|
231
|
+
const e = new Set(x), t = new Set(w);
|
|
232
|
+
return { reserved: e, unavailable: t };
|
|
233
|
+
}, [x, w]), _ = W((e) => {
|
|
234
|
+
const t = e.id, o = e.seatNumber || "";
|
|
235
|
+
return z.unavailable.has(t) || z.unavailable.has(o) ? "unavailable" : z.reserved.has(t) || z.reserved.has(o) ? "reserved" : I.has(t) ? "selected" : e.state;
|
|
236
|
+
}, [z, I]);
|
|
237
|
+
F(() => {
|
|
238
|
+
n && E && E(n);
|
|
239
|
+
}, [n, E]), F(() => {
|
|
240
|
+
Y && D && D(Y);
|
|
241
|
+
}, [Y, D]);
|
|
242
|
+
const ge = W((e) => {
|
|
243
|
+
const t = _(e);
|
|
244
|
+
if (t !== "available" && t !== "selected")
|
|
151
245
|
return;
|
|
152
|
-
const
|
|
153
|
-
|
|
154
|
-
const
|
|
155
|
-
return
|
|
156
|
-
}),
|
|
157
|
-
}, [
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
}, [
|
|
161
|
-
const
|
|
162
|
-
if (!
|
|
246
|
+
const o = I.has(e.id);
|
|
247
|
+
le((s) => {
|
|
248
|
+
const a = new Set(s);
|
|
249
|
+
return o ? a.delete(e.id) : a.add(e.id), a;
|
|
250
|
+
}), o ? g?.(e) : (p?.(e), p || console.log("Seat selected:", e));
|
|
251
|
+
}, [_, I, p, g]), X = k(() => n ? M.filter((e) => I.has(e.id)) : [], [M, I]);
|
|
252
|
+
F(() => {
|
|
253
|
+
d?.(X);
|
|
254
|
+
}, [X, d]);
|
|
255
|
+
const me = W((e) => {
|
|
256
|
+
if (!A) return;
|
|
163
257
|
e.evt.preventDefault();
|
|
164
|
-
const
|
|
165
|
-
if (!
|
|
166
|
-
const
|
|
167
|
-
if (!
|
|
168
|
-
const
|
|
169
|
-
x: (
|
|
170
|
-
y: (
|
|
171
|
-
},
|
|
172
|
-
let
|
|
173
|
-
|
|
174
|
-
x:
|
|
175
|
-
y:
|
|
258
|
+
const t = q.current;
|
|
259
|
+
if (!t) return;
|
|
260
|
+
const o = B, s = t.getPointerPosition();
|
|
261
|
+
if (!s) return;
|
|
262
|
+
const a = {
|
|
263
|
+
x: (s.x - R.x) / o,
|
|
264
|
+
y: (s.y - R.y) / o
|
|
265
|
+
}, l = e.evt.deltaY > 0 ? -1 : 1, N = 1.05;
|
|
266
|
+
let C = l > 0 ? o * N : o / N;
|
|
267
|
+
C = Math.max(0.5, Math.min(5, C)), G(C), K({
|
|
268
|
+
x: s.x - a.x * C,
|
|
269
|
+
y: s.y - a.y * C
|
|
176
270
|
});
|
|
177
|
-
}, [
|
|
178
|
-
return
|
|
271
|
+
}, [A, B, R]);
|
|
272
|
+
return he ? /* @__PURE__ */ r("div", { className: `flex items-center justify-center h-full ${u}`, children: /* @__PURE__ */ r("p", { children: "Loading seat map..." }) }) : Y ? /* @__PURE__ */ r("div", { className: `flex items-center justify-center h-full ${u}`, children: /* @__PURE__ */ v("p", { className: "text-red-500", children: [
|
|
179
273
|
"Error loading seat map: ",
|
|
180
|
-
|
|
181
|
-
] }) }) :
|
|
182
|
-
/* @__PURE__ */
|
|
183
|
-
|
|
274
|
+
Y.message
|
|
275
|
+
] }) }) : n ? /* @__PURE__ */ v("div", { className: `relative ${u}`, children: [
|
|
276
|
+
pe && P.length > 0 && /* @__PURE__ */ r(
|
|
277
|
+
ie,
|
|
278
|
+
{
|
|
279
|
+
floors: P,
|
|
280
|
+
currentFloorId: m,
|
|
281
|
+
onFloorChange: ue,
|
|
282
|
+
showAllOption: se,
|
|
283
|
+
allLabel: ae,
|
|
284
|
+
position: oe,
|
|
285
|
+
className: re
|
|
286
|
+
}
|
|
287
|
+
),
|
|
288
|
+
/* @__PURE__ */ v(
|
|
289
|
+
Ce,
|
|
184
290
|
{
|
|
185
|
-
ref:
|
|
186
|
-
width:
|
|
187
|
-
height:
|
|
188
|
-
scaleX:
|
|
189
|
-
scaleY:
|
|
190
|
-
x:
|
|
191
|
-
y:
|
|
192
|
-
onWheel:
|
|
193
|
-
style: { backgroundColor:
|
|
291
|
+
ref: q,
|
|
292
|
+
width: n.canvas.width,
|
|
293
|
+
height: n.canvas.height,
|
|
294
|
+
scaleX: B,
|
|
295
|
+
scaleY: B,
|
|
296
|
+
x: R.x,
|
|
297
|
+
y: R.y,
|
|
298
|
+
onWheel: me,
|
|
299
|
+
style: { backgroundColor: n.canvas.backgroundColor },
|
|
194
300
|
children: [
|
|
195
|
-
/* @__PURE__ */
|
|
196
|
-
|
|
301
|
+
/* @__PURE__ */ r(Z, { listening: !1, children: T.map((e) => /* @__PURE__ */ r(
|
|
302
|
+
ne,
|
|
197
303
|
{
|
|
198
304
|
stage: e,
|
|
199
|
-
stageColor:
|
|
305
|
+
stageColor: V.stageColor
|
|
200
306
|
},
|
|
201
307
|
e.id
|
|
202
308
|
)) }),
|
|
203
|
-
/* @__PURE__ */
|
|
204
|
-
|
|
309
|
+
/* @__PURE__ */ r(Z, { children: M.map((e) => /* @__PURE__ */ r(
|
|
310
|
+
te,
|
|
205
311
|
{
|
|
206
312
|
seat: e,
|
|
207
|
-
state:
|
|
208
|
-
colors:
|
|
209
|
-
onClick:
|
|
313
|
+
state: _(e),
|
|
314
|
+
colors: V,
|
|
315
|
+
onClick: ge
|
|
210
316
|
},
|
|
211
317
|
e.id
|
|
212
318
|
)) })
|
|
213
319
|
]
|
|
214
320
|
}
|
|
215
321
|
),
|
|
216
|
-
|
|
217
|
-
/* @__PURE__ */
|
|
322
|
+
X.length > 0 && /* @__PURE__ */ v("div", { className: "absolute top-4 right-4 bg-white dark:bg-gray-800 p-4 rounded shadow-lg", children: [
|
|
323
|
+
/* @__PURE__ */ v("h3", { className: "font-semibold mb-2", children: [
|
|
218
324
|
"Selected Seats (",
|
|
219
|
-
|
|
325
|
+
X.length,
|
|
220
326
|
")"
|
|
221
327
|
] }),
|
|
222
|
-
/* @__PURE__ */
|
|
328
|
+
/* @__PURE__ */ r("div", { className: "max-h-48 overflow-y-auto space-y-1", children: X.map((e) => /* @__PURE__ */ v("div", { className: "text-sm", children: [
|
|
223
329
|
e.seatNumber,
|
|
224
|
-
e.price && ` - ${
|
|
330
|
+
e.price && ` - ${V.currency} ${e.price.toFixed(2)}`
|
|
225
331
|
] }, e.id)) })
|
|
226
332
|
] })
|
|
227
|
-
] }) : /* @__PURE__ */
|
|
333
|
+
] }) : /* @__PURE__ */ r("div", { className: `flex items-center justify-center h-full ${u}`, children: /* @__PURE__ */ r("p", { children: "No configuration provided" }) });
|
|
228
334
|
};
|
|
229
335
|
export {
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
336
|
+
Fe as DEFAULT_COLORS,
|
|
337
|
+
Ye as SeatMapViewer,
|
|
338
|
+
Ne as useConfigFetcher
|
|
233
339
|
};
|
package/dist/types/index.d.ts
CHANGED
|
@@ -44,6 +44,12 @@ export interface SerializedStage {
|
|
|
44
44
|
config: any;
|
|
45
45
|
floorId?: string;
|
|
46
46
|
}
|
|
47
|
+
export interface FloorConfig {
|
|
48
|
+
id: string;
|
|
49
|
+
name: string;
|
|
50
|
+
order: number;
|
|
51
|
+
color?: string;
|
|
52
|
+
}
|
|
47
53
|
export interface ColorSettings {
|
|
48
54
|
canvasBackground: string;
|
|
49
55
|
stageColor: string;
|
|
@@ -74,5 +80,6 @@ export interface SeatMapConfig {
|
|
|
74
80
|
seats: SerializedSeat[];
|
|
75
81
|
sections?: SerializedSection[];
|
|
76
82
|
stages?: SerializedStage[];
|
|
83
|
+
floors?: FloorConfig[];
|
|
77
84
|
}
|
|
78
85
|
export declare const DEFAULT_COLORS: ColorSettings;
|