UNPKG

@mittwald/react-tunnel

Version:

It's like a Portal – but with React components

122 lines (121 loc) 3.58 kB
"use client" /* */ import { jsx as x } from "react/jsx-runtime"; import { useId as P, useState as w, useRef as a, createContext as I, useContext as v, useLayoutEffect as g, useEffect as b } from "react"; import { observable as m, makeObservable as y, action as p } from "mobx"; import { observer as R } from "mobx-react-lite"; const l = "default", u = "default"; class c { id; instanceId; committedChildren = m.map( {}, { deep: !1 } ); renderPhaseChildren = /* @__PURE__ */ new Map(); nextIndex = 0; constructor(e = u, t = u) { this.id = e, this.instanceId = t, y(this, { id: !1, deleteChildren: p.bound, setChildren: p.bound }); } static useNew(e) { const t = P(), n = w(() => new c(e, t))[0]; return n.resetIndex(), n; } resetIndex() { this.nextIndex = 0; } useEntryIndex() { const e = a(this.instanceId), t = a(null); return (t.current === null || e.current !== this.instanceId) && (e.current = this.instanceId, t.current = this.nextIndex++), t.current; } setChildren(e = l, t, n, r) { const s = { id: t, index: n, children: r }, i = this.committedChildren.get(e) ?? m.map({}, { deep: !1 }); i.set(t, s), this.renderPhaseChildren.get(e)?.delete(t), this.committedChildren.set(e, i); } setRenderPhaseChildren(e = l, t, n, r) { const s = { id: t, index: n, children: r }, i = this.renderPhaseChildren.get(e) ?? /* @__PURE__ */ new Map(); i.set(t, s), this.renderPhaseChildren.set(e, i); } deleteChildrenFromMap(e, t, n) { const r = e.get(t); r?.delete(n), r?.size === 0 && e.delete(t); } deleteChildren(e = l, t) { this.deleteChildrenFromMap(this.committedChildren, e, t), this.deleteChildrenFromMap(this.renderPhaseChildren, e, t); } takeRenderPhaseChildren(e) { if (this.renderPhaseChildren.has(e)) { const t = this.renderPhaseChildren.get(e)?.values(); return typeof window < "u" && this.renderPhaseChildren.delete(e), t; } } getEntries(e = l) { const t = this.takeRenderPhaseChildren(e), n = this.committedChildren.get(e)?.values(), r = n ?? t; if (r) { const s = !!n, i = Array.from(r).sort( (o, h) => o.index - h.index ); return { committed: s, entries: i }; } } } const C = I({ state: new c() }), E = (d = u) => { let e = v(C); for (; e; ) { if (e.state.id === d) return e.state; e = e.parentContext; } throw new Error( `Could not get tunnel for provider ${d}. Please provider a TunnelProvider with this ID.` ); }, N = (d) => { const { children: e, id: t } = d, n = v(C), r = c.useNew(t); return /* @__PURE__ */ x( C.Provider, { value: { state: r, parentContext: n }, children: e } ); }, j = (d) => { const { children: e, id: t, staticEntryId: n, providerId: r } = d, s = E(r), i = P(), o = n ?? i, h = s.useEntryIndex(), f = a(!1); return f.current || s.setRenderPhaseChildren(t, o, h, e), g(() => { f.current = !0, s.setChildren(t, o, h, e); }, [e, t, o, h, r]), b(() => () => { s.deleteChildren(t, o); }, [t, o, r]), null; }, T = (d) => { const { children: e } = d; return typeof e == "function" ? e() : e; }, z = R((d) => { const { children: e, id: t, providerId: n } = d, s = E(n).getEntries(t)?.entries.map((i) => /* @__PURE__ */ x(T, { children: i.children }, i.id)); return typeof e == "function" ? e(s) : s ?? e; }); export { j as TunnelEntry, z as TunnelExit, N as TunnelProvider };