react-svg-seatmap
Version:
React components that render a highly-customizable seatmap based on a provided SVG
23 lines (22 loc) • 16.2 kB
JavaScript
(function(A,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):(A=typeof globalThis<"u"?globalThis:A||self,i(A.index={},A.React,A.svgPanZoom,A.lodash,A.Selecto))})(this,function(A,i,oe,ne,re){"use strict";var W={exports:{}},V={};/**
* @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 V;B=1;var c=Symbol.for("react.transitional.element"),d=Symbol.for("react.fragment");function S(u,_,p){var k=null;if(p!==void 0&&(k=""+p),_.key!==void 0&&(k=""+_.key),"key"in _){p={};for(var j in _)j!=="key"&&(p[j]=_[j])}else p=_;return _=p.ref,{$$typeof:c,type:u,key:k,ref:_!==void 0?_:null,props:p}}return V.Fragment=d,V.jsx=S,V.jsxs=S,V}var q={};/**
* @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===a?null:e.displayName||e.name||null;if(typeof e=="string")return e;switch(e){case v:return"Fragment";case Z:return"Profiler";case C:return"StrictMode";case z:return"Suspense";case s:return"SuspenseList";case o: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 P:return"Portal";case N:return(e.displayName||"Context")+".Provider";case O:return(e._context.displayName||"Context")+".Consumer";case F:var r=e.render;return e=e.displayName,e||(e=r.displayName||r.name||"",e=e!==""?"ForwardRef("+e+")":"ForwardRef"),e;case t:return r=e.displayName||null,r!==null?r:c(e.type)||"Memo";case n:r=e._payload,e=e._init;try{return c(e(r))}catch{}}return null}function d(e){return""+e}function S(e){try{d(e);var r=!1}catch{r=!0}if(r){r=console;var h=r.error,x=typeof Symbol=="function"&&Symbol.toStringTag&&e[Symbol.toStringTag]||e.constructor.name||"Object";return h.call(r,"The provided key is an unsupported type %s. This value must be coerced to a string before using it here.",x),d(e)}}function u(e){if(e===v)return"<>";if(typeof e=="object"&&e!==null&&e.$$typeof===n)return"<...>";try{var r=c(e);return r?"<"+r+">":"<...>"}catch{return"<...>"}}function _(){var e=m.A;return e===null?null:e.getOwner()}function p(){return Error("react-stack-top-frame")}function k(e){if(w.call(e,"key")){var r=Object.getOwnPropertyDescriptor(e,"key").get;if(r&&r.isReactWarning)return!1}return e.key!==void 0}function j(e,r){function h(){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)",r))}h.isReactWarning=!0,Object.defineProperty(e,"key",{get:h,configurable:!0})}function R(){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 T(e,r,h,x,I,L,U,G){return h=L.ref,e={$$typeof:y,type:e,key:r,props:L,_owner:I},(h!==void 0?h:null)!==null?Object.defineProperty(e,"ref",{enumerable:!1,get:R}):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:U}),Object.defineProperty(e,"_debugTask",{configurable:!1,enumerable:!1,writable:!0,value:G}),Object.freeze&&(Object.freeze(e.props),Object.freeze(e)),e}function b(e,r,h,x,I,L,U,G){var g=r.children;if(g!==void 0)if(x)if(Y(g)){for(x=0;x<g.length;x++)f(g[x]);Object.freeze&&Object.freeze(g)}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(g);if(w.call(r,"key")){g=c(e);var D=Object.keys(r).filter(function(de){return de!=="key"});x=0<D.length?"{key: someKey, "+D.join(": ..., ")+": ...}":"{key: someKey}",se[g+x]||(D=0<D.length?"{"+D.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} />`,x,g,D,g),se[g+x]=!0)}if(g=null,h!==void 0&&(S(h),g=""+h),k(r)&&(S(r.key),g=""+r.key),"key"in r){h={};for(var M in r)M!=="key"&&(h[M]=r[M])}else h=r;return g&&j(h,typeof e=="function"?e.displayName||e.name||"Unknown":e),T(e,g,L,I,_(),h,U,G)}function f(e){typeof e=="object"&&e!==null&&e.$$typeof===y&&e._store&&(e._store.validated=1)}var E=i,y=Symbol.for("react.transitional.element"),P=Symbol.for("react.portal"),v=Symbol.for("react.fragment"),C=Symbol.for("react.strict_mode"),Z=Symbol.for("react.profiler"),O=Symbol.for("react.consumer"),N=Symbol.for("react.context"),F=Symbol.for("react.forward_ref"),z=Symbol.for("react.suspense"),s=Symbol.for("react.suspense_list"),t=Symbol.for("react.memo"),n=Symbol.for("react.lazy"),o=Symbol.for("react.activity"),a=Symbol.for("react.client.reference"),m=E.__CLIENT_INTERNALS_DO_NOT_USE_OR_WARN_USERS_THEY_CANNOT_UPGRADE,w=Object.prototype.hasOwnProperty,Y=Array.isArray,H=console.createTask?console.createTask:function(){return null};E={"react-stack-bottom-frame":function(e){return e()}};var K,$={},ee=E["react-stack-bottom-frame"].bind(E,p)(),te=H(u(p)),se={};q.Fragment=v,q.jsx=function(e,r,h,x,I){var L=1e4>m.recentlyCreatedOwnerStacks++;return b(e,r,h,!1,x,I,L?Error("react-stack-top-frame"):ee,L?H(u(e)):te)},q.jsxs=function(e,r,h,x,I){var L=1e4>m.recentlyCreatedOwnerStacks++;return b(e,r,h,!0,x,I,L?Error("react-stack-top-frame"):ee,L?H(u(e)):te)}}()),q}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 d=i.useRef(void 0);return i.useEffect(()=>{d.current=c}),d.current},ue=({availableSeats:c,selectedSeatIds:d,svg:S,onSeatSelect:u,onSeatDeselect:_,onSeatHover:p,onSeatHoverEnd:k,allowDragAndPan:j=!0,showZoomControls:R=!0,leftControls:T,rightControls:b})=>{const[f,E]=i.useState(""),[y,P]=i.useState(!1),v=ie({svgString:f,selectedSeatIds:d,availableSeats:c}),C=i.useCallback(s=>d&&d.includes(s.id),[d]),Z=i.useCallback(s=>c.find(t=>{try{return s.matches(t.cssSelector)}catch{return!1}}),[c]),O=i.useCallback(()=>oe(".seatmap__svg svg",{dblClickZoomEnabled:!1,mouseWheelZoomEnabled:!0,zoomScaleSensitivity:.5}),[]);i.useEffect(()=>{fetch(S).then(s=>s.text()).then(s=>E(s)).catch(s=>console.error("Failed to load SVG:",s))},[S]);const N=i.useCallback((s,t)=>{t?(s.classList.remove("seat--unavailable"),d!=null&&d.includes(t.id)?(s.classList.add("seat--selected"),s.classList.remove("seat--available")):(s.classList.add("seat--available"),s.classList.remove("seat--selected")),t.color&&s.setAttribute("style",`stroke: ${t.color} !important;${C(t)?" fill: "+t.color+" !important;":""}`)):(s.classList.add("seat--unavailable"),s.classList.remove("seat--available"),s.classList.remove("seat--selected"))},[C,d]);i.useEffect(()=>{if(!f){console.log("No SVG, exiting");return}y&&(console.log("SVG changed, repainting all seats..."),document.querySelectorAll(".seatmap__svg circle, .seatmap__svg path, .seatmap__svg ellipse").forEach(t=>{const n=Z(t);N(t,n)}),console.log("Finished initial paint of SVG"),P(!1))},[y,Z,N,f]),i.useEffect(()=>{if(!f)return;const s=O();s.resize(),s.center(),s.fit()},[f,O]),i.useEffect(()=>{if(!f)return;console.log("Updating pan and zoom...",j);const s=O();j?s.enablePan():s.disablePan()},[f,j,O]),i.useEffect(()=>{const s=o=>{const a=o.target,m=Z(a);m&&(C(m)&&_?_(m):u&&u(m))},t=o=>{const a=o.target,m=Z(a);m&&(o.type==="mouseover"&&p?p(m):k&&k(m))},n=document.querySelectorAll(".seatmap__svg circle, .seatmap__svg path, .seatmap__svg ellipse");return n.forEach(o=>{o.removeEventListener("pointerdown",s),o.removeEventListener("mouseover",t),o.removeEventListener("mouseout",t),o.addEventListener("pointerdown",s),o.addEventListener("mouseover",t),o.addEventListener("mouseout",t)}),()=>{n.forEach(o=>{o.removeEventListener("pointerdown",s),o.removeEventListener("mouseover",t),o.removeEventListener("mouseout",t)})}},[_,u,c,f,Z,C]),i.useEffect(()=>{if(!f)return;const s=c.filter(t=>{const n=v==null?void 0:v.availableSeats.find(o=>o.id===t.id);return!ne.isEqual(t,n)});for(const t of s){const n=document.querySelector(t.cssSelector);n&&N(n,t)}},[c,v==null?void 0:v.availableSeats,N,f]),i.useEffect(()=>{let s=d||[];d&&(v!=null&&v.selectedSeatIds)&&(s=d.filter(t=>v.selectedSeatIds&&!v.selectedSeatIds.includes(t)).concat(v.selectedSeatIds.filter(t=>!d.includes(t))));for(const t of s){const n=c.find(a=>a.id===t);if(!n)continue;const o=document.querySelector(n.cssSelector);o&&N(o,n)}},[c,v==null?void 0:v.selectedSeatIds,d,N]);const F=s=>s&&typeof s=="object"&&"style"in s?(console.log("Control is an object with style",s.style),l.jsx("div",{className:"seatmap__action-group"+(s.style==="none"?" seatmap__action-group--unstyled":""),children:s.control})):l.jsx("div",{className:"seatmap__action-group",children:s}),z=i.useMemo(()=>(P(!0),l.jsx("div",{className:"seatmap__svg",dangerouslySetInnerHTML:{__html:f}})),[f]);return i.useMemo(()=>{const s=()=>{O().zoomIn()},t=()=>{const o=O();o.resize(),o.center(),o.fit()},n=()=>{O().zoomOut()};return l.jsxs("div",{className:"seatmap",children:[l.jsxs("div",{className:"seatmap__actions seatmap__actions--left",children:[R&&l.jsxs("div",{className:"seatmap__action-group",children:[l.jsx("button",{type:"button",className:"seatmap__action",onClick:s,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:t,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:n,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"})]})})]}),T==null?void 0:T.map(o=>F(o))]}),z,l.jsx("div",{className:"seatmap__actions seatmap__actions--right",children:b==null?void 0:b.map(o=>F(o))})]})},[T,b,O,z,R])},Q=({availableSeats:c,selectedSeatIds:d,svg:S,displayGroupMapping:u,onSeatSelect:_,onSeatDeselect:p,rightControls:k,leftControls:j,withDragSelection:R=!0,withGroupSelection:T=!0})=>{const[b,f]=i.useState(null),[E,y]=i.useState(null),P=(t,n)=>{var m;const o=(m=t==null?void 0:t.selectionGroups)==null?void 0:m[n];return typeof o=="object"?c.filter(w=>v(w,t,n)&&v(w,t,o.parent)):c.filter(w=>v(w,t,n))},v=(t,n,o)=>{var w,Y;let a=(w=t.selectionGroups)==null?void 0:w[o];typeof a=="object"&&(a=a.value);const m=(Y=n.selectionGroups)==null?void 0:Y[o];return typeof m=="object"?a===m.value:a===m},C=(t,n)=>{const o=c.find(m=>m.id===t.id);if(!o)return;let a=[o];if(b==="group"&&E){const m=P(o,E);a=[...a,...m]}n&&_?_(a):!n&&p&&p(a)},Z=t=>{const n=[];t.added.forEach(o=>{const a=c.find(m=>o.matches(m.cssSelector));a&&(o.classList.add("seat--selected"),o.classList.remove("seat--available"),n.push(a))}),n.length>0&&_&&_(n)},O=t=>{t.added.forEach(n=>{n.classList.add("seat--selected"),n.classList.remove("seat--available")}),t.removed.forEach(n=>{n.classList.remove("seat--selected"),n.classList.add("seat--available")})},N=(t,n)=>{const o=c.find(m=>m.id===t.id);if(!o)return;const a=document.querySelector(o.cssSelector);if(a&&(n?a.classList.add("seat--hover"):a.classList.remove("seat--hover"),b==="group"&&E)){const m=P(o,E);for(const w of m){const Y=document.querySelector(w.cssSelector);Y&&(n?Y.classList.add("seat--hover"):Y.classList.remove("seat--hover"))}}},F=i.useMemo(()=>c.map(t=>({id:t.id,cssSelector:t.cssSelector,color:t.displayGroup&&typeof(u==null?void 0:u[t.displayGroup])=="string"?u==null?void 0:u[t.displayGroup]:void 0,icon:t.displayGroup&&typeof(u==null?void 0:u[t.displayGroup])!="string"?u==null?void 0:u[t.displayGroup]:void 0,selected:!1})),[c,u]),z=i.useMemo(()=>{const t=[];if(T){const n=c.map(a=>{if(a.selectionGroups)return Object.keys(a.selectionGroups)}).flat(),o=Array.from(new Set(n)).filter(a=>a!==void 0);t.push(...o.map(a=>l.jsx("button",{type:"button",className:"seatmap__action"+(b==="group"&&E===a?" seatmap__action--selected":""),onClick:()=>{b==="group"&&E===a?(f(null),y(null)):(f("group"),y(a))},children:Array.from(a)[0]},a)))}return R&&t.push(l.jsx("button",{type:"button",className:"seatmap__action"+(b==="drag"?" seatmap__action--selected":""),onClick:()=>f(b==="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")),t},[c,E,b,R,T]),s=i.useMemo(()=>{const t=[];return z&&z.length&&t.push(z),j&&j.length&&t.push(...j),t},[j,z]);return l.jsxs(l.Fragment,{children:[b==="drag"&&l.jsx(re,{container:document.body,dragContainer:window,selectableTargets:[".seatmap__svg .seat--available"],selectByClick:!1,selectFromInside:!0,continueSelect:!0,toggleContinueSelect:"shift",keyContainer:window,hitRate:100,onSelect:O,onSelectEnd:Z}),l.jsx(ue,{svg:S,availableSeats:F,selectedSeatIds:d,onSeatSelect:t=>C(t,!0),onSeatDeselect:t=>C(t,!1),onSeatHover:t=>N(t,!0),onSeatHoverEnd:t=>N(t,!1),leftControls:s,rightControls:k,allowDragAndPan:b!=="drag"})]})},fe=({seats:c,value:d,onChange:S,svg:u,displayGroupMapping:_,leftControls:p,rightControls:k,withDragSelection:j,withGroupSelection:R})=>{const T=(f,E)=>{let y=[...d||[]];for(const P of f)E?y=[...y,P]:y=y.filter(v=>v!==P);S&&S(y)},b=i.useMemo(()=>c.map(f=>({...f,cssSelector:f.cssSelector||""})),[c]);if(u)return l.jsx(Q,{availableSeats:b,selectedSeatIds:d,svg:u,onSeatSelect:f=>T(f.map(E=>E.id),!0),onSeatDeselect:f=>T(f.map(E=>E.id),!1),displayGroupMapping:_,leftControls:p,rightControls:k,withDragSelection:j,withGroupSelection:R})},me=({seats:c,selectedSeatIds:d,onClick:S,svg:u,displayGroupMapping:_,leftControls:p,rightControls:k})=>{const j=i.useMemo(()=>c.map(R=>({...R,cssSelector:R.cssSelector||""})),[c]);if(u)return l.jsx(Q,{availableSeats:j,selectedSeatIds:d,svg:u,onSeatSelect:R=>{S&&S(R.map(T=>T.id)[0],!0)},onSeatDeselect:R=>{S&&S(R.map(T=>T.id)[0],!1)},displayGroupMapping:_,leftControls:p,rightControls:k,withDragSelection:!1,withGroupSelection:!1})};A.SeatmapAccordion=me,A.SeatmapInput=fe,Object.defineProperty(A,Symbol.toStringTag,{value:"Module"})});