UNPKG

react-svg-seatmap

Version:

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

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