@mittwald/react-tunnel
Version:
It's like a Portal – but with React components
122 lines (121 loc) • 3.58 kB
JavaScript
"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
};