UNPKG

react-svg-seatmap

Version:

React components that render a highly-customizable seatmap based on a provided SVG

23 lines (22 loc) 16.2 kB
(function(L,i){typeof exports=="object"&&typeof module<"u"?i(exports,require("react"),require("svg-pan-zoom"),require("lodash"),require("react-selecto")):typeof define=="function"&&define.amd?define(["exports","react","svg-pan-zoom","lodash","react-selecto"],i):(L=typeof globalThis<"u"?globalThis:L||self,i(L.index={},L.React,L.svgPanZoom,L.lodash,L.Selecto))})(this,function(L,i,se,ne,oe){"use strict";var W={exports:{}},q={};/** * @license React * react-jsx-runtime.production.js * * Copyright (c) Meta Platforms, Inc. and affiliates. * * This source code is licensed under the MIT license found in the * LICENSE file in the root directory of this source tree. */var B;function ae(){if(B)return q;B=1;var c=Symbol.for("react.transitional.element"),m=Symbol.for("react.fragment");function b(u,v,E){var y=null;if(E!==void 0&&(y=""+E),v.key!==void 0&&(y=""+v.key),"key"in v){E={};for(var j in v)j!=="key"&&(E[j]=v[j])}else E=v;return v=E.ref,{$$typeof:c,type:u,key:y,ref:v!==void 0?v:null,props:E}}return q.Fragment=m,q.jsx=b,q.jsxs=b,q}var U={};/** * @license React * react-jsx-runtime.development.js * * Copyright (c) Meta Platforms, Inc. and affiliates. * * This source code is licensed under the MIT license found in the * LICENSE file in the root directory of this source tree. */var J;function ce(){return J||(J=1,process.env.NODE_ENV!=="production"&&function(){function c(e){if(e==null)return null;if(typeof e=="function")return e.$$typeof===r?null:e.displayName||e.name||null;if(typeof e=="string")return e;switch(e){case N:return"Fragment";case R:return"Profiler";case I:return"StrictMode";case w:return"Suspense";case D:return"SuspenseList";case t:return"Activity"}if(typeof e=="object")switch(typeof e.tag=="number"&&console.error("Received an unexpected object in getComponentNameFromType(). This is likely a bug in React. Please file an issue."),e.$$typeof){case Y:return"Portal";case C:return(e.displayName||"Context")+".Provider";case z:return(e._context.displayName||"Context")+".Consumer";case A:var o=e.render;return e=e.displayName,e||(e=o.displayName||o.name||"",e=e!==""?"ForwardRef("+e+")":"ForwardRef"),e;case s:return o=e.displayName||null,o!==null?o:c(e.type)||"Memo";case d:o=e._payload,e=e._init;try{return c(e(o))}catch{}}return null}function m(e){return""+e}function b(e){try{m(e);var o=!1}catch{o=!0}if(o){o=console;var _=o.error,g=typeof Symbol=="function"&&Symbol.toStringTag&&e[Symbol.toStringTag]||e.constructor.name||"Object";return _.call(o,"The provided key is an unsupported type %s. This value must be coerced to a string before using it here.",g),m(e)}}function u(e){if(e===N)return"<>";if(typeof e=="object"&&e!==null&&e.$$typeof===d)return"<...>";try{var o=c(e);return o?"<"+o+">":"<...>"}catch{return"<...>"}}function v(){var e=a.A;return e===null?null:e.getOwner()}function E(){return Error("react-stack-top-frame")}function y(e){if(n.call(e,"key")){var o=Object.getOwnPropertyDescriptor(e,"key").get;if(o&&o.isReactWarning)return!1}return e.key!==void 0}function j(e,o){function _(){K||(K=!0,console.error("%s: `key` is not a prop. Trying to access it will result in `undefined` being returned. If you need to access the same value within the child component, you should pass it as a different prop. (https://react.dev/link/special-props)",o))}_.isReactWarning=!0,Object.defineProperty(e,"key",{get:_,configurable:!0})}function T(){var e=c(this.type);return $[e]||($[e]=!0,console.error("Accessing element.ref was removed in React 19. ref is now a regular prop. It will be removed from the JSX Element type in a future release.")),e=this.props.ref,e!==void 0?e:null}function x(e,o,_,g,F,Z,H,G){return _=Z.ref,e={$$typeof:h,type:e,key:o,props:Z,_owner:F},(_!==void 0?_:null)!==null?Object.defineProperty(e,"ref",{enumerable:!1,get:T}):Object.defineProperty(e,"ref",{enumerable:!1,value:null}),e._store={},Object.defineProperty(e._store,"validated",{configurable:!1,enumerable:!1,writable:!0,value:0}),Object.defineProperty(e,"_debugInfo",{configurable:!1,enumerable:!1,writable:!0,value:null}),Object.defineProperty(e,"_debugStack",{configurable:!1,enumerable:!1,writable:!0,value:H}),Object.defineProperty(e,"_debugTask",{configurable:!1,enumerable:!1,writable:!0,value:G}),Object.freeze&&(Object.freeze(e.props),Object.freeze(e)),e}function S(e,o,_,g,F,Z,H,G){var k=o.children;if(k!==void 0)if(g)if(O(k)){for(g=0;g<k.length;g++)f(k[g]);Object.freeze&&Object.freeze(k)}else console.error("React.jsx: Static children should always be an array. You are likely explicitly calling React.jsxs or React.jsxDEV. Use the Babel transform instead.");else f(k);if(n.call(o,"key")){k=c(e);var V=Object.keys(o).filter(function(de){return de!=="key"});g=0<V.length?"{key: someKey, "+V.join(": ..., ")+": ...}":"{key: someKey}",re[k+g]||(V=0<V.length?"{"+V.join(": ..., ")+": ...}":"{}",console.error(`A props object containing a "key" prop is being spread into JSX: let props = %s; <%s {...props} /> React keys must be passed directly to JSX without using spread: let props = %s; <%s key={someKey} {...props} />`,g,k,V,k),re[k+g]=!0)}if(k=null,_!==void 0&&(b(_),k=""+_),y(o)&&(b(o.key),k=""+o.key),"key"in o){_={};for(var M in o)M!=="key"&&(_[M]=o[M])}else _=o;return k&&j(_,typeof e=="function"?e.displayName||e.name||"Unknown":e),x(e,k,Z,F,v(),_,H,G)}function f(e){typeof e=="object"&&e!==null&&e.$$typeof===h&&e._store&&(e._store.validated=1)}var p=i,h=Symbol.for("react.transitional.element"),Y=Symbol.for("react.portal"),N=Symbol.for("react.fragment"),I=Symbol.for("react.strict_mode"),R=Symbol.for("react.profiler"),z=Symbol.for("react.consumer"),C=Symbol.for("react.context"),A=Symbol.for("react.forward_ref"),w=Symbol.for("react.suspense"),D=Symbol.for("react.suspense_list"),s=Symbol.for("react.memo"),d=Symbol.for("react.lazy"),t=Symbol.for("react.activity"),r=Symbol.for("react.client.reference"),a=p.__CLIENT_INTERNALS_DO_NOT_USE_OR_WARN_USERS_THEY_CANNOT_UPGRADE,n=Object.prototype.hasOwnProperty,O=Array.isArray,P=console.createTask?console.createTask:function(){return null};p={"react-stack-bottom-frame":function(e){return e()}};var K,$={},ee=p["react-stack-bottom-frame"].bind(p,E)(),te=P(u(E)),re={};U.Fragment=N,U.jsx=function(e,o,_,g,F){var Z=1e4>a.recentlyCreatedOwnerStacks++;return S(e,o,_,!1,g,F,Z?Error("react-stack-top-frame"):ee,Z?P(u(e)):te)},U.jsxs=function(e,o,_,g,F){var Z=1e4>a.recentlyCreatedOwnerStacks++;return S(e,o,_,!0,g,F,Z?Error("react-stack-top-frame"):ee,Z?P(u(e)):te)}}()),U}var X;function le(){return X||(X=1,process.env.NODE_ENV==="production"?W.exports=ae():W.exports=ce()),W.exports}var l=le();const ie=c=>{const m=i.useRef(void 0);return i.useEffect(()=>{m.current=c}),m.current},ue=({availableSeats:c,selectedSeatIds:m,svg:b,onSeatSelect:u,onSeatDeselect:v,onSeatHover:E,onSeatHoverEnd:y,allowDragAndPan:j=!0,showZoomControls:T=!0,leftControls:x,rightControls:S})=>{const[f,p]=i.useState(""),[h,Y]=i.useState(!1),[N,I]=i.useState(!1),R=ie({svgString:f,selectedSeatIds:m,availableSeats:c}),z=i.useCallback(t=>m&&m.includes(t.id),[m]),C=i.useCallback(t=>c.find(r=>{try{return t.matches(r.cssSelector)}catch{return!1}}),[c]),A=i.useCallback(()=>se(".seatmap__svg svg",{dblClickZoomEnabled:!1,mouseWheelZoomEnabled:!0,zoomScaleSensitivity:.5}),[]);i.useEffect(()=>{fetch(b).then(t=>{if(t.ok)return t.text();throw new Error("Unable to fetch SVG")}).then(t=>p(t)).catch(t=>{Y(!0),console.error("Failed to load SVG:",t)})},[b]);const w=i.useCallback((t,r)=>{r?(t.classList.remove("seat--unavailable"),m!=null&&m.includes(r.id)?(t.classList.add("seat--selected"),t.classList.remove("seat--available")):(t.classList.add("seat--available"),t.classList.remove("seat--selected")),r.color&&t.setAttribute("style",`stroke: ${r.color} !important;${z(r)?" fill: "+r.color+" !important;":""}`)):(t.classList.add("seat--unavailable"),t.classList.remove("seat--available"),t.classList.remove("seat--selected"))},[z,m]);i.useEffect(()=>{if(!f||h){console.error("No SVG, exiting");return}N&&(document.querySelectorAll(".seatmap__svg circle, .seatmap__svg path, .seatmap__svg ellipse").forEach(r=>{const a=C(r);w(r,a)}),I(!1))},[N,C,w,f,h]),i.useEffect(()=>{if(!f||h)return;const t=A();t.resize(),t.center(),t.fit()},[f,A,h]),i.useEffect(()=>{if(!f||h)return;const t=A();j?t.enablePan():t.disablePan()},[f,j,A,h]),i.useEffect(()=>{const t=n=>{const O=n.target,P=C(O);P&&(z(P)&&v?v(P):u&&u(P))},r=n=>{const O=n.target,P=C(O);P&&(n.type==="mouseover"&&E?E(P):y&&y(P))},a=document.querySelectorAll(".seatmap__svg circle, .seatmap__svg path, .seatmap__svg ellipse");return a.forEach(n=>{n.removeEventListener("pointerdown",t),n.removeEventListener("mouseover",r),n.removeEventListener("mouseout",r),n.addEventListener("pointerdown",t),n.addEventListener("mouseover",r),n.addEventListener("mouseout",r)}),()=>{a.forEach(n=>{n.removeEventListener("pointerdown",t),n.removeEventListener("mouseover",r),n.removeEventListener("mouseout",r)})}},[v,u,c,f,C,z]),i.useEffect(()=>{if(!f||h)return;const t=c.filter(r=>{const a=R==null?void 0:R.availableSeats.find(n=>n.id===r.id);return!ne.isEqual(r,a)});for(const r of t){const a=document.querySelector(r.cssSelector);a&&w(a,r)}},[c,R==null?void 0:R.availableSeats,w,f,h]),i.useEffect(()=>{let t=m||[];m&&(R!=null&&R.selectedSeatIds)&&(t=m.filter(r=>R.selectedSeatIds&&!R.selectedSeatIds.includes(r)).concat(R.selectedSeatIds.filter(r=>!m.includes(r))));for(const r of t){const a=c.find(O=>O.id===r);if(!a)continue;const n=document.querySelector(a.cssSelector);n&&w(n,a)}},[c,R==null?void 0:R.selectedSeatIds,m,w]);const D=t=>t&&typeof t=="object"&&"style"in t?l.jsx("div",{className:"seatmap__action-group"+(t.style==="none"?" seatmap__action-group--unstyled":""),children:t.control}):l.jsx("div",{className:"seatmap__action-group",children:t}),s=i.useMemo(()=>(I(!0),l.jsx("div",{className:"seatmap__svg",dangerouslySetInnerHTML:{__html:f}})),[f]),d=i.useMemo(()=>{const t=()=>{A().zoomIn()},r=()=>{const n=A();n.resize(),n.center(),n.fit()},a=()=>{A().zoomOut()};return l.jsxs("div",{className:"seatmap",children:[l.jsxs("div",{className:"seatmap__actions seatmap__actions--left",children:[T&&l.jsxs("div",{className:"seatmap__action-group",children:[l.jsx("button",{type:"button",className:"seatmap__action",onClick:t,title:"Zoom In",children:l.jsxs("svg",{className:"seatmap__icon seatmap__icon--zoom-in",viewBox:"0 -960 960 960",children:[l.jsx("title",{children:"Zoom In"}),l.jsx("path",{d:"M440-440H200v-80h240v-240h80v240h240v80H520v240h-80v-240Z"})]})}),l.jsx("button",{type:"button",className:"seatmap__action",onClick:r,title:"Reset Zoom",children:l.jsxs("svg",{className:"seatmap__icon seatmap__icon--reset",viewBox:"0 -960 960 960",children:[l.jsx("title",{children:"Reset zoom"}),l.jsx("path",{d:"M480-160q-134 0-227-93t-93-227q0-134 93-227t227-93q69 0 132 28.5T720-690v-110h80v280H520v-80h168q-32-56-87.5-88T480-720q-100 0-170 70t-70 170q0 100 70 170t170 70q77 0 139-44t87-116h84q-28 106-114 173t-196 67Z"})]})}),l.jsx("button",{type:"button",className:"seatmap__action",onClick:a,title:"Zoom Out",children:l.jsxs("svg",{className:"seatmap__icon seatmap__icon--zoom-out",viewBox:"0 -960 960 960",children:[l.jsx("title",{children:"Zoom Out"}),l.jsx("path",{d:"M200-440v-80h560v80H200Z"})]})})]}),x==null?void 0:x.map(n=>D(n))]}),s,l.jsx("div",{className:"seatmap__actions seatmap__actions--right",children:S==null?void 0:S.map(n=>D(n))})]})},[x,S,A,s,T]);return h?l.jsx("div",{className:"seatmap",children:l.jsxs("div",{className:"seatmap__error",children:["ERROR: Unable to fetch SVG from the given URL: ",b]})}):d},Q=({availableSeats:c,selectedSeatIds:m,svg:b,displayGroupMapping:u,onSeatSelect:v,onSeatDeselect:E,rightControls:y,leftControls:j,withDragSelection:T=!0,withGroupSelection:x=!0})=>{const[S,f]=i.useState(null),[p,h]=i.useState(null),Y=(s,d)=>{var a;const t=(a=s==null?void 0:s.selectionGroups)==null?void 0:a[d];return typeof t=="object"?c.filter(n=>N(n,s,d)&&N(n,s,t.parent)):c.filter(n=>N(n,s,d))},N=(s,d,t)=>{var n,O;let r=(n=s.selectionGroups)==null?void 0:n[t];typeof r=="object"&&(r=r.value);const a=(O=d.selectionGroups)==null?void 0:O[t];return typeof a=="object"?r===a.value:r===a},I=(s,d)=>{const t=c.find(a=>a.id===s.id);if(!t)return;let r=[t];if(S==="group"&&p){const a=Y(t,p);r=[...r,...a]}d&&v?v(r):!d&&E&&E(r)},R=s=>{const d=[];s.added.forEach(t=>{const r=c.find(a=>t.matches(a.cssSelector));r&&(t.classList.add("seat--selected"),t.classList.remove("seat--available"),d.push(r))}),d.length>0&&v&&v(d)},z=s=>{s.added.forEach(d=>{d.classList.add("seat--selected"),d.classList.remove("seat--available")}),s.removed.forEach(d=>{d.classList.remove("seat--selected"),d.classList.add("seat--available")})},C=(s,d)=>{const t=c.find(a=>a.id===s.id);if(!t)return;const r=document.querySelector(t.cssSelector);if(r&&(d?r.classList.add("seat--hover"):r.classList.remove("seat--hover"),S==="group"&&p)){const a=Y(t,p);for(const n of a){const O=document.querySelector(n.cssSelector);O&&(d?O.classList.add("seat--hover"):O.classList.remove("seat--hover"))}}},A=i.useMemo(()=>c.map(s=>({id:s.id,cssSelector:s.cssSelector,color:s.displayGroup&&typeof(u==null?void 0:u[s.displayGroup])=="string"?u==null?void 0:u[s.displayGroup]:void 0,icon:s.displayGroup&&typeof(u==null?void 0:u[s.displayGroup])!="string"?u==null?void 0:u[s.displayGroup]:void 0,selected:!1})),[c,u]),w=i.useMemo(()=>{const s=[];if(x){const d=c.map(r=>{if(r.selectionGroups)return Object.keys(r.selectionGroups)}).flat(),t=Array.from(new Set(d)).filter(r=>r!==void 0);s.push(...t.map(r=>l.jsx("button",{type:"button",className:"seatmap__action"+(S==="group"&&p===r?" seatmap__action--selected":""),onClick:()=>{S==="group"&&p===r?(f(null),h(null)):(f("group"),h(r))},children:Array.from(r)[0]},r)))}return T&&s.push(l.jsx("button",{type:"button",className:"seatmap__action"+(S==="drag"?" seatmap__action--selected":""),onClick:()=>f(S==="drag"?null:"drag"),title:"Lasso select",children:l.jsxs("svg",{className:"seatmap__icon seatmap__icon--zoom-in",viewBox:"0 -960 960 960",children:[l.jsx("title",{children:"Lasso select"}),l.jsx("path",{d:"m161-516-80-8q6-46 20.5-89.5T141-696l68 42q-20 31-31.5 66T161-516Zm36 316q-33-32-57-70.5T101-352l76-26q12 35 31 65.5t45 56.5l-56 56Zm110-552-42-68q39-25 82.5-39.5T437-880l8 80q-37 5-72 16.5T307-752ZM479-82q-35 0-69.5-5.5T343-106l26-76q27 9 54 14.5t56 5.5v80Zm226-626q-26-26-56.5-45T583-784l26-76q43 15 81.5 39t70.5 57l-56 56Zm86 594L679-226v104h-80v-240h240v80H735l112 112-56 56Zm8-368q0-29-5.5-56T779-592l76-26q13 32 18.5 66.5T879-482h-80Z"})]})},"multi-select")),s},[c,p,S,T,x]),D=i.useMemo(()=>{const s=[];return w&&w.length&&s.push(w),j&&j.length&&s.push(...j),s},[j,w]);return l.jsxs(l.Fragment,{children:[S==="drag"&&l.jsx(oe,{container:document.body,dragContainer:window,selectableTargets:[".seatmap__svg .seat--available"],selectByClick:!1,selectFromInside:!0,continueSelect:!0,toggleContinueSelect:"shift",keyContainer:window,hitRate:100,onSelect:z,onSelectEnd:R}),l.jsx(ue,{svg:b,availableSeats:A,selectedSeatIds:m,onSeatSelect:s=>I(s,!0),onSeatDeselect:s=>I(s,!1),onSeatHover:s=>C(s,!0),onSeatHoverEnd:s=>C(s,!1),leftControls:D,rightControls:y,allowDragAndPan:S!=="drag"})]})},fe=({seats:c,value:m,onChange:b,svg:u,displayGroupMapping:v,leftControls:E,rightControls:y,withDragSelection:j,withGroupSelection:T})=>{const x=(f,p)=>{let h=[...m||[]];for(const Y of f)p?h=[...h,Y]:h=h.filter(N=>N!==Y);b&&b(h)},S=i.useMemo(()=>c.map(f=>({...f,cssSelector:f.cssSelector||""})),[c]);if(u)return l.jsx(Q,{availableSeats:S,selectedSeatIds:m,svg:u,onSeatSelect:f=>x(f.map(p=>p.id),!0),onSeatDeselect:f=>x(f.map(p=>p.id),!1),displayGroupMapping:v,leftControls:E,rightControls:y,withDragSelection:j,withGroupSelection:T})},me=({seats:c,selectedSeatIds:m,onClick:b,svg:u,displayGroupMapping:v,leftControls:E,rightControls:y})=>{const j=i.useMemo(()=>c.map(T=>({...T,cssSelector:T.cssSelector||""})),[c]);if(u)return l.jsx(Q,{availableSeats:j,selectedSeatIds:m,svg:u,onSeatSelect:T=>{b&&b(T.map(x=>x.id)[0],!0)},onSeatDeselect:T=>{b&&b(T.map(x=>x.id)[0],!1)},displayGroupMapping:v,leftControls:E,rightControls:y,withDragSelection:!1,withGroupSelection:!1})};L.SeatmapAccordion=me,L.SeatmapInput=fe,Object.defineProperty(L,Symbol.toStringTag,{value:"Module"})});