@zonetrix/viewer 2.4.1 → 2.6.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
@@ -108,11 +108,12 @@ function BookingApp() {
108
108
  | `onFloorChange` | `(floorId: string) => void` | No | Callback when floor changes |
109
109
  | `reservedSeats` | `string[]` | No | Array of seat IDs/numbers to mark as reserved |
110
110
  | `unavailableSeats` | `string[]` | No | Array of seat IDs/numbers to mark as unavailable |
111
+ | `selectedSeats` | `string[]` | No | Array of seat IDs for controlled selection mode |
111
112
  | `onSeatSelect` | `(seat: SeatData) => void` | No | Callback when a seat is selected |
112
113
  | `onSeatDeselect` | `(seat: SeatData) => void` | No | Callback when a seat is deselected |
113
114
  | `onSelectionChange` | `(seats: SeatData[]) => void` | No | Callback when selection changes |
114
115
  | `colorOverrides` | `Partial<ColorSettings>` | No | Custom colors for seat states |
115
- | `showTooltip` | `boolean` | No | Show tooltips on hover (default: true) |
116
+ | `showTooltip` | `boolean` | No | Show seat tooltips on hover with seat info, price, status (default: true) |
116
117
  | `zoomEnabled` | `boolean` | No | Enable/disable zoom functionality (default: true) |
117
118
  | `className` | `string` | No | Custom CSS class for the container |
118
119
  | `onConfigLoad` | `(config: SeatMapConfig) => void` | No | Callback when config is loaded |
@@ -341,7 +342,88 @@ function CustomFloorSelector() {
341
342
  }
342
343
  ```
343
344
 
344
- ### 7. Error Handling
345
+ ### 7. Controlled Selection Mode
346
+
347
+ For complete control over seat selection state (e.g., syncing with external state, URL params, or database):
348
+
349
+ ```tsx
350
+ import { useState } from 'react';
351
+ import { SeatMapViewer } from '@zonetrix/viewer';
352
+ import type { SeatData } from '@zonetrix/viewer';
353
+
354
+ function ControlledSelection() {
355
+ const [selectedSeatIds, setSelectedSeatIds] = useState<string[]>([]);
356
+
357
+ const handleSeatSelect = (seat: SeatData) => {
358
+ setSelectedSeatIds(prev => [...prev, seat.id]);
359
+ };
360
+
361
+ const handleSeatDeselect = (seat: SeatData) => {
362
+ setSelectedSeatIds(prev => prev.filter(id => id !== seat.id));
363
+ };
364
+
365
+ return (
366
+ <SeatMapViewer
367
+ config={venueConfig}
368
+ selectedSeats={selectedSeatIds} // Controlled mode
369
+ onSeatSelect={handleSeatSelect}
370
+ onSeatDeselect={handleSeatDeselect}
371
+ />
372
+ );
373
+ }
374
+ ```
375
+
376
+ **Use cases for controlled selection mode:**
377
+ - Syncing selection with URL query parameters
378
+ - Persisting selection in Redux/Zustand store
379
+ - Loading pre-selected seats from database
380
+ - Implementing undo/redo functionality
381
+ - Syncing selection across multiple components
382
+ - Integrating with shopping cart state management
383
+
384
+ **Example with cart integration:**
385
+
386
+ ```tsx
387
+ import { useState } from 'react';
388
+ import { SeatMapViewer } from '@zonetrix/viewer';
389
+ import type { SeatData } from '@zonetrix/viewer';
390
+
391
+ function CartIntegration() {
392
+ const [cartItems, setCartItems] = useState<SeatData[]>([]);
393
+
394
+ // Derive selected IDs from cart
395
+ const selectedSeatIds = cartItems.map(seat => seat.id);
396
+
397
+ const handleSeatSelect = (seat: SeatData) => {
398
+ setCartItems(prev => [...prev, seat]);
399
+ };
400
+
401
+ const handleSeatDeselect = (seat: SeatData) => {
402
+ setCartItems(prev => prev.filter(item => item.id !== seat.id));
403
+ };
404
+
405
+ const totalPrice = cartItems.reduce((sum, seat) => sum + (seat.price || 0), 0);
406
+
407
+ return (
408
+ <div>
409
+ <SeatMapViewer
410
+ config={venueConfig}
411
+ selectedSeats={selectedSeatIds}
412
+ onSeatSelect={handleSeatSelect}
413
+ onSeatDeselect={handleSeatDeselect}
414
+ />
415
+
416
+ <div className="cart">
417
+ <h3>Shopping Cart</h3>
418
+ <p>Selected: {cartItems.length} seat(s)</p>
419
+ <p>Total: ${totalPrice.toFixed(2)}</p>
420
+ </div>
421
+ </div>
422
+ );
423
+ }
424
+ ```
425
+
426
+ ### 8. Error Handling
345
427
 
346
428
  ```tsx
347
429
  import { SeatMapViewer } from '@zonetrix/viewer';
@@ -7,6 +7,7 @@ export interface SeatMapViewerProps {
7
7
  onFloorChange?: (floorId: string) => void;
8
8
  reservedSeats?: string[];
9
9
  unavailableSeats?: string[];
10
+ selectedSeats?: string[];
10
11
  onSeatSelect?: (seat: SeatData) => void;
11
12
  onSeatDeselect?: (seat: SeatData) => void;
12
13
  onSelectionChange?: (seats: SeatData[]) => void;
@@ -28,5 +29,6 @@ export interface SeatMapViewerProps {
28
29
  zoomControlsClassName?: string;
29
30
  maxZoom?: number;
30
31
  zoomStep?: number;
32
+ touchEnabled?: boolean;
31
33
  }
32
34
  export declare const SeatMapViewer: React.FC<SeatMapViewerProps>;
@@ -0,0 +1,11 @@
1
+ import { RefObject } from 'react';
2
+ interface ContainerSize {
3
+ width: number;
4
+ height: number;
5
+ }
6
+ /**
7
+ * Hook to track the size of a container element using ResizeObserver.
8
+ * Returns { width: 0, height: 0 } until the container is mounted and measured.
9
+ */
10
+ export declare function useContainerSize(containerRef: RefObject<HTMLDivElement | null>): ContainerSize;
11
+ export {};
@@ -0,0 +1,25 @@
1
+ import { RefObject } from 'react';
2
+ import { default as Konva } from 'konva';
3
+ interface TouchGestureConfig {
4
+ enabled: boolean;
5
+ minScale: number;
6
+ maxScale: number;
7
+ currentScale: number;
8
+ currentPosition: {
9
+ x: number;
10
+ y: number;
11
+ };
12
+ onScaleChange: (scale: number, position: {
13
+ x: number;
14
+ y: number;
15
+ }) => void;
16
+ onPositionChange: (position: {
17
+ x: number;
18
+ y: number;
19
+ }) => void;
20
+ }
21
+ /**
22
+ * Hook to handle touch gestures (pinch-to-zoom and two-finger pan) on a Konva Stage.
23
+ */
24
+ export declare function useTouchGestures(stageRef: RefObject<Konva.Stage | null>, config: TouchGestureConfig): void;
25
+ export {};
package/dist/index.d.ts CHANGED
@@ -3,3 +3,5 @@ export type { SeatMapViewerProps } from './components/SeatMapViewer';
3
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';
6
+ export { useContainerSize } from './hooks/useContainerSize';
7
+ export { useTouchGestures } from './hooks/useTouchGestures';
package/dist/index.js CHANGED
@@ -1 +1 @@
1
- "use strict";Object.defineProperty(exports,Symbol.toStringTag,{value:"Module"});const t=require("react/jsx-runtime"),n=require("react"),E=require("react-konva");function J(o){const[p,h]=n.useState(null),[a,y]=n.useState(!1),[m,S]=n.useState(null),j=async()=>{if(o){y(!0),S(null);try{const x=await fetch(o);if(!x.ok)throw new Error(`Failed to fetch config: ${x.statusText}`);const r=await x.json();h(r)}catch(x){const r=x instanceof Error?x:new Error("Unknown error occurred");S(r),console.error("Failed to fetch seat map config:",r)}finally{y(!1)}}};return n.useEffect(()=>{j()},[o]),{config:p,loading:a,error:m,refetch:j}}const Q={canvasBackground:"#1a1a1a",stageColor:"#808080",seatAvailable:"#2C2B30",seatReserved:"#FCEA00",seatSelected:"#3A7DE5",seatUnavailable:"#6b7280",seatHidden:"#4a4a4a",gridLines:"#404040",currency:"KD"},ee=n.memo(({seat:o,state:p,colors:h,onClick:a,onMouseEnter:y,onMouseLeave:m})=>{const x={available:h.seatAvailable,reserved:h.seatReserved,selected:h.seatSelected,unavailable:h.seatUnavailable,hidden:h.seatHidden}[p],r=p==="available"||p==="selected",g=n.useCallback(()=>{r&&a(o)},[o,a,r]),b=n.useCallback(k=>{y(o,k);const M=k.target.getStage();M&&r&&(M.container().style.cursor="pointer")},[o,y,r]),f=n.useCallback(k=>{m();const M=k.target.getStage();M&&(M.container().style.cursor="grab")},[m]),w={x:o.position.x,y:o.position.y,fill:x,stroke:"#ffffff",strokeWidth:1,onClick:g,onTap:g,onMouseEnter:b,onMouseLeave:f};return o.shape==="circle"?t.jsx(E.Circle,{...w,radius:12}):t.jsx(E.Rect,{...w,width:24,height:24,offsetX:12,offsetY:12,cornerRadius:o.shape==="square"?0:4})});ee.displayName="ViewerSeat";const te=n.memo(({stage:o,stageColor:p})=>t.jsxs(E.Group,{x:o.position.x,y:o.position.y,children:[t.jsx(E.Rect,{width:o.config.width,height:o.config.height,fill:p+"80",stroke:"#ffffff",strokeWidth:2,cornerRadius:10}),t.jsx(E.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"})]}));te.displayName="ViewerStage";const ne=n.memo(({floors:o,currentFloorId:p,onFloorChange:h,showAllOption:a,allLabel:y,position:m,className:S})=>{const j=n.useMemo(()=>[...o].sort((f,w)=>f.order-w.order),[o]),r={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]},g={padding:"6px 14px",fontSize:"14px",fontWeight:500,border:"1px solid #444",borderRadius:"6px",backgroundColor:"transparent",color:"#fff",cursor:"pointer",transition:"all 0.2s ease"},b={...g,backgroundColor:"#3A7DE5",borderColor:"#3A7DE5"};return t.jsxs("div",{className:S,style:r,children:[a&&t.jsx("button",{type:"button",onClick:()=>h(null),style:p===null?b:g,children:y}),j.map(f=>t.jsx("button",{type:"button",onClick:()=>h(f.id),style:p===f.id?b:g,children:f.name},f.id))]})});ne.displayName="FloorSelectorBar";const se=n.memo(({scale:o,minScale:p,maxScale:h,onZoomIn:a,onZoomOut:y,position:m,className:S})=>{const x={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}}[m]},r={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"},g={...r,opacity:.4,cursor:"not-allowed"},b=o<h,f=o>p;return t.jsxs("div",{className:S,style:x,children:[t.jsx("button",{type:"button",onClick:a,disabled:!b,style:b?r:g,title:"Zoom In",children:"+"}),t.jsx("button",{type:"button",onClick:y,disabled:!f,style:f?r:g,title:"Zoom Out",children:"−"})]})});se.displayName="ZoomControls";const oe=n.memo(({visible:o,x:p,y:h,seat:a,currency:y,state:m})=>{if(!o||!a)return null;const S=a.seatNumber||(a.rowLabel&&a.columnLabel?`${a.rowLabel}-${a.columnLabel}`:"N/A"),j={position:"fixed",left:`${p+15}px`,top:`${h+15}px`,zIndex:1e3,pointerEvents:"none"},x={backgroundColor:"rgba(26, 26, 26, 0.95)",color:"#fff",border:"1px solid #444",borderRadius:"8px",padding:"8px 12px",fontSize:"13px",boxShadow:"0 4px 12px rgba(0, 0, 0, 0.3)",minWidth:"140px"},r={color:"#9ca3af",marginRight:"4px"},g={fontWeight:600},b={color:"#4ade80",fontWeight:600},f={fontSize:"11px",color:"#6b7280",textTransform:"capitalize",marginTop:"4px"};return t.jsx("div",{style:j,children:t.jsxs("div",{style:x,children:[a.sectionName&&t.jsxs("div",{style:{marginBottom:"4px"},children:[t.jsx("span",{style:r,children:"Section:"}),t.jsx("span",{style:{...g,color:"#3b82f6"},children:a.sectionName})]}),t.jsxs("div",{style:{marginBottom:"4px"},children:[t.jsx("span",{style:r,children:"Seat:"}),t.jsx("span",{style:g,children:S})]}),a.price!==void 0&&a.price>0&&t.jsxs("div",{style:{marginBottom:"4px"},children:[t.jsx("span",{style:r,children:"Price:"}),t.jsxs("span",{style:b,children:[y," ",a.price.toFixed(2)]})]}),t.jsxs("div",{style:f,children:["Status: ",m]})]})})});oe.displayName="SeatTooltip";const Be=({config:o,configUrl:p,floorId:h,onFloorChange:a,reservedSeats:y=[],unavailableSeats:m=[],onSeatSelect:S,onSeatDeselect:j,onSelectionChange:x,colorOverrides:r,showTooltip:g=!0,zoomEnabled:b=!0,className:f="",onConfigLoad:w,onError:k,showFloorSelector:M,floorSelectorPosition:ie="top-left",floorSelectorClassName:ae,showAllFloorsOption:re=!0,allFloorsLabel:le="All",fitToView:$=!0,fitPadding:P=40,showZoomControls:ce=!0,zoomControlsPosition:de="bottom-right",zoomControlsClassName:ue,maxZoom:L=3,zoomStep:D=.25})=>{const fe=n.useRef(null),[R,he]=n.useState(new Set),[v,Z]=n.useState(1),[I,T]=n.useState({x:0,y:0}),[pe,xe]=n.useState(null),[U,K]=n.useState(!1),[ge,be]=n.useState(1),[A,G]=n.useState({visible:!1,x:0,y:0,seat:null,state:"available"}),{config:ye,loading:me,error:B}=J(p),s=o||ye,V=h!==void 0,C=V?h||null:pe,Se=n.useCallback(e=>{V||xe(e),a?.(e)},[V,a]),q=s?.floors||[],ve=M!==void 0?M:q.length>1,z=n.useMemo(()=>s?{...s.colors,...r}:{...Q,...r},[s,r]),F=n.useMemo(()=>{if(!s)return[];let e=s.seats.filter(i=>i.state!=="hidden");return C&&(e=e.filter(i=>i.floorId===C||!i.floorId&&C==="floor_default")),e},[s,C]),O=n.useMemo(()=>s?.stages?C?s.stages.filter(e=>e.floorId===C||!e.floorId&&C==="floor_default"):s.stages:[],[s,C]),N=n.useMemo(()=>{if(!s||F.length===0&&O.length===0)return null;const e=12;let i=1/0,l=1/0,d=-1/0,c=-1/0;return F.forEach(u=>{i=Math.min(i,u.position.x-e),l=Math.min(l,u.position.y-e),d=Math.max(d,u.position.x+e),c=Math.max(c,u.position.y+e)}),O.forEach(u=>{i=Math.min(i,u.position.x),l=Math.min(l,u.position.y),d=Math.max(d,u.position.x+(u.config?.width||200)),c=Math.max(c,u.position.y+(u.config?.height||100))}),{minX:i,minY:l,maxX:d,maxY:c,width:d-i,height:c-l}},[s,F,O]);n.useEffect(()=>{if(!$||U||!s||!N)return;const e=s.canvas.width,i=s.canvas.height,l=e-P*2,d=i-P*2,c=l/N.width,u=d/N.height,H=Math.min(c,u,L),Ne=N.minX+N.width/2,Ee=N.minY+N.height/2,Re=e/2,Fe=i/2,Le=Re-Ne*H,Ae=Fe-Ee*H;Z(H),T({x:Le,y:Ae}),be(H),K(!0)},[$,U,s,N,P,L]),n.useEffect(()=>{$&&K(!1)},[C,$]);const W=n.useMemo(()=>{const e=new Set(y),i=new Set(m);return{reserved:e,unavailable:i}},[y,m]),X=n.useCallback(e=>{const i=e.id,l=e.seatNumber||"";return W.unavailable.has(i)||W.unavailable.has(l)?"unavailable":W.reserved.has(i)||W.reserved.has(l)?"reserved":R.has(i)?"selected":e.state},[W,R]);n.useEffect(()=>{s&&w&&w(s)},[s,w]),n.useEffect(()=>{B&&k&&k(B)},[B,k]);const je=n.useCallback(e=>{const i=X(e);if(i!=="available"&&i!=="selected")return;const l=R.has(e.id);he(d=>{const c=new Set(d);return l?c.delete(e.id):c.add(e.id),c}),l?j?.(e):(S?.(e),S||console.log("Seat selected:",e))},[X,R,S,j]),Y=n.useMemo(()=>s?F.filter(e=>R.has(e.id)):[],[F,R]);n.useEffect(()=>{x?.(Y)},[Y,x]);const _=ge,Ce=n.useCallback(()=>{if(!b)return;const e=Math.min(v+D,L);if(e!==v){const i=s?.canvas.width||800,l=s?.canvas.height||600,d=i/2,c=l/2,u={x:(d-I.x)/v,y:(c-I.y)/v};Z(e),T({x:d-u.x*e,y:c-u.y*e})}},[b,v,D,L,s,I]),we=n.useCallback(()=>{if(!b)return;const e=Math.max(v-D,_);if(e!==v){const i=s?.canvas.width||800,l=s?.canvas.height||600,d=i/2,c=l/2,u={x:(d-I.x)/v,y:(c-I.y)/v};Z(e),T({x:d-u.x*e,y:c-u.y*e})}},[b,v,D,_,s,I]),ke=n.useCallback(e=>{T({x:e.target.x(),y:e.target.y()})},[]),Me=n.useCallback((e,i)=>{if(!g)return;const l=i.target.getStage();if(!l)return;const d=l.getPointerPosition();if(!d)return;const c=l.container().getBoundingClientRect();G({visible:!0,x:c.left+d.x,y:c.top+d.y,seat:e,state:X(e)})},[g,X]),Ie=n.useCallback(()=>{G(e=>({...e,visible:!1}))},[]);return me?t.jsx("div",{className:`flex items-center justify-center h-full ${f}`,children:t.jsx("p",{children:"Loading seat map..."})}):B?t.jsx("div",{className:`flex items-center justify-center h-full ${f}`,children:t.jsxs("p",{className:"text-red-500",children:["Error loading seat map: ",B.message]})}):s?t.jsxs("div",{className:`relative ${f}`,children:[ve&&q.length>0&&t.jsx(ne,{floors:q,currentFloorId:C,onFloorChange:Se,showAllOption:re,allLabel:le,position:ie,className:ae}),t.jsxs(E.Stage,{ref:fe,width:s.canvas.width,height:s.canvas.height,scaleX:v,scaleY:v,x:I.x,y:I.y,draggable:!0,onDragEnd:ke,style:{backgroundColor:s.canvas.backgroundColor,cursor:"grab"},children:[t.jsx(E.Layer,{listening:!1,children:O.map(e=>t.jsx(te,{stage:e,stageColor:z.stageColor},e.id))}),t.jsx(E.Layer,{children:F.map(e=>t.jsx(ee,{seat:e,state:X(e),colors:z,onClick:je,onMouseEnter:Me,onMouseLeave:Ie},e.id))})]}),g&&t.jsx(oe,{visible:A.visible,x:A.x,y:A.y,seat:A.seat,currency:z.currency,state:A.state}),ce&&b&&t.jsx(se,{scale:v,minScale:_,maxScale:L,onZoomIn:Ce,onZoomOut:we,position:de,className:ue}),Y.length>0&&t.jsxs("div",{className:"absolute top-4 right-4 bg-white dark:bg-gray-800 p-4 rounded shadow-lg",children:[t.jsxs("h3",{className:"font-semibold mb-2",children:["Selected Seats (",Y.length,")"]}),t.jsx("div",{className:"max-h-48 overflow-y-auto space-y-1",children:Y.map(e=>t.jsxs("div",{className:"text-sm",children:[e.seatNumber,e.price&&` - ${z.currency} ${e.price.toFixed(2)}`]},e.id))})]})]}):t.jsx("div",{className:`flex items-center justify-center h-full ${f}`,children:t.jsx("p",{children:"No configuration provided"})})};exports.DEFAULT_COLORS=Q;exports.SeatMapViewer=Be;exports.useConfigFetcher=J;
1
+ "use strict";Object.defineProperty(exports,Symbol.toStringTag,{value:"Module"});const o=require("react/jsx-runtime"),e=require("react"),L=require("react-konva");function pt(n){const[i,d]=e.useState(null),[r,b]=e.useState(!1),[S,x]=e.useState(null),v=async()=>{if(n){b(!0),x(null);try{const y=await fetch(n);if(!y.ok)throw new Error(`Failed to fetch config: ${y.statusText}`);const a=await y.json();d(a)}catch(y){const a=y instanceof Error?y:new Error("Unknown error occurred");x(a),console.error("Failed to fetch seat map config:",a)}finally{b(!1)}}};return e.useEffect(()=>{v()},[n]),{config:i,loading:r,error:S,refetch:v}}function gt(n){const[i,d]=e.useState({width:0,height:0});return e.useEffect(()=>{const r=n.current;if(!r)return;const{width:b,height:S}=r.getBoundingClientRect();b>0&&S>0&&d({width:b,height:S});const x=new ResizeObserver(v=>{const y=v[0];if(!y)return;const{width:a,height:s}=y.contentRect;a>0&&s>0&&d(g=>g.width===a&&g.height===s?g:{width:a,height:s})});return x.observe(r),()=>{x.disconnect()}},[n]),i}function ft(n,i){return Math.sqrt(Math.pow(i.x-n.x,2)+Math.pow(i.y-n.y,2))}function xt(n,i){return{x:(n.x+i.x)/2,y:(n.y+i.y)/2}}function yt(n,i){const d=e.useRef(null),r=e.useRef(null),b=e.useRef(1);e.useEffect(()=>{const S=n.current;if(!S||!i.enabled)return;const x=S.container(),v=s=>{if(s.touches.length===2){s.preventDefault();const g={x:s.touches[0].clientX,y:s.touches[0].clientY},u={x:s.touches[1].clientX,y:s.touches[1].clientY};d.current=ft(g,u),r.current=xt(g,u),b.current=i.currentScale}},y=s=>{if(s.touches.length!==2)return;s.preventDefault();const g={x:s.touches[0].clientX,y:s.touches[0].clientY},u={x:s.touches[1].clientX,y:s.touches[1].clientY},M=ft(g,u),C=xt(g,u);if(d.current!==null&&r.current!==null){const R=M/d.current,T=Math.min(Math.max(i.currentScale*R,i.minScale),i.maxScale),V=x.getBoundingClientRect(),_=C.x-V.left,G=C.y-V.top,U=i.currentScale,P={x:(_-i.currentPosition.x)/U,y:(G-i.currentPosition.y)/U},W=C.x-r.current.x,ot=C.y-r.current.y,st={x:_-P.x*T+W,y:G-P.y*T+ot};i.onScaleChange(T,st),d.current=M,r.current=C}},a=s=>{s.touches.length<2&&(d.current=null,r.current=null)};return x.addEventListener("touchstart",v,{passive:!1}),x.addEventListener("touchmove",y,{passive:!1}),x.addEventListener("touchend",a),()=>{x.removeEventListener("touchstart",v),x.removeEventListener("touchmove",y),x.removeEventListener("touchend",a)}},[n,i])}const mt={canvasBackground:"#1a1a1a",stageColor:"#808080",seatAvailable:"#2C2B30",seatReserved:"#FCEA00",seatSelected:"#3A7DE5",seatUnavailable:"#6b7280",seatHidden:"#4a4a4a",gridLines:"#404040",currency:"KD"},bt=e.memo(({seat:n,state:i,colors:d,onClick:r,onMouseEnter:b,onMouseLeave:S})=>{const y={available:d.seatAvailable,reserved:d.seatReserved,selected:d.seatSelected,unavailable:d.seatUnavailable,hidden:d.seatHidden}[i],a=i==="available"||i==="selected",s=e.useCallback(()=>{a&&r(n)},[n,r,a]),g=e.useCallback(C=>{b(n,C);const R=C.target.getStage();R&&a&&(R.container().style.cursor="pointer")},[n,b,a]),u=e.useCallback(C=>{S();const R=C.target.getStage();R&&(R.container().style.cursor="grab")},[S]),M={x:n.position.x,y:n.position.y,fill:y,stroke:"#ffffff",strokeWidth:1,onClick:s,onTap:s,onMouseEnter:g,onMouseLeave:u};return n.shape==="circle"?o.jsx(L.Circle,{...M,radius:12}):o.jsx(L.Rect,{...M,width:24,height:24,offsetX:12,offsetY:12,cornerRadius:n.shape==="square"?0:4})});bt.displayName="ViewerSeat";const St=e.memo(({stage:n,stageColor:i})=>o.jsxs(L.Group,{x:n.position.x,y:n.position.y,children:[o.jsx(L.Rect,{width:n.config.width,height:n.config.height,fill:i+"80",stroke:"#ffffff",strokeWidth:2,cornerRadius:10}),o.jsx(L.Text,{text:n.config.label,x:0,y:0,width:n.config.width,height:n.config.height,fontSize:24,fontStyle:"bold",fill:"#ffffff",align:"center",verticalAlign:"middle"})]}));St.displayName="ViewerStage";const vt=e.memo(({floors:n,currentFloorId:i,onFloorChange:d,showAllOption:r,allLabel:b,position:S,className:x})=>{const v=e.useMemo(()=>[...n].sort((u,M)=>u.order-M.order),[n]),a={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}}[S]},s={padding:"10px 16px",fontSize:"14px",fontWeight:500,border:"1px solid #444",borderRadius:"6px",backgroundColor:"transparent",color:"#fff",cursor:"pointer",transition:"all 0.2s ease",minHeight:"44px",touchAction:"manipulation"},g={...s,backgroundColor:"#3A7DE5",borderColor:"#3A7DE5"};return o.jsxs("div",{className:x,style:a,children:[r&&o.jsx("button",{type:"button",onClick:()=>d(null),style:i===null?g:s,children:b}),v.map(u=>o.jsx("button",{type:"button",onClick:()=>d(u.id),style:i===u.id?g:s,children:u.name},u.id))]})});vt.displayName="FloorSelectorBar";const wt=e.memo(({scale:n,minScale:i,maxScale:d,onZoomIn:r,onZoomOut:b,position:S,className:x})=>{const y={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}}[S]},a={width:"44px",height:"44px",minWidth:"44px",minHeight:"44px",fontSize:"22px",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",touchAction:"manipulation"},s={...a,opacity:.4,cursor:"not-allowed"},g=n<d,u=n>i;return o.jsxs("div",{className:x,style:y,children:[o.jsx("button",{type:"button",onClick:r,disabled:!g,style:g?a:s,title:"Zoom In",children:"+"}),o.jsx("button",{type:"button",onClick:b,disabled:!u,style:u?a:s,title:"Zoom Out",children:"−"})]})});wt.displayName="ZoomControls";const Ct=e.memo(({visible:n,x:i,y:d,seat:r,currency:b,state:S})=>{if(!n||!r)return null;const x=r.seatNumber||(r.rowLabel&&r.columnLabel?`${r.rowLabel}-${r.columnLabel}`:"N/A"),v={position:"fixed",left:`${i+15}px`,top:`${d+15}px`,zIndex:1e3,pointerEvents:"none"},y={backgroundColor:"rgba(26, 26, 26, 0.95)",color:"#fff",border:"1px solid #444",borderRadius:"8px",padding:"8px 12px",fontSize:"13px",boxShadow:"0 4px 12px rgba(0, 0, 0, 0.3)",minWidth:"140px"},a={color:"#9ca3af",marginRight:"4px"},s={fontWeight:600},g={color:"#4ade80",fontWeight:600},u={fontSize:"11px",color:"#6b7280",textTransform:"capitalize",marginTop:"4px"};return o.jsx("div",{style:v,children:o.jsxs("div",{style:y,children:[r.sectionName&&o.jsxs("div",{style:{marginBottom:"4px"},children:[o.jsx("span",{style:a,children:"Section:"}),o.jsx("span",{style:{...s,color:"#3b82f6"},children:r.sectionName})]}),o.jsxs("div",{style:{marginBottom:"4px"},children:[o.jsx("span",{style:a,children:"Seat:"}),o.jsx("span",{style:s,children:x})]}),r.price!==void 0&&r.price>0&&o.jsxs("div",{style:{marginBottom:"4px"},children:[o.jsx("span",{style:a,children:"Price:"}),o.jsxs("span",{style:g,children:[b," ",r.price.toFixed(2)]})]}),o.jsxs("div",{style:u,children:["Status: ",S]})]})})});Ct.displayName="SeatTooltip";const Ut=({config:n,configUrl:i,floorId:d,onFloorChange:r,reservedSeats:b=[],unavailableSeats:S=[],selectedSeats:x,onSeatSelect:v,onSeatDeselect:y,onSelectionChange:a,colorOverrides:s,showTooltip:g=!0,zoomEnabled:u=!0,className:M="",onConfigLoad:C,onError:R,showFloorSelector:T,floorSelectorPosition:V="top-left",floorSelectorClassName:_,showAllFloorsOption:G=!0,allFloorsLabel:U="All",fitToView:P=!0,fitPadding:W=40,showZoomControls:ot=!0,zoomControlsPosition:st="bottom-right",zoomControlsClassName:jt,maxZoom:I=3,zoomStep:Z=.25,touchEnabled:Mt=!0})=>{const it=e.useRef(null),ut=e.useRef(null),w=gt(ut),[X,ht]=e.useState(new Set),[j,A]=e.useState(1),[k,D]=e.useState({x:0,y:0}),[kt,Rt]=e.useState(null),[Et,It]=e.useState(1),K=e.useRef({width:0,height:0}),[B,dt]=e.useState({visible:!1,x:0,y:0,seat:null,state:"available"}),{config:Nt,loading:Lt,error:z}=pt(i),l=n||Nt,rt=d!==void 0,E=rt?d||null:kt,J=x!==void 0,Dt=e.useCallback(t=>{rt||Rt(t),r?.(t)},[rt,r]),ct=l?.floors||[],Tt=T!==void 0?T:ct.length>1,Q=e.useMemo(()=>l?{...l.colors,...s}:{...mt,...s},[l,s]),Y=e.useMemo(()=>{if(!l)return[];let t=l.seats.filter(c=>c.state!=="hidden");return E&&(t=t.filter(c=>c.floorId===E||!c.floorId&&E==="floor_default")),t},[l,E]),tt=e.useMemo(()=>l?.stages?E?l.stages.filter(t=>t.floorId===E||!t.floorId&&E==="floor_default"):l.stages:[],[l,E]),N=e.useMemo(()=>{if(!l||Y.length===0&&tt.length===0)return null;const t=12;let c=1/0,f=1/0,h=-1/0,p=-1/0;return Y.forEach(m=>{c=Math.min(c,m.position.x-t),f=Math.min(f,m.position.y-t),h=Math.max(h,m.position.x+t),p=Math.max(p,m.position.y+t)}),tt.forEach(m=>{c=Math.min(c,m.position.x),f=Math.min(f,m.position.y),h=Math.max(h,m.position.x+(m.config?.width||200)),p=Math.max(p,m.position.y+(m.config?.height||100))}),{minX:c,minY:f,maxX:h,maxY:p,width:h-c,height:p-f}},[l,Y,tt]);e.useEffect(()=>{if(!P||!l||!N||w.width===0||w.height===0)return;const t=Math.abs(w.width-K.current.width),c=Math.abs(w.height-K.current.height);if(!(K.current.width===0)&&t<10&&c<10)return;K.current=w;const h=w.width,p=w.height,m=h-W*2,q=p-W*2,et=m/N.width,lt=q/N.height,nt=Math.min(et,lt,I),Ht=N.minX+N.width/2,Ot=N.minY+N.height/2,qt=h/2,Vt=p/2,_t=qt-Ht*nt,Gt=Vt-Ot*nt;A(nt),D({x:_t,y:Gt}),It(nt)},[P,l,N,W,I,w,E]);const $=e.useMemo(()=>{const t=new Set(b),c=new Set(S);return{reserved:t,unavailable:c}},[b,S]),at=e.useMemo(()=>x?new Set(x):null,[x]),H=e.useCallback(t=>{const c=t.id,f=t.seatNumber||"";return $.unavailable.has(c)||$.unavailable.has(f)?"unavailable":$.reserved.has(c)||$.reserved.has(f)?"reserved":X.has(c)?"selected":t.state},[$,X]);e.useEffect(()=>{l&&C&&C(l)},[l,C]),e.useEffect(()=>{z&&R&&R(z)},[z,R]),e.useEffect(()=>{J&&at&&ht(at)},[J,at]);const Xt=e.useCallback(t=>{const c=H(t);if(c!=="available"&&c!=="selected")return;const f=X.has(t.id);J||ht(h=>{const p=new Set(h);return f?p.delete(t.id):p.add(t.id),p}),f?y?.(t):(v?.(t),v||console.log("Seat selected:",t))},[H,X,J,v,y]),O=e.useMemo(()=>l?Y.filter(t=>X.has(t.id)):[],[Y,X]);e.useEffect(()=>{a?.(O)},[O,a]);const F=Et,Yt=e.useCallback(()=>{if(!u)return;const t=Math.min(j+Z,I);if(t!==j){const c=w.width||l?.canvas.width||800,f=w.height||l?.canvas.height||600,h=c/2,p=f/2,m={x:(h-k.x)/j,y:(p-k.y)/j};A(t),D({x:h-m.x*t,y:p-m.y*t})}},[u,j,Z,I,w,l,k]),Ft=e.useCallback(()=>{if(!u)return;const t=Math.max(j-Z,F);if(t!==j){const c=w.width||l?.canvas.width||800,f=w.height||l?.canvas.height||600,h=c/2,p=f/2,m={x:(h-k.x)/j,y:(p-k.y)/j};A(t),D({x:h-m.x*t,y:p-m.y*t})}},[u,j,Z,F,w,l,k]),Pt=e.useCallback(t=>{D({x:t.target.x(),y:t.target.y()})},[]),Wt=e.useCallback(t=>{if(!u)return;t.evt.preventDefault();const c=it.current;if(!c)return;const f=c.scaleX(),h=c.getPointerPosition();if(!h)return;const p=1.1,m=t.evt.deltaY>0?f/p:f*p,q=Math.min(Math.max(m,F),I),et={x:(h.x-k.x)/f,y:(h.y-k.y)/f},lt={x:h.x-et.x*q,y:h.y-et.y*q};A(q),D(lt)},[u,k,F,I]);yt(it,{enabled:Mt&&u,minScale:F,maxScale:I,currentScale:j,currentPosition:k,onScaleChange:(t,c)=>{A(t),D(c)},onPositionChange:t=>{D(t)}});const At=e.useCallback((t,c)=>{if(!g)return;const f=c.target.getStage();if(!f)return;const h=f.getPointerPosition();if(!h)return;const p=f.container().getBoundingClientRect();dt({visible:!0,x:p.left+h.x,y:p.top+h.y,seat:t,state:H(t)})},[g,H]),Bt=e.useCallback(()=>{dt(t=>({...t,visible:!1}))},[]);if(Lt)return o.jsx("div",{className:`flex items-center justify-center h-full ${M}`,children:o.jsx("p",{children:"Loading seat map..."})});if(z)return o.jsx("div",{className:`flex items-center justify-center h-full ${M}`,children:o.jsxs("p",{className:"text-red-500",children:["Error loading seat map: ",z.message]})});if(!l)return o.jsx("div",{className:`flex items-center justify-center h-full ${M}`,children:o.jsx("p",{children:"No configuration provided"})});const zt=w.width||l.canvas.width,$t=w.height||l.canvas.height;return o.jsxs("div",{ref:ut,className:`relative ${M}`,style:{width:"100%",height:"100%"},children:[Tt&&ct.length>0&&o.jsx(vt,{floors:ct,currentFloorId:E,onFloorChange:Dt,showAllOption:G,allLabel:U,position:V,className:_}),o.jsxs(L.Stage,{ref:it,width:zt,height:$t,scaleX:j,scaleY:j,x:k.x,y:k.y,draggable:!0,onDragEnd:Pt,onWheel:Wt,style:{backgroundColor:l.canvas.backgroundColor,cursor:"grab"},children:[o.jsx(L.Layer,{listening:!1,children:tt.map(t=>o.jsx(St,{stage:t,stageColor:Q.stageColor},t.id))}),o.jsx(L.Layer,{children:Y.map(t=>o.jsx(bt,{seat:t,state:H(t),colors:Q,onClick:Xt,onMouseEnter:At,onMouseLeave:Bt},t.id))})]}),g&&o.jsx(Ct,{visible:B.visible,x:B.x,y:B.y,seat:B.seat,currency:Q.currency,state:B.state}),ot&&u&&o.jsx(wt,{scale:j,minScale:F,maxScale:I,onZoomIn:Yt,onZoomOut:Ft,position:st,className:jt}),O.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 (",O.length,")"]}),o.jsx("div",{className:"max-h-48 overflow-y-auto space-y-1",children:O.map(t=>o.jsxs("div",{className:"text-sm",children:[t.seatNumber,t.price&&` - ${Q.currency} ${t.price.toFixed(2)}`]},t.id))})]})]})};exports.DEFAULT_COLORS=mt;exports.SeatMapViewer=Ut;exports.useConfigFetcher=pt;exports.useContainerSize=gt;exports.useTouchGestures=yt;
package/dist/index.mjs CHANGED
@@ -1,34 +1,97 @@
1
- import { jsx as i, jsxs as x } from "react/jsx-runtime";
2
- import { useState as I, useEffect as W, useRef as zt, useCallback as C, useMemo as L, memo as j } from "react";
3
- import { Stage as Dt, Layer as nt, Group as Tt, Rect as ot, Text as jt, Circle as Ht } from "react-konva";
4
- function Ot(n) {
5
- const [u, h] = I(null), [r, b] = I(!1), [m, v] = I(null), w = async () => {
6
- if (n) {
7
- b(!0), v(null);
1
+ import { jsx as h, jsxs as w } from "react/jsx-runtime";
2
+ import { useState as L, useEffect as T, useRef as z, useCallback as N, useMemo as E, memo as U } from "react";
3
+ import { Stage as Jt, Layer as mt, Group as Qt, Rect as St, Text as te, Circle as ee } from "react-konva";
4
+ function ne(e) {
5
+ const [o, d] = L(null), [i, m] = L(!1), [b, f] = L(null), v = async () => {
6
+ if (e) {
7
+ m(!0), f(null);
8
8
  try {
9
- const p = await fetch(n);
10
- if (!p.ok)
11
- throw new Error(`Failed to fetch config: ${p.statusText}`);
12
- const s = await p.json();
13
- h(s);
14
- } catch (p) {
15
- const s = p instanceof Error ? p : new Error("Unknown error occurred");
16
- v(s), console.error("Failed to fetch seat map config:", s);
9
+ const y = await fetch(e);
10
+ if (!y.ok)
11
+ throw new Error(`Failed to fetch config: ${y.statusText}`);
12
+ const s = await y.json();
13
+ d(s);
14
+ } catch (y) {
15
+ const s = y instanceof Error ? y : new Error("Unknown error occurred");
16
+ f(s), console.error("Failed to fetch seat map config:", s);
17
17
  } finally {
18
- b(!1);
18
+ m(!1);
19
19
  }
20
20
  }
21
21
  };
22
- return W(() => {
23
- w();
24
- }, [n]), {
25
- config: u,
26
- loading: r,
27
- error: m,
28
- refetch: w
22
+ return T(() => {
23
+ v();
24
+ }, [e]), {
25
+ config: o,
26
+ loading: i,
27
+ error: b,
28
+ refetch: v
29
29
  };
30
30
  }
31
- const Zt = {
31
+ function oe(e) {
32
+ const [o, d] = L({ width: 0, height: 0 });
33
+ return T(() => {
34
+ const i = e.current;
35
+ if (!i) return;
36
+ const { width: m, height: b } = i.getBoundingClientRect();
37
+ m > 0 && b > 0 && d({ width: m, height: b });
38
+ const f = new ResizeObserver((v) => {
39
+ const y = v[0];
40
+ if (!y) return;
41
+ const { width: s, height: n } = y.contentRect;
42
+ s > 0 && n > 0 && d((g) => g.width === s && g.height === n ? g : { width: s, height: n });
43
+ });
44
+ return f.observe(i), () => {
45
+ f.disconnect();
46
+ };
47
+ }, [e]), o;
48
+ }
49
+ function bt(e, o) {
50
+ return Math.sqrt(Math.pow(o.x - e.x, 2) + Math.pow(o.y - e.y, 2));
51
+ }
52
+ function vt(e, o) {
53
+ return {
54
+ x: (e.x + o.x) / 2,
55
+ y: (e.y + o.y) / 2
56
+ };
57
+ }
58
+ function ie(e, o) {
59
+ const d = z(null), i = z(null), m = z(1);
60
+ T(() => {
61
+ const b = e.current;
62
+ if (!b || !o.enabled) return;
63
+ const f = b.container(), v = (n) => {
64
+ if (n.touches.length === 2) {
65
+ n.preventDefault();
66
+ const g = { x: n.touches[0].clientX, y: n.touches[0].clientY }, a = { x: n.touches[1].clientX, y: n.touches[1].clientY };
67
+ d.current = bt(g, a), i.current = vt(g, a), m.current = o.currentScale;
68
+ }
69
+ }, y = (n) => {
70
+ if (n.touches.length !== 2) return;
71
+ n.preventDefault();
72
+ const g = { x: n.touches[0].clientX, y: n.touches[0].clientY }, a = { x: n.touches[1].clientX, y: n.touches[1].clientY }, k = bt(g, a), C = vt(g, a);
73
+ if (d.current !== null && i.current !== null) {
74
+ const D = k / d.current, P = Math.min(
75
+ Math.max(o.currentScale * D, o.minScale),
76
+ o.maxScale
77
+ ), K = f.getBoundingClientRect(), J = C.x - K.left, Q = C.y - K.top, tt = o.currentScale, $ = {
78
+ x: (J - o.currentPosition.x) / tt,
79
+ y: (Q - o.currentPosition.y) / tt
80
+ }, H = C.x - i.current.x, at = C.y - i.current.y, lt = {
81
+ x: J - $.x * P + H,
82
+ y: Q - $.y * P + at
83
+ };
84
+ o.onScaleChange(P, lt), d.current = k, i.current = C;
85
+ }
86
+ }, s = (n) => {
87
+ n.touches.length < 2 && (d.current = null, i.current = null);
88
+ };
89
+ return f.addEventListener("touchstart", v, { passive: !1 }), f.addEventListener("touchmove", y, { passive: !1 }), f.addEventListener("touchend", s), () => {
90
+ f.removeEventListener("touchstart", v), f.removeEventListener("touchmove", y), f.removeEventListener("touchend", s);
91
+ };
92
+ }, [e, o]);
93
+ }
94
+ const re = {
32
95
  canvasBackground: "#1a1a1a",
33
96
  stageColor: "#808080",
34
97
  seatAvailable: "#2C2B30",
@@ -38,74 +101,74 @@ const Zt = {
38
101
  seatHidden: "#4a4a4a",
39
102
  gridLines: "#404040",
40
103
  currency: "KD"
41
- }, it = j(({ seat: n, state: u, colors: h, onClick: r, onMouseEnter: b, onMouseLeave: m }) => {
42
- const p = {
43
- available: h.seatAvailable,
44
- reserved: h.seatReserved,
45
- selected: h.seatSelected,
46
- unavailable: h.seatUnavailable,
47
- hidden: h.seatHidden
104
+ }, wt = U(({ seat: e, state: o, colors: d, onClick: i, onMouseEnter: m, onMouseLeave: b }) => {
105
+ const y = {
106
+ available: d.seatAvailable,
107
+ reserved: d.seatReserved,
108
+ selected: d.seatSelected,
109
+ unavailable: d.seatUnavailable,
110
+ hidden: d.seatHidden
48
111
  // Hidden seats are filtered out, but included for type safety
49
- }[u], s = u === "available" || u === "selected", g = C(() => {
50
- s && r(n);
51
- }, [n, r, s]), y = C((M) => {
52
- b(n, M);
53
- const R = M.target.getStage();
54
- R && s && (R.container().style.cursor = "pointer");
55
- }, [n, b, s]), f = C((M) => {
56
- m();
57
- const R = M.target.getStage();
58
- R && (R.container().style.cursor = "grab");
59
- }, [m]), N = {
60
- x: n.position.x,
61
- y: n.position.y,
62
- fill: p,
112
+ }[o], s = o === "available" || o === "selected", n = N(() => {
113
+ s && i(e);
114
+ }, [e, i, s]), g = N((C) => {
115
+ m(e, C);
116
+ const D = C.target.getStage();
117
+ D && s && (D.container().style.cursor = "pointer");
118
+ }, [e, m, s]), a = N((C) => {
119
+ b();
120
+ const D = C.target.getStage();
121
+ D && (D.container().style.cursor = "grab");
122
+ }, [b]), k = {
123
+ x: e.position.x,
124
+ y: e.position.y,
125
+ fill: y,
63
126
  stroke: "#ffffff",
64
127
  strokeWidth: 1,
65
- onClick: g,
66
- onTap: g,
67
- onMouseEnter: y,
68
- onMouseLeave: f
128
+ onClick: n,
129
+ onTap: n,
130
+ onMouseEnter: g,
131
+ onMouseLeave: a
69
132
  };
70
- return n.shape === "circle" ? /* @__PURE__ */ i(
71
- Ht,
133
+ return e.shape === "circle" ? /* @__PURE__ */ h(
134
+ ee,
72
135
  {
73
- ...N,
136
+ ...k,
74
137
  radius: 12
75
138
  }
76
- ) : /* @__PURE__ */ i(
77
- ot,
139
+ ) : /* @__PURE__ */ h(
140
+ St,
78
141
  {
79
- ...N,
142
+ ...k,
80
143
  width: 24,
81
144
  height: 24,
82
145
  offsetX: 12,
83
146
  offsetY: 12,
84
- cornerRadius: n.shape === "square" ? 0 : 4
147
+ cornerRadius: e.shape === "square" ? 0 : 4
85
148
  }
86
149
  );
87
150
  });
88
- it.displayName = "ViewerSeat";
89
- const rt = j(({ stage: n, stageColor: u }) => /* @__PURE__ */ x(Tt, { x: n.position.x, y: n.position.y, children: [
90
- /* @__PURE__ */ i(
91
- ot,
151
+ wt.displayName = "ViewerSeat";
152
+ const Ct = U(({ stage: e, stageColor: o }) => /* @__PURE__ */ w(Qt, { x: e.position.x, y: e.position.y, children: [
153
+ /* @__PURE__ */ h(
154
+ St,
92
155
  {
93
- width: n.config.width,
94
- height: n.config.height,
95
- fill: u + "80",
156
+ width: e.config.width,
157
+ height: e.config.height,
158
+ fill: o + "80",
96
159
  stroke: "#ffffff",
97
160
  strokeWidth: 2,
98
161
  cornerRadius: 10
99
162
  }
100
163
  ),
101
- /* @__PURE__ */ i(
102
- jt,
164
+ /* @__PURE__ */ h(
165
+ te,
103
166
  {
104
- text: n.config.label,
167
+ text: e.config.label,
105
168
  x: 0,
106
169
  y: 0,
107
- width: n.config.width,
108
- height: n.config.height,
170
+ width: e.config.width,
171
+ height: e.config.height,
109
172
  fontSize: 24,
110
173
  fontStyle: "bold",
111
174
  fill: "#ffffff",
@@ -114,19 +177,19 @@ const rt = j(({ stage: n, stageColor: u }) => /* @__PURE__ */ x(Tt, { x: n.posit
114
177
  }
115
178
  )
116
179
  ] }));
117
- rt.displayName = "ViewerStage";
118
- const st = j(({
119
- floors: n,
120
- currentFloorId: u,
121
- onFloorChange: h,
122
- showAllOption: r,
123
- allLabel: b,
124
- position: m,
125
- className: v
180
+ Ct.displayName = "ViewerStage";
181
+ const Mt = U(({
182
+ floors: e,
183
+ currentFloorId: o,
184
+ onFloorChange: d,
185
+ showAllOption: i,
186
+ allLabel: m,
187
+ position: b,
188
+ className: f
126
189
  }) => {
127
- const w = L(
128
- () => [...n].sort((f, N) => f.order - N.order),
129
- [n]
190
+ const v = E(
191
+ () => [...e].sort((a, k) => a.order - k.order),
192
+ [e]
130
193
  ), s = {
131
194
  position: "absolute",
132
195
  display: "flex",
@@ -142,9 +205,9 @@ const st = j(({
142
205
  "top-right": { top: 0, right: 0 },
143
206
  "bottom-left": { bottom: 0, left: 0 },
144
207
  "bottom-right": { bottom: 0, right: 0 }
145
- }[m]
146
- }, g = {
147
- padding: "6px 14px",
208
+ }[b]
209
+ }, n = {
210
+ padding: "10px 16px",
148
211
  fontSize: "14px",
149
212
  fontWeight: 500,
150
213
  border: "1px solid #444",
@@ -152,45 +215,47 @@ const st = j(({
152
215
  backgroundColor: "transparent",
153
216
  color: "#fff",
154
217
  cursor: "pointer",
155
- transition: "all 0.2s ease"
156
- }, y = {
157
- ...g,
218
+ transition: "all 0.2s ease",
219
+ minHeight: "44px",
220
+ touchAction: "manipulation"
221
+ }, g = {
222
+ ...n,
158
223
  backgroundColor: "#3A7DE5",
159
224
  borderColor: "#3A7DE5"
160
225
  };
161
- return /* @__PURE__ */ x("div", { className: v, style: s, children: [
162
- r && /* @__PURE__ */ i(
226
+ return /* @__PURE__ */ w("div", { className: f, style: s, children: [
227
+ i && /* @__PURE__ */ h(
163
228
  "button",
164
229
  {
165
230
  type: "button",
166
- onClick: () => h(null),
167
- style: u === null ? y : g,
168
- children: b
231
+ onClick: () => d(null),
232
+ style: o === null ? g : n,
233
+ children: m
169
234
  }
170
235
  ),
171
- w.map((f) => /* @__PURE__ */ i(
236
+ v.map((a) => /* @__PURE__ */ h(
172
237
  "button",
173
238
  {
174
239
  type: "button",
175
- onClick: () => h(f.id),
176
- style: u === f.id ? y : g,
177
- children: f.name
240
+ onClick: () => d(a.id),
241
+ style: o === a.id ? g : n,
242
+ children: a.name
178
243
  },
179
- f.id
244
+ a.id
180
245
  ))
181
246
  ] });
182
247
  });
183
- st.displayName = "FloorSelectorBar";
184
- const at = j(({
185
- scale: n,
186
- minScale: u,
187
- maxScale: h,
188
- onZoomIn: r,
189
- onZoomOut: b,
190
- position: m,
191
- className: v
248
+ Mt.displayName = "FloorSelectorBar";
249
+ const kt = U(({
250
+ scale: e,
251
+ minScale: o,
252
+ maxScale: d,
253
+ onZoomIn: i,
254
+ onZoomOut: m,
255
+ position: b,
256
+ className: f
192
257
  }) => {
193
- const p = {
258
+ const y = {
194
259
  position: "absolute",
195
260
  display: "flex",
196
261
  flexDirection: "column",
@@ -205,11 +270,13 @@ const at = j(({
205
270
  "top-right": { top: 0, right: 0 },
206
271
  "bottom-left": { bottom: 0, left: 0 },
207
272
  "bottom-right": { bottom: 0, right: 0 }
208
- }[m]
273
+ }[b]
209
274
  }, s = {
210
- width: "36px",
211
- height: "36px",
212
- fontSize: "20px",
275
+ width: "44px",
276
+ height: "44px",
277
+ minWidth: "44px",
278
+ minHeight: "44px",
279
+ fontSize: "22px",
213
280
  fontWeight: "bold",
214
281
  border: "1px solid #444",
215
282
  borderRadius: "6px",
@@ -219,54 +286,55 @@ const at = j(({
219
286
  display: "flex",
220
287
  alignItems: "center",
221
288
  justifyContent: "center",
222
- transition: "all 0.2s ease"
223
- }, g = {
289
+ transition: "all 0.2s ease",
290
+ touchAction: "manipulation"
291
+ }, n = {
224
292
  ...s,
225
293
  opacity: 0.4,
226
294
  cursor: "not-allowed"
227
- }, y = n < h, f = n > u;
228
- return /* @__PURE__ */ x("div", { className: v, style: p, children: [
229
- /* @__PURE__ */ i(
295
+ }, g = e < d, a = e > o;
296
+ return /* @__PURE__ */ w("div", { className: f, style: y, children: [
297
+ /* @__PURE__ */ h(
230
298
  "button",
231
299
  {
232
300
  type: "button",
233
- onClick: r,
234
- disabled: !y,
235
- style: y ? s : g,
301
+ onClick: i,
302
+ disabled: !g,
303
+ style: g ? s : n,
236
304
  title: "Zoom In",
237
305
  children: "+"
238
306
  }
239
307
  ),
240
- /* @__PURE__ */ i(
308
+ /* @__PURE__ */ h(
241
309
  "button",
242
310
  {
243
311
  type: "button",
244
- onClick: b,
245
- disabled: !f,
246
- style: f ? s : g,
312
+ onClick: m,
313
+ disabled: !a,
314
+ style: a ? s : n,
247
315
  title: "Zoom Out",
248
316
  children: "−"
249
317
  }
250
318
  )
251
319
  ] });
252
320
  });
253
- at.displayName = "ZoomControls";
254
- const lt = j(({
255
- visible: n,
256
- x: u,
257
- y: h,
258
- seat: r,
259
- currency: b,
260
- state: m
321
+ kt.displayName = "ZoomControls";
322
+ const It = U(({
323
+ visible: e,
324
+ x: o,
325
+ y: d,
326
+ seat: i,
327
+ currency: m,
328
+ state: b
261
329
  }) => {
262
- if (!n || !r) return null;
263
- const v = r.seatNumber || (r.rowLabel && r.columnLabel ? `${r.rowLabel}-${r.columnLabel}` : "N/A"), w = {
330
+ if (!e || !i) return null;
331
+ const f = i.seatNumber || (i.rowLabel && i.columnLabel ? `${i.rowLabel}-${i.columnLabel}` : "N/A"), v = {
264
332
  position: "fixed",
265
- left: `${u + 15}px`,
266
- top: `${h + 15}px`,
333
+ left: `${o + 15}px`,
334
+ top: `${d + 15}px`,
267
335
  zIndex: 1e3,
268
336
  pointerEvents: "none"
269
- }, p = {
337
+ }, y = {
270
338
  backgroundColor: "rgba(26, 26, 26, 0.95)",
271
339
  color: "#fff",
272
340
  border: "1px solid #444",
@@ -278,267 +346,320 @@ const lt = j(({
278
346
  }, s = {
279
347
  color: "#9ca3af",
280
348
  marginRight: "4px"
281
- }, g = {
349
+ }, n = {
282
350
  fontWeight: 600
283
- }, y = {
351
+ }, g = {
284
352
  color: "#4ade80",
285
353
  fontWeight: 600
286
- }, f = {
354
+ }, a = {
287
355
  fontSize: "11px",
288
356
  color: "#6b7280",
289
357
  textTransform: "capitalize",
290
358
  marginTop: "4px"
291
359
  };
292
- return /* @__PURE__ */ i("div", { style: w, children: /* @__PURE__ */ x("div", { style: p, children: [
293
- r.sectionName && /* @__PURE__ */ x("div", { style: { marginBottom: "4px" }, children: [
294
- /* @__PURE__ */ i("span", { style: s, children: "Section:" }),
295
- /* @__PURE__ */ i("span", { style: { ...g, color: "#3b82f6" }, children: r.sectionName })
360
+ return /* @__PURE__ */ h("div", { style: v, children: /* @__PURE__ */ w("div", { style: y, children: [
361
+ i.sectionName && /* @__PURE__ */ w("div", { style: { marginBottom: "4px" }, children: [
362
+ /* @__PURE__ */ h("span", { style: s, children: "Section:" }),
363
+ /* @__PURE__ */ h("span", { style: { ...n, color: "#3b82f6" }, children: i.sectionName })
296
364
  ] }),
297
- /* @__PURE__ */ x("div", { style: { marginBottom: "4px" }, children: [
298
- /* @__PURE__ */ i("span", { style: s, children: "Seat:" }),
299
- /* @__PURE__ */ i("span", { style: g, children: v })
365
+ /* @__PURE__ */ w("div", { style: { marginBottom: "4px" }, children: [
366
+ /* @__PURE__ */ h("span", { style: s, children: "Seat:" }),
367
+ /* @__PURE__ */ h("span", { style: n, children: f })
300
368
  ] }),
301
- r.price !== void 0 && r.price > 0 && /* @__PURE__ */ x("div", { style: { marginBottom: "4px" }, children: [
302
- /* @__PURE__ */ i("span", { style: s, children: "Price:" }),
303
- /* @__PURE__ */ x("span", { style: y, children: [
304
- b,
369
+ i.price !== void 0 && i.price > 0 && /* @__PURE__ */ w("div", { style: { marginBottom: "4px" }, children: [
370
+ /* @__PURE__ */ h("span", { style: s, children: "Price:" }),
371
+ /* @__PURE__ */ w("span", { style: g, children: [
372
+ m,
305
373
  " ",
306
- r.price.toFixed(2)
374
+ i.price.toFixed(2)
307
375
  ] })
308
376
  ] }),
309
- /* @__PURE__ */ x("div", { style: f, children: [
377
+ /* @__PURE__ */ w("div", { style: a, children: [
310
378
  "Status: ",
311
- m
379
+ b
312
380
  ] })
313
381
  ] }) });
314
382
  });
315
- lt.displayName = "SeatTooltip";
316
- const Ut = ({
317
- config: n,
318
- configUrl: u,
319
- floorId: h,
320
- onFloorChange: r,
321
- reservedSeats: b = [],
322
- unavailableSeats: m = [],
383
+ It.displayName = "SeatTooltip";
384
+ const le = ({
385
+ config: e,
386
+ configUrl: o,
387
+ floorId: d,
388
+ onFloorChange: i,
389
+ reservedSeats: m = [],
390
+ unavailableSeats: b = [],
391
+ selectedSeats: f,
323
392
  onSeatSelect: v,
324
- onSeatDeselect: w,
325
- onSelectionChange: p,
326
- colorOverrides: s,
393
+ onSeatDeselect: y,
394
+ onSelectionChange: s,
395
+ colorOverrides: n,
327
396
  showTooltip: g = !0,
328
- zoomEnabled: y = !0,
329
- className: f = "",
330
- onConfigLoad: N,
331
- onError: M,
397
+ zoomEnabled: a = !0,
398
+ className: k = "",
399
+ onConfigLoad: C,
400
+ onError: D,
332
401
  // Floor selector props
333
- showFloorSelector: R,
334
- floorSelectorPosition: ct = "top-left",
335
- floorSelectorClassName: dt,
336
- showAllFloorsOption: ft = !0,
337
- allFloorsLabel: ht = "All",
338
- fitToView: H = !0,
339
- fitPadding: U = 40,
402
+ showFloorSelector: P,
403
+ floorSelectorPosition: K = "top-left",
404
+ floorSelectorClassName: J,
405
+ showAllFloorsOption: Q = !0,
406
+ allFloorsLabel: tt = "All",
407
+ fitToView: $ = !0,
408
+ fitPadding: H = 40,
340
409
  // Zoom controls
341
- showZoomControls: ut = !0,
342
- zoomControlsPosition: pt = "bottom-right",
343
- zoomControlsClassName: gt,
410
+ showZoomControls: at = !0,
411
+ zoomControlsPosition: lt = "bottom-right",
412
+ zoomControlsClassName: Nt,
344
413
  maxZoom: X = 3,
345
- zoomStep: O = 0.25
414
+ zoomStep: et = 0.25,
415
+ // Touch gestures
416
+ touchEnabled: Dt = !0
346
417
  }) => {
347
- const yt = zt(null), [A, xt] = I(/* @__PURE__ */ new Set()), [S, q] = I(1), [F, Z] = I({ x: 0, y: 0 }), [bt, mt] = I(null), [Q, tt] = I(!1), [vt, St] = I(1), [Y, et] = I({ visible: !1, x: 0, y: 0, seat: null, state: "available" }), { config: wt, loading: Ct, error: $ } = Ot(u), e = n || wt, G = h !== void 0, k = G ? h || null : bt, kt = C((t) => {
348
- G || mt(t), r?.(t);
349
- }, [G, r]), K = e?.floors || [], It = R !== void 0 ? R : K.length > 1, P = L(
350
- () => e ? { ...e.colors, ...s } : { ...Zt, ...s },
351
- [e, s]
352
- ), B = L(() => {
353
- if (!e) return [];
354
- let t = e.seats.filter((o) => o.state !== "hidden");
355
- return k && (t = t.filter(
356
- (o) => o.floorId === k || !o.floorId && k === "floor_default"
418
+ const ht = z(null), gt = z(null), S = oe(gt), [B, yt] = L(/* @__PURE__ */ new Set()), [M, j] = L(1), [I, W] = L({ x: 0, y: 0 }), [Rt, Lt] = L(null), [Xt, Yt] = L(1), nt = z({ width: 0, height: 0 }), [O, xt] = L({ visible: !1, x: 0, y: 0, seat: null, state: "available" }), { config: Et, loading: Tt, error: V } = ne(o), c = e || Et, dt = d !== void 0, R = dt ? d || null : Rt, ot = f !== void 0, Wt = N((t) => {
419
+ dt || Lt(t), i?.(t);
420
+ }, [dt, i]), ut = c?.floors || [], Pt = P !== void 0 ? P : ut.length > 1, it = E(
421
+ () => c ? { ...c.colors, ...n } : { ...re, ...n },
422
+ [c, n]
423
+ ), F = E(() => {
424
+ if (!c) return [];
425
+ let t = c.seats.filter((r) => r.state !== "hidden");
426
+ return R && (t = t.filter(
427
+ (r) => r.floorId === R || !r.floorId && R === "floor_default"
357
428
  )), t;
358
- }, [e, k]), V = L(() => e?.stages ? k ? e.stages.filter(
359
- (t) => t.floorId === k || !t.floorId && k === "floor_default"
360
- ) : e.stages : [], [e, k]), E = L(() => {
361
- if (!e || B.length === 0 && V.length === 0)
429
+ }, [c, R]), rt = E(() => c?.stages ? R ? c.stages.filter(
430
+ (t) => t.floorId === R || !t.floorId && R === "floor_default"
431
+ ) : c.stages : [], [c, R]), Y = E(() => {
432
+ if (!c || F.length === 0 && rt.length === 0)
362
433
  return null;
363
434
  const t = 12;
364
- let o = 1 / 0, a = 1 / 0, c = -1 / 0, l = -1 / 0;
365
- return B.forEach((d) => {
366
- o = Math.min(o, d.position.x - t), a = Math.min(a, d.position.y - t), c = Math.max(c, d.position.x + t), l = Math.max(l, d.position.y + t);
367
- }), V.forEach((d) => {
368
- o = Math.min(o, d.position.x), a = Math.min(a, d.position.y), c = Math.max(c, d.position.x + (d.config?.width || 200)), l = Math.max(l, d.position.y + (d.config?.height || 100));
369
- }), { minX: o, minY: a, maxX: c, maxY: l, width: c - o, height: l - a };
370
- }, [e, B, V]);
371
- W(() => {
372
- if (!H || Q || !e || !E) return;
373
- const t = e.canvas.width, o = e.canvas.height, a = t - U * 2, c = o - U * 2, l = a / E.width, d = c / E.height, _ = Math.min(l, d, X), At = E.minX + E.width / 2, Bt = E.minY + E.height / 2, Wt = t / 2, Xt = o / 2, Yt = Wt - At * _, $t = Xt - Bt * _;
374
- q(_), Z({ x: Yt, y: $t }), St(_), tt(!0);
375
- }, [H, Q, e, E, U, X]), W(() => {
376
- H && tt(!1);
377
- }, [k, H]);
378
- const z = L(() => {
379
- const t = new Set(b), o = new Set(m);
380
- return { reserved: t, unavailable: o };
381
- }, [b, m]), D = C((t) => {
382
- const o = t.id, a = t.seatNumber || "";
383
- return z.unavailable.has(o) || z.unavailable.has(a) ? "unavailable" : z.reserved.has(o) || z.reserved.has(a) ? "reserved" : A.has(o) ? "selected" : t.state;
384
- }, [z, A]);
385
- W(() => {
386
- e && N && N(e);
387
- }, [e, N]), W(() => {
388
- $ && M && M($);
389
- }, [$, M]);
390
- const Nt = C((t) => {
391
- const o = D(t);
392
- if (o !== "available" && o !== "selected")
435
+ let r = 1 / 0, u = 1 / 0, l = -1 / 0, p = -1 / 0;
436
+ return F.forEach((x) => {
437
+ r = Math.min(r, x.position.x - t), u = Math.min(u, x.position.y - t), l = Math.max(l, x.position.x + t), p = Math.max(p, x.position.y + t);
438
+ }), rt.forEach((x) => {
439
+ r = Math.min(r, x.position.x), u = Math.min(u, x.position.y), l = Math.max(l, x.position.x + (x.config?.width || 200)), p = Math.max(p, x.position.y + (x.config?.height || 100));
440
+ }), { minX: r, minY: u, maxX: l, maxY: p, width: l - r, height: p - u };
441
+ }, [c, F, rt]);
442
+ T(() => {
443
+ if (!$ || !c || !Y || S.width === 0 || S.height === 0) return;
444
+ const t = Math.abs(S.width - nt.current.width), r = Math.abs(S.height - nt.current.height);
445
+ if (!(nt.current.width === 0) && t < 10 && r < 10) return;
446
+ nt.current = S;
447
+ const l = S.width, p = S.height, x = l - H * 2, G = p - H * 2, st = x / Y.width, pt = G / Y.height, ct = Math.min(st, pt, X), Zt = Y.minX + Y.width / 2, _t = Y.minY + Y.height / 2, qt = l / 2, Gt = p / 2, Ut = qt - Zt * ct, Kt = Gt - _t * ct;
448
+ j(ct), W({ x: Ut, y: Kt }), Yt(ct);
449
+ }, [$, c, Y, H, X, S, R]);
450
+ const Z = E(() => {
451
+ const t = new Set(m), r = new Set(b);
452
+ return { reserved: t, unavailable: r };
453
+ }, [m, b]), ft = E(() => f ? new Set(f) : null, [f]), _ = N((t) => {
454
+ const r = t.id, u = t.seatNumber || "";
455
+ return Z.unavailable.has(r) || Z.unavailable.has(u) ? "unavailable" : Z.reserved.has(r) || Z.reserved.has(u) ? "reserved" : B.has(r) ? "selected" : t.state;
456
+ }, [Z, B]);
457
+ T(() => {
458
+ c && C && C(c);
459
+ }, [c, C]), T(() => {
460
+ V && D && D(V);
461
+ }, [V, D]), T(() => {
462
+ ot && ft && yt(ft);
463
+ }, [ot, ft]);
464
+ const Bt = N((t) => {
465
+ const r = _(t);
466
+ if (r !== "available" && r !== "selected")
393
467
  return;
394
- const a = A.has(t.id);
395
- xt((c) => {
396
- const l = new Set(c);
397
- return a ? l.delete(t.id) : l.add(t.id), l;
398
- }), a ? w?.(t) : (v?.(t), v || console.log("Seat selected:", t));
399
- }, [D, A, v, w]), T = L(() => e ? B.filter((t) => A.has(t.id)) : [], [B, A]);
400
- W(() => {
401
- p?.(T);
402
- }, [T, p]);
403
- const J = vt, Mt = C(() => {
404
- if (!y) return;
405
- const t = Math.min(S + O, X);
406
- if (t !== S) {
407
- const o = e?.canvas.width || 800, a = e?.canvas.height || 600, c = o / 2, l = a / 2, d = {
408
- x: (c - F.x) / S,
409
- y: (l - F.y) / S
468
+ const u = B.has(t.id);
469
+ ot || yt((l) => {
470
+ const p = new Set(l);
471
+ return u ? p.delete(t.id) : p.add(t.id), p;
472
+ }), u ? y?.(t) : (v?.(t), v || console.log("Seat selected:", t));
473
+ }, [_, B, ot, v, y]), q = E(() => c ? F.filter((t) => B.has(t.id)) : [], [F, B]);
474
+ T(() => {
475
+ s?.(q);
476
+ }, [q, s]);
477
+ const A = Xt, Ft = N(() => {
478
+ if (!a) return;
479
+ const t = Math.min(M + et, X);
480
+ if (t !== M) {
481
+ const r = S.width || c?.canvas.width || 800, u = S.height || c?.canvas.height || 600, l = r / 2, p = u / 2, x = {
482
+ x: (l - I.x) / M,
483
+ y: (p - I.y) / M
410
484
  };
411
- q(t), Z({
412
- x: c - d.x * t,
413
- y: l - d.y * t
485
+ j(t), W({
486
+ x: l - x.x * t,
487
+ y: p - x.y * t
414
488
  });
415
489
  }
416
- }, [y, S, O, X, e, F]), Rt = C(() => {
417
- if (!y) return;
418
- const t = Math.max(S - O, J);
419
- if (t !== S) {
420
- const o = e?.canvas.width || 800, a = e?.canvas.height || 600, c = o / 2, l = a / 2, d = {
421
- x: (c - F.x) / S,
422
- y: (l - F.y) / S
490
+ }, [a, M, et, X, S, c, I]), At = N(() => {
491
+ if (!a) return;
492
+ const t = Math.max(M - et, A);
493
+ if (t !== M) {
494
+ const r = S.width || c?.canvas.width || 800, u = S.height || c?.canvas.height || 600, l = r / 2, p = u / 2, x = {
495
+ x: (l - I.x) / M,
496
+ y: (p - I.y) / M
423
497
  };
424
- q(t), Z({
425
- x: c - d.x * t,
426
- y: l - d.y * t
498
+ j(t), W({
499
+ x: l - x.x * t,
500
+ y: p - x.y * t
427
501
  });
428
502
  }
429
- }, [y, S, O, J, e, F]), Ft = C((t) => {
430
- Z({
503
+ }, [a, M, et, A, S, c, I]), zt = N((t) => {
504
+ W({
431
505
  x: t.target.x(),
432
506
  y: t.target.y()
433
507
  });
434
- }, []), Et = C((t, o) => {
435
- if (!g) return;
436
- const a = o.target.getStage();
508
+ }, []), $t = N((t) => {
437
509
  if (!a) return;
438
- const c = a.getPointerPosition();
439
- if (!c) return;
440
- const l = a.container().getBoundingClientRect();
441
- et({
510
+ t.evt.preventDefault();
511
+ const r = ht.current;
512
+ if (!r) return;
513
+ const u = r.scaleX(), l = r.getPointerPosition();
514
+ if (!l) return;
515
+ const p = 1.1, x = t.evt.deltaY > 0 ? u / p : u * p, G = Math.min(Math.max(x, A), X), st = {
516
+ x: (l.x - I.x) / u,
517
+ y: (l.y - I.y) / u
518
+ }, pt = {
519
+ x: l.x - st.x * G,
520
+ y: l.y - st.y * G
521
+ };
522
+ j(G), W(pt);
523
+ }, [a, I, A, X]);
524
+ ie(ht, {
525
+ enabled: Dt && a,
526
+ minScale: A,
527
+ maxScale: X,
528
+ currentScale: M,
529
+ currentPosition: I,
530
+ onScaleChange: (t, r) => {
531
+ j(t), W(r);
532
+ },
533
+ onPositionChange: (t) => {
534
+ W(t);
535
+ }
536
+ });
537
+ const Ht = N((t, r) => {
538
+ if (!g) return;
539
+ const u = r.target.getStage();
540
+ if (!u) return;
541
+ const l = u.getPointerPosition();
542
+ if (!l) return;
543
+ const p = u.container().getBoundingClientRect();
544
+ xt({
442
545
  visible: !0,
443
- x: l.left + c.x,
444
- y: l.top + c.y,
546
+ x: p.left + l.x,
547
+ y: p.top + l.y,
445
548
  seat: t,
446
- state: D(t)
549
+ state: _(t)
447
550
  });
448
- }, [g, D]), Lt = C(() => {
449
- et((t) => ({ ...t, visible: !1 }));
551
+ }, [g, _]), jt = N(() => {
552
+ xt((t) => ({ ...t, visible: !1 }));
450
553
  }, []);
451
- return Ct ? /* @__PURE__ */ i("div", { className: `flex items-center justify-center h-full ${f}`, children: /* @__PURE__ */ i("p", { children: "Loading seat map..." }) }) : $ ? /* @__PURE__ */ i("div", { className: `flex items-center justify-center h-full ${f}`, children: /* @__PURE__ */ x("p", { className: "text-red-500", children: [
452
- "Error loading seat map: ",
453
- $.message
454
- ] }) }) : e ? /* @__PURE__ */ x("div", { className: `relative ${f}`, children: [
455
- It && K.length > 0 && /* @__PURE__ */ i(
456
- st,
457
- {
458
- floors: K,
459
- currentFloorId: k,
460
- onFloorChange: kt,
461
- showAllOption: ft,
462
- allLabel: ht,
463
- position: ct,
464
- className: dt
465
- }
466
- ),
467
- /* @__PURE__ */ x(
468
- Dt,
469
- {
470
- ref: yt,
471
- width: e.canvas.width,
472
- height: e.canvas.height,
473
- scaleX: S,
474
- scaleY: S,
475
- x: F.x,
476
- y: F.y,
477
- draggable: !0,
478
- onDragEnd: Ft,
479
- style: { backgroundColor: e.canvas.backgroundColor, cursor: "grab" },
480
- children: [
481
- /* @__PURE__ */ i(nt, { listening: !1, children: V.map((t) => /* @__PURE__ */ i(
482
- rt,
483
- {
484
- stage: t,
485
- stageColor: P.stageColor
486
- },
487
- t.id
488
- )) }),
489
- /* @__PURE__ */ i(nt, { children: B.map((t) => /* @__PURE__ */ i(
490
- it,
491
- {
492
- seat: t,
493
- state: D(t),
494
- colors: P,
495
- onClick: Nt,
496
- onMouseEnter: Et,
497
- onMouseLeave: Lt
498
- },
499
- t.id
500
- )) })
501
- ]
502
- }
503
- ),
504
- g && /* @__PURE__ */ i(
505
- lt,
506
- {
507
- visible: Y.visible,
508
- x: Y.x,
509
- y: Y.y,
510
- seat: Y.seat,
511
- currency: P.currency,
512
- state: Y.state
513
- }
514
- ),
515
- ut && y && /* @__PURE__ */ i(
516
- at,
517
- {
518
- scale: S,
519
- minScale: J,
520
- maxScale: X,
521
- onZoomIn: Mt,
522
- onZoomOut: Rt,
523
- position: pt,
524
- className: gt
525
- }
526
- ),
527
- T.length > 0 && /* @__PURE__ */ x("div", { className: "absolute top-4 right-4 bg-white dark:bg-gray-800 p-4 rounded shadow-lg", children: [
528
- /* @__PURE__ */ x("h3", { className: "font-semibold mb-2", children: [
529
- "Selected Seats (",
530
- T.length,
531
- ")"
532
- ] }),
533
- /* @__PURE__ */ i("div", { className: "max-h-48 overflow-y-auto space-y-1", children: T.map((t) => /* @__PURE__ */ x("div", { className: "text-sm", children: [
534
- t.seatNumber,
535
- t.price && ` - ${P.currency} ${t.price.toFixed(2)}`
536
- ] }, t.id)) })
537
- ] })
538
- ] }) : /* @__PURE__ */ i("div", { className: `flex items-center justify-center h-full ${f}`, children: /* @__PURE__ */ i("p", { children: "No configuration provided" }) });
554
+ if (Tt)
555
+ return /* @__PURE__ */ h("div", { className: `flex items-center justify-center h-full ${k}`, children: /* @__PURE__ */ h("p", { children: "Loading seat map..." }) });
556
+ if (V)
557
+ return /* @__PURE__ */ h("div", { className: `flex items-center justify-center h-full ${k}`, children: /* @__PURE__ */ w("p", { className: "text-red-500", children: [
558
+ "Error loading seat map: ",
559
+ V.message
560
+ ] }) });
561
+ if (!c)
562
+ return /* @__PURE__ */ h("div", { className: `flex items-center justify-center h-full ${k}`, children: /* @__PURE__ */ h("p", { children: "No configuration provided" }) });
563
+ const Ot = S.width || c.canvas.width, Vt = S.height || c.canvas.height;
564
+ return /* @__PURE__ */ w(
565
+ "div",
566
+ {
567
+ ref: gt,
568
+ className: `relative ${k}`,
569
+ style: { width: "100%", height: "100%" },
570
+ children: [
571
+ Pt && ut.length > 0 && /* @__PURE__ */ h(
572
+ Mt,
573
+ {
574
+ floors: ut,
575
+ currentFloorId: R,
576
+ onFloorChange: Wt,
577
+ showAllOption: Q,
578
+ allLabel: tt,
579
+ position: K,
580
+ className: J
581
+ }
582
+ ),
583
+ /* @__PURE__ */ w(
584
+ Jt,
585
+ {
586
+ ref: ht,
587
+ width: Ot,
588
+ height: Vt,
589
+ scaleX: M,
590
+ scaleY: M,
591
+ x: I.x,
592
+ y: I.y,
593
+ draggable: !0,
594
+ onDragEnd: zt,
595
+ onWheel: $t,
596
+ style: { backgroundColor: c.canvas.backgroundColor, cursor: "grab" },
597
+ children: [
598
+ /* @__PURE__ */ h(mt, { listening: !1, children: rt.map((t) => /* @__PURE__ */ h(
599
+ Ct,
600
+ {
601
+ stage: t,
602
+ stageColor: it.stageColor
603
+ },
604
+ t.id
605
+ )) }),
606
+ /* @__PURE__ */ h(mt, { children: F.map((t) => /* @__PURE__ */ h(
607
+ wt,
608
+ {
609
+ seat: t,
610
+ state: _(t),
611
+ colors: it,
612
+ onClick: Bt,
613
+ onMouseEnter: Ht,
614
+ onMouseLeave: jt
615
+ },
616
+ t.id
617
+ )) })
618
+ ]
619
+ }
620
+ ),
621
+ g && /* @__PURE__ */ h(
622
+ It,
623
+ {
624
+ visible: O.visible,
625
+ x: O.x,
626
+ y: O.y,
627
+ seat: O.seat,
628
+ currency: it.currency,
629
+ state: O.state
630
+ }
631
+ ),
632
+ at && a && /* @__PURE__ */ h(
633
+ kt,
634
+ {
635
+ scale: M,
636
+ minScale: A,
637
+ maxScale: X,
638
+ onZoomIn: Ft,
639
+ onZoomOut: At,
640
+ position: lt,
641
+ className: Nt
642
+ }
643
+ ),
644
+ q.length > 0 && /* @__PURE__ */ w("div", { className: "absolute top-4 right-4 bg-white dark:bg-gray-800 p-4 rounded shadow-lg", children: [
645
+ /* @__PURE__ */ w("h3", { className: "font-semibold mb-2", children: [
646
+ "Selected Seats (",
647
+ q.length,
648
+ ")"
649
+ ] }),
650
+ /* @__PURE__ */ h("div", { className: "max-h-48 overflow-y-auto space-y-1", children: q.map((t) => /* @__PURE__ */ w("div", { className: "text-sm", children: [
651
+ t.seatNumber,
652
+ t.price && ` - ${it.currency} ${t.price.toFixed(2)}`
653
+ ] }, t.id)) })
654
+ ] })
655
+ ]
656
+ }
657
+ );
539
658
  };
540
659
  export {
541
- Zt as DEFAULT_COLORS,
542
- Ut as SeatMapViewer,
543
- Ot as useConfigFetcher
660
+ re as DEFAULT_COLORS,
661
+ le as SeatMapViewer,
662
+ ne as useConfigFetcher,
663
+ oe as useContainerSize,
664
+ ie as useTouchGestures
544
665
  };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@zonetrix/viewer",
3
- "version": "2.4.1",
3
+ "version": "2.6.0",
4
4
  "type": "module",
5
5
  "description": "Lightweight React component for rendering interactive seat maps",
6
6
  "main": "./dist/index.js",