If con on-off

This commit is contained in:
2026-04-08 01:23:01 +02:00
parent 59f464a70f
commit c1beda24ca

View File

@@ -127,12 +127,13 @@ export const onUnmount = (fn) => context?.u.push(fn);
export const provide = (key, value) => context && (context.p[key] = value); export const provide = (key, value) => context && (context.p[key] = value);
export const inject = (key, dft) => context && (key in context.p ? context.p[key] : dft); export const inject = (key, dft) => context && (key in context.p ? context.p[key] : dft);
const remove = (node) => { const remove = async (node) => {
if (Array.isArray(node)) return node.forEach(remove); if (Array.isArray(node)) return Promise.all(node.map(remove));
if (node.$off) await node.$off(node);
else if (node.$l) await new Promise(res => node.$l(res));
node.$s?.(); node.$s?.();
if (node.$c) node.$c.u.forEach(f => f()); if (node.$c) node.$c.u.forEach(f => f());
const done = () => node.remove(); node.remove();
node.$l ? node.$l(done) : done();
} }
const render = (fn, ...data) => { const render = (fn, ...data) => {
@@ -159,6 +160,8 @@ export const h = (tag, props = {}, ...children) => {
const out = isNode(el) ? el : document.createTextNode(String(el)); const out = isNode(el) ? el : document.createTextNode(String(el));
out.$c = ctx; out.$c = ctx;
out.$s = stop; out.$s = stop;
if (props.on) out.$on = props.on;
if (props.off) out.$off = props.off;
context = prev; context = prev;
return out; return out;
} }
@@ -166,10 +169,13 @@ export const h = (tag, props = {}, ...children) => {
const isSvg = tag === 'svg' || tag === 'path' || tag === 'circle'; const isSvg = tag === 'svg' || tag === 'path' || tag === 'circle';
const el = isSvg ? document.createElementNS("http://www.w3.org/2000/svg", tag) : document.createElement(tag); const el = isSvg ? document.createElementNS("http://www.w3.org/2000/svg", tag) : document.createElement(tag);
for (const key in props) { for (const key in props) {
if (key.startsWith('on')) el.addEventListener(key.slice(2).toLowerCase(), props[key]); const val = props[key];
else if (key === "ref") isFn(props[key]) ? props[key](el) : props[key].value = el; if (key.startsWith('on') && key !== 'on' && key !== 'off') el.addEventListener(key.slice(2).toLowerCase(), val);
else if (isFn(props[key])) effect(() => el[key] = props[key]()); else if (key === "ref") isFn(val) ? val(el) : val.value = el;
else el[key] = props[key]; else if (key === "on") el.$on = val;
else if (key === "off") el.$off = val;
else if (isFn(val)) effect(() => el[key] = val());
else el[key] = val;
} }
children.forEach(child => append(el, child)); children.forEach(child => append(el, child));
return el; return el;
@@ -181,31 +187,41 @@ const append = (parent, child) => {
const anchor = document.createTextNode(''); const anchor = document.createTextNode('');
parent.appendChild(anchor); parent.appendChild(anchor);
let nodes = []; let nodes = [];
effect(() => { effect(async () => {
const raw = [child()].flat(Infinity).filter(n => n != null); const raw = [child()].flat(Infinity).filter(n => n != null);
const newNodes = raw.map(n => isNode(n) ? n : document.createTextNode(String(n))); const newNodes = raw.map(n => isNode(n) ? n : document.createTextNode(String(n)));
nodes.forEach(n => { if (!newNodes.includes(n)) remove(n); }); for (const n of nodes) { if (!newNodes.includes(n)) await remove(n); }
newNodes.forEach((n, i) => { newNodes.forEach((n, i) => {
if (!nodes.includes(n)) { if (!nodes.includes(n)) {
parent.insertBefore(n, newNodes[i+1] || anchor); parent.insertBefore(n, newNodes[i+1] || anchor);
if (n.$on) n.$on(n);
if (n.$c) n.$c.m.forEach(f => f()); if (n.$c) n.$c.m.forEach(f => f());
} }
}); });
nodes = newNodes; nodes = newNodes;
}, true); }, true);
} else { } else {
parent.appendChild(isNode(child) ? child : document.createTextNode(String(child))); const n = isNode(child) ? child : document.createTextNode(String(child));
parent.appendChild(n);
if (n.$on) n.$on(n);
} }
} }
export const If = (cond, renderFn, fallback = null) => { export const If = (cond, renderFn, fallback = null, transitions = {}) => {
let cached, current; let cached, current;
return () => { return () => {
const show = !!cond(); const show = !!cond();
if (show !== current) { if (show !== current) {
if (cached) remove(cached); const update = async () => {
if (cached) await remove(cached);
cached = show ? render(renderFn) : (isFn(fallback) ? render(fallback) : fallback); cached = show ? render(renderFn) : (isFn(fallback) ? render(fallback) : fallback);
if (isNode(cached)) {
if (transitions.on) cached.$on = transitions.on;
if (transitions.off) cached.$off = transitions.off;
}
current = show; current = show;
};
update();
} }
return cached; return cached;
} }
@@ -223,7 +239,7 @@ export const For = (list, key, renderFn) => {
next.set(id, node); next.set(id, node);
return node; return node;
}); });
cache.forEach((node, id) => { if (!next.has(id)) remove(node); }); cache.forEach(async (node, id) => { if (!next.has(id)) await remove(node); });
cache = next; cache = next;
return res; return res;
} }
@@ -236,7 +252,8 @@ export const Transition = ({ enter: e, idle, leave: l }, { children: [c] }) => {
if (!isNode(el)) return el; if (!isNode(el)) return el;
const addClass = c => c && el.classList.add(...c.split(' ')); const addClass = c => c && el.classList.add(...c.split(' '));
const removeClass = c => c && el.classList.remove(...c.split(' ')); const removeClass = c => c && el.classList.remove(...c.split(' '));
if (e) { el.$on = () => {
if (!e) return;
requestAnimationFrame(() => { requestAnimationFrame(() => {
addClass(e[1]); addClass(e[1]);
requestAnimationFrame(() => { requestAnimationFrame(() => {
@@ -246,18 +263,19 @@ export const Transition = ({ enter: e, idle, leave: l }, { children: [c] }) => {
}, { once: true }); }, { once: true });
}); });
}); });
} };
if (l) { el.$off = (node) => {
el.$l = (done) => { if (!l) return node.remove();
return new Promise(res => {
removeClass(idle); addClass(l[1]); removeClass(idle); addClass(l[1]);
requestAnimationFrame(() => { requestAnimationFrame(() => {
addClass(l[0]); removeClass(l[1]); addClass(l[2]); addClass(l[0]); removeClass(l[1]); addClass(l[2]);
el.addEventListener('transitionend', () => { el.addEventListener('transitionend', () => {
removeClass(l[2]); removeClass(l[0]); done(); removeClass(l[2]); removeClass(l[0]); res();
}, { once: true }); }, { once: true });
}); });
} });
} };
return el; return el;
} }
return isFn(c) ? () => decorate(c()) : decorate(c); return isFn(c) ? () => decorate(c()) : decorate(c);
@@ -266,6 +284,7 @@ export const Transition = ({ enter: e, idle, leave: l }, { children: [c] }) => {
export default (target, root, props) => { export default (target, root, props) => {
const el = h(root, props); const el = h(root, props);
target.appendChild(el); target.appendChild(el);
if (el.$on) el.$on(el);
if (el.$c) el.$c.m.forEach(f => f()); if (el.$c) el.$c.m.forEach(f => f());
return () => remove(el); return () => remove(el);
} }