From 53f7b7980d702eeefe7c70bb3b80a7049dd1f5db Mon Sep 17 00:00:00 2001 From: natxocc Date: Tue, 7 Apr 2026 01:30:06 +0200 Subject: [PATCH] Actualizar sigpro.ts --- sigpro.ts | 108 ++++++++++++++++++++++++++++++++++++++++++++++++++++-- 1 file changed, 104 insertions(+), 4 deletions(-) diff --git a/sigpro.ts b/sigpro.ts index 12a9947..1dd4242 100644 --- a/sigpro.ts +++ b/sigpro.ts @@ -29,6 +29,10 @@ type Runtime = { }; type Component = (props?: Record, children?: any[]) => any; +type Transition = { + in?: (el: HTMLElement) => void; + out?: (el: HTMLElement, done: () => void) => void; +}; const SIGNAL = Symbol("signal"); @@ -67,10 +71,13 @@ const cleanupNode = (node: Node) => { const flushEffects = () => { if (isFlushing) return; isFlushing = true; - for (const effect of effectQueue) { - if (!effect._deleted) effect(); + while (effectQueue.size > 0) { + const sortedEffects = Array.from(effectQueue).sort((a, b) => (a.depth || 0) - (b.depth || 0)); + effectQueue.clear(); + for (const effect of sortedEffects) { + if (!effect._deleted) effect(); + } } - effectQueue.clear(); isFlushing = false; }; @@ -400,6 +407,98 @@ export function Tag(tag: string, props: any = {}, children: any = []): HTMLEleme return el; } +// Componente If con soporte de transiciones +export function If( + condition: (() => boolean) | boolean, + thenVal: any, + otherwiseVal: any = null, + transition: Transition | null = null +): HTMLElement { + const marker = createText(""); + const container = Tag("div", { style: "display:contents" }, [marker]); + let currentView: Runtime | null = null; + let lastState: boolean | null = null; + + Watch(() => { + const state = !!(isFunc(condition) ? condition() : condition); + if (state === lastState) return; + lastState = state; + + const dispose = () => { + if (currentView) { + currentView.destroy(); + currentView = null; + } + }; + + if (currentView && !state && transition?.out) { + transition.out(currentView.container, dispose); + } else { + dispose(); + } + + const branch = state ? thenVal : otherwiseVal; + if (branch) { + currentView = Render(() => isFunc(branch) ? branch() : branch); + container.insertBefore(currentView.container, marker); + if (state && transition?.in) transition.in(currentView.container); + } + }); + + return container; +} + +// Componente For con keyed rendering +export function For( + source: (() => T[]) | T[], + renderFn: (item: T, index: number) => any, + keyFn?: (item: T, index: number) => string | number, + tag: string = "div", + props: Record = { style: "display:contents" } +): HTMLElement { + const marker = createText(""); + const container = Tag(tag, props, [marker]); + let viewCache = new Map void }>(); + + Watch(() => { + const items = (isFunc(source) ? source() : source) || []; + const nextCache = new Map(); + const order: (string | number)[] = []; + + for (let i = 0; i < items.length; i++) { + const item = items[i]; + const key = keyFn ? keyFn(item, i) : i; + let view = viewCache.get(key); + + if (!view) { + const result = renderFn(item, i); + view = result instanceof Node + ? { container: result, destroy: () => { cleanupNode(result); result.remove(); } } + : Render(() => result); + } + + viewCache.delete(key); + nextCache.set(key, view); + order.push(key); + } + + viewCache.forEach(v => v.destroy()); + viewCache = nextCache; + + let anchor = marker; + for (let i = order.length - 1; i >= 0; i--) { + const view = nextCache.get(order[i]); + if (view.container.nextSibling !== anchor) { + container.insertBefore(view.container, anchor); + } + anchor = view.container; + } + }); + + return container; +} + +// Router con soporte mejorado export const Router = { params: $({} as Record), to: (path: string) => { window.location.hash = path.replace(/^#?\/?/, "#/"); }, @@ -448,7 +547,8 @@ export function Mount(component: Component | (() => any), target: string | HTMLE return instance; } -const sigPro = { $, $$, Render, Watch, Tag, Router, Mount }; +const sigPro = { $, $$, Render, Watch, Tag, If, For, Router, Mount }; + if (typeof window !== "undefined") { Object.assign(window, sigPro); const tags = "div span p h1 h2 h3 h4 h5 h6 br hr section article aside nav main header footer address ul ol li dl dt dd a em strong small i b u mark time sub sup pre code blockquote details summary dialog form label input textarea select button option fieldset legend table thead tbody tfoot tr th td caption img video audio canvas svg iframe picture source progress meter".split(" ");