UNPKG

vanjs-htm

Version:

HTM with VanJS for JSX-like syntax in vanilla JavaScript using VanJS reactivity.

111 lines (109 loc) 3.84 kB
// src/index.ts var vanHTM = (options) => { const { htm, van, vanX } = options; const _document = document; const _undefined = void 0; const isTypeOfFunction = (object) => typeof object === "function"; const isTypeOfString = (value) => typeof value === "string"; const objectHasOwn = Object.hasOwn; const directives = { f: { e: "for:each" }, p: { m: "portal:mount" }, s: { f: "show:fallback", w: "show:when" }, svg: "vh:svg" }; const extractProperty = (object, key) => { const value = object[key]; delete object[key]; return value; }; const hasShowWhenProperty = (props) => objectHasOwn(props, directives.s.w); const handleShow = (fnOrNode, props, children, isTag = true) => { let fallback = extractProperty(props, directives.s.f) ?? ""; let when = extractProperty(props, directives.s.w); return () => { let condition = when && typeof when === "object" && "val" in when ? when.val : isTypeOfFunction(when) ? when() : when; return condition ? isTag ? fnOrNode(props, ...children) : fnOrNode() : isTypeOfFunction(fallback) ? fallback() : fallback; }; }; let portalIdCounter = 0; const handleFor = (tag, props, itemFunc) => { const items = extractProperty(props, directives.f.e); const listFn = () => vanX.list(tag(props), items, itemFunc); return hasShowWhenProperty(props) ? handleShow(listFn, props, _undefined, false) : listFn(); }, handlePortal = (tag, props, children) => { const mount = extractProperty(props, directives.p.m); const targetElement = isTypeOfString(mount) ? _document.querySelector(mount) : mount; const portalId = `p-${portalIdCounter++}`; const portalContentFn = () => { const element = tag(props, ...children); element?.setAttribute?.("p:id", portalId); return element; }; van.add( targetElement, hasShowWhenProperty(props) ? handleShow(portalContentFn, props, _undefined, false) : portalContentFn() ); return _document.createComment(portalId); }; const svgElements = /* @__PURE__ */ new Set([ "circle", "clipPath", "defs", "desc", "ellipse", "filter", "foreignObject", "g", "line", "linearGradient", "marker", "mask", "path", "pattern", "polygon", "polyline", "radialGradient", "rect", "stop", "svg", "symbol", "text", "textPath", "tspan", "use" ]); function h(type, props, ...children) { this[0] = 3; const tag = isTypeOfString(type) ? (props && objectHasOwn(props, directives.svg) ? !!extractProperty(props, directives.svg) : svgElements.has(type)) ? van.tags("http://www.w3.org/2000/svg")[type] : van.tags[type] : type; const decodedChildren = children; if (props) { if (objectHasOwn(props, directives.f.e)) { return handleFor(tag, props, decodedChildren[0]); } else if (objectHasOwn(props, directives.p.m)) { return handlePortal(tag, props, decodedChildren); } else if (objectHasOwn(props, directives.s.w)) { return handleShow(tag, props, decodedChildren); } } return tag(props, ...decodedChildren); } return { html: htm.bind(h), rmPortals: (parent, portalTarget = _document.body) => { let targetElem = isTypeOfString(portalTarget) ? _document.querySelector(portalTarget) : portalTarget; if (!targetElem) return; const result = []; let child = parent.firstChild; while (child) { if (child.nodeType === Node.COMMENT_NODE && child.data.startsWith("p-")) { result.push(child.data); } child = child.nextSibling; } for (const portalId of result) targetElem.querySelector(`[p\\:id="${portalId}"]`)?.remove(); } }; }; var src_default = vanHTM; export { src_default as default };