@zonetrix/viewer 2.7.0 → 2.8.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -128,6 +128,7 @@ function BookingApp() {
128
128
  | `showZoomControls` | `boolean` | No | Show zoom +/- buttons (default: true) |
129
129
  | `zoomControlsPosition` | `string` | No | Position: 'top-left' \| 'top-right' \| 'bottom-left' \| 'bottom-right' (default: 'bottom-right') |
130
130
  | `zoomControlsClassName` | `string` | No | Custom CSS class for zoom controls |
131
+ | `minZoom` | `number` | No | Minimum zoom level. If not set, users cannot zoom out below fit-to-view scale (default: undefined) |
131
132
  | `maxZoom` | `number` | No | Maximum zoom level (default: 3) |
132
133
  | `zoomStep` | `number` | No | Zoom increment per click (default: 0.25) |
133
134
 
@@ -450,6 +451,39 @@ function RobustViewer() {
450
451
  }
451
452
  ```
452
453
 
454
+ ### 9. Custom Minimum Zoom
455
+
456
+ By default, users cannot zoom out below the "fit to screen" level. To allow zooming out further:
457
+
458
+ ```tsx
459
+ import { SeatMapViewer } from '@zonetrix/viewer';
460
+
461
+ // Allow zooming out to 25% for better overview
462
+ function WideViewMap() {
463
+ return (
464
+ <SeatMapViewer
465
+ config={venueConfig}
466
+ minZoom={0.25}
467
+ maxZoom={5}
468
+ onSeatSelect={(seat) => handleSelection(seat)}
469
+ />
470
+ );
471
+ }
472
+
473
+ // Extreme zoom out for large stadiums
474
+ function BirdEyeView() {
475
+ return (
476
+ <SeatMapViewer
477
+ config={largeStadiumConfig}
478
+ minZoom={0.1}
479
+ onSeatSelect={(seat) => handleSelection(seat)}
480
+ />
481
+ );
482
+ }
483
+ ```
484
+
485
+ **Note**: Very low minZoom values (< 0.1) may impact rendering performance with large seat maps.
486
+
453
487
  ## Configuration Format
454
488
 
455
489
  The viewer accepts a `SeatMapConfig` object. You can create these configurations using our creator studio or build them programmatically.
@@ -27,6 +27,23 @@ export interface SeatMapViewerProps {
27
27
  showZoomControls?: boolean;
28
28
  zoomControlsPosition?: 'top-left' | 'top-right' | 'bottom-left' | 'bottom-right';
29
29
  zoomControlsClassName?: string;
30
+ /**
31
+ * Minimum zoom level.
32
+ *
33
+ * When undefined (default), users cannot zoom out below the calculated
34
+ * "fit to screen" scale. When set, this value becomes the absolute
35
+ * minimum zoom level, allowing users to zoom out below the fit scale.
36
+ *
37
+ * Examples:
38
+ * - undefined: Default behavior, minimum is fit-to-screen scale
39
+ * - 0.1: Allow zooming out to 10% of original size
40
+ * - 0.25: Allow zooming out to 25% of original size
41
+ * - 1.0: Prevent zooming out below 100% (actual size)
42
+ *
43
+ * Recommended range: 0.1 - 10 (very low values may impact performance)
44
+ * @default undefined
45
+ */
46
+ minZoom?: number;
30
47
  maxZoom?: number;
31
48
  zoomStep?: number;
32
49
  touchEnabled?: boolean;
package/dist/index.js CHANGED
@@ -1 +1 @@
1
- "use strict";Object.defineProperty(exports,Symbol.toStringTag,{value:"Module"});const o=require("react/jsx-runtime"),e=require("react"),I=require("react-konva");function pt(n){const[r,u]=e.useState(null),[i,m]=e.useState(!1),[b,f]=e.useState(null),v=async()=>{if(n){m(!0),f(null);try{const y=await fetch(n);if(!y.ok)throw new Error(`Failed to fetch config: ${y.statusText}`);const a=await y.json();u(a)}catch(y){const a=y instanceof Error?y:new Error("Unknown error occurred");f(a),console.error("Failed to fetch seat map config:",a)}finally{m(!1)}}};return e.useEffect(()=>{v()},[n]),{config:r,loading:i,error:b,refetch:v}}function gt(n){const[r,u]=e.useState({width:0,height:0});return e.useEffect(()=>{const i=n.current;if(!i)return;const{width:m,height:b}=i.getBoundingClientRect();m>0&&b>0&&u({width:m,height:b});const f=new ResizeObserver(v=>{const y=v[0];if(!y)return;const{width:a,height:s}=y.contentRect;a>0&&s>0&&u(g=>g.width===a&&g.height===s?g:{width:a,height:s})});return f.observe(i),()=>{f.disconnect()}},[n]),r}function ft(n,r){return Math.sqrt(Math.pow(r.x-n.x,2)+Math.pow(r.y-n.y,2))}function xt(n,r){return{x:(n.x+r.x)/2,y:(n.y+r.y)/2}}function yt(n,r){const u=e.useRef(null),i=e.useRef(null),m=e.useRef(1);e.useEffect(()=>{const b=n.current;if(!b||!r.enabled)return;const f=b.container(),v=s=>{if(s.touches.length===2){s.preventDefault();const g={x:s.touches[0].clientX,y:s.touches[0].clientY},h={x:s.touches[1].clientX,y:s.touches[1].clientY};u.current=ft(g,h),i.current=xt(g,h),m.current=r.currentScale}},y=s=>{if(s.touches.length!==2)return;s.preventDefault();const g={x:s.touches[0].clientX,y:s.touches[0].clientY},h={x:s.touches[1].clientX,y:s.touches[1].clientY},M=ft(g,h),C=xt(g,h);if(u.current!==null&&i.current!==null){const R=M/u.current,D=Math.min(Math.max(r.currentScale*R,r.minScale),r.maxScale),V=f.getBoundingClientRect(),_=C.x-V.left,G=C.y-V.top,U=r.currentScale,P={x:(_-r.currentPosition.x)/U,y:(G-r.currentPosition.y)/U},W=C.x-i.current.x,ot=C.y-i.current.y,it={x:_-P.x*D+W,y:G-P.y*D+ot};r.onScaleChange(D,it),u.current=M,i.current=C}},a=s=>{s.touches.length<2&&(u.current=null,i.current=null)};return f.addEventListener("touchstart",v,{passive:!1}),f.addEventListener("touchmove",y,{passive:!1}),f.addEventListener("touchend",a),()=>{f.removeEventListener("touchstart",v),f.removeEventListener("touchmove",y),f.removeEventListener("touchend",a)}},[n,r])}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:r,colors:u,onClick:i,onMouseEnter:m,onMouseLeave:b})=>{const y={available:u.seatAvailable,reserved:u.seatReserved,selected:u.seatSelected,unavailable:u.seatUnavailable,hidden:u.seatHidden}[r],a=r==="available"||r==="selected",s=e.useCallback(()=>{a&&i(n)},[n,i,a]),g=e.useCallback(C=>{m(n,C);const R=C.target.getStage();R&&a&&(R.container().style.cursor="pointer")},[n,m,a]),h=e.useCallback(C=>{b();const R=C.target.getStage();R&&(R.container().style.cursor="grab")},[b]),M={x:n.position.x,y:n.position.y,fill:y,stroke:"#ffffff",strokeWidth:1,onClick:s,onTap:s,onMouseEnter:g,onMouseLeave:h};return n.shape==="circle"?o.jsx(I.Circle,{...M,radius:12}):o.jsx(I.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:r})=>{const u=b=>({stage:"🎭",table:"⬜",wall:"▬",barrier:"🛡️","dj-booth":"🎵",bar:"🍷","entry-exit":"🚪",custom:"➕"})[b||"stage"]||"🎭",i=n.config.color||r,m=u(n.config.objectType);return o.jsxs(I.Group,{x:n.position.x,y:n.position.y,rotation:n.config.rotation||0,children:[o.jsx(I.Rect,{width:n.config.width,height:n.config.height,fill:i+"80",stroke:"#ffffff",strokeWidth:2,cornerRadius:10}),o.jsx(I.Text,{text:m,x:0,y:0,width:n.config.width,height:n.config.height*.4,fontSize:32,fill:"#ffffff",align:"center",verticalAlign:"middle"}),o.jsx(I.Text,{text:n.config.label,x:0,y:n.config.height*.4,width:n.config.width,height:n.config.height*.6,fontSize:20,fontStyle:"bold",fill:"#ffffff",align:"center",verticalAlign:"middle"})]})});St.displayName="ViewerStage";const vt=e.memo(({floors:n,currentFloorId:r,onFloorChange:u,showAllOption:i,allLabel:m,position:b,className:f})=>{const v=e.useMemo(()=>[...n].sort((h,M)=>h.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}}[b]},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:f,style:a,children:[i&&o.jsx("button",{type:"button",onClick:()=>u(null),style:r===null?g:s,children:m}),v.map(h=>o.jsx("button",{type:"button",onClick:()=>u(h.id),style:r===h.id?g:s,children:h.name},h.id))]})});vt.displayName="FloorSelectorBar";const wt=e.memo(({scale:n,minScale:r,maxScale:u,onZoomIn:i,onZoomOut:m,position:b,className:f})=>{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}}[b]},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<u,h=n>r;return o.jsxs("div",{className:f,style:y,children:[o.jsx("button",{type:"button",onClick:i,disabled:!g,style:g?a:s,title:"Zoom In",children:"+"}),o.jsx("button",{type:"button",onClick:m,disabled:!h,style:h?a:s,title:"Zoom Out",children:"−"})]})});wt.displayName="ZoomControls";const Ct=e.memo(({visible:n,x:r,y:u,seat:i,currency:m,state:b})=>{if(!n||!i)return null;const f=i.seatNumber||(i.rowLabel&&i.columnLabel?`${i.rowLabel}-${i.columnLabel}`:"N/A"),v={position:"fixed",left:`${r+15}px`,top:`${u+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},h={fontSize:"11px",color:"#6b7280",textTransform:"capitalize",marginTop:"4px"};return o.jsx("div",{style:v,children:o.jsxs("div",{style:y,children:[i.sectionName&&o.jsxs("div",{style:{marginBottom:"4px"},children:[o.jsx("span",{style:a,children:"Section:"}),o.jsx("span",{style:{...s,color:"#3b82f6"},children:i.sectionName})]}),o.jsxs("div",{style:{marginBottom:"4px"},children:[o.jsx("span",{style:a,children:"Seat:"}),o.jsx("span",{style:s,children:f})]}),i.price!==void 0&&i.price>0&&o.jsxs("div",{style:{marginBottom:"4px"},children:[o.jsx("span",{style:a,children:"Price:"}),o.jsxs("span",{style:g,children:[m," ",i.price.toFixed(2)]})]}),o.jsxs("div",{style:h,children:["Status: ",b]})]})})});Ct.displayName="SeatTooltip";const Ut=({config:n,configUrl:r,floorId:u,onFloorChange:i,reservedSeats:m=[],unavailableSeats:b=[],selectedSeats:f,onSeatSelect:v,onSeatDeselect:y,onSelectionChange:a,colorOverrides:s,showTooltip:g=!0,zoomEnabled:h=!0,className:M="",onConfigLoad:C,onError:R,showFloorSelector:D,floorSelectorPosition:V="top-left",floorSelectorClassName:_,showAllFloorsOption:G=!0,allFloorsLabel:U="All",fitToView:P=!0,fitPadding:W=40,showZoomControls:ot=!0,zoomControlsPosition:it="bottom-right",zoomControlsClassName:jt,maxZoom:N=3,zoomStep:Z=.25,touchEnabled:Mt=!0})=>{const st=e.useRef(null),ut=e.useRef(null),w=gt(ut),[X,ht]=e.useState(new Set),[j,A]=e.useState(1),[k,T]=e.useState({x:0,y:0}),[kt,Rt]=e.useState(null),[Et,It]=e.useState(1),K=e.useRef({width:0,height:0}),[z,dt]=e.useState({visible:!1,x:0,y:0,seat:null,state:"available"}),{config:Nt,loading:Lt,error:B}=pt(r),l=n||Nt,rt=u!==void 0,E=rt?u||null:kt,J=f!==void 0,Tt=e.useCallback(t=>{rt||Rt(t),i?.(t)},[rt,i]),ct=l?.floors||[],Dt=D!==void 0?D: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]),L=e.useMemo(()=>{if(!l||Y.length===0&&tt.length===0)return null;const t=12;let c=1/0,x=1/0,d=-1/0,p=-1/0;return Y.forEach(S=>{c=Math.min(c,S.position.x-t),x=Math.min(x,S.position.y-t),d=Math.max(d,S.position.x+t),p=Math.max(p,S.position.y+t)}),tt.forEach(S=>{c=Math.min(c,S.position.x),x=Math.min(x,S.position.y),d=Math.max(d,S.position.x+(S.config?.width||200)),p=Math.max(p,S.position.y+(S.config?.height||100))}),{minX:c,minY:x,maxX:d,maxY:p,width:d-c,height:p-x}},[l,Y,tt]);e.useEffect(()=>{if(!P||!l||!L||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 d=w.width,p=w.height,S=d-W*2,q=p-W*2,et=S/L.width,lt=q/L.height,nt=Math.min(et,lt,N),$t=L.minX+L.width/2,Ht=L.minY+L.height/2,qt=d/2,Vt=p/2,_t=qt-$t*nt,Gt=Vt-Ht*nt;A(nt),T({x:_t,y:Gt}),It(nt)},[P,l,L,W,N,w,E]);const O=e.useMemo(()=>{const t=new Set(m),c=new Set(b);return{reserved:t,unavailable:c}},[m,b]),at=e.useMemo(()=>f?new Set(f):null,[f]),$=e.useCallback(t=>{const c=t.id,x=t.seatNumber||"";return O.unavailable.has(c)||O.unavailable.has(x)?"unavailable":O.reserved.has(c)||O.reserved.has(x)?"reserved":X.has(c)?"selected":t.state},[O,X]);e.useEffect(()=>{l&&C&&C(l)},[l,C]),e.useEffect(()=>{B&&R&&R(B)},[B,R]),e.useEffect(()=>{J&&at&&ht(at)},[J,at]);const Xt=e.useCallback(t=>{const c=$(t);if(c!=="available"&&c!=="selected")return;const x=X.has(t.id);J||ht(d=>{const p=new Set(d);return x?p.delete(t.id):p.add(t.id),p}),x?y?.(t):(v?.(t),v||console.log("Seat selected:",t))},[$,X,J,v,y]),H=e.useMemo(()=>l?Y.filter(t=>X.has(t.id)):[],[Y,X]);e.useEffect(()=>{a?.(H)},[H,a]);const F=Et,Yt=e.useCallback(()=>{if(!h)return;const t=Math.min(j+Z,N);if(t!==j){const c=w.width||l?.canvas.width||800,x=w.height||l?.canvas.height||600,d=c/2,p=x/2,S={x:(d-k.x)/j,y:(p-k.y)/j};A(t),T({x:d-S.x*t,y:p-S.y*t})}},[h,j,Z,N,w,l,k]),Ft=e.useCallback(()=>{if(!h)return;const t=Math.max(j-Z,F);if(t!==j){const c=w.width||l?.canvas.width||800,x=w.height||l?.canvas.height||600,d=c/2,p=x/2,S={x:(d-k.x)/j,y:(p-k.y)/j};A(t),T({x:d-S.x*t,y:p-S.y*t})}},[h,j,Z,F,w,l,k]),Pt=e.useCallback(t=>{T({x:t.target.x(),y:t.target.y()})},[]),Wt=e.useCallback(t=>{if(!h)return;t.evt.preventDefault();const c=st.current;if(!c)return;const x=c.scaleX(),d=c.getPointerPosition();if(!d)return;const p=1.1,S=t.evt.deltaY>0?x/p:x*p,q=Math.min(Math.max(S,F),N),et={x:(d.x-k.x)/x,y:(d.y-k.y)/x},lt={x:d.x-et.x*q,y:d.y-et.y*q};A(q),T(lt)},[h,k,F,N]);yt(st,{enabled:Mt&&h,minScale:F,maxScale:N,currentScale:j,currentPosition:k,onScaleChange:(t,c)=>{A(t),T(c)},onPositionChange:t=>{T(t)}});const At=e.useCallback((t,c)=>{if(!g)return;const x=c.target.getStage();if(!x)return;const d=x.getPointerPosition();if(!d)return;const p=x.container().getBoundingClientRect();dt({visible:!0,x:p.left+d.x,y:p.top+d.y,seat:t,state:$(t)})},[g,$]),zt=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(B)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: ",B.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 Bt=w.width||l.canvas.width,Ot=w.height||l.canvas.height;return o.jsxs("div",{ref:ut,className:`relative ${M}`,style:{width:"100%",height:"100%"},children:[Dt&&ct.length>0&&o.jsx(vt,{floors:ct,currentFloorId:E,onFloorChange:Tt,showAllOption:G,allLabel:U,position:V,className:_}),o.jsxs(I.Stage,{ref:st,width:Bt,height:Ot,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(I.Layer,{listening:!1,children:tt.map(t=>o.jsx(St,{stage:t,stageColor:Q.stageColor},t.id))}),o.jsx(I.Layer,{children:Y.map(t=>o.jsx(bt,{seat:t,state:$(t),colors:Q,onClick:Xt,onMouseEnter:At,onMouseLeave:zt},t.id))})]}),g&&o.jsx(Ct,{visible:z.visible,x:z.x,y:z.y,seat:z.seat,currency:Q.currency,state:z.state}),ot&&h&&o.jsx(wt,{scale:j,minScale:F,maxScale:N,onZoomIn:Yt,onZoomOut:Ft,position:it,className:jt}),H.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 (",H.length,")"]}),o.jsx("div",{className:"max-h-48 overflow-y-auto space-y-1",children:H.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;
1
+ "use strict";Object.defineProperty(exports,Symbol.toStringTag,{value:"Module"});const o=require("react/jsx-runtime"),e=require("react"),I=require("react-konva");function gt(n){const[r,u]=e.useState(null),[i,m]=e.useState(!1),[y,f]=e.useState(null),v=async()=>{if(n){m(!0),f(null);try{const b=await fetch(n);if(!b.ok)throw new Error(`Failed to fetch config: ${b.statusText}`);const a=await b.json();u(a)}catch(b){const a=b instanceof Error?b:new Error("Unknown error occurred");f(a),console.error("Failed to fetch seat map config:",a)}finally{m(!1)}}};return e.useEffect(()=>{v()},[n]),{config:r,loading:i,error:y,refetch:v}}function yt(n){const[r,u]=e.useState({width:0,height:0});return e.useEffect(()=>{const i=n.current;if(!i)return;const{width:m,height:y}=i.getBoundingClientRect();m>0&&y>0&&u({width:m,height:y});const f=new ResizeObserver(v=>{const b=v[0];if(!b)return;const{width:a,height:s}=b.contentRect;a>0&&s>0&&u(g=>g.width===a&&g.height===s?g:{width:a,height:s})});return f.observe(i),()=>{f.disconnect()}},[n]),r}function xt(n,r){return Math.sqrt(Math.pow(r.x-n.x,2)+Math.pow(r.y-n.y,2))}function pt(n,r){return{x:(n.x+r.x)/2,y:(n.y+r.y)/2}}function bt(n,r){const u=e.useRef(null),i=e.useRef(null),m=e.useRef(1);e.useEffect(()=>{const y=n.current;if(!y||!r.enabled)return;const f=y.container(),v=s=>{if(s.touches.length===2){s.preventDefault();const g={x:s.touches[0].clientX,y:s.touches[0].clientY},h={x:s.touches[1].clientX,y:s.touches[1].clientY};u.current=xt(g,h),i.current=pt(g,h),m.current=r.currentScale}},b=s=>{if(s.touches.length!==2)return;s.preventDefault();const g={x:s.touches[0].clientX,y:s.touches[0].clientY},h={x:s.touches[1].clientX,y:s.touches[1].clientY},M=xt(g,h),C=pt(g,h);if(u.current!==null&&i.current!==null){const R=M/u.current,D=Math.min(Math.max(r.currentScale*R,r.minScale),r.maxScale),V=f.getBoundingClientRect(),_=C.x-V.left,G=C.y-V.top,U=r.currentScale,P={x:(_-r.currentPosition.x)/U,y:(G-r.currentPosition.y)/U},W=C.x-i.current.x,ot=C.y-i.current.y,it={x:_-P.x*D+W,y:G-P.y*D+ot};r.onScaleChange(D,it),u.current=M,i.current=C}},a=s=>{s.touches.length<2&&(u.current=null,i.current=null)};return f.addEventListener("touchstart",v,{passive:!1}),f.addEventListener("touchmove",b,{passive:!1}),f.addEventListener("touchend",a),()=>{f.removeEventListener("touchstart",v),f.removeEventListener("touchmove",b),f.removeEventListener("touchend",a)}},[n,r])}const mt={canvasBackground:"#1a1a1a",stageColor:"#808080",seatAvailable:"#2C2B30",seatReserved:"#FCEA00",seatSelected:"#3A7DE5",seatUnavailable:"#6b7280",seatHidden:"#4a4a4a",gridLines:"#404040",currency:"KD"},St=e.memo(({seat:n,state:r,colors:u,onClick:i,onMouseEnter:m,onMouseLeave:y})=>{const b={available:u.seatAvailable,reserved:u.seatReserved,selected:u.seatSelected,unavailable:u.seatUnavailable,hidden:u.seatHidden}[r],a=r==="available"||r==="selected",s=e.useCallback(()=>{a&&i(n)},[n,i,a]),g=e.useCallback(C=>{m(n,C);const R=C.target.getStage();R&&a&&(R.container().style.cursor="pointer")},[n,m,a]),h=e.useCallback(C=>{y();const R=C.target.getStage();R&&(R.container().style.cursor="grab")},[y]),M={x:n.position.x,y:n.position.y,fill:b,stroke:"#ffffff",strokeWidth:1,onClick:s,onTap:s,onMouseEnter:g,onMouseLeave:h};return n.shape==="circle"?o.jsx(I.Circle,{...M,radius:12}):o.jsx(I.Rect,{...M,width:24,height:24,offsetX:12,offsetY:12,cornerRadius:n.shape==="square"?0:4})});St.displayName="ViewerSeat";const vt=e.memo(({stage:n,stageColor:r})=>{const u=y=>({stage:"🎭",table:"⬜",wall:"▬",barrier:"🛡️","dj-booth":"🎵",bar:"🍷","entry-exit":"🚪",custom:"➕"})[y||"stage"]||"🎭",i=n.config.color||r,m=u(n.config.objectType);return o.jsxs(I.Group,{x:n.position.x,y:n.position.y,rotation:n.config.rotation||0,children:[o.jsx(I.Rect,{width:n.config.width,height:n.config.height,fill:i+"80",stroke:"#ffffff",strokeWidth:2,cornerRadius:10}),o.jsx(I.Text,{text:m,x:0,y:0,width:n.config.width,height:n.config.height*.4,fontSize:32,fill:"#ffffff",align:"center",verticalAlign:"middle"}),o.jsx(I.Text,{text:n.config.label,x:0,y:n.config.height*.4,width:n.config.width,height:n.config.height*.6,fontSize:20,fontStyle:"bold",fill:"#ffffff",align:"center",verticalAlign:"middle"})]})});vt.displayName="ViewerStage";const wt=e.memo(({floors:n,currentFloorId:r,onFloorChange:u,showAllOption:i,allLabel:m,position:y,className:f})=>{const v=e.useMemo(()=>[...n].sort((h,M)=>h.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}}[y]},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:f,style:a,children:[i&&o.jsx("button",{type:"button",onClick:()=>u(null),style:r===null?g:s,children:m}),v.map(h=>o.jsx("button",{type:"button",onClick:()=>u(h.id),style:r===h.id?g:s,children:h.name},h.id))]})});wt.displayName="FloorSelectorBar";const Ct=e.memo(({scale:n,minScale:r,maxScale:u,onZoomIn:i,onZoomOut:m,position:y,className:f})=>{const b={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}}[y]},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<u,h=n>r;return o.jsxs("div",{className:f,style:b,children:[o.jsx("button",{type:"button",onClick:i,disabled:!g,style:g?a:s,title:"Zoom In",children:"+"}),o.jsx("button",{type:"button",onClick:m,disabled:!h,style:h?a:s,title:"Zoom Out",children:"−"})]})});Ct.displayName="ZoomControls";const jt=e.memo(({visible:n,x:r,y:u,seat:i,currency:m,state:y})=>{if(!n||!i)return null;const f=i.seatNumber||(i.rowLabel&&i.columnLabel?`${i.rowLabel}-${i.columnLabel}`:"N/A"),v={position:"fixed",left:`${r+15}px`,top:`${u+15}px`,zIndex:1e3,pointerEvents:"none"},b={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},h={fontSize:"11px",color:"#6b7280",textTransform:"capitalize",marginTop:"4px"};return o.jsx("div",{style:v,children:o.jsxs("div",{style:b,children:[i.sectionName&&o.jsxs("div",{style:{marginBottom:"4px"},children:[o.jsx("span",{style:a,children:"Section:"}),o.jsx("span",{style:{...s,color:"#3b82f6"},children:i.sectionName})]}),o.jsxs("div",{style:{marginBottom:"4px"},children:[o.jsx("span",{style:a,children:"Seat:"}),o.jsx("span",{style:s,children:f})]}),i.price!==void 0&&i.price>0&&y==="available"&&o.jsxs("div",{style:{marginBottom:"4px"},children:[o.jsx("span",{style:a,children:"Price:"}),o.jsxs("span",{style:g,children:[m," ",i.price.toFixed(2)]})]}),o.jsxs("div",{style:h,children:["Status: ",y]})]})})});jt.displayName="SeatTooltip";const Kt=({config:n,configUrl:r,floorId:u,onFloorChange:i,reservedSeats:m=[],unavailableSeats:y=[],selectedSeats:f,onSeatSelect:v,onSeatDeselect:b,onSelectionChange:a,colorOverrides:s,showTooltip:g=!0,zoomEnabled:h=!0,className:M="",onConfigLoad:C,onError:R,showFloorSelector:D,floorSelectorPosition:V="top-left",floorSelectorClassName:_,showAllFloorsOption:G=!0,allFloorsLabel:U="All",fitToView:P=!0,fitPadding:W=40,showZoomControls:ot=!0,zoomControlsPosition:it="bottom-right",zoomControlsClassName:Mt,minZoom:ut,maxZoom:N=3,zoomStep:K=.25,touchEnabled:kt=!0})=>{const st=e.useRef(null),ht=e.useRef(null),w=yt(ht),[X,dt]=e.useState(new Set),[j,A]=e.useState(1),[k,T]=e.useState({x:0,y:0}),[Rt,Et]=e.useState(null),[It,Nt]=e.useState(1),Z=e.useRef({width:0,height:0}),[z,ft]=e.useState({visible:!1,x:0,y:0,seat:null,state:"available"}),{config:Lt,loading:Tt,error:B}=gt(r),l=n||Lt,rt=u!==void 0,E=rt?u||null:Rt,J=f!==void 0,Dt=e.useCallback(t=>{rt||Et(t),i?.(t)},[rt,i]),ct=l?.floors||[],Xt=D!==void 0?D: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]),L=e.useMemo(()=>{if(!l||Y.length===0&&tt.length===0)return null;const t=12;let c=1/0,x=1/0,d=-1/0,p=-1/0;return Y.forEach(S=>{c=Math.min(c,S.position.x-t),x=Math.min(x,S.position.y-t),d=Math.max(d,S.position.x+t),p=Math.max(p,S.position.y+t)}),tt.forEach(S=>{c=Math.min(c,S.position.x),x=Math.min(x,S.position.y),d=Math.max(d,S.position.x+(S.config?.width||200)),p=Math.max(p,S.position.y+(S.config?.height||100))}),{minX:c,minY:x,maxX:d,maxY:p,width:d-c,height:p-x}},[l,Y,tt]);e.useEffect(()=>{if(!P||!l||!L||w.width===0||w.height===0)return;const t=Math.abs(w.width-Z.current.width),c=Math.abs(w.height-Z.current.height);if(!(Z.current.width===0)&&t<10&&c<10)return;Z.current=w;const d=w.width,p=w.height,S=d-W*2,q=p-W*2,et=S/L.width,lt=q/L.height,nt=Math.min(et,lt,N),Ht=L.minX+L.width/2,qt=L.minY+L.height/2,Vt=d/2,_t=p/2,Gt=Vt-Ht*nt,Ut=_t-qt*nt;A(nt),T({x:Gt,y:Ut}),Nt(nt)},[P,l,L,W,N,w,E]);const O=e.useMemo(()=>{const t=new Set(m),c=new Set(y);return{reserved:t,unavailable:c}},[m,y]),at=e.useMemo(()=>f?new Set(f):null,[f]),$=e.useCallback(t=>{const c=t.id,x=t.seatNumber||"";return O.unavailable.has(c)||O.unavailable.has(x)?"unavailable":O.reserved.has(c)||O.reserved.has(x)?"reserved":X.has(c)?"selected":t.state},[O,X]);e.useEffect(()=>{l&&C&&C(l)},[l,C]),e.useEffect(()=>{B&&R&&R(B)},[B,R]),e.useEffect(()=>{J&&at&&dt(at)},[J,at]);const Yt=e.useCallback(t=>{const c=$(t);if(c!=="available"&&c!=="selected")return;const x=X.has(t.id);J||dt(d=>{const p=new Set(d);return x?p.delete(t.id):p.add(t.id),p}),x?b?.(t):(v?.(t),v||console.log("Seat selected:",t))},[$,X,J,v,b]),H=e.useMemo(()=>l?Y.filter(t=>X.has(t.id)):[],[Y,X]);e.useEffect(()=>{a?.(H)},[H,a]);const F=ut!==void 0?ut:It,Ft=e.useCallback(()=>{if(!h)return;const t=Math.min(j+K,N);if(t!==j){const c=w.width||l?.canvas.width||800,x=w.height||l?.canvas.height||600,d=c/2,p=x/2,S={x:(d-k.x)/j,y:(p-k.y)/j};A(t),T({x:d-S.x*t,y:p-S.y*t})}},[h,j,K,N,w,l,k]),Pt=e.useCallback(()=>{if(!h)return;const t=Math.max(j-K,F);if(t!==j){const c=w.width||l?.canvas.width||800,x=w.height||l?.canvas.height||600,d=c/2,p=x/2,S={x:(d-k.x)/j,y:(p-k.y)/j};A(t),T({x:d-S.x*t,y:p-S.y*t})}},[h,j,K,F,w,l,k]),Wt=e.useCallback(t=>{T({x:t.target.x(),y:t.target.y()})},[]),At=e.useCallback(t=>{if(!h)return;t.evt.preventDefault();const c=st.current;if(!c)return;const x=c.scaleX(),d=c.getPointerPosition();if(!d)return;const p=1.1,S=t.evt.deltaY>0?x/p:x*p,q=Math.min(Math.max(S,F),N),et={x:(d.x-k.x)/x,y:(d.y-k.y)/x},lt={x:d.x-et.x*q,y:d.y-et.y*q};A(q),T(lt)},[h,k,F,N]);bt(st,{enabled:kt&&h,minScale:F,maxScale:N,currentScale:j,currentPosition:k,onScaleChange:(t,c)=>{A(t),T(c)},onPositionChange:t=>{T(t)}});const zt=e.useCallback((t,c)=>{if(!g)return;const x=c.target.getStage();if(!x)return;const d=x.getPointerPosition();if(!d)return;const p=x.container().getBoundingClientRect();ft({visible:!0,x:p.left+d.x,y:p.top+d.y,seat:t,state:$(t)})},[g,$]),Bt=e.useCallback(()=>{ft(t=>({...t,visible:!1}))},[]);if(Tt)return o.jsx("div",{className:`flex items-center justify-center h-full ${M}`,children:o.jsx("p",{children:"Loading seat map..."})});if(B)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: ",B.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 Ot=w.width||l.canvas.width,$t=w.height||l.canvas.height;return o.jsxs("div",{ref:ht,className:`relative ${M}`,style:{width:"100%",height:"100%"},children:[Xt&&ct.length>0&&o.jsx(wt,{floors:ct,currentFloorId:E,onFloorChange:Dt,showAllOption:G,allLabel:U,position:V,className:_}),o.jsxs(I.Stage,{ref:st,width:Ot,height:$t,scaleX:j,scaleY:j,x:k.x,y:k.y,draggable:!0,onDragEnd:Wt,onWheel:At,style:{backgroundColor:l.canvas.backgroundColor,cursor:"grab"},children:[o.jsx(I.Layer,{listening:!1,children:tt.map(t=>o.jsx(vt,{stage:t,stageColor:Q.stageColor},t.id))}),o.jsx(I.Layer,{children:Y.map(t=>o.jsx(St,{seat:t,state:$(t),colors:Q,onClick:Yt,onMouseEnter:zt,onMouseLeave:Bt},t.id))})]}),g&&o.jsx(jt,{visible:z.visible,x:z.x,y:z.y,seat:z.seat,currency:Q.currency,state:z.state}),ot&&h&&o.jsx(Ct,{scale:j,minScale:F,maxScale:N,onZoomIn:Ft,onZoomOut:Pt,position:it,className:Mt}),H.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 (",H.length,")"]}),o.jsx("div",{className:"max-h-48 overflow-y-auto space-y-1",children:H.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=Kt;exports.useConfigFetcher=gt;exports.useContainerSize=yt;exports.useTouchGestures=bt;
package/dist/index.mjs CHANGED
@@ -1,21 +1,21 @@
1
1
  import { jsx as h, jsxs as w } from "react/jsx-runtime";
2
- import { useState as E, useEffect as T, useRef as F, useCallback as N, useMemo as Y, memo as U } from "react";
3
- import { Stage as Qt, Layer as mt, Group as te, Rect as wt, Text as bt, Circle as ee } from "react-konva";
4
- function ne(e) {
5
- const [i, a] = E(null), [n, x] = E(!1), [m, u] = E(null), v = async () => {
2
+ import { useState as E, useEffect as T, useRef as F, useCallback as N, useMemo as Y, memo as Z } from "react";
3
+ import { Stage as te, Layer as bt, Group as ee, Rect as Ct, Text as vt, Circle as ne } from "react-konva";
4
+ function oe(e) {
5
+ const [i, a] = E(null), [n, m] = E(!1), [y, u] = E(null), v = async () => {
6
6
  if (e) {
7
- x(!0), u(null);
7
+ m(!0), u(null);
8
8
  try {
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();
9
+ const x = await fetch(e);
10
+ if (!x.ok)
11
+ throw new Error(`Failed to fetch config: ${x.statusText}`);
12
+ const s = await x.json();
13
13
  a(s);
14
- } catch (y) {
15
- const s = y instanceof Error ? y : new Error("Unknown error occurred");
14
+ } catch (x) {
15
+ const s = x instanceof Error ? x : new Error("Unknown error occurred");
16
16
  u(s), console.error("Failed to fetch seat map config:", s);
17
17
  } finally {
18
- x(!1);
18
+ m(!1);
19
19
  }
20
20
  }
21
21
  };
@@ -24,21 +24,21 @@ function ne(e) {
24
24
  }, [e]), {
25
25
  config: i,
26
26
  loading: n,
27
- error: m,
27
+ error: y,
28
28
  refetch: v
29
29
  };
30
30
  }
31
- function oe(e) {
31
+ function ie(e) {
32
32
  const [i, a] = E({ width: 0, height: 0 });
33
33
  return T(() => {
34
34
  const n = e.current;
35
35
  if (!n) return;
36
- const { width: x, height: m } = n.getBoundingClientRect();
37
- x > 0 && m > 0 && a({ width: x, height: m });
36
+ const { width: m, height: y } = n.getBoundingClientRect();
37
+ m > 0 && y > 0 && a({ width: m, height: y });
38
38
  const u = new ResizeObserver((v) => {
39
- const y = v[0];
40
- if (!y) return;
41
- const { width: s, height: o } = y.contentRect;
39
+ const x = v[0];
40
+ if (!x) return;
41
+ const { width: s, height: o } = x.contentRect;
42
42
  s > 0 && o > 0 && a((g) => g.width === s && g.height === o ? g : { width: s, height: o });
43
43
  });
44
44
  return u.observe(n), () => {
@@ -46,30 +46,30 @@ function oe(e) {
46
46
  };
47
47
  }, [e]), i;
48
48
  }
49
- function vt(e, i) {
49
+ function St(e, i) {
50
50
  return Math.sqrt(Math.pow(i.x - e.x, 2) + Math.pow(i.y - e.y, 2));
51
51
  }
52
- function St(e, i) {
52
+ function wt(e, i) {
53
53
  return {
54
54
  x: (e.x + i.x) / 2,
55
55
  y: (e.y + i.y) / 2
56
56
  };
57
57
  }
58
- function ie(e, i) {
59
- const a = F(null), n = F(null), x = F(1);
58
+ function re(e, i) {
59
+ const a = F(null), n = F(null), m = F(1);
60
60
  T(() => {
61
- const m = e.current;
62
- if (!m || !i.enabled) return;
63
- const u = m.container(), v = (o) => {
61
+ const y = e.current;
62
+ if (!y || !i.enabled) return;
63
+ const u = y.container(), v = (o) => {
64
64
  if (o.touches.length === 2) {
65
65
  o.preventDefault();
66
66
  const g = { x: o.touches[0].clientX, y: o.touches[0].clientY }, l = { x: o.touches[1].clientX, y: o.touches[1].clientY };
67
- a.current = vt(g, l), n.current = St(g, l), x.current = i.currentScale;
67
+ a.current = St(g, l), n.current = wt(g, l), m.current = i.currentScale;
68
68
  }
69
- }, y = (o) => {
69
+ }, x = (o) => {
70
70
  if (o.touches.length !== 2) return;
71
71
  o.preventDefault();
72
- const g = { x: o.touches[0].clientX, y: o.touches[0].clientY }, l = { x: o.touches[1].clientX, y: o.touches[1].clientY }, k = vt(g, l), C = St(g, l);
72
+ const g = { x: o.touches[0].clientX, y: o.touches[0].clientY }, l = { x: o.touches[1].clientX, y: o.touches[1].clientY }, k = St(g, l), C = wt(g, l);
73
73
  if (a.current !== null && n.current !== null) {
74
74
  const D = k / a.current, P = Math.min(
75
75
  Math.max(i.currentScale * D, i.minScale),
@@ -86,12 +86,12 @@ function ie(e, i) {
86
86
  }, s = (o) => {
87
87
  o.touches.length < 2 && (a.current = null, n.current = null);
88
88
  };
89
- return u.addEventListener("touchstart", v, { passive: !1 }), u.addEventListener("touchmove", y, { passive: !1 }), u.addEventListener("touchend", s), () => {
90
- u.removeEventListener("touchstart", v), u.removeEventListener("touchmove", y), u.removeEventListener("touchend", s);
89
+ return u.addEventListener("touchstart", v, { passive: !1 }), u.addEventListener("touchmove", x, { passive: !1 }), u.addEventListener("touchend", s), () => {
90
+ u.removeEventListener("touchstart", v), u.removeEventListener("touchmove", x), u.removeEventListener("touchend", s);
91
91
  };
92
92
  }, [e, i]);
93
93
  }
94
- const re = {
94
+ const se = {
95
95
  canvasBackground: "#1a1a1a",
96
96
  stageColor: "#808080",
97
97
  seatAvailable: "#2C2B30",
@@ -101,8 +101,8 @@ const re = {
101
101
  seatHidden: "#4a4a4a",
102
102
  gridLines: "#404040",
103
103
  currency: "KD"
104
- }, Ct = U(({ seat: e, state: i, colors: a, onClick: n, onMouseEnter: x, onMouseLeave: m }) => {
105
- const y = {
104
+ }, Mt = Z(({ seat: e, state: i, colors: a, onClick: n, onMouseEnter: m, onMouseLeave: y }) => {
105
+ const x = {
106
106
  available: a.seatAvailable,
107
107
  reserved: a.seatReserved,
108
108
  selected: a.seatSelected,
@@ -112,17 +112,17 @@ const re = {
112
112
  }[i], s = i === "available" || i === "selected", o = N(() => {
113
113
  s && n(e);
114
114
  }, [e, n, s]), g = N((C) => {
115
- x(e, C);
115
+ m(e, C);
116
116
  const D = C.target.getStage();
117
117
  D && s && (D.container().style.cursor = "pointer");
118
- }, [e, x, s]), l = N((C) => {
119
- m();
118
+ }, [e, m, s]), l = N((C) => {
119
+ y();
120
120
  const D = C.target.getStage();
121
121
  D && (D.container().style.cursor = "grab");
122
- }, [m]), k = {
122
+ }, [y]), k = {
123
123
  x: e.position.x,
124
124
  y: e.position.y,
125
- fill: y,
125
+ fill: x,
126
126
  stroke: "#ffffff",
127
127
  strokeWidth: 1,
128
128
  onClick: o,
@@ -131,13 +131,13 @@ const re = {
131
131
  onMouseLeave: l
132
132
  };
133
133
  return e.shape === "circle" ? /* @__PURE__ */ h(
134
- ee,
134
+ ne,
135
135
  {
136
136
  ...k,
137
137
  radius: 12
138
138
  }
139
139
  ) : /* @__PURE__ */ h(
140
- wt,
140
+ Ct,
141
141
  {
142
142
  ...k,
143
143
  width: 24,
@@ -148,9 +148,9 @@ const re = {
148
148
  }
149
149
  );
150
150
  });
151
- Ct.displayName = "ViewerSeat";
152
- const Mt = U(({ stage: e, stageColor: i }) => {
153
- const a = (m) => ({
151
+ Mt.displayName = "ViewerSeat";
152
+ const kt = Z(({ stage: e, stageColor: i }) => {
153
+ const a = (y) => ({
154
154
  stage: "🎭",
155
155
  table: "⬜",
156
156
  wall: "▬",
@@ -159,10 +159,10 @@ const Mt = U(({ stage: e, stageColor: i }) => {
159
159
  bar: "🍷",
160
160
  "entry-exit": "🚪",
161
161
  custom: "➕"
162
- })[m || "stage"] || "🎭", n = e.config.color || i, x = a(e.config.objectType);
163
- return /* @__PURE__ */ w(te, { x: e.position.x, y: e.position.y, rotation: e.config.rotation || 0, children: [
162
+ })[y || "stage"] || "🎭", n = e.config.color || i, m = a(e.config.objectType);
163
+ return /* @__PURE__ */ w(ee, { x: e.position.x, y: e.position.y, rotation: e.config.rotation || 0, children: [
164
164
  /* @__PURE__ */ h(
165
- wt,
165
+ Ct,
166
166
  {
167
167
  width: e.config.width,
168
168
  height: e.config.height,
@@ -173,9 +173,9 @@ const Mt = U(({ stage: e, stageColor: i }) => {
173
173
  }
174
174
  ),
175
175
  /* @__PURE__ */ h(
176
- bt,
176
+ vt,
177
177
  {
178
- text: x,
178
+ text: m,
179
179
  x: 0,
180
180
  y: 0,
181
181
  width: e.config.width,
@@ -187,7 +187,7 @@ const Mt = U(({ stage: e, stageColor: i }) => {
187
187
  }
188
188
  ),
189
189
  /* @__PURE__ */ h(
190
- bt,
190
+ vt,
191
191
  {
192
192
  text: e.config.label,
193
193
  x: 0,
@@ -203,14 +203,14 @@ const Mt = U(({ stage: e, stageColor: i }) => {
203
203
  )
204
204
  ] });
205
205
  });
206
- Mt.displayName = "ViewerStage";
207
- const kt = U(({
206
+ kt.displayName = "ViewerStage";
207
+ const It = Z(({
208
208
  floors: e,
209
209
  currentFloorId: i,
210
210
  onFloorChange: a,
211
211
  showAllOption: n,
212
- allLabel: x,
213
- position: m,
212
+ allLabel: m,
213
+ position: y,
214
214
  className: u
215
215
  }) => {
216
216
  const v = Y(
@@ -231,7 +231,7 @@ const kt = U(({
231
231
  "top-right": { top: 0, right: 0 },
232
232
  "bottom-left": { bottom: 0, left: 0 },
233
233
  "bottom-right": { bottom: 0, right: 0 }
234
- }[m]
234
+ }[y]
235
235
  }, o = {
236
236
  padding: "10px 16px",
237
237
  fontSize: "14px",
@@ -256,7 +256,7 @@ const kt = U(({
256
256
  type: "button",
257
257
  onClick: () => a(null),
258
258
  style: i === null ? g : o,
259
- children: x
259
+ children: m
260
260
  }
261
261
  ),
262
262
  v.map((l) => /* @__PURE__ */ h(
@@ -271,17 +271,17 @@ const kt = U(({
271
271
  ))
272
272
  ] });
273
273
  });
274
- kt.displayName = "FloorSelectorBar";
275
- const It = U(({
274
+ It.displayName = "FloorSelectorBar";
275
+ const Nt = Z(({
276
276
  scale: e,
277
277
  minScale: i,
278
278
  maxScale: a,
279
279
  onZoomIn: n,
280
- onZoomOut: x,
281
- position: m,
280
+ onZoomOut: m,
281
+ position: y,
282
282
  className: u
283
283
  }) => {
284
- const y = {
284
+ const x = {
285
285
  position: "absolute",
286
286
  display: "flex",
287
287
  flexDirection: "column",
@@ -296,7 +296,7 @@ const It = U(({
296
296
  "top-right": { top: 0, right: 0 },
297
297
  "bottom-left": { bottom: 0, left: 0 },
298
298
  "bottom-right": { bottom: 0, right: 0 }
299
- }[m]
299
+ }[y]
300
300
  }, s = {
301
301
  width: "44px",
302
302
  height: "44px",
@@ -319,7 +319,7 @@ const It = U(({
319
319
  opacity: 0.4,
320
320
  cursor: "not-allowed"
321
321
  }, g = e < a, l = e > i;
322
- return /* @__PURE__ */ w("div", { className: u, style: y, children: [
322
+ return /* @__PURE__ */ w("div", { className: u, style: x, children: [
323
323
  /* @__PURE__ */ h(
324
324
  "button",
325
325
  {
@@ -335,7 +335,7 @@ const It = U(({
335
335
  "button",
336
336
  {
337
337
  type: "button",
338
- onClick: x,
338
+ onClick: m,
339
339
  disabled: !l,
340
340
  style: l ? s : o,
341
341
  title: "Zoom Out",
@@ -344,14 +344,14 @@ const It = U(({
344
344
  )
345
345
  ] });
346
346
  });
347
- It.displayName = "ZoomControls";
348
- const Nt = U(({
347
+ Nt.displayName = "ZoomControls";
348
+ const Dt = Z(({
349
349
  visible: e,
350
350
  x: i,
351
351
  y: a,
352
352
  seat: n,
353
- currency: x,
354
- state: m
353
+ currency: m,
354
+ state: y
355
355
  }) => {
356
356
  if (!e || !n) return null;
357
357
  const u = n.seatNumber || (n.rowLabel && n.columnLabel ? `${n.rowLabel}-${n.columnLabel}` : "N/A"), v = {
@@ -360,7 +360,7 @@ const Nt = U(({
360
360
  top: `${a + 15}px`,
361
361
  zIndex: 1e3,
362
362
  pointerEvents: "none"
363
- }, y = {
363
+ }, x = {
364
364
  backgroundColor: "rgba(26, 26, 26, 0.95)",
365
365
  color: "#fff",
366
366
  border: "1px solid #444",
@@ -383,7 +383,7 @@ const Nt = U(({
383
383
  textTransform: "capitalize",
384
384
  marginTop: "4px"
385
385
  };
386
- return /* @__PURE__ */ h("div", { style: v, children: /* @__PURE__ */ w("div", { style: y, children: [
386
+ return /* @__PURE__ */ h("div", { style: v, children: /* @__PURE__ */ w("div", { style: x, children: [
387
387
  n.sectionName && /* @__PURE__ */ w("div", { style: { marginBottom: "4px" }, children: [
388
388
  /* @__PURE__ */ h("span", { style: s, children: "Section:" }),
389
389
  /* @__PURE__ */ h("span", { style: { ...o, color: "#3b82f6" }, children: n.sectionName })
@@ -392,31 +392,31 @@ const Nt = U(({
392
392
  /* @__PURE__ */ h("span", { style: s, children: "Seat:" }),
393
393
  /* @__PURE__ */ h("span", { style: o, children: u })
394
394
  ] }),
395
- n.price !== void 0 && n.price > 0 && /* @__PURE__ */ w("div", { style: { marginBottom: "4px" }, children: [
395
+ n.price !== void 0 && n.price > 0 && y === "available" && /* @__PURE__ */ w("div", { style: { marginBottom: "4px" }, children: [
396
396
  /* @__PURE__ */ h("span", { style: s, children: "Price:" }),
397
397
  /* @__PURE__ */ w("span", { style: g, children: [
398
- x,
398
+ m,
399
399
  " ",
400
400
  n.price.toFixed(2)
401
401
  ] })
402
402
  ] }),
403
403
  /* @__PURE__ */ w("div", { style: l, children: [
404
404
  "Status: ",
405
- m
405
+ y
406
406
  ] })
407
407
  ] }) });
408
408
  });
409
- Nt.displayName = "SeatTooltip";
410
- const le = ({
409
+ Dt.displayName = "SeatTooltip";
410
+ const he = ({
411
411
  config: e,
412
412
  configUrl: i,
413
413
  floorId: a,
414
414
  onFloorChange: n,
415
- reservedSeats: x = [],
416
- unavailableSeats: m = [],
415
+ reservedSeats: m = [],
416
+ unavailableSeats: y = [],
417
417
  selectedSeats: u,
418
418
  onSeatSelect: v,
419
- onSeatDeselect: y,
419
+ onSeatDeselect: x,
420
420
  onSelectionChange: s,
421
421
  colorOverrides: o,
422
422
  showTooltip: g = !0,
@@ -435,16 +435,17 @@ const le = ({
435
435
  // Zoom controls
436
436
  showZoomControls: at = !0,
437
437
  zoomControlsPosition: lt = "bottom-right",
438
- zoomControlsClassName: Dt,
438
+ zoomControlsClassName: Rt,
439
+ minZoom: gt,
439
440
  maxZoom: L = 3,
440
441
  zoomStep: et = 0.25,
441
442
  // Touch gestures
442
- touchEnabled: Rt = !0
443
+ touchEnabled: Et = !0
443
444
  }) => {
444
- const ht = F(null), gt = F(null), S = oe(gt), [j, yt] = E(/* @__PURE__ */ new Set()), [M, H] = E(1), [I, W] = E({ x: 0, y: 0 }), [Et, Lt] = E(null), [Xt, Yt] = E(1), nt = F({ width: 0, height: 0 }), [O, xt] = E({ visible: !1, x: 0, y: 0, seat: null, state: "available" }), { config: Tt, loading: Wt, error: V } = ne(i), c = e || Tt, dt = a !== void 0, R = dt ? a || null : Et, ot = u !== void 0, Pt = N((t) => {
445
- dt || Lt(t), n?.(t);
446
- }, [dt, n]), ut = c?.floors || [], jt = P !== void 0 ? P : ut.length > 1, it = Y(
447
- () => c ? { ...c.colors, ...o } : { ...re, ...o },
445
+ const ht = F(null), yt = F(null), S = ie(yt), [j, xt] = E(/* @__PURE__ */ new Set()), [M, H] = E(1), [I, W] = E({ x: 0, y: 0 }), [Lt, Xt] = E(null), [Yt, Tt] = E(1), nt = F({ width: 0, height: 0 }), [O, mt] = E({ visible: !1, x: 0, y: 0, seat: null, state: "available" }), { config: Wt, loading: Pt, error: V } = oe(i), c = e || Wt, dt = a !== void 0, R = dt ? a || null : Lt, ot = u !== void 0, jt = N((t) => {
446
+ dt || Xt(t), n?.(t);
447
+ }, [dt, n]), ut = c?.floors || [], At = P !== void 0 ? P : ut.length > 1, it = Y(
448
+ () => c ? { ...c.colors, ...o } : { ...se, ...o },
448
449
  [c, o]
449
450
  ), A = Y(() => {
450
451
  if (!c) return [];
@@ -470,37 +471,37 @@ const le = ({
470
471
  const t = Math.abs(S.width - nt.current.width), r = Math.abs(S.height - nt.current.height);
471
472
  if (!(nt.current.width === 0) && t < 10 && r < 10) return;
472
473
  nt.current = S;
473
- const d = S.width, p = S.height, b = d - $ * 2, G = p - $ * 2, st = b / X.width, pt = G / X.height, ct = Math.min(st, pt, L), _t = X.minX + X.width / 2, qt = X.minY + X.height / 2, Gt = d / 2, Ut = p / 2, Kt = Gt - _t * ct, Jt = Ut - qt * ct;
474
- H(ct), W({ x: Kt, y: Jt }), Yt(ct);
474
+ const d = S.width, p = S.height, b = d - $ * 2, U = p - $ * 2, st = b / X.width, pt = U / X.height, ct = Math.min(st, pt, L), Gt = X.minX + X.width / 2, Ut = X.minY + X.height / 2, Zt = d / 2, Kt = p / 2, Jt = Zt - Gt * ct, Qt = Kt - Ut * ct;
475
+ H(ct), W({ x: Jt, y: Qt }), Tt(ct);
475
476
  }, [z, c, X, $, L, S, R]);
476
- const Z = Y(() => {
477
- const t = new Set(x), r = new Set(m);
477
+ const _ = Y(() => {
478
+ const t = new Set(m), r = new Set(y);
478
479
  return { reserved: t, unavailable: r };
479
- }, [x, m]), ft = Y(() => u ? new Set(u) : null, [u]), _ = N((t) => {
480
+ }, [m, y]), ft = Y(() => u ? new Set(u) : null, [u]), q = N((t) => {
480
481
  const r = t.id, f = t.seatNumber || "";
481
- return Z.unavailable.has(r) || Z.unavailable.has(f) ? "unavailable" : Z.reserved.has(r) || Z.reserved.has(f) ? "reserved" : j.has(r) ? "selected" : t.state;
482
- }, [Z, j]);
482
+ return _.unavailable.has(r) || _.unavailable.has(f) ? "unavailable" : _.reserved.has(r) || _.reserved.has(f) ? "reserved" : j.has(r) ? "selected" : t.state;
483
+ }, [_, j]);
483
484
  T(() => {
484
485
  c && C && C(c);
485
486
  }, [c, C]), T(() => {
486
487
  V && D && D(V);
487
488
  }, [V, D]), T(() => {
488
- ot && ft && yt(ft);
489
+ ot && ft && xt(ft);
489
490
  }, [ot, ft]);
490
- const At = N((t) => {
491
- const r = _(t);
491
+ const Bt = N((t) => {
492
+ const r = q(t);
492
493
  if (r !== "available" && r !== "selected")
493
494
  return;
494
495
  const f = j.has(t.id);
495
- ot || yt((d) => {
496
+ ot || xt((d) => {
496
497
  const p = new Set(d);
497
498
  return f ? p.delete(t.id) : p.add(t.id), p;
498
- }), f ? y?.(t) : (v?.(t), v || console.log("Seat selected:", t));
499
- }, [_, j, ot, v, y]), q = Y(() => c ? A.filter((t) => j.has(t.id)) : [], [A, j]);
499
+ }), f ? x?.(t) : (v?.(t), v || console.log("Seat selected:", t));
500
+ }, [q, j, ot, v, x]), G = Y(() => c ? A.filter((t) => j.has(t.id)) : [], [A, j]);
500
501
  T(() => {
501
- s?.(q);
502
- }, [q, s]);
503
- const B = Xt, Bt = N(() => {
502
+ s?.(G);
503
+ }, [G, s]);
504
+ const B = gt !== void 0 ? gt : Yt, Ft = N(() => {
504
505
  if (!l) return;
505
506
  const t = Math.min(M + et, L);
506
507
  if (t !== M) {
@@ -513,7 +514,7 @@ const le = ({
513
514
  y: p - b.y * t
514
515
  });
515
516
  }
516
- }, [l, M, et, L, S, c, I]), Ft = N(() => {
517
+ }, [l, M, et, L, S, c, I]), zt = N(() => {
517
518
  if (!l) return;
518
519
  const t = Math.max(M - et, B);
519
520
  if (t !== M) {
@@ -526,29 +527,29 @@ const le = ({
526
527
  y: p - b.y * t
527
528
  });
528
529
  }
529
- }, [l, M, et, B, S, c, I]), zt = N((t) => {
530
+ }, [l, M, et, B, S, c, I]), $t = N((t) => {
530
531
  W({
531
532
  x: t.target.x(),
532
533
  y: t.target.y()
533
534
  });
534
- }, []), $t = N((t) => {
535
+ }, []), Ht = N((t) => {
535
536
  if (!l) return;
536
537
  t.evt.preventDefault();
537
538
  const r = ht.current;
538
539
  if (!r) return;
539
540
  const f = r.scaleX(), d = r.getPointerPosition();
540
541
  if (!d) return;
541
- const p = 1.1, b = t.evt.deltaY > 0 ? f / p : f * p, G = Math.min(Math.max(b, B), L), st = {
542
+ const p = 1.1, b = t.evt.deltaY > 0 ? f / p : f * p, U = Math.min(Math.max(b, B), L), st = {
542
543
  x: (d.x - I.x) / f,
543
544
  y: (d.y - I.y) / f
544
545
  }, pt = {
545
- x: d.x - st.x * G,
546
- y: d.y - st.y * G
546
+ x: d.x - st.x * U,
547
+ y: d.y - st.y * U
547
548
  };
548
- H(G), W(pt);
549
+ H(U), W(pt);
549
550
  }, [l, I, B, L]);
550
- ie(ht, {
551
- enabled: Rt && l,
551
+ re(ht, {
552
+ enabled: Et && l,
552
553
  minScale: B,
553
554
  maxScale: L,
554
555
  currentScale: M,
@@ -560,24 +561,24 @@ const le = ({
560
561
  W(t);
561
562
  }
562
563
  });
563
- const Ht = N((t, r) => {
564
+ const Ot = N((t, r) => {
564
565
  if (!g) return;
565
566
  const f = r.target.getStage();
566
567
  if (!f) return;
567
568
  const d = f.getPointerPosition();
568
569
  if (!d) return;
569
570
  const p = f.container().getBoundingClientRect();
570
- xt({
571
+ mt({
571
572
  visible: !0,
572
573
  x: p.left + d.x,
573
574
  y: p.top + d.y,
574
575
  seat: t,
575
- state: _(t)
576
+ state: q(t)
576
577
  });
577
- }, [g, _]), Ot = N(() => {
578
- xt((t) => ({ ...t, visible: !1 }));
578
+ }, [g, q]), Vt = N(() => {
579
+ mt((t) => ({ ...t, visible: !1 }));
579
580
  }, []);
580
- if (Wt)
581
+ if (Pt)
581
582
  return /* @__PURE__ */ h("div", { className: `flex items-center justify-center h-full ${k}`, children: /* @__PURE__ */ h("p", { children: "Loading seat map..." }) });
582
583
  if (V)
583
584
  return /* @__PURE__ */ h("div", { className: `flex items-center justify-center h-full ${k}`, children: /* @__PURE__ */ w("p", { className: "text-red-500", children: [
@@ -586,20 +587,20 @@ const le = ({
586
587
  ] }) });
587
588
  if (!c)
588
589
  return /* @__PURE__ */ h("div", { className: `flex items-center justify-center h-full ${k}`, children: /* @__PURE__ */ h("p", { children: "No configuration provided" }) });
589
- const Vt = S.width || c.canvas.width, Zt = S.height || c.canvas.height;
590
+ const _t = S.width || c.canvas.width, qt = S.height || c.canvas.height;
590
591
  return /* @__PURE__ */ w(
591
592
  "div",
592
593
  {
593
- ref: gt,
594
+ ref: yt,
594
595
  className: `relative ${k}`,
595
596
  style: { width: "100%", height: "100%" },
596
597
  children: [
597
- jt && ut.length > 0 && /* @__PURE__ */ h(
598
- kt,
598
+ At && ut.length > 0 && /* @__PURE__ */ h(
599
+ It,
599
600
  {
600
601
  floors: ut,
601
602
  currentFloorId: R,
602
- onFloorChange: Pt,
603
+ onFloorChange: jt,
603
604
  showAllOption: Q,
604
605
  allLabel: tt,
605
606
  position: K,
@@ -607,37 +608,37 @@ const le = ({
607
608
  }
608
609
  ),
609
610
  /* @__PURE__ */ w(
610
- Qt,
611
+ te,
611
612
  {
612
613
  ref: ht,
613
- width: Vt,
614
- height: Zt,
614
+ width: _t,
615
+ height: qt,
615
616
  scaleX: M,
616
617
  scaleY: M,
617
618
  x: I.x,
618
619
  y: I.y,
619
620
  draggable: !0,
620
- onDragEnd: zt,
621
- onWheel: $t,
621
+ onDragEnd: $t,
622
+ onWheel: Ht,
622
623
  style: { backgroundColor: c.canvas.backgroundColor, cursor: "grab" },
623
624
  children: [
624
- /* @__PURE__ */ h(mt, { listening: !1, children: rt.map((t) => /* @__PURE__ */ h(
625
- Mt,
625
+ /* @__PURE__ */ h(bt, { listening: !1, children: rt.map((t) => /* @__PURE__ */ h(
626
+ kt,
626
627
  {
627
628
  stage: t,
628
629
  stageColor: it.stageColor
629
630
  },
630
631
  t.id
631
632
  )) }),
632
- /* @__PURE__ */ h(mt, { children: A.map((t) => /* @__PURE__ */ h(
633
- Ct,
633
+ /* @__PURE__ */ h(bt, { children: A.map((t) => /* @__PURE__ */ h(
634
+ Mt,
634
635
  {
635
636
  seat: t,
636
- state: _(t),
637
+ state: q(t),
637
638
  colors: it,
638
- onClick: At,
639
- onMouseEnter: Ht,
640
- onMouseLeave: Ot
639
+ onClick: Bt,
640
+ onMouseEnter: Ot,
641
+ onMouseLeave: Vt
641
642
  },
642
643
  t.id
643
644
  )) })
@@ -645,7 +646,7 @@ const le = ({
645
646
  }
646
647
  ),
647
648
  g && /* @__PURE__ */ h(
648
- Nt,
649
+ Dt,
649
650
  {
650
651
  visible: O.visible,
651
652
  x: O.x,
@@ -656,24 +657,24 @@ const le = ({
656
657
  }
657
658
  ),
658
659
  at && l && /* @__PURE__ */ h(
659
- It,
660
+ Nt,
660
661
  {
661
662
  scale: M,
662
663
  minScale: B,
663
664
  maxScale: L,
664
- onZoomIn: Bt,
665
- onZoomOut: Ft,
665
+ onZoomIn: Ft,
666
+ onZoomOut: zt,
666
667
  position: lt,
667
- className: Dt
668
+ className: Rt
668
669
  }
669
670
  ),
670
- q.length > 0 && /* @__PURE__ */ w("div", { className: "absolute top-4 right-4 bg-white dark:bg-gray-800 p-4 rounded shadow-lg", children: [
671
+ G.length > 0 && /* @__PURE__ */ w("div", { className: "absolute top-4 right-4 bg-white dark:bg-gray-800 p-4 rounded shadow-lg", children: [
671
672
  /* @__PURE__ */ w("h3", { className: "font-semibold mb-2", children: [
672
673
  "Selected Seats (",
673
- q.length,
674
+ G.length,
674
675
  ")"
675
676
  ] }),
676
- /* @__PURE__ */ h("div", { className: "max-h-48 overflow-y-auto space-y-1", children: q.map((t) => /* @__PURE__ */ w("div", { className: "text-sm", children: [
677
+ /* @__PURE__ */ h("div", { className: "max-h-48 overflow-y-auto space-y-1", children: G.map((t) => /* @__PURE__ */ w("div", { className: "text-sm", children: [
677
678
  t.seatNumber,
678
679
  t.price && ` - ${it.currency} ${t.price.toFixed(2)}`
679
680
  ] }, t.id)) })
@@ -683,9 +684,9 @@ const le = ({
683
684
  );
684
685
  };
685
686
  export {
686
- re as DEFAULT_COLORS,
687
- le as SeatMapViewer,
688
- ne as useConfigFetcher,
689
- oe as useContainerSize,
690
- ie as useTouchGestures
687
+ se as DEFAULT_COLORS,
688
+ he as SeatMapViewer,
689
+ oe as useConfigFetcher,
690
+ ie as useContainerSize,
691
+ re as useTouchGestures
691
692
  };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@zonetrix/viewer",
3
- "version": "2.7.0",
3
+ "version": "2.8.1",
4
4
  "type": "module",
5
5
  "description": "Lightweight React component for rendering interactive seat maps",
6
6
  "main": "./dist/index.js",