@zonetrix/viewer 2.2.0 → 2.4.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 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 |
@@ -112,10 +113,22 @@ function BookingApp() {
112
113
  | `onSelectionChange` | `(seats: SeatData[]) => void` | No | Callback when selection changes |
113
114
  | `colorOverrides` | `Partial<ColorSettings>` | No | Custom colors for seat states |
114
115
  | `showTooltip` | `boolean` | No | Show tooltips on hover (default: true) |
115
- | `zoomEnabled` | `boolean` | No | Enable/disable mouse wheel zoom (default: true) |
116
+ | `zoomEnabled` | `boolean` | No | Enable/disable zoom functionality (default: true) |
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') |
125
+ | `fitToView` | `boolean` | No | Auto-fit content on load (default: true) |
126
+ | `fitPadding` | `number` | No | Padding around content when fitting (default: 40) |
127
+ | `showZoomControls` | `boolean` | No | Show zoom +/- buttons (default: true) |
128
+ | `zoomControlsPosition` | `string` | No | Position: 'top-left' \| 'top-right' \| 'bottom-left' \| 'bottom-right' (default: 'bottom-right') |
129
+ | `zoomControlsClassName` | `string` | No | Custom CSS class for zoom controls |
130
+ | `maxZoom` | `number` | No | Maximum zoom level (default: 3) |
131
+ | `zoomStep` | `number` | No | Zoom increment per click (default: 0.25) |
119
132
 
120
133
  *Note: Either `config` or `configUrl` must be provided.
121
134
 
@@ -239,37 +252,72 @@ function SelectionTracker() {
239
252
  }
240
253
  ```
241
254
 
242
- ### 5. Disable Zoom for Mobile
255
+ ### 5. Customize Zoom Controls
243
256
 
244
257
  ```tsx
245
258
  import { SeatMapViewer } from '@zonetrix/viewer';
246
259
 
247
- function MobileOptimized() {
248
- const isMobile = window.innerWidth < 768;
260
+ function CustomZoom() {
261
+ return (
262
+ <SeatMapViewer
263
+ config={venueConfig}
264
+ // Zoom controls appear in bottom-right by default
265
+ zoomControlsPosition="bottom-left"
266
+ maxZoom={5} // Allow up to 5x zoom
267
+ zoomStep={0.5} // Larger zoom increments
268
+ onSeatSelect={(seat) => handleSelection(seat)}
269
+ />
270
+ );
271
+ }
272
+
273
+ // Hide zoom controls entirely
274
+ function NoZoomControls() {
275
+ return (
276
+ <SeatMapViewer
277
+ config={venueConfig}
278
+ showZoomControls={false}
279
+ />
280
+ );
281
+ }
282
+ ```
283
+
284
+ ### 6. Multi-floor Venue (Built-in Floor Selector)
249
285
 
286
+ The viewer includes a built-in floor selector that automatically appears when your config has multiple floors.
287
+
288
+ ```tsx
289
+ import { SeatMapViewer } from '@zonetrix/viewer';
290
+
291
+ function MultiFloorVenue() {
250
292
  return (
251
293
  <SeatMapViewer
252
294
  config={venueConfig}
253
- zoomEnabled={!isMobile}
254
295
  onSeatSelect={(seat) => handleSelection(seat)}
296
+ // Floor selector auto-shows when config.floors.length > 1
297
+ // Customize position and labels:
298
+ floorSelectorPosition="top-right"
299
+ allFloorsLabel="All Floors"
255
300
  />
256
301
  );
257
302
  }
258
303
  ```
259
304
 
260
- ### 6. Multi-floor Venue
305
+ #### Custom Floor Selector (Controlled Mode)
306
+
307
+ For full control over the floor selector UI, use controlled mode:
261
308
 
262
309
  ```tsx
263
310
  import { useState } from 'react';
264
311
  import { SeatMapViewer } from '@zonetrix/viewer';
265
312
 
266
- function MultiFloorVenue() {
267
- const [currentFloor, setCurrentFloor] = useState('floor_1');
313
+ function CustomFloorSelector() {
314
+ const [currentFloor, setCurrentFloor] = useState<string | null>(null);
268
315
 
269
316
  return (
270
317
  <div>
271
- {/* Floor selector */}
318
+ {/* Your custom floor selector */}
272
319
  <div className="floor-tabs">
320
+ <button onClick={() => setCurrentFloor(null)}>All</button>
273
321
  {venueConfig.floors?.map((floor) => (
274
322
  <button
275
323
  key={floor.id}
@@ -283,7 +331,8 @@ function MultiFloorVenue() {
283
331
 
284
332
  <SeatMapViewer
285
333
  config={venueConfig}
286
- floorId={currentFloor}
334
+ showFloorSelector={false} // Hide built-in selector
335
+ floorId={currentFloor || undefined}
287
336
  onFloorChange={setCurrentFloor}
288
337
  onSeatSelect={(seat) => handleSelection(seat)}
289
338
  />
@@ -364,7 +413,11 @@ The viewer accepts a `SeatMapConfig` object. You can create these configurations
364
413
  }
365
414
  ],
366
415
  "sections": [],
367
- "stages": []
416
+ "stages": [],
417
+ "floors": [
418
+ { "id": "floor_1", "name": "Ground Floor", "order": 0 },
419
+ { "id": "floor_2", "name": "First Floor", "order": 1 }
420
+ ]
368
421
  }
369
422
  ```
370
423
 
@@ -416,6 +469,17 @@ interface ColorSettings {
416
469
  }
417
470
  ```
418
471
 
472
+ ### FloorConfig
473
+
474
+ ```typescript
475
+ interface FloorConfig {
476
+ id: string; // Unique identifier (e.g., "floor_1")
477
+ name: string; // Display name (e.g., "Ground Floor")
478
+ order: number; // Sort order (0 = first)
479
+ color?: string; // Optional floor color
480
+ }
481
+ ```
482
+
419
483
  ## Seat States Explained
420
484
 
421
485
  | State | Description | User Can Select? | Visual |
@@ -21,5 +21,12 @@ export interface SeatMapViewerProps {
21
21
  floorSelectorClassName?: string;
22
22
  showAllFloorsOption?: boolean;
23
23
  allFloorsLabel?: string;
24
+ fitToView?: boolean;
25
+ fitPadding?: number;
26
+ showZoomControls?: boolean;
27
+ zoomControlsPosition?: 'top-left' | 'top-right' | 'bottom-left' | 'bottom-right';
28
+ zoomControlsClassName?: string;
29
+ maxZoom?: number;
30
+ zoomStep?: number;
24
31
  }
25
32
  export declare const SeatMapViewer: React.FC<SeatMapViewerProps>;
package/dist/index.js CHANGED
@@ -1 +1 @@
1
- "use strict";Object.defineProperty(exports,Symbol.toStringTag,{value:"Module"});const s=require("react/jsx-runtime"),t=require("react"),S=require("react-konva");function B(o){const[c,i]=t.useState(null),[g,x]=t.useState(!1),[m,f]=t.useState(null),u=async()=>{if(o){x(!0),f(null);try{const a=await fetch(o);if(!a.ok)throw new Error(`Failed to fetch config: ${a.statusText}`);const l=await a.json();i(l)}catch(a){const l=a instanceof Error?a:new Error("Unknown error occurred");f(l),console.error("Failed to fetch seat map config:",l)}finally{x(!1)}}};return t.useEffect(()=>{u()},[o]),{config:c,loading:g,error:m,refetch:u}}const O={canvasBackground:"#1a1a1a",stageColor:"#808080",seatAvailable:"#2C2B30",seatReserved:"#FCEA00",seatSelected:"#3A7DE5",seatUnavailable:"#6b7280",seatHidden:"#4a4a4a",gridLines:"#404040",currency:"KD"},W=t.memo(({seat:o,state:c,colors:i,onClick:g})=>{const f={available:i.seatAvailable,reserved:i.seatReserved,selected:i.seatSelected,unavailable:i.seatUnavailable,hidden:i.seatHidden}[c],u=c==="available"||c==="selected",a=t.useCallback(()=>{u&&g(o)},[o,g,u]),l={x:o.position.x,y:o.position.y,fill:f,stroke:"#ffffff",strokeWidth:1,onClick:a,onTap:a};return o.shape==="circle"?s.jsx(S.Circle,{...l,radius:12}):s.jsx(S.Rect,{...l,width:24,height:24,offsetX:12,offsetY:12,cornerRadius:o.shape==="square"?0:4})});W.displayName="ViewerSeat";const _=t.memo(({stage:o,stageColor:c})=>s.jsxs(S.Group,{x:o.position.x,y:o.position.y,children:[s.jsx(S.Rect,{width:o.config.width,height:o.config.height,fill:c+"80",stroke:"#ffffff",strokeWidth:2,cornerRadius:10}),s.jsx(S.Text,{text:o.config.label,x:0,y:0,width:o.config.width,height:o.config.height,fontSize:24,fontStyle:"bold",fill:"#ffffff",align:"center",verticalAlign:"middle"})]}));_.displayName="ViewerStage";const q=t.memo(({floors:o,currentFloorId:c,onFloorChange:i,showAllOption:g,allLabel:x,position:m,className:f})=>{const u=t.useMemo(()=>[...o].sort((d,w)=>d.order-w.order),[o]),l={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}}[m]},E={padding:"6px 14px",fontSize:"14px",fontWeight:500,border:"1px solid #444",borderRadius:"6px",backgroundColor:"transparent",color:"#fff",cursor:"pointer",transition:"all 0.2s ease"},j={...E,backgroundColor:"#3A7DE5",borderColor:"#3A7DE5"};return s.jsxs("div",{className:f,style:l,children:[g&&s.jsx("button",{type:"button",onClick:()=>i(null),style:c===null?j:E,children:x}),u.map(d=>s.jsx("button",{type:"button",onClick:()=>i(d.id),style:c===d.id?j:E,children:d.name},d.id))]})});q.displayName="FloorSelectorBar";const le=({config:o,configUrl:c,floorId:i,onFloorChange:g,reservedSeats:x=[],unavailableSeats:m=[],onSeatSelect:f,onSeatDeselect:u,onSelectionChange:a,colorOverrides:l,showTooltip:E=!0,zoomEnabled:j=!0,className:d="",onConfigLoad:w,onError:A,showFloorSelector:$,floorSelectorPosition:U="top-left",floorSelectorClassName:Y,showAllFloorsOption:H=!0,allFloorsLabel:K="All"})=>{const D=t.useRef(null),[y,X]=t.useState(new Set),[F,G]=t.useState(1),[C,J]=t.useState({x:0,y:0}),[Q,Z]=t.useState(null),{config:ee,loading:te,error:k}=B(c),r=o||ee,I=i!==void 0,p=I?i||null:Q,se=t.useCallback(e=>{I||Z(e),g?.(e)},[I,g]),T=r?.floors||[],oe=$!==void 0?$:T.length>1,z=t.useMemo(()=>r?{...r.colors,...l}:{...O,...l},[r,l]),L=t.useMemo(()=>{if(!r)return[];let e=r.seats.filter(n=>n.state!=="hidden");return p&&(e=e.filter(n=>n.floorId===p||!n.floorId&&p==="floor_default")),e},[r,p]),re=t.useMemo(()=>r?.stages?p?r.stages.filter(e=>e.floorId===p||!e.floorId&&p==="floor_default"):r.stages:[],[r,p]),N=t.useMemo(()=>{const e=new Set(x),n=new Set(m);return{reserved:e,unavailable:n}},[x,m]),P=t.useCallback(e=>{const n=e.id,h=e.seatNumber||"";return N.unavailable.has(n)||N.unavailable.has(h)?"unavailable":N.reserved.has(n)||N.reserved.has(h)?"reserved":y.has(n)?"selected":e.state},[N,y]);t.useEffect(()=>{r&&w&&w(r)},[r,w]),t.useEffect(()=>{k&&A&&A(k)},[k,A]);const ne=t.useCallback(e=>{const n=P(e);if(n!=="available"&&n!=="selected")return;const h=y.has(e.id);X(b=>{const v=new Set(b);return h?v.delete(e.id):v.add(e.id),v}),h?u?.(e):(f?.(e),f||console.log("Seat selected:",e))},[P,y,f,u]),M=t.useMemo(()=>r?L.filter(e=>y.has(e.id)):[],[L,y]);t.useEffect(()=>{a?.(M)},[M,a]);const ie=t.useCallback(e=>{if(!j)return;e.evt.preventDefault();const n=D.current;if(!n)return;const h=F,b=n.getPointerPosition();if(!b)return;const v={x:(b.x-C.x)/h,y:(b.y-C.y)/h},ae=e.evt.deltaY>0?-1:1,V=1.05;let R=ae>0?h*V:h/V;R=Math.max(.5,Math.min(5,R)),G(R),J({x:b.x-v.x*R,y:b.y-v.y*R})},[j,F,C]);return te?s.jsx("div",{className:`flex items-center justify-center h-full ${d}`,children:s.jsx("p",{children:"Loading seat map..."})}):k?s.jsx("div",{className:`flex items-center justify-center h-full ${d}`,children:s.jsxs("p",{className:"text-red-500",children:["Error loading seat map: ",k.message]})}):r?s.jsxs("div",{className:`relative ${d}`,children:[oe&&T.length>0&&s.jsx(q,{floors:T,currentFloorId:p,onFloorChange:se,showAllOption:H,allLabel:K,position:U,className:Y}),s.jsxs(S.Stage,{ref:D,width:r.canvas.width,height:r.canvas.height,scaleX:F,scaleY:F,x:C.x,y:C.y,onWheel:ie,style:{backgroundColor:r.canvas.backgroundColor},children:[s.jsx(S.Layer,{listening:!1,children:re.map(e=>s.jsx(_,{stage:e,stageColor:z.stageColor},e.id))}),s.jsx(S.Layer,{children:L.map(e=>s.jsx(W,{seat:e,state:P(e),colors:z,onClick:ne},e.id))})]}),M.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 (",M.length,")"]}),s.jsx("div",{className:"max-h-48 overflow-y-auto space-y-1",children:M.map(e=>s.jsxs("div",{className:"text-sm",children:[e.seatNumber,e.price&&` - ${z.currency} ${e.price.toFixed(2)}`]},e.id))})]})]}):s.jsx("div",{className:`flex items-center justify-center h-full ${d}`,children:s.jsx("p",{children:"No configuration provided"})})};exports.DEFAULT_COLORS=O;exports.SeatMapViewer=le;exports.useConfigFetcher=B;
1
+ "use strict";Object.defineProperty(exports,Symbol.toStringTag,{value:"Module"});const o=require("react/jsx-runtime"),t=require("react"),k=require("react-konva");function P(i){const[h,u]=t.useState(null),[m,S]=t.useState(!1),[C,x]=t.useState(null),b=async()=>{if(i){S(!0),x(null);try{const f=await fetch(i);if(!f.ok)throw new Error(`Failed to fetch config: ${f.statusText}`);const c=await f.json();u(c)}catch(f){const c=f instanceof Error?f:new Error("Unknown error occurred");x(c),console.error("Failed to fetch seat map config:",c)}finally{S(!1)}}};return t.useEffect(()=>{b()},[i]),{config:h,loading:m,error:C,refetch:b}}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:u,onClick:m})=>{const x={available:u.seatAvailable,reserved:u.seatReserved,selected:u.seatSelected,unavailable:u.seatUnavailable,hidden:u.seatHidden}[h],b=h==="available"||h==="selected",f=t.useCallback(()=>{b&&m(i)},[i,m,b]),c={x:i.position.x,y:i.position.y,fill:x,stroke:"#ffffff",strokeWidth:1,onClick:f,onTap:f};return i.shape==="circle"?o.jsx(k.Circle,{...c,radius:12}):o.jsx(k.Rect,{...c,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(k.Group,{x:i.position.x,y:i.position.y,children:[o.jsx(k.Rect,{width:i.config.width,height:i.config.height,fill:h+"80",stroke:"#ffffff",strokeWidth:2,cornerRadius:10}),o.jsx(k.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 ee=t.memo(({floors:i,currentFloorId:h,onFloorChange:u,showAllOption:m,allLabel:S,position:C,className:x})=>{const b=t.useMemo(()=>[...i].sort((p,E)=>p.order-E.order),[i]),c={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}}[C]},M={padding:"6px 14px",fontSize:"14px",fontWeight:500,border:"1px solid #444",borderRadius:"6px",backgroundColor:"transparent",color:"#fff",cursor:"pointer",transition:"all 0.2s ease"},y={...M,backgroundColor:"#3A7DE5",borderColor:"#3A7DE5"};return o.jsxs("div",{className:x,style:c,children:[m&&o.jsx("button",{type:"button",onClick:()=>u(null),style:h===null?y:M,children:S}),b.map(p=>o.jsx("button",{type:"button",onClick:()=>u(p.id),style:h===p.id?y:M,children:p.name},p.id))]})});ee.displayName="FloorSelectorBar";const te=t.memo(({scale:i,minScale:h,maxScale:u,onZoomIn:m,onZoomOut:S,position:C,className:x})=>{const f={position:"absolute",display:"flex",flexDirection:"column",gap:"4px",padding:"8px",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}}[C]},c={width:"36px",height:"36px",fontSize:"20px",fontWeight:"bold",border:"1px solid #444",borderRadius:"6px",backgroundColor:"transparent",color:"#fff",cursor:"pointer",display:"flex",alignItems:"center",justifyContent:"center",transition:"all 0.2s ease"},M={...c,opacity:.4,cursor:"not-allowed"},y=i<u,p=i>h;return o.jsxs("div",{className:x,style:f,children:[o.jsx("button",{type:"button",onClick:m,disabled:!y,style:y?c:M,title:"Zoom In",children:"+"}),o.jsx("button",{type:"button",onClick:S,disabled:!p,style:p?c:M,title:"Zoom Out",children:"−"})]})});te.displayName="ZoomControls";const Ee=({config:i,configUrl:h,floorId:u,onFloorChange:m,reservedSeats:S=[],unavailableSeats:C=[],onSeatSelect:x,onSeatDeselect:b,onSelectionChange:f,colorOverrides:c,showTooltip:M=!0,zoomEnabled:y=!0,className:p="",onConfigLoad:E,onError:W,showFloorSelector:q,floorSelectorPosition:ne="top-left",floorSelectorClassName:oe,showAllFloorsOption:se=!0,allFloorsLabel:ie="All",fitToView:Y=!0,fitPadding:B=40,showZoomControls:ae=!0,zoomControlsPosition:re="bottom-right",zoomControlsClassName:le,maxZoom:F=3,zoomStep:O=.25})=>{const ce=t.useRef(null),[I,de]=t.useState(new Set),[g,H]=t.useState(1),[w,T]=t.useState({x:0,y:0}),[ue,fe]=t.useState(null),[U,K]=t.useState(!1),[he,pe]=t.useState(1),{config:ge,loading:xe,error:R}=P(h),n=i||ge,$=u!==void 0,v=$?u||null:ue,me=t.useCallback(e=>{$||fe(e),m?.(e)},[$,m]),Z=n?.floors||[],be=q!==void 0?q:Z.length>1,z=t.useMemo(()=>n?{...n.colors,...c}:{...G,...c},[n,c]),N=t.useMemo(()=>{if(!n)return[];let e=n.seats.filter(s=>s.state!=="hidden");return v&&(e=e.filter(s=>s.floorId===v||!s.floorId&&v==="floor_default")),e},[n,v]),D=t.useMemo(()=>n?.stages?v?n.stages.filter(e=>e.floorId===v||!e.floorId&&v==="floor_default"):n.stages:[],[n,v]),j=t.useMemo(()=>{if(!n||N.length===0&&D.length===0)return null;const e=12;let s=1/0,r=1/0,d=-1/0,l=-1/0;return N.forEach(a=>{s=Math.min(s,a.position.x-e),r=Math.min(r,a.position.y-e),d=Math.max(d,a.position.x+e),l=Math.max(l,a.position.y+e)}),D.forEach(a=>{s=Math.min(s,a.position.x),r=Math.min(r,a.position.y),d=Math.max(d,a.position.x+(a.config?.width||200)),l=Math.max(l,a.position.y+(a.config?.height||100))}),{minX:s,minY:r,maxX:d,maxY:l,width:d-s,height:l-r}},[n,N,D]);t.useEffect(()=>{if(!Y||U||!n||!j)return;const e=n.canvas.width,s=n.canvas.height,r=e-B*2,d=s-B*2,l=r/j.width,a=d/j.height,L=Math.min(l,a,F),we=j.minX+j.width/2,je=j.minY+j.height/2,ke=e/2,Me=s/2,Ie=ke-we*L,Ne=Me-je*L;H(L),T({x:Ie,y:Ne}),pe(L),K(!0)},[Y,U,n,j,B,F]),t.useEffect(()=>{Y&&K(!1)},[v,Y]);const A=t.useMemo(()=>{const e=new Set(S),s=new Set(C);return{reserved:e,unavailable:s}},[S,C]),V=t.useCallback(e=>{const s=e.id,r=e.seatNumber||"";return A.unavailable.has(s)||A.unavailable.has(r)?"unavailable":A.reserved.has(s)||A.reserved.has(r)?"reserved":I.has(s)?"selected":e.state},[A,I]);t.useEffect(()=>{n&&E&&E(n)},[n,E]),t.useEffect(()=>{R&&W&&W(R)},[R,W]);const ye=t.useCallback(e=>{const s=V(e);if(s!=="available"&&s!=="selected")return;const r=I.has(e.id);de(d=>{const l=new Set(d);return r?l.delete(e.id):l.add(e.id),l}),r?b?.(e):(x?.(e),x||console.log("Seat selected:",e))},[V,I,x,b]),X=t.useMemo(()=>n?N.filter(e=>I.has(e.id)):[],[N,I]);t.useEffect(()=>{f?.(X)},[X,f]);const _=he,Se=t.useCallback(()=>{if(!y)return;const e=Math.min(g+O,F);if(e!==g){const s=n?.canvas.width||800,r=n?.canvas.height||600,d=s/2,l=r/2,a={x:(d-w.x)/g,y:(l-w.y)/g};H(e),T({x:d-a.x*e,y:l-a.y*e})}},[y,g,O,F,n,w]),ve=t.useCallback(()=>{if(!y)return;const e=Math.max(g-O,_);if(e!==g){const s=n?.canvas.width||800,r=n?.canvas.height||600,d=s/2,l=r/2,a={x:(d-w.x)/g,y:(l-w.y)/g};H(e),T({x:d-a.x*e,y:l-a.y*e})}},[y,g,O,_,n,w]),Ce=t.useCallback(e=>{T({x:e.target.x(),y:e.target.y()})},[]);return xe?o.jsx("div",{className:`flex items-center justify-center h-full ${p}`,children:o.jsx("p",{children:"Loading seat map..."})}):R?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: ",R.message]})}):n?o.jsxs("div",{className:`relative ${p}`,children:[be&&Z.length>0&&o.jsx(ee,{floors:Z,currentFloorId:v,onFloorChange:me,showAllOption:se,allLabel:ie,position:ne,className:oe}),o.jsxs(k.Stage,{ref:ce,width:n.canvas.width,height:n.canvas.height,scaleX:g,scaleY:g,x:w.x,y:w.y,draggable:!0,onDragEnd:Ce,style:{backgroundColor:n.canvas.backgroundColor,cursor:"grab"},children:[o.jsx(k.Layer,{listening:!1,children:D.map(e=>o.jsx(Q,{stage:e,stageColor:z.stageColor},e.id))}),o.jsx(k.Layer,{children:N.map(e=>o.jsx(J,{seat:e,state:V(e),colors:z,onClick:ye},e.id))})]}),ae&&y&&o.jsx(te,{scale:g,minScale:_,maxScale:F,onZoomIn:Se,onZoomOut:ve,position:re,className:le}),X.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 (",X.length,")"]}),o.jsx("div",{className:"max-h-48 overflow-y-auto space-y-1",children:X.map(e=>o.jsxs("div",{className:"text-sm",children:[e.seatNumber,e.price&&` - ${z.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=Ee;exports.useConfigFetcher=P;
package/dist/index.mjs CHANGED
@@ -1,34 +1,34 @@
1
- import { jsx as r, jsxs as m } from "react/jsx-runtime";
2
- import { useState as y, useEffect as $, useRef as ue, useCallback as z, useMemo as w, memo as V } from "react";
3
- import { Stage as he, Layer as H, Group as pe, Rect as O, Text as ge, Circle as me } from "react-konva";
4
- function be(t) {
5
- const [a, i] = y(null), [h, g] = y(!1), [b, d] = y(null), f = async () => {
6
- if (t) {
7
- g(!0), d(null);
1
+ import { jsx as i, jsxs as w } from "react/jsx-runtime";
2
+ import { useState as C, useEffect as Y, useRef as Et, useCallback as M, useMemo as F, memo as Z } from "react";
3
+ import { Stage as Wt, Layer as tt, Group as jt, Rect as et, Text as Bt, Circle as Dt } from "react-konva";
4
+ function Ht(o) {
5
+ const [f, d] = C(null), [m, x] = C(!1), [S, g] = C(null), y = async () => {
6
+ if (o) {
7
+ x(!0), g(null);
8
8
  try {
9
- const s = await fetch(t);
10
- if (!s.ok)
11
- throw new Error(`Failed to fetch config: ${s.statusText}`);
12
- const l = await s.json();
13
- i(l);
14
- } catch (s) {
15
- const l = s instanceof Error ? s : new Error("Unknown error occurred");
16
- d(l), console.error("Failed to fetch seat map config:", l);
9
+ const h = await fetch(o);
10
+ if (!h.ok)
11
+ throw new Error(`Failed to fetch config: ${h.statusText}`);
12
+ const l = await h.json();
13
+ d(l);
14
+ } catch (h) {
15
+ const l = h instanceof Error ? h : new Error("Unknown error occurred");
16
+ g(l), console.error("Failed to fetch seat map config:", l);
17
17
  } finally {
18
- g(!1);
18
+ x(!1);
19
19
  }
20
20
  }
21
21
  };
22
- return $(() => {
23
- f();
24
- }, [t]), {
25
- config: a,
26
- loading: h,
27
- error: b,
28
- refetch: f
22
+ return Y(() => {
23
+ y();
24
+ }, [o]), {
25
+ config: f,
26
+ loading: m,
27
+ error: S,
28
+ refetch: y
29
29
  };
30
30
  }
31
- const Se = {
31
+ const Tt = {
32
32
  canvasBackground: "#1a1a1a",
33
33
  stageColor: "#808080",
34
34
  seatAvailable: "#2C2B30",
@@ -38,64 +38,64 @@ const Se = {
38
38
  seatHidden: "#4a4a4a",
39
39
  gridLines: "#404040",
40
40
  currency: "KD"
41
- }, U = V(({ seat: t, state: a, colors: i, onClick: h }) => {
42
- const d = {
43
- available: i.seatAvailable,
44
- reserved: i.seatReserved,
45
- selected: i.seatSelected,
46
- unavailable: i.seatUnavailable,
47
- hidden: i.seatHidden
41
+ }, nt = Z(({ seat: o, state: f, colors: d, onClick: m }) => {
42
+ const g = {
43
+ available: d.seatAvailable,
44
+ reserved: d.seatReserved,
45
+ selected: d.seatSelected,
46
+ unavailable: d.seatUnavailable,
47
+ hidden: d.seatHidden
48
48
  // Hidden seats are filtered out, but included for type safety
49
- }[a], f = a === "available" || a === "selected", s = z(() => {
50
- f && h(t);
51
- }, [t, h, f]), l = {
52
- x: t.position.x,
53
- y: t.position.y,
54
- fill: d,
49
+ }[f], y = f === "available" || f === "selected", h = M(() => {
50
+ y && m(o);
51
+ }, [o, m, y]), l = {
52
+ x: o.position.x,
53
+ y: o.position.y,
54
+ fill: g,
55
55
  stroke: "#ffffff",
56
56
  strokeWidth: 1,
57
- onClick: s,
58
- onTap: s
57
+ onClick: h,
58
+ onTap: h
59
59
  };
60
- return t.shape === "circle" ? /* @__PURE__ */ r(
61
- me,
60
+ return o.shape === "circle" ? /* @__PURE__ */ i(
61
+ Dt,
62
62
  {
63
63
  ...l,
64
64
  radius: 12
65
65
  }
66
- ) : /* @__PURE__ */ r(
67
- O,
66
+ ) : /* @__PURE__ */ i(
67
+ et,
68
68
  {
69
69
  ...l,
70
70
  width: 24,
71
71
  height: 24,
72
72
  offsetX: 12,
73
73
  offsetY: 12,
74
- cornerRadius: t.shape === "square" ? 0 : 4
74
+ cornerRadius: o.shape === "square" ? 0 : 4
75
75
  }
76
76
  );
77
77
  });
78
- U.displayName = "ViewerSeat";
79
- const X = V(({ stage: t, stageColor: a }) => /* @__PURE__ */ m(pe, { x: t.position.x, y: t.position.y, children: [
80
- /* @__PURE__ */ r(
81
- O,
78
+ nt.displayName = "ViewerSeat";
79
+ const ot = Z(({ stage: o, stageColor: f }) => /* @__PURE__ */ w(jt, { x: o.position.x, y: o.position.y, children: [
80
+ /* @__PURE__ */ i(
81
+ et,
82
82
  {
83
- width: t.config.width,
84
- height: t.config.height,
85
- fill: a + "80",
83
+ width: o.config.width,
84
+ height: o.config.height,
85
+ fill: f + "80",
86
86
  stroke: "#ffffff",
87
87
  strokeWidth: 2,
88
88
  cornerRadius: 10
89
89
  }
90
90
  ),
91
- /* @__PURE__ */ r(
92
- ge,
91
+ /* @__PURE__ */ i(
92
+ Bt,
93
93
  {
94
- text: t.config.label,
94
+ text: o.config.label,
95
95
  x: 0,
96
96
  y: 0,
97
- width: t.config.width,
98
- height: t.config.height,
97
+ width: o.config.width,
98
+ height: o.config.height,
99
99
  fontSize: 24,
100
100
  fontStyle: "bold",
101
101
  fill: "#ffffff",
@@ -104,19 +104,19 @@ const X = V(({ stage: t, stageColor: a }) => /* @__PURE__ */ m(pe, { x: t.positi
104
104
  }
105
105
  )
106
106
  ] }));
107
- X.displayName = "ViewerStage";
108
- const q = V(({
109
- floors: t,
110
- currentFloorId: a,
111
- onFloorChange: i,
112
- showAllOption: h,
113
- allLabel: g,
114
- position: b,
115
- className: d
107
+ ot.displayName = "ViewerStage";
108
+ const it = Z(({
109
+ floors: o,
110
+ currentFloorId: f,
111
+ onFloorChange: d,
112
+ showAllOption: m,
113
+ allLabel: x,
114
+ position: S,
115
+ className: g
116
116
  }) => {
117
- const f = w(
118
- () => [...t].sort((c, k) => c.order - k.order),
119
- [t]
117
+ const y = F(
118
+ () => [...o].sort((u, A) => u.order - A.order),
119
+ [o]
120
120
  ), l = {
121
121
  position: "absolute",
122
122
  display: "flex",
@@ -132,8 +132,8 @@ const q = V(({
132
132
  "top-right": { top: 0, right: 0 },
133
133
  "bottom-left": { bottom: 0, left: 0 },
134
134
  "bottom-right": { bottom: 0, right: 0 }
135
- }[b]
136
- }, E = {
135
+ }[S]
136
+ }, N = {
137
137
  padding: "6px 14px",
138
138
  fontSize: "14px",
139
139
  fontWeight: 500,
@@ -143,177 +143,301 @@ const q = V(({
143
143
  color: "#fff",
144
144
  cursor: "pointer",
145
145
  transition: "all 0.2s ease"
146
- }, C = {
147
- ...E,
146
+ }, b = {
147
+ ...N,
148
148
  backgroundColor: "#3A7DE5",
149
149
  borderColor: "#3A7DE5"
150
150
  };
151
- return /* @__PURE__ */ m("div", { className: d, style: l, children: [
152
- h && /* @__PURE__ */ r(
151
+ return /* @__PURE__ */ w("div", { className: g, style: l, children: [
152
+ m && /* @__PURE__ */ i(
153
153
  "button",
154
154
  {
155
155
  type: "button",
156
- onClick: () => i(null),
157
- style: a === null ? C : E,
158
- children: g
156
+ onClick: () => d(null),
157
+ style: f === null ? b : N,
158
+ children: x
159
159
  }
160
160
  ),
161
- f.map((c) => /* @__PURE__ */ r(
161
+ y.map((u) => /* @__PURE__ */ i(
162
162
  "button",
163
163
  {
164
164
  type: "button",
165
- onClick: () => i(c.id),
166
- style: a === c.id ? C : E,
167
- children: c.name
165
+ onClick: () => d(u.id),
166
+ style: f === u.id ? b : N,
167
+ children: u.name
168
168
  },
169
- c.id
169
+ u.id
170
170
  ))
171
171
  ] });
172
172
  });
173
- q.displayName = "FloorSelectorBar";
174
- const we = ({
175
- config: t,
176
- configUrl: a,
177
- floorId: i,
178
- onFloorChange: h,
179
- reservedSeats: g = [],
180
- unavailableSeats: b = [],
181
- onSeatSelect: d,
182
- onSeatDeselect: f,
183
- onSelectionChange: s,
173
+ it.displayName = "FloorSelectorBar";
174
+ const st = Z(({
175
+ scale: o,
176
+ minScale: f,
177
+ maxScale: d,
178
+ onZoomIn: m,
179
+ onZoomOut: x,
180
+ position: S,
181
+ className: g
182
+ }) => {
183
+ const h = {
184
+ position: "absolute",
185
+ display: "flex",
186
+ flexDirection: "column",
187
+ gap: "4px",
188
+ padding: "8px",
189
+ backgroundColor: "rgba(26, 26, 26, 0.95)",
190
+ borderRadius: "8px",
191
+ margin: "12px",
192
+ zIndex: 10,
193
+ ...{
194
+ "top-left": { top: 0, left: 0 },
195
+ "top-right": { top: 0, right: 0 },
196
+ "bottom-left": { bottom: 0, left: 0 },
197
+ "bottom-right": { bottom: 0, right: 0 }
198
+ }[S]
199
+ }, l = {
200
+ width: "36px",
201
+ height: "36px",
202
+ fontSize: "20px",
203
+ fontWeight: "bold",
204
+ border: "1px solid #444",
205
+ borderRadius: "6px",
206
+ backgroundColor: "transparent",
207
+ color: "#fff",
208
+ cursor: "pointer",
209
+ display: "flex",
210
+ alignItems: "center",
211
+ justifyContent: "center",
212
+ transition: "all 0.2s ease"
213
+ }, N = {
214
+ ...l,
215
+ opacity: 0.4,
216
+ cursor: "not-allowed"
217
+ }, b = o < d, u = o > f;
218
+ return /* @__PURE__ */ w("div", { className: g, style: h, children: [
219
+ /* @__PURE__ */ i(
220
+ "button",
221
+ {
222
+ type: "button",
223
+ onClick: m,
224
+ disabled: !b,
225
+ style: b ? l : N,
226
+ title: "Zoom In",
227
+ children: "+"
228
+ }
229
+ ),
230
+ /* @__PURE__ */ i(
231
+ "button",
232
+ {
233
+ type: "button",
234
+ onClick: x,
235
+ disabled: !u,
236
+ style: u ? l : N,
237
+ title: "Zoom Out",
238
+ children: "−"
239
+ }
240
+ )
241
+ ] });
242
+ });
243
+ st.displayName = "ZoomControls";
244
+ const zt = ({
245
+ config: o,
246
+ configUrl: f,
247
+ floorId: d,
248
+ onFloorChange: m,
249
+ reservedSeats: x = [],
250
+ unavailableSeats: S = [],
251
+ onSeatSelect: g,
252
+ onSeatDeselect: y,
253
+ onSelectionChange: h,
184
254
  colorOverrides: l,
185
- showTooltip: E = !0,
186
- zoomEnabled: C = !0,
187
- className: c = "",
188
- onConfigLoad: k,
189
- onError: j,
255
+ showTooltip: N = !0,
256
+ zoomEnabled: b = !0,
257
+ className: u = "",
258
+ onConfigLoad: A,
259
+ onError: z,
190
260
  // Floor selector props
191
- showFloorSelector: W,
192
- floorSelectorPosition: G = "top-left",
193
- floorSelectorClassName: K,
194
- showAllFloorsOption: J = !0,
195
- allFloorsLabel: Q = "All"
261
+ showFloorSelector: P,
262
+ floorSelectorPosition: rt = "top-left",
263
+ floorSelectorClassName: at,
264
+ showAllFloorsOption: lt = !0,
265
+ allFloorsLabel: ct = "All",
266
+ fitToView: D = !0,
267
+ fitPadding: L = 40,
268
+ // Zoom controls
269
+ showZoomControls: dt = !0,
270
+ zoomControlsPosition: ht = "bottom-right",
271
+ zoomControlsClassName: ft,
272
+ maxZoom: E = 3,
273
+ zoomStep: H = 0.25
196
274
  }) => {
197
- const _ = ue(null), [v, Z] = y(/* @__PURE__ */ new Set()), [T, ee] = y(1), [N, te] = y({ x: 0, y: 0 }), [oe, re] = y(null), { config: ne, loading: ie, error: I } = be(a), o = t || ne, M = i !== void 0, p = M ? i || null : oe, se = z((e) => {
198
- M || re(e), h?.(e);
199
- }, [M, h]), P = o?.floors || [], le = W !== void 0 ? W : P.length > 1, B = w(
200
- () => o ? { ...o.colors, ...l } : { ...Se, ...l },
201
- [o, l]
202
- ), D = w(() => {
203
- if (!o) return [];
204
- let e = o.seats.filter((n) => n.state !== "hidden");
205
- return p && (e = e.filter(
206
- (n) => n.floorId === p || !n.floorId && p === "floor_default"
207
- )), e;
208
- }, [o, p]), ae = w(() => o?.stages ? p ? o.stages.filter(
209
- (e) => e.floorId === p || !e.floorId && p === "floor_default"
210
- ) : o.stages : [], [o, p]), A = w(() => {
211
- const e = new Set(g), n = new Set(b);
212
- return { reserved: e, unavailable: n };
213
- }, [g, b]), L = z((e) => {
214
- const n = e.id, u = e.seatNumber || "";
215
- return A.unavailable.has(n) || A.unavailable.has(u) ? "unavailable" : A.reserved.has(n) || A.reserved.has(u) ? "reserved" : v.has(n) ? "selected" : e.state;
216
- }, [A, v]);
217
- $(() => {
218
- o && k && k(o);
219
- }, [o, k]), $(() => {
220
- I && j && j(I);
221
- }, [I, j]);
222
- const ce = z((e) => {
223
- const n = L(e);
275
+ const ut = Et(null), [R, pt] = C(/* @__PURE__ */ new Set()), [p, V] = C(1), [k, T] = C({ x: 0, y: 0 }), [gt, mt] = C(null), [J, Q] = C(!1), [yt, bt] = C(1), { config: xt, loading: vt, error: W } = Ht(f), e = o || xt, _ = d !== void 0, v = _ ? d || null : gt, St = M((t) => {
276
+ _ || mt(t), m?.(t);
277
+ }, [_, m]), U = e?.floors || [], wt = P !== void 0 ? P : U.length > 1, q = F(
278
+ () => e ? { ...e.colors, ...l } : { ...Tt, ...l },
279
+ [e, l]
280
+ ), X = F(() => {
281
+ if (!e) return [];
282
+ let t = e.seats.filter((n) => n.state !== "hidden");
283
+ return v && (t = t.filter(
284
+ (n) => n.floorId === v || !n.floorId && v === "floor_default"
285
+ )), t;
286
+ }, [e, v]), $ = F(() => e?.stages ? v ? e.stages.filter(
287
+ (t) => t.floorId === v || !t.floorId && v === "floor_default"
288
+ ) : e.stages : [], [e, v]), I = F(() => {
289
+ if (!e || X.length === 0 && $.length === 0)
290
+ return null;
291
+ const t = 12;
292
+ let n = 1 / 0, r = 1 / 0, c = -1 / 0, a = -1 / 0;
293
+ return X.forEach((s) => {
294
+ n = Math.min(n, s.position.x - t), r = Math.min(r, s.position.y - t), c = Math.max(c, s.position.x + t), a = Math.max(a, s.position.y + t);
295
+ }), $.forEach((s) => {
296
+ n = Math.min(n, s.position.x), r = Math.min(r, s.position.y), c = Math.max(c, s.position.x + (s.config?.width || 200)), a = Math.max(a, s.position.y + (s.config?.height || 100));
297
+ }), { minX: n, minY: r, maxX: c, maxY: a, width: c - n, height: a - r };
298
+ }, [e, X, $]);
299
+ Y(() => {
300
+ if (!D || J || !e || !I) return;
301
+ const t = e.canvas.width, n = e.canvas.height, r = t - L * 2, c = n - L * 2, a = r / I.width, s = c / I.height, O = Math.min(a, s, E), Mt = I.minX + I.width / 2, Ft = I.minY + I.height / 2, Rt = t / 2, Xt = n / 2, Yt = Rt - Mt * O, At = Xt - Ft * O;
302
+ V(O), T({ x: Yt, y: At }), bt(O), Q(!0);
303
+ }, [D, J, e, I, L, E]), Y(() => {
304
+ D && Q(!1);
305
+ }, [v, D]);
306
+ const j = F(() => {
307
+ const t = new Set(x), n = new Set(S);
308
+ return { reserved: t, unavailable: n };
309
+ }, [x, S]), G = M((t) => {
310
+ const n = t.id, r = t.seatNumber || "";
311
+ return j.unavailable.has(n) || j.unavailable.has(r) ? "unavailable" : j.reserved.has(n) || j.reserved.has(r) ? "reserved" : R.has(n) ? "selected" : t.state;
312
+ }, [j, R]);
313
+ Y(() => {
314
+ e && A && A(e);
315
+ }, [e, A]), Y(() => {
316
+ W && z && z(W);
317
+ }, [W, z]);
318
+ const Ct = M((t) => {
319
+ const n = G(t);
224
320
  if (n !== "available" && n !== "selected")
225
321
  return;
226
- const u = v.has(e.id);
227
- Z((S) => {
228
- const x = new Set(S);
229
- return u ? x.delete(e.id) : x.add(e.id), x;
230
- }), u ? f?.(e) : (d?.(e), d || console.log("Seat selected:", e));
231
- }, [L, v, d, f]), F = w(() => o ? D.filter((e) => v.has(e.id)) : [], [D, v]);
232
- $(() => {
233
- s?.(F);
234
- }, [F, s]);
235
- const de = z((e) => {
236
- if (!C) return;
237
- e.evt.preventDefault();
238
- const n = _.current;
239
- if (!n) return;
240
- const u = T, S = n.getPointerPosition();
241
- if (!S) return;
242
- const x = {
243
- x: (S.x - N.x) / u,
244
- y: (S.y - N.y) / u
245
- }, fe = e.evt.deltaY > 0 ? -1 : 1, Y = 1.05;
246
- let R = fe > 0 ? u * Y : u / Y;
247
- R = Math.max(0.5, Math.min(5, R)), ee(R), te({
248
- x: S.x - x.x * R,
249
- y: S.y - x.y * R
322
+ const r = R.has(t.id);
323
+ pt((c) => {
324
+ const a = new Set(c);
325
+ return r ? a.delete(t.id) : a.add(t.id), a;
326
+ }), r ? y?.(t) : (g?.(t), g || console.log("Seat selected:", t));
327
+ }, [G, R, g, y]), B = F(() => e ? X.filter((t) => R.has(t.id)) : [], [X, R]);
328
+ Y(() => {
329
+ h?.(B);
330
+ }, [B, h]);
331
+ const K = yt, kt = M(() => {
332
+ if (!b) return;
333
+ const t = Math.min(p + H, E);
334
+ if (t !== p) {
335
+ const n = e?.canvas.width || 800, r = e?.canvas.height || 600, c = n / 2, a = r / 2, s = {
336
+ x: (c - k.x) / p,
337
+ y: (a - k.y) / p
338
+ };
339
+ V(t), T({
340
+ x: c - s.x * t,
341
+ y: a - s.y * t
342
+ });
343
+ }
344
+ }, [b, p, H, E, e, k]), It = M(() => {
345
+ if (!b) return;
346
+ const t = Math.max(p - H, K);
347
+ if (t !== p) {
348
+ const n = e?.canvas.width || 800, r = e?.canvas.height || 600, c = n / 2, a = r / 2, s = {
349
+ x: (c - k.x) / p,
350
+ y: (a - k.y) / p
351
+ };
352
+ V(t), T({
353
+ x: c - s.x * t,
354
+ y: a - s.y * t
355
+ });
356
+ }
357
+ }, [b, p, H, K, e, k]), Nt = M((t) => {
358
+ T({
359
+ x: t.target.x(),
360
+ y: t.target.y()
250
361
  });
251
- }, [C, T, N]);
252
- return ie ? /* @__PURE__ */ r("div", { className: `flex items-center justify-center h-full ${c}`, children: /* @__PURE__ */ r("p", { children: "Loading seat map..." }) }) : I ? /* @__PURE__ */ r("div", { className: `flex items-center justify-center h-full ${c}`, children: /* @__PURE__ */ m("p", { className: "text-red-500", children: [
362
+ }, []);
363
+ return vt ? /* @__PURE__ */ i("div", { className: `flex items-center justify-center h-full ${u}`, children: /* @__PURE__ */ i("p", { children: "Loading seat map..." }) }) : W ? /* @__PURE__ */ i("div", { className: `flex items-center justify-center h-full ${u}`, children: /* @__PURE__ */ w("p", { className: "text-red-500", children: [
253
364
  "Error loading seat map: ",
254
- I.message
255
- ] }) }) : o ? /* @__PURE__ */ m("div", { className: `relative ${c}`, children: [
256
- le && P.length > 0 && /* @__PURE__ */ r(
257
- q,
365
+ W.message
366
+ ] }) }) : e ? /* @__PURE__ */ w("div", { className: `relative ${u}`, children: [
367
+ wt && U.length > 0 && /* @__PURE__ */ i(
368
+ it,
258
369
  {
259
- floors: P,
260
- currentFloorId: p,
261
- onFloorChange: se,
262
- showAllOption: J,
263
- allLabel: Q,
264
- position: G,
265
- className: K
370
+ floors: U,
371
+ currentFloorId: v,
372
+ onFloorChange: St,
373
+ showAllOption: lt,
374
+ allLabel: ct,
375
+ position: rt,
376
+ className: at
266
377
  }
267
378
  ),
268
- /* @__PURE__ */ m(
269
- he,
379
+ /* @__PURE__ */ w(
380
+ Wt,
270
381
  {
271
- ref: _,
272
- width: o.canvas.width,
273
- height: o.canvas.height,
274
- scaleX: T,
275
- scaleY: T,
276
- x: N.x,
277
- y: N.y,
278
- onWheel: de,
279
- style: { backgroundColor: o.canvas.backgroundColor },
382
+ ref: ut,
383
+ width: e.canvas.width,
384
+ height: e.canvas.height,
385
+ scaleX: p,
386
+ scaleY: p,
387
+ x: k.x,
388
+ y: k.y,
389
+ draggable: !0,
390
+ onDragEnd: Nt,
391
+ style: { backgroundColor: e.canvas.backgroundColor, cursor: "grab" },
280
392
  children: [
281
- /* @__PURE__ */ r(H, { listening: !1, children: ae.map((e) => /* @__PURE__ */ r(
282
- X,
393
+ /* @__PURE__ */ i(tt, { listening: !1, children: $.map((t) => /* @__PURE__ */ i(
394
+ ot,
283
395
  {
284
- stage: e,
285
- stageColor: B.stageColor
396
+ stage: t,
397
+ stageColor: q.stageColor
286
398
  },
287
- e.id
399
+ t.id
288
400
  )) }),
289
- /* @__PURE__ */ r(H, { children: D.map((e) => /* @__PURE__ */ r(
290
- U,
401
+ /* @__PURE__ */ i(tt, { children: X.map((t) => /* @__PURE__ */ i(
402
+ nt,
291
403
  {
292
- seat: e,
293
- state: L(e),
294
- colors: B,
295
- onClick: ce
404
+ seat: t,
405
+ state: G(t),
406
+ colors: q,
407
+ onClick: Ct
296
408
  },
297
- e.id
409
+ t.id
298
410
  )) })
299
411
  ]
300
412
  }
301
413
  ),
302
- F.length > 0 && /* @__PURE__ */ m("div", { className: "absolute top-4 right-4 bg-white dark:bg-gray-800 p-4 rounded shadow-lg", children: [
303
- /* @__PURE__ */ m("h3", { className: "font-semibold mb-2", children: [
414
+ dt && b && /* @__PURE__ */ i(
415
+ st,
416
+ {
417
+ scale: p,
418
+ minScale: K,
419
+ maxScale: E,
420
+ onZoomIn: kt,
421
+ onZoomOut: It,
422
+ position: ht,
423
+ className: ft
424
+ }
425
+ ),
426
+ B.length > 0 && /* @__PURE__ */ w("div", { className: "absolute top-4 right-4 bg-white dark:bg-gray-800 p-4 rounded shadow-lg", children: [
427
+ /* @__PURE__ */ w("h3", { className: "font-semibold mb-2", children: [
304
428
  "Selected Seats (",
305
- F.length,
429
+ B.length,
306
430
  ")"
307
431
  ] }),
308
- /* @__PURE__ */ r("div", { className: "max-h-48 overflow-y-auto space-y-1", children: F.map((e) => /* @__PURE__ */ m("div", { className: "text-sm", children: [
309
- e.seatNumber,
310
- e.price && ` - ${B.currency} ${e.price.toFixed(2)}`
311
- ] }, e.id)) })
432
+ /* @__PURE__ */ i("div", { className: "max-h-48 overflow-y-auto space-y-1", children: B.map((t) => /* @__PURE__ */ w("div", { className: "text-sm", children: [
433
+ t.seatNumber,
434
+ t.price && ` - ${q.currency} ${t.price.toFixed(2)}`
435
+ ] }, t.id)) })
312
436
  ] })
313
- ] }) : /* @__PURE__ */ r("div", { className: `flex items-center justify-center h-full ${c}`, children: /* @__PURE__ */ r("p", { children: "No configuration provided" }) });
437
+ ] }) : /* @__PURE__ */ i("div", { className: `flex items-center justify-center h-full ${u}`, children: /* @__PURE__ */ i("p", { children: "No configuration provided" }) });
314
438
  };
315
439
  export {
316
- Se as DEFAULT_COLORS,
317
- we as SeatMapViewer,
318
- be as useConfigFetcher
440
+ Tt as DEFAULT_COLORS,
441
+ zt as SeatMapViewer,
442
+ Ht as useConfigFetcher
319
443
  };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@zonetrix/viewer",
3
- "version": "2.2.0",
3
+ "version": "2.4.0",
4
4
  "type": "module",
5
5
  "description": "Lightweight React component for rendering interactive seat maps",
6
6
  "main": "./dist/index.js",