UNPKG

react-svg-seatmap

Version:

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

784 lines (783 loc) 23.8 kB
import ae, { useRef as ce, useEffect as Y, useState as U, useCallback as W, useMemo as I } from "react"; import ie from "svg-pan-zoom"; import le from "lodash"; import ue from "react-selecto"; var H = { 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 te; function fe() { if (te) return V; te = 1; var c = Symbol.for("react.transitional.element"), f = Symbol.for("react.fragment"); function p(l, d, h) { var k = null; if (h !== void 0 && (k = "" + h), d.key !== void 0 && (k = "" + d.key), "key" in d) { h = {}; for (var S in d) S !== "key" && (h[S] = d[S]); } else h = d; return d = h.ref, { $$typeof: c, type: l, key: k, ref: d !== void 0 ? d : null, props: h }; } return V.Fragment = f, V.jsx = p, V.jsxs = p, V; } var $ = {}; /** * @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 re; function me() { return re || (re = 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 O: return "Fragment"; case R: return "Profiler"; case F: return "StrictMode"; case y: return "Suspense"; case z: 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 q: return "Portal"; case P: return (e.displayName || "Context") + ".Provider"; case C: return (e._context.displayName || "Context") + ".Consumer"; case N: var n = e.render; return e = e.displayName, e || (e = n.displayName || n.name || "", e = e !== "" ? "ForwardRef(" + e + ")" : "ForwardRef"), e; case s: return n = e.displayName || null, n !== null ? n : c(e.type) || "Memo"; case m: n = e._payload, e = e._init; try { return c(e(n)); } catch { } } return null; } function f(e) { return "" + e; } function p(e) { try { f(e); var n = !1; } catch { n = !0; } if (n) { n = console; var v = n.error, T = typeof Symbol == "function" && Symbol.toStringTag && e[Symbol.toStringTag] || e.constructor.name || "Object"; return v.call( n, "The provided key is an unsupported type %s. This value must be coerced to a string before using it here.", T ), f(e); } } function l(e) { if (e === O) return "<>"; if (typeof e == "object" && e !== null && e.$$typeof === m) return "<...>"; try { var n = c(e); return n ? "<" + n + ">" : "<...>"; } catch { return "<...>"; } } function d() { var e = a.A; return e === null ? null : e.getOwner(); } function h() { return Error("react-stack-top-frame"); } function k(e) { if (o.call(e, "key")) { var n = Object.getOwnPropertyDescriptor(e, "key").get; if (n && n.isReactWarning) return !1; } return e.key !== void 0; } function S(e, n) { function v() { X || (X = !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)", n )); } v.isReactWarning = !0, Object.defineProperty(e, "key", { get: v, configurable: !0 }); } function j() { var e = c(this.type); return M[e] || (M[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, n, v, T, Z, L, G, B) { return v = L.ref, e = { $$typeof: _, type: e, key: n, props: L, _owner: Z }, (v !== void 0 ? v : null) !== null ? Object.defineProperty(e, "ref", { enumerable: !1, get: j }) : 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: G }), Object.defineProperty(e, "_debugTask", { configurable: !1, enumerable: !1, writable: !0, value: B }), Object.freeze && (Object.freeze(e.props), Object.freeze(e)), e; } function b(e, n, v, T, Z, L, G, B) { var g = n.children; if (g !== void 0) if (T) if (w(g)) { for (T = 0; T < g.length; T++) u(g[T]); 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 u(g); if (o.call(n, "key")) { g = c(e); var D = Object.keys(n).filter(function(ne) { return ne !== "key"; }); T = 0 < D.length ? "{key: someKey, " + D.join(": ..., ") + ": ...}" : "{key: someKey}", ee[g + T] || (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} />`, T, g, D, g ), ee[g + T] = !0); } if (g = null, v !== void 0 && (p(v), g = "" + v), k(n) && (p(n.key), g = "" + n.key), "key" in n) { v = {}; for (var J in n) J !== "key" && (v[J] = n[J]); } else v = n; return g && S( v, typeof e == "function" ? e.displayName || e.name || "Unknown" : e ), x( e, g, L, Z, d(), v, G, B ); } function u(e) { typeof e == "object" && e !== null && e.$$typeof === _ && e._store && (e._store.validated = 1); } var E = ae, _ = Symbol.for("react.transitional.element"), q = Symbol.for("react.portal"), O = Symbol.for("react.fragment"), F = Symbol.for("react.strict_mode"), R = Symbol.for("react.profiler"), C = Symbol.for("react.consumer"), P = Symbol.for("react.context"), N = Symbol.for("react.forward_ref"), y = Symbol.for("react.suspense"), z = Symbol.for("react.suspense_list"), s = Symbol.for("react.memo"), m = Symbol.for("react.lazy"), t = Symbol.for("react.activity"), r = Symbol.for("react.client.reference"), a = E.__CLIENT_INTERNALS_DO_NOT_USE_OR_WARN_USERS_THEY_CANNOT_UPGRADE, o = Object.prototype.hasOwnProperty, w = Array.isArray, A = console.createTask ? console.createTask : function() { return null; }; E = { "react-stack-bottom-frame": function(e) { return e(); } }; var X, M = {}, Q = E["react-stack-bottom-frame"].bind( E, h )(), K = A(l(h)), ee = {}; $.Fragment = O, $.jsx = function(e, n, v, T, Z) { var L = 1e4 > a.recentlyCreatedOwnerStacks++; return b( e, n, v, !1, T, Z, L ? Error("react-stack-top-frame") : Q, L ? A(l(e)) : K ); }, $.jsxs = function(e, n, v, T, Z) { var L = 1e4 > a.recentlyCreatedOwnerStacks++; return b( e, n, v, !0, T, Z, L ? Error("react-stack-top-frame") : Q, L ? A(l(e)) : K ); }; }()), $; } var se; function de() { return se || (se = 1, process.env.NODE_ENV === "production" ? H.exports = fe() : H.exports = me()), H.exports; } var i = de(); const ve = (c) => { const f = ce(void 0); return Y(() => { f.current = c; }), f.current; }, _e = ({ availableSeats: c, selectedSeatIds: f, svg: p, onSeatSelect: l, onSeatDeselect: d, onSeatHover: h, onSeatHoverEnd: k, allowDragAndPan: S = !0, showZoomControls: j = !0, leftControls: x, rightControls: b }) => { const [u, E] = U(""), [_, q] = U(!1), [O, F] = U(!1), R = ve({ svgString: u, selectedSeatIds: f, availableSeats: c }), C = W( (t) => f && f.includes(t.id), [f] ), P = W( (t) => c.find((r) => { try { return t.matches(r.cssSelector); } catch { return !1; } }), [c] ), N = W( () => ie(".seatmap__svg svg", { dblClickZoomEnabled: !1, mouseWheelZoomEnabled: !0, zoomScaleSensitivity: 0.5 }), [] ); Y(() => { fetch(p).then((t) => { if (t.ok) return t.text(); throw new Error("Unable to fetch SVG"); }).then((t) => E(t)).catch((t) => { q(!0), console.error("Failed to load SVG:", t); }); }, [p]); const y = W( (t, r) => { r ? (t.classList.remove("seat--unavailable"), f != null && f.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;${C(r) ? " fill: " + r.color + " !important;" : ""}` )) : (t.classList.add("seat--unavailable"), t.classList.remove("seat--available"), t.classList.remove("seat--selected")); }, [C, f] ); Y(() => { if (!u || _) { console.error("No SVG, exiting"); return; } O && (document.querySelectorAll( ".seatmap__svg circle, .seatmap__svg path, .seatmap__svg ellipse" ).forEach((r) => { const a = P(r); y(r, a); }), F(!1)); }, [ O, P, y, u, _ ]), Y(() => { if (!u || _) return; const t = N(); t.resize(), t.center(), t.fit(); }, [u, N, _]), Y(() => { if (!u || _) return; const t = N(); S ? t.enablePan() : t.disablePan(); }, [u, S, N, _]), Y(() => { const t = (o) => { const w = o.target, A = P(w); A && (C(A) && d ? d(A) : l && l(A)); }, r = (o) => { const w = o.target, A = P(w); A && (o.type === "mouseover" && h ? h(A) : k && k(A)); }, a = document.querySelectorAll( ".seatmap__svg circle, .seatmap__svg path, .seatmap__svg ellipse" ); return a.forEach((o) => { o.removeEventListener("pointerdown", t), o.removeEventListener("mouseover", r), o.removeEventListener("mouseout", r), o.addEventListener("pointerdown", t), o.addEventListener("mouseover", r), o.addEventListener("mouseout", r); }), () => { a.forEach((o) => { o.removeEventListener("pointerdown", t), o.removeEventListener("mouseover", r), o.removeEventListener("mouseout", r); }); }; }, [ d, l, c, u, P, C ]), Y(() => { if (!u || _) return; const t = c.filter((r) => { const a = R == null ? void 0 : R.availableSeats.find( (o) => o.id === r.id ); return !le.isEqual(r, a); }); for (const r of t) { const a = document.querySelector( r.cssSelector ); a && y(a, r); } }, [ c, R == null ? void 0 : R.availableSeats, y, u, _ ]), Y(() => { let t = f || []; f && (R != null && R.selectedSeatIds) && (t = f.filter( (r) => R.selectedSeatIds && !R.selectedSeatIds.includes(r) ).concat( R.selectedSeatIds.filter( (r) => !f.includes(r) ) )); for (const r of t) { const a = c.find((w) => w.id === r); if (!a) continue; const o = document.querySelector( a.cssSelector ); o && y(o, a); } }, [ c, R == null ? void 0 : R.selectedSeatIds, f, y ]); const z = (t) => t && typeof t == "object" && "style" in t ? /* @__PURE__ */ i.jsx( "div", { className: "seatmap__action-group" + (t.style === "none" ? " seatmap__action-group--unstyled" : ""), children: t.control } ) : /* @__PURE__ */ i.jsx("div", { className: "seatmap__action-group", children: t }), s = I(() => (F(!0), /* @__PURE__ */ i.jsx( "div", { className: "seatmap__svg", dangerouslySetInnerHTML: { __html: u } } )), [u]), m = I(() => { const t = () => { N().zoomIn(); }, r = () => { const o = N(); o.resize(), o.center(), o.fit(); }, a = () => { N().zoomOut(); }; return /* @__PURE__ */ i.jsxs("div", { className: "seatmap", children: [ /* @__PURE__ */ i.jsxs("div", { className: "seatmap__actions seatmap__actions--left", children: [ j && /* @__PURE__ */ i.jsxs("div", { className: "seatmap__action-group", children: [ /* @__PURE__ */ i.jsx( "button", { type: "button", className: "seatmap__action", onClick: t, title: "Zoom In", children: /* @__PURE__ */ i.jsxs( "svg", { className: "seatmap__icon seatmap__icon--zoom-in", viewBox: "0 -960 960 960", children: [ /* @__PURE__ */ i.jsx("title", { children: "Zoom In" }), /* @__PURE__ */ i.jsx("path", { d: "M440-440H200v-80h240v-240h80v240h240v80H520v240h-80v-240Z" }) ] } ) } ), /* @__PURE__ */ i.jsx( "button", { type: "button", className: "seatmap__action", onClick: r, title: "Reset Zoom", children: /* @__PURE__ */ i.jsxs( "svg", { className: "seatmap__icon seatmap__icon--reset", viewBox: "0 -960 960 960", children: [ /* @__PURE__ */ i.jsx("title", { children: "Reset zoom" }), /* @__PURE__ */ i.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" }) ] } ) } ), /* @__PURE__ */ i.jsx( "button", { type: "button", className: "seatmap__action", onClick: a, title: "Zoom Out", children: /* @__PURE__ */ i.jsxs( "svg", { className: "seatmap__icon seatmap__icon--zoom-out", viewBox: "0 -960 960 960", children: [ /* @__PURE__ */ i.jsx("title", { children: "Zoom Out" }), /* @__PURE__ */ i.jsx("path", { d: "M200-440v-80h560v80H200Z" }) ] } ) } ) ] }), x == null ? void 0 : x.map((o) => z(o)) ] }), s, /* @__PURE__ */ i.jsx("div", { className: "seatmap__actions seatmap__actions--right", children: b == null ? void 0 : b.map((o) => z(o)) }) ] }); }, [ x, b, N, s, j ]); return _ ? /* @__PURE__ */ i.jsx("div", { className: "seatmap", children: /* @__PURE__ */ i.jsxs("div", { className: "seatmap__error", children: [ "ERROR: Unable to fetch SVG from the given URL: ", p ] }) }) : m; }, oe = ({ availableSeats: c, selectedSeatIds: f, svg: p, displayGroupMapping: l, onSeatSelect: d, onSeatDeselect: h, rightControls: k, leftControls: S, withDragSelection: j = !0, withGroupSelection: x = !0 }) => { const [b, u] = U(null), [E, _] = U(null), q = (s, m) => { var a; const t = (a = s == null ? void 0 : s.selectionGroups) == null ? void 0 : a[m]; return typeof t == "object" ? c.filter( (o) => O(o, s, m) && O(o, s, t.parent) ) : c.filter((o) => O(o, s, m)); }, O = (s, m, t) => { var o, w; let r = (o = s.selectionGroups) == null ? void 0 : o[t]; typeof r == "object" && (r = r.value); const a = (w = m.selectionGroups) == null ? void 0 : w[t]; return typeof a == "object" ? r === a.value : r === a; }, F = (s, m) => { const t = c.find((a) => a.id === s.id); if (!t) return; let r = [t]; if (b === "group" && E) { const a = q(t, E); r = [...r, ...a]; } m && d ? d(r) : !m && h && h(r); }, R = (s) => { const m = []; s.added.forEach((t) => { const r = c.find( (a) => t.matches(a.cssSelector) ); r && (t.classList.add("seat--selected"), t.classList.remove("seat--available"), m.push(r)); }), m.length > 0 && d && d(m); }, C = (s) => { s.added.forEach((m) => { m.classList.add("seat--selected"), m.classList.remove("seat--available"); }), s.removed.forEach((m) => { m.classList.remove("seat--selected"), m.classList.add("seat--available"); }); }, P = (s, m) => { const t = c.find((a) => a.id === s.id); if (!t) return; const r = document.querySelector( t.cssSelector ); if (r && (m ? r.classList.add("seat--hover") : r.classList.remove("seat--hover"), b === "group" && E)) { const a = q(t, E); for (const o of a) { const w = document.querySelector( o.cssSelector ); w && (m ? w.classList.add("seat--hover") : w.classList.remove("seat--hover")); } } }, N = I( () => c.map((s) => ({ id: s.id, cssSelector: s.cssSelector, color: s.displayGroup && typeof (l == null ? void 0 : l[s.displayGroup]) == "string" ? l == null ? void 0 : l[s.displayGroup] : void 0, icon: s.displayGroup && typeof (l == null ? void 0 : l[s.displayGroup]) != "string" ? l == null ? void 0 : l[s.displayGroup] : void 0, selected: !1 })), [c, l] ), y = I(() => { const s = []; if (x) { const m = c.map((r) => { if (r.selectionGroups) return Object.keys(r.selectionGroups); }).flat(), t = Array.from(new Set(m)).filter( (r) => r !== void 0 ); s.push( ...t.map((r) => /* @__PURE__ */ i.jsx( "button", { type: "button", className: "seatmap__action" + (b === "group" && E === r ? " seatmap__action--selected" : ""), onClick: () => { b === "group" && E === r ? (u(null), _(null)) : (u("group"), _(r)); }, children: Array.from(r)[0] }, r )) ); } return j && s.push( /* @__PURE__ */ i.jsx( "button", { type: "button", className: "seatmap__action" + (b === "drag" ? " seatmap__action--selected" : ""), onClick: () => u(b === "drag" ? null : "drag"), title: "Lasso select", children: /* @__PURE__ */ i.jsxs( "svg", { className: "seatmap__icon seatmap__icon--zoom-in", viewBox: "0 -960 960 960", children: [ /* @__PURE__ */ i.jsx("title", { children: "Lasso select" }), /* @__PURE__ */ i.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, E, b, j, x ]), z = I(() => { const s = []; return y && y.length && s.push(y), S && S.length && s.push(...S), s; }, [S, y]); return /* @__PURE__ */ i.jsxs(i.Fragment, { children: [ b === "drag" && /* @__PURE__ */ i.jsx( ue, { container: document.body, dragContainer: window, selectableTargets: [".seatmap__svg .seat--available"], selectByClick: !1, selectFromInside: !0, continueSelect: !0, toggleContinueSelect: "shift", keyContainer: window, hitRate: 100, onSelect: C, onSelectEnd: R } ), /* @__PURE__ */ i.jsx( _e, { svg: p, availableSeats: N, selectedSeatIds: f, onSeatSelect: (s) => F(s, !0), onSeatDeselect: (s) => F(s, !1), onSeatHover: (s) => P(s, !0), onSeatHoverEnd: (s) => P(s, !1), leftControls: z, rightControls: k, allowDragAndPan: b !== "drag" } ) ] }); }, Re = ({ seats: c, value: f, onChange: p, svg: l, displayGroupMapping: d, leftControls: h, rightControls: k, withDragSelection: S, withGroupSelection: j }) => { const x = (u, E) => { let _ = [...f || []]; for (const q of u) E ? _ = [..._, q] : _ = _.filter((O) => O !== q); p && p(_); }, b = I( () => c.map((u) => ({ ...u, cssSelector: u.cssSelector || "" })), [c] ); if (l) return /* @__PURE__ */ i.jsx( oe, { availableSeats: b, selectedSeatIds: f, svg: l, onSeatSelect: (u) => x( u.map((E) => E.id), !0 ), onSeatDeselect: (u) => x( u.map((E) => E.id), !1 ), displayGroupMapping: d, leftControls: h, rightControls: k, withDragSelection: S, withGroupSelection: j } ); }, Se = ({ seats: c, selectedSeatIds: f, onClick: p, svg: l, displayGroupMapping: d, leftControls: h, rightControls: k }) => { const S = I( () => c.map((j) => ({ ...j, cssSelector: j.cssSelector || "" })), [c] ); if (l) return /* @__PURE__ */ i.jsx( oe, { availableSeats: S, selectedSeatIds: f, svg: l, onSeatSelect: (j) => { p && p(j.map((x) => x.id)[0], !0); }, onSeatDeselect: (j) => { p && p(j.map((x) => x.id)[0], !1); }, displayGroupMapping: d, leftControls: h, rightControls: k, withDragSelection: !1, withGroupSelection: !1 } ); }; export { Se as SeatmapAccordion, Re as SeatmapInput };