UNPKG

@jay-js/system

Version:

A powerful and flexible TypeScript library for UI, state management, lazy loading, routing and managing draggable elements in modern web applications.

350 lines 13.2 kB
var __rest = (this && this.__rest) || function (s, e) { var t = {}; for (var p in s) if (Object.prototype.hasOwnProperty.call(s, p) && e.indexOf(p) < 0) t[p] = s[p]; if (s != null && typeof Object.getOwnPropertySymbols === "function") for (var i = 0, p = Object.getOwnPropertySymbols(s); i < p.length; i++) { if (e.indexOf(p[i]) < 0 && Object.prototype.propertyIsEnumerable.call(s, p[i])) t[p[i]] = s[p[i]]; } return t; }; import { REACTIVE_MARKER, values } from "../../state"; import { childs } from "../../state/utils/helpers"; import { registerJayJsElement } from "./jay-js-element.js"; /** * Checks if a node reference is a FragmentRange */ function isFragmentRange(node) { return typeof node === "object" && node !== null && "type" in node && node.type === "fragment-range"; } /** * Removes all nodes between two marker nodes (exclusive) */ function removeNodesBetween(start, end) { let current = start.nextSibling; while (current && current !== end) { const next = current.nextSibling; current.remove(); current = next; } } /** * Inserts an array of nodes after a given node */ function insertNodesAfter(afterNode, nodes) { let current = afterNode; for (const node of nodes) { current.after(node); current = node; } } function isReactiveValue(value) { return typeof value === "function" && value[REACTIVE_MARKER] === true; } function autoWrapReactiveValues(value, element) { if (typeof value === "function") { if (value[REACTIVE_MARKER] === true) { return value; } return values(value, element); } return value; } function isEventHandler(propName, value) { if (typeof value !== "function") { return false; } if (propName.startsWith("on") && propName.length > 2) { const thirdChar = propName[2]; return thirdChar === thirdChar.toLowerCase() && thirdChar !== thirdChar.toUpperCase(); } return false; } export function Base(_a = { tag: "div", }) { var { id, tag, ref, style, children, dataset, className, listeners, onmount, onunmount } = _a, props = __rest(_a, ["id", "tag", "ref", "style", "children", "dataset", "className", "listeners", "onmount", "onunmount"]); const hasReactiveChildren = typeof children === "function" || (Array.isArray(children) && children.some((c) => typeof c === "function")); const hasReactiveProps = typeof id === "function" || typeof className === "function" || (typeof style === "object" && style !== null && !isReactiveValue(style) && Object.values(style).some((v) => typeof v === "function")) || (typeof dataset === "object" && dataset !== null && !isReactiveValue(dataset) && Object.values(dataset).some((v) => typeof v === "function")) || Object.values(props).some((v) => typeof v === "function" && !isEventHandler("", v)); const hasLifecycle = Boolean(onmount || onunmount || ref || hasReactiveChildren || hasReactiveProps); if (hasLifecycle) { registerJayJsElement(tag || "div"); } const elementOptions = hasLifecycle ? { is: `jayjs-${tag || "div"}` } : undefined; const base = document.createElement(tag || "div", elementOptions); if (hasLifecycle) { const lyfercycleElement = base; if (onmount) lyfercycleElement.onmount = onmount; if (onunmount) lyfercycleElement.onunmount = onunmount; if (ref) { lyfercycleElement._ref = ref; ref.current = base; } } if (id) { const wrappedId = autoWrapReactiveValues(id, base); if (isReactiveValue(wrappedId)) { wrappedId(base, "id"); } else { base.id = wrappedId; } } if (className) { const wrappedClassName = autoWrapReactiveValues(className, base); if (isReactiveValue(wrappedClassName)) { wrappedClassName(base, "className"); } else { base.className = wrappedClassName; } } listeners && Object.entries(listeners).forEach(([key, value]) => { base.addEventListener(key, value); }); if (style) { if (isReactiveValue(style)) { style(base, "style"); } else { Object.entries(style).forEach(([key, value]) => { if (key === "parentRule" || key === "length") return; const wrappedValue = autoWrapReactiveValues(value, base); if (isReactiveValue(wrappedValue)) { wrappedValue(base.style, key); } else { base.style[key] = wrappedValue; } }); } } if (dataset) { if (isReactiveValue(dataset)) { dataset(base, "dataset"); } else { Object.entries(dataset).forEach(([key, value]) => { const wrappedValue = autoWrapReactiveValues(value, base); if (isReactiveValue(wrappedValue)) { wrappedValue(base.dataset, key); } else { base.dataset[key] = wrappedValue; } }); } } if (children !== null && children !== undefined && typeof children !== "boolean") { if (typeof children === "function") { appendChildToBase(base, children, base); } else if (children instanceof Promise) { const elementSlot = document.createElement("jayjs-lazy-slot"); base.appendChild(elementSlot); children .then((resolvedChild) => { if (resolvedChild !== null && resolvedChild !== undefined && typeof resolvedChild !== "boolean") { if (typeof resolvedChild === "string" || typeof resolvedChild === "number") { elementSlot.replaceWith(document.createTextNode(String(resolvedChild))); } else { elementSlot.replaceWith(resolvedChild); } } }) .catch((error) => { console.error("Failed to resolve child promise:", error); }); } else { if (Array.isArray(children)) { children.forEach((child) => { if (child !== null && child !== undefined && typeof child !== "boolean") { appendChildToBase(base, child, base); } }); } else { if (typeof children !== "boolean") { appendChildToBase(base, children, base); } } } } props && Object.entries(props).forEach(([key, value]) => { if (isEventHandler(key, value)) { try { base[key] = value; } catch (error) { if (error instanceof TypeError) { console.warn(`JayJS: Cannot set property "${key}" of type "${typeof value}" to "${value}".`); } throw error; } return; } const wrappedValue = autoWrapReactiveValues(value, base); if (isReactiveValue(wrappedValue)) { wrappedValue(base, key); } else { try { base[key] = wrappedValue; } catch (error) { if (error instanceof TypeError) { console.warn(`JayJS: Cannot set property "${key}" of type "${typeof value}" to "${value}".`); } throw error; } } }); return base; } function updateChildNode(currentNode, newValue) { // Handle DocumentFragment if (newValue instanceof DocumentFragment) { const children = Array.from(newValue.childNodes); if (children.length === 0) { // Empty fragment - treat as null return updateChildNode(currentNode, null); } if (isFragmentRange(currentNode)) { // Already have a range - remove old content and insert new removeNodesBetween(currentNode.start, currentNode.end); insertNodesAfter(currentNode.start, children); return currentNode; // Reuse the same markers } // First time - create markers const startMarker = document.createComment("jayjs-fragment-start"); const endMarker = document.createComment("jayjs-fragment-end"); currentNode.replaceWith(startMarker, ...children, endMarker); return { start: startMarker, end: endMarker, type: "fragment-range", }; } // If currentNode is a range but newValue is not a fragment, collapse the range if (isFragmentRange(currentNode)) { removeNodesBetween(currentNode.start, currentNode.end); const placeholder = document.createTextNode(""); currentNode.start.replaceWith(placeholder); currentNode.end.remove(); // Continue with normal logic using the placeholder currentNode = placeholder; } if (typeof newValue === "string" || typeof newValue === "number") { if (currentNode instanceof Text) { currentNode.textContent = String(newValue); return currentNode; } const newTextNode = document.createTextNode(String(newValue)); currentNode.replaceWith(newTextNode); return newTextNode; } if (newValue instanceof Node) { currentNode.replaceWith(newValue); return newValue; } if (newValue === null || newValue === undefined || newValue === false || newValue === true) { if (currentNode instanceof Text) { currentNode.textContent = ""; return currentNode; } const emptyTextNode = document.createTextNode(""); currentNode.replaceWith(emptyTextNode); return emptyTextNode; } return currentNode; } function appendChildToBase(base, child, rootElement) { if (Array.isArray(child)) { for (const nestedChild of child) { appendChildToBase(base, nestedChild, rootElement); } return; } if (typeof child === "function") { const nodeRefId = crypto.getRandomValues(new Uint32Array(1))[0].toString(16); const nodeRef = { current: document.createTextNode(""), id: nodeRefId, }; base.appendChild(nodeRef.current); const setChild = () => { const result = child(); if (result instanceof Promise) { result .then((resolved) => { nodeRef.current = updateChildNode(nodeRef.current, resolved); }) .catch((error) => { console.error("JayJS: Error resolving child Promise:", error); nodeRef.current = updateChildNode(nodeRef.current, null); }); return; } if (Array.isArray(result)) { const fragment = document.createDocumentFragment(); result.forEach((item) => { if (item instanceof Node) { fragment.appendChild(item); } }); nodeRef.current = updateChildNode(nodeRef.current, fragment); return; } nodeRef.current = updateChildNode(nodeRef.current, result); }; const targetElement = rootElement || base; childs(child, nodeRefId, setChild, targetElement); return; } if (child instanceof Promise) { const elementSlot = document.createElement("jayjs-lazy-slot"); base.appendChild(elementSlot); child .then((resolvedChild) => { if (resolvedChild !== null && resolvedChild !== undefined && typeof resolvedChild !== "boolean") { if (typeof resolvedChild === "string" || typeof resolvedChild === "number") { elementSlot.replaceWith(document.createTextNode(String(resolvedChild))); } else { elementSlot.replaceWith(resolvedChild); } } }) .catch((error) => { console.error("JayJS: Error resolving child Promise:", error); elementSlot.remove(); }); return; } if (typeof child === "string" || typeof child === "number") { base.appendChild(document.createTextNode(String(child))); return; } if (child !== null && child !== undefined && typeof child !== "boolean") { base.appendChild(child); } } //# sourceMappingURL=base.js.map