woby
Version:
A high-performance framework with fine-grained observable/signal-based reactivity for building rich applications.
81 lines (80 loc) • 2.85 kB
JavaScript
import { m as setChild, F as FragmentUtils, E as get, t as createHTMLNode, G as useRenderEffect, h as assign, H as context, I as SYMBOL_SUSPENSE_COLLECTOR, J as effect } from "./setters-Ddu0s6oK.js";
import { r as root } from "./root-B8Ruf358.js";
import { b as boolean, r as resolve, o as observable, m as memo } from "./merge_style-CyMhMoCQ.js";
const render = (child, parent) => {
if (!parent || !(parent instanceof HTMLElement)) throw new Error("Invalid parent node");
parent.textContent = "";
return root((stack, dispose) => {
setChild(parent, child, FragmentUtils.make(), stack);
return () => {
dispose(stack);
parent.textContent = "";
};
});
};
const Portal = ({ when = true, mount, wrapper, children }) => {
const portal = get(wrapper) || createHTMLNode("div");
if (!(portal instanceof HTMLElement)) throw new Error("Invalid wrapper node");
const condition = boolean(when);
const stack = new Error();
useRenderEffect(() => {
if (!get(condition)) return;
const parent = get(mount) || document.body;
if (!(parent instanceof Element)) throw new Error("Invalid mount node");
parent.insertBefore(portal, null);
return () => {
parent.removeChild(portal);
};
}, stack);
useRenderEffect(() => {
if (!get(condition)) return;
return render(children, portal);
}, stack);
return assign(() => get(condition) || children, { metadata: { portal } });
};
const SuspenseCollector = {
create: () => {
const parent = SuspenseCollector.get();
const suspenses = observable([]);
const active = memo(() => suspenses().some((suspense) => suspense.active()));
const register = (suspense) => {
parent == null ? void 0 : parent.register(suspense);
suspenses((prev) => [...prev, suspense]);
};
const unregister = (suspense) => {
parent == null ? void 0 : parent.unregister(suspense);
suspenses((prev) => prev.filter((other) => other !== suspense));
};
const data = { suspenses, active, register, unregister };
return data;
},
get: () => {
return context(SYMBOL_SUSPENSE_COLLECTOR);
},
wrap: (fn) => {
const data = SuspenseCollector.create();
return context({ [SYMBOL_SUSPENSE_COLLECTOR]: data }, () => {
return resolve(() => fn(data));
});
}
};
const renderToString = (child) => {
return new Promise((resolve2) => {
root((stack, dispose) => {
get(SuspenseCollector.wrap((suspenses) => {
const { portal } = Portal({ children: child }).metadata;
effect((stack2) => {
if (suspenses.active()) return;
resolve2(portal.innerHTML);
dispose(stack2);
}, { suspense: false, stack });
}));
});
});
};
export {
Portal as P,
renderToString as a,
render as r
};
//# sourceMappingURL=render_to_string-DPtjoKMd.js.map