UNPKG

react-svg-seatmap

Version:

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

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