@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
JavaScript
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