Tabs y Accordion animados

This commit is contained in:
2026-04-13 12:09:35 +02:00
parent 00b9b8f911
commit 0697b4b4b7
12 changed files with 282 additions and 183 deletions

View File

@@ -6913,6 +6913,32 @@
color: oklch(28% 0.01 260); color: oklch(28% 0.01 260);
font-size: 1.1rem; font-size: 1.1rem;
} }
.collapse .collapse-content {
transform: scaleY(0);
transform-origin: top;
transition: transform 0.3s cubic-bezier(0.4, 0, 0.2, 1);
height: 0;
opacity: 0;
}
.collapse:has(input:checked) .collapse-content {
transform: scaleY(1);
height: auto;
opacity: 1;
}
.tab-content-inner {
animation: tabFadeIn 0.3s cubic-bezier(0.4, 0, 0.2, 1);
transform-origin: top;
}
@keyframes tabFadeIn {
from {
opacity: 0;
transform: scaleY(0.95);
}
to {
opacity: 1;
transform: scaleY(1);
}
}
@layer base { @layer base {
:where(:root),:root:has(input.theme-controller[value=light]:checked),[data-theme=light] { :where(:root),:root:has(input.theme-controller[value=light]:checked),[data-theme=light] {
color-scheme: light; color-scheme: light;

2
css/sigpro.min.css vendored

File diff suppressed because one or more lines are too long

98
dist/sigpro-ui.esm.js vendored
View File

@@ -44,6 +44,10 @@ var onUnmount = (fn) => {
if (activeOwner) if (activeOwner)
(activeOwner._cleanups ||= new Set).add(fn); (activeOwner._cleanups ||= new Set).add(fn);
}; };
var onMount = (fn) => {
if (activeOwner)
(activeOwner._mounts ||= []).push(fn);
};
var createEffect = (fn, isComputed = false) => { var createEffect = (fn, isComputed = false) => {
const effect = () => { const effect = () => {
if (effect._disposed) if (effect._disposed)
@@ -119,20 +123,16 @@ var untrack = (fn) => {
activeEffect = prev; activeEffect = prev;
} }
}; };
var onMount = (fn) => { var $2 = (initialValue, storageKey = null) => {
if (activeOwner)
(activeOwner._mounts ||= []).push(fn);
};
var $2 = (value, storageKey = null) => {
const subs = new Set; const subs = new Set;
if (isFunc(value)) { if (isFunc(initialValue)) {
let cache, dirty = true; let cache, dirty = true;
const computed = () => { const computed = () => {
if (dirty) { if (dirty) {
const prev = activeEffect; const prev = activeEffect;
activeEffect = computed; activeEffect = computed;
try { try {
const next = value(); const next = initialValue();
if (!Object.is(cache, next)) { if (!Object.is(cache, next)) {
cache = next; cache = next;
dirty = false; dirty = false;
@@ -167,20 +167,20 @@ var $2 = (value, storageKey = null) => {
} }
if (storageKey) if (storageKey)
try { try {
value = JSON.parse(localStorage.getItem(storageKey)) ?? value; initialValue = JSON.parse(localStorage.getItem(storageKey)) ?? initialValue;
} catch (e) {} } catch (e) {}
return (...args) => { return (...args) => {
if (args.length) { if (args.length) {
const next = isFunc(args[0]) ? args[0](value) : args[0]; const next = isFunc(args[0]) ? args[0](initialValue) : args[0];
if (!Object.is(value, next)) { if (!Object.is(initialValue, next)) {
value = next; initialValue = next;
if (storageKey) if (storageKey)
localStorage.setItem(storageKey, JSON.stringify(value)); localStorage.setItem(storageKey, JSON.stringify(initialValue));
trackUpdate(subs, true); trackUpdate(subs, true);
} }
} }
trackUpdate(subs); trackUpdate(subs);
return value; return initialValue;
}; };
}; };
var Watch2 = (sources, callback) => { var Watch2 = (sources, callback) => {
@@ -208,12 +208,33 @@ var cleanupNode = (node) => {
}; };
var DANGEROUS_PROTOCOL = /^\s*(javascript|data|vbscript):/i; var DANGEROUS_PROTOCOL = /^\s*(javascript|data|vbscript):/i;
var isDangerousAttr = (key) => key === "src" || key === "href" || key.startsWith("on"); var isDangerousAttr = (key) => key === "src" || key === "href" || key.startsWith("on");
var validateAttr = (key, val) => { var applyProp = (elem, key, value, isSVG) => {
if (val == null || val === false) if (value == null || value === false) {
return null; if (key === "class" || key === "className")
if (isDangerousAttr(key) && DANGEROUS_PROTOCOL.test(String(val))) elem.className = "";
return "#"; else if (key in elem && !isSVG)
return val; elem[key] = "";
else
elem.removeAttribute(key);
return;
}
if (key === "class" || key === "className") {
elem.className = value;
} else if (key === "style" && typeof value === "object") {
Object.assign(elem.style, value);
} else if (key in elem && !isSVG) {
elem[key] = value;
} else if (isSVG) {
if (key.startsWith("xlink:")) {
elem.setAttributeNS("http://www.w3.org/1999/xlink", key, value);
} else if (key === "xmlns" || key.startsWith("xmlns:")) {
elem.setAttributeNS("http://www.w3.org/2000/xmlns/", key, value);
} else {
elem.setAttribute(key, value === true ? "" : value);
}
} else {
elem.setAttribute(key, value === true ? "" : value);
}
}; };
var Tag2 = (tag, props = {}, children = []) => { var Tag2 = (tag, props = {}, children = []) => {
if (props instanceof Node || isArr(props) || props && typeof props !== "object") { if (props instanceof Node || isArr(props) || props && typeof props !== "object") {
@@ -234,14 +255,14 @@ var Tag2 = (tag, props = {}, children = []) => {
ctx._mounts = effect._mounts || []; ctx._mounts = effect._mounts || [];
ctx._cleanups = effect._cleanups || new Set; ctx._cleanups = effect._cleanups || new Set;
const result = effect._result; const result = effect._result;
const attachLifecycle = (node) => { const attach = (node) => {
if (node && typeof node === "object" && !node._isRuntime) { if (node && typeof node === "object" && !node._isRuntime) {
node._mounts = ctx._mounts; node._mounts = ctx._mounts;
node._cleanups = ctx._cleanups; node._cleanups = ctx._cleanups;
node._ownerEffect = effect; node._ownerEffect = effect;
} }
}; };
isArr(result) ? result.forEach(attachLifecycle) : attachLifecycle(result); isArr(result) ? result.forEach(attach) : attach(result);
if (result == null) if (result == null)
return null; return null;
if (result instanceof Node || isArr(result) && result.every((n) => n instanceof Node)) if (result instanceof Node || isArr(result) && result.every((n) => n instanceof Node))
@@ -267,15 +288,10 @@ var Tag2 = (tag, props = {}, children = []) => {
onUnmount(off); onUnmount(off);
} else if (isFunc(value)) { } else if (isFunc(value)) {
const effect = createEffect(() => { const effect = createEffect(() => {
const val = validateAttr(key, value()); let val = value();
if (key === "class") if (isDangerousAttr(key) && DANGEROUS_PROTOCOL.test(String(val)))
elem.className = val || ""; val = "#";
else if (val == null) applyProp(elem, key, val, isSVG);
elem.removeAttribute(key);
else if (key in elem && !isSVG)
elem[key] = val;
else
elem.setAttribute(key, val === true ? "" : val);
}); });
effect(); effect();
elem._cleanups.add(() => dispose(effect)); elem._cleanups.add(() => dispose(effect));
@@ -285,18 +301,16 @@ var Tag2 = (tag, props = {}, children = []) => {
elem.addEventListener(eventType, (ev) => value(ev.target[key])); elem.addEventListener(eventType, (ev) => value(ev.target[key]));
} }
} else { } else {
const val = validateAttr(key, value); let val = value;
if (val != null) { if (isDangerousAttr(key) && DANGEROUS_PROTOCOL.test(String(val)))
if (key in elem && !isSVG) val = "#";
elem[key] = val; if (val != null)
else applyProp(elem, key, val, isSVG);
elem.setAttribute(key, val === true ? "" : val);
} }
} }
} const mountChild = (child) => {
const append = (child) => {
if (isArr(child)) if (isArr(child))
return child.forEach(append); return child.forEach(mountChild);
if (isFunc(child)) { if (isFunc(child)) {
const anchor = doc.createTextNode(""); const anchor = doc.createTextNode("");
elem.appendChild(anchor); elem.appendChild(anchor);
@@ -333,7 +347,7 @@ var Tag2 = (tag, props = {}, children = []) => {
node._mounts.forEach((fn) => fn()); node._mounts.forEach((fn) => fn());
} }
}; };
append(children); mountChild(children);
return elem; return elem;
}; };
var createView = (renderFn) => { var createView = (renderFn) => {
@@ -1781,7 +1795,9 @@ var Tabs = (props) => {
const panel = Tag("div", { const panel = Tag("div", {
class: "tab-content bg-base-100 border-base-300 p-6", class: "tab-content bg-base-100 border-base-300 p-6",
style: () => isActive() ? "display: block" : "display: none" style: () => isActive() ? "display: block" : "display: none"
}, () => val(item.content)); }, [
Tag("div", { class: "tab-content-inner" }, () => val(item.content))
]);
elements.push(panel); elements.push(panel);
} }
return elements; return elements;

File diff suppressed because one or more lines are too long

98
dist/sigpro-ui.js vendored
View File

@@ -110,6 +110,10 @@
if (activeOwner) if (activeOwner)
(activeOwner._cleanups ||= new Set).add(fn); (activeOwner._cleanups ||= new Set).add(fn);
}; };
var onMount = (fn) => {
if (activeOwner)
(activeOwner._mounts ||= []).push(fn);
};
var createEffect = (fn, isComputed = false) => { var createEffect = (fn, isComputed = false) => {
const effect = () => { const effect = () => {
if (effect._disposed) if (effect._disposed)
@@ -185,20 +189,16 @@
activeEffect = prev; activeEffect = prev;
} }
}; };
var onMount = (fn) => { var $2 = (initialValue, storageKey = null) => {
if (activeOwner)
(activeOwner._mounts ||= []).push(fn);
};
var $2 = (value, storageKey = null) => {
const subs = new Set; const subs = new Set;
if (isFunc(value)) { if (isFunc(initialValue)) {
let cache, dirty = true; let cache, dirty = true;
const computed = () => { const computed = () => {
if (dirty) { if (dirty) {
const prev = activeEffect; const prev = activeEffect;
activeEffect = computed; activeEffect = computed;
try { try {
const next = value(); const next = initialValue();
if (!Object.is(cache, next)) { if (!Object.is(cache, next)) {
cache = next; cache = next;
dirty = false; dirty = false;
@@ -233,20 +233,20 @@
} }
if (storageKey) if (storageKey)
try { try {
value = JSON.parse(localStorage.getItem(storageKey)) ?? value; initialValue = JSON.parse(localStorage.getItem(storageKey)) ?? initialValue;
} catch (e) {} } catch (e) {}
return (...args) => { return (...args) => {
if (args.length) { if (args.length) {
const next = isFunc(args[0]) ? args[0](value) : args[0]; const next = isFunc(args[0]) ? args[0](initialValue) : args[0];
if (!Object.is(value, next)) { if (!Object.is(initialValue, next)) {
value = next; initialValue = next;
if (storageKey) if (storageKey)
localStorage.setItem(storageKey, JSON.stringify(value)); localStorage.setItem(storageKey, JSON.stringify(initialValue));
trackUpdate(subs, true); trackUpdate(subs, true);
} }
} }
trackUpdate(subs); trackUpdate(subs);
return value; return initialValue;
}; };
}; };
var Watch2 = (sources, callback) => { var Watch2 = (sources, callback) => {
@@ -274,12 +274,33 @@
}; };
var DANGEROUS_PROTOCOL = /^\s*(javascript|data|vbscript):/i; var DANGEROUS_PROTOCOL = /^\s*(javascript|data|vbscript):/i;
var isDangerousAttr = (key) => key === "src" || key === "href" || key.startsWith("on"); var isDangerousAttr = (key) => key === "src" || key === "href" || key.startsWith("on");
var validateAttr = (key, val) => { var applyProp = (elem, key, value, isSVG) => {
if (val == null || val === false) if (value == null || value === false) {
return null; if (key === "class" || key === "className")
if (isDangerousAttr(key) && DANGEROUS_PROTOCOL.test(String(val))) elem.className = "";
return "#"; else if (key in elem && !isSVG)
return val; elem[key] = "";
else
elem.removeAttribute(key);
return;
}
if (key === "class" || key === "className") {
elem.className = value;
} else if (key === "style" && typeof value === "object") {
Object.assign(elem.style, value);
} else if (key in elem && !isSVG) {
elem[key] = value;
} else if (isSVG) {
if (key.startsWith("xlink:")) {
elem.setAttributeNS("http://www.w3.org/1999/xlink", key, value);
} else if (key === "xmlns" || key.startsWith("xmlns:")) {
elem.setAttributeNS("http://www.w3.org/2000/xmlns/", key, value);
} else {
elem.setAttribute(key, value === true ? "" : value);
}
} else {
elem.setAttribute(key, value === true ? "" : value);
}
}; };
var Tag2 = (tag, props = {}, children = []) => { var Tag2 = (tag, props = {}, children = []) => {
if (props instanceof Node || isArr(props) || props && typeof props !== "object") { if (props instanceof Node || isArr(props) || props && typeof props !== "object") {
@@ -300,14 +321,14 @@
ctx._mounts = effect._mounts || []; ctx._mounts = effect._mounts || [];
ctx._cleanups = effect._cleanups || new Set; ctx._cleanups = effect._cleanups || new Set;
const result = effect._result; const result = effect._result;
const attachLifecycle = (node) => { const attach = (node) => {
if (node && typeof node === "object" && !node._isRuntime) { if (node && typeof node === "object" && !node._isRuntime) {
node._mounts = ctx._mounts; node._mounts = ctx._mounts;
node._cleanups = ctx._cleanups; node._cleanups = ctx._cleanups;
node._ownerEffect = effect; node._ownerEffect = effect;
} }
}; };
isArr(result) ? result.forEach(attachLifecycle) : attachLifecycle(result); isArr(result) ? result.forEach(attach) : attach(result);
if (result == null) if (result == null)
return null; return null;
if (result instanceof Node || isArr(result) && result.every((n) => n instanceof Node)) if (result instanceof Node || isArr(result) && result.every((n) => n instanceof Node))
@@ -333,15 +354,10 @@
onUnmount(off); onUnmount(off);
} else if (isFunc(value)) { } else if (isFunc(value)) {
const effect = createEffect(() => { const effect = createEffect(() => {
const val = validateAttr(key, value()); let val = value();
if (key === "class") if (isDangerousAttr(key) && DANGEROUS_PROTOCOL.test(String(val)))
elem.className = val || ""; val = "#";
else if (val == null) applyProp(elem, key, val, isSVG);
elem.removeAttribute(key);
else if (key in elem && !isSVG)
elem[key] = val;
else
elem.setAttribute(key, val === true ? "" : val);
}); });
effect(); effect();
elem._cleanups.add(() => dispose(effect)); elem._cleanups.add(() => dispose(effect));
@@ -351,18 +367,16 @@
elem.addEventListener(eventType, (ev) => value(ev.target[key])); elem.addEventListener(eventType, (ev) => value(ev.target[key]));
} }
} else { } else {
const val = validateAttr(key, value); let val = value;
if (val != null) { if (isDangerousAttr(key) && DANGEROUS_PROTOCOL.test(String(val)))
if (key in elem && !isSVG) val = "#";
elem[key] = val; if (val != null)
else applyProp(elem, key, val, isSVG);
elem.setAttribute(key, val === true ? "" : val);
} }
} }
} const mountChild = (child) => {
const append = (child) => {
if (isArr(child)) if (isArr(child))
return child.forEach(append); return child.forEach(mountChild);
if (isFunc(child)) { if (isFunc(child)) {
const anchor = doc.createTextNode(""); const anchor = doc.createTextNode("");
elem.appendChild(anchor); elem.appendChild(anchor);
@@ -399,7 +413,7 @@
node._mounts.forEach((fn) => fn()); node._mounts.forEach((fn) => fn());
} }
}; };
append(children); mountChild(children);
return elem; return elem;
}; };
var createView = (renderFn) => { var createView = (renderFn) => {
@@ -1847,7 +1861,9 @@
const panel = Tag("div", { const panel = Tag("div", {
class: "tab-content bg-base-100 border-base-300 p-6", class: "tab-content bg-base-100 border-base-300 p-6",
style: () => isActive() ? "display: block" : "display: none" style: () => isActive() ? "display: block" : "display: none"
}, () => val(item.content)); }, [
Tag("div", { class: "tab-content-inner" }, () => val(item.content))
]);
elements.push(panel); elements.push(panel);
} }
return elements; return elements;

File diff suppressed because one or more lines are too long

98
docs/sigpro-ui.min.js vendored
View File

@@ -110,6 +110,10 @@
if (activeOwner) if (activeOwner)
(activeOwner._cleanups ||= new Set).add(fn); (activeOwner._cleanups ||= new Set).add(fn);
}; };
var onMount = (fn) => {
if (activeOwner)
(activeOwner._mounts ||= []).push(fn);
};
var createEffect = (fn, isComputed = false) => { var createEffect = (fn, isComputed = false) => {
const effect = () => { const effect = () => {
if (effect._disposed) if (effect._disposed)
@@ -185,20 +189,16 @@
activeEffect = prev; activeEffect = prev;
} }
}; };
var onMount = (fn) => { var $2 = (initialValue, storageKey = null) => {
if (activeOwner)
(activeOwner._mounts ||= []).push(fn);
};
var $2 = (value, storageKey = null) => {
const subs = new Set; const subs = new Set;
if (isFunc(value)) { if (isFunc(initialValue)) {
let cache, dirty = true; let cache, dirty = true;
const computed = () => { const computed = () => {
if (dirty) { if (dirty) {
const prev = activeEffect; const prev = activeEffect;
activeEffect = computed; activeEffect = computed;
try { try {
const next = value(); const next = initialValue();
if (!Object.is(cache, next)) { if (!Object.is(cache, next)) {
cache = next; cache = next;
dirty = false; dirty = false;
@@ -233,20 +233,20 @@
} }
if (storageKey) if (storageKey)
try { try {
value = JSON.parse(localStorage.getItem(storageKey)) ?? value; initialValue = JSON.parse(localStorage.getItem(storageKey)) ?? initialValue;
} catch (e) {} } catch (e) {}
return (...args) => { return (...args) => {
if (args.length) { if (args.length) {
const next = isFunc(args[0]) ? args[0](value) : args[0]; const next = isFunc(args[0]) ? args[0](initialValue) : args[0];
if (!Object.is(value, next)) { if (!Object.is(initialValue, next)) {
value = next; initialValue = next;
if (storageKey) if (storageKey)
localStorage.setItem(storageKey, JSON.stringify(value)); localStorage.setItem(storageKey, JSON.stringify(initialValue));
trackUpdate(subs, true); trackUpdate(subs, true);
} }
} }
trackUpdate(subs); trackUpdate(subs);
return value; return initialValue;
}; };
}; };
var Watch2 = (sources, callback) => { var Watch2 = (sources, callback) => {
@@ -274,12 +274,33 @@
}; };
var DANGEROUS_PROTOCOL = /^\s*(javascript|data|vbscript):/i; var DANGEROUS_PROTOCOL = /^\s*(javascript|data|vbscript):/i;
var isDangerousAttr = (key) => key === "src" || key === "href" || key.startsWith("on"); var isDangerousAttr = (key) => key === "src" || key === "href" || key.startsWith("on");
var validateAttr = (key, val) => { var applyProp = (elem, key, value, isSVG) => {
if (val == null || val === false) if (value == null || value === false) {
return null; if (key === "class" || key === "className")
if (isDangerousAttr(key) && DANGEROUS_PROTOCOL.test(String(val))) elem.className = "";
return "#"; else if (key in elem && !isSVG)
return val; elem[key] = "";
else
elem.removeAttribute(key);
return;
}
if (key === "class" || key === "className") {
elem.className = value;
} else if (key === "style" && typeof value === "object") {
Object.assign(elem.style, value);
} else if (key in elem && !isSVG) {
elem[key] = value;
} else if (isSVG) {
if (key.startsWith("xlink:")) {
elem.setAttributeNS("http://www.w3.org/1999/xlink", key, value);
} else if (key === "xmlns" || key.startsWith("xmlns:")) {
elem.setAttributeNS("http://www.w3.org/2000/xmlns/", key, value);
} else {
elem.setAttribute(key, value === true ? "" : value);
}
} else {
elem.setAttribute(key, value === true ? "" : value);
}
}; };
var Tag2 = (tag, props = {}, children = []) => { var Tag2 = (tag, props = {}, children = []) => {
if (props instanceof Node || isArr(props) || props && typeof props !== "object") { if (props instanceof Node || isArr(props) || props && typeof props !== "object") {
@@ -300,14 +321,14 @@
ctx._mounts = effect._mounts || []; ctx._mounts = effect._mounts || [];
ctx._cleanups = effect._cleanups || new Set; ctx._cleanups = effect._cleanups || new Set;
const result = effect._result; const result = effect._result;
const attachLifecycle = (node) => { const attach = (node) => {
if (node && typeof node === "object" && !node._isRuntime) { if (node && typeof node === "object" && !node._isRuntime) {
node._mounts = ctx._mounts; node._mounts = ctx._mounts;
node._cleanups = ctx._cleanups; node._cleanups = ctx._cleanups;
node._ownerEffect = effect; node._ownerEffect = effect;
} }
}; };
isArr(result) ? result.forEach(attachLifecycle) : attachLifecycle(result); isArr(result) ? result.forEach(attach) : attach(result);
if (result == null) if (result == null)
return null; return null;
if (result instanceof Node || isArr(result) && result.every((n) => n instanceof Node)) if (result instanceof Node || isArr(result) && result.every((n) => n instanceof Node))
@@ -333,15 +354,10 @@
onUnmount(off); onUnmount(off);
} else if (isFunc(value)) { } else if (isFunc(value)) {
const effect = createEffect(() => { const effect = createEffect(() => {
const val = validateAttr(key, value()); let val = value();
if (key === "class") if (isDangerousAttr(key) && DANGEROUS_PROTOCOL.test(String(val)))
elem.className = val || ""; val = "#";
else if (val == null) applyProp(elem, key, val, isSVG);
elem.removeAttribute(key);
else if (key in elem && !isSVG)
elem[key] = val;
else
elem.setAttribute(key, val === true ? "" : val);
}); });
effect(); effect();
elem._cleanups.add(() => dispose(effect)); elem._cleanups.add(() => dispose(effect));
@@ -351,18 +367,16 @@
elem.addEventListener(eventType, (ev) => value(ev.target[key])); elem.addEventListener(eventType, (ev) => value(ev.target[key]));
} }
} else { } else {
const val = validateAttr(key, value); let val = value;
if (val != null) { if (isDangerousAttr(key) && DANGEROUS_PROTOCOL.test(String(val)))
if (key in elem && !isSVG) val = "#";
elem[key] = val; if (val != null)
else applyProp(elem, key, val, isSVG);
elem.setAttribute(key, val === true ? "" : val);
} }
} }
} const mountChild = (child) => {
const append = (child) => {
if (isArr(child)) if (isArr(child))
return child.forEach(append); return child.forEach(mountChild);
if (isFunc(child)) { if (isFunc(child)) {
const anchor = doc.createTextNode(""); const anchor = doc.createTextNode("");
elem.appendChild(anchor); elem.appendChild(anchor);
@@ -399,7 +413,7 @@
node._mounts.forEach((fn) => fn()); node._mounts.forEach((fn) => fn());
} }
}; };
append(children); mountChild(children);
return elem; return elem;
}; };
var createView = (renderFn) => { var createView = (renderFn) => {
@@ -1847,7 +1861,9 @@
const panel = Tag("div", { const panel = Tag("div", {
class: "tab-content bg-base-100 border-base-300 p-6", class: "tab-content bg-base-100 border-base-300 p-6",
style: () => isActive() ? "display: block" : "display: none" style: () => isActive() ? "display: block" : "display: none"
}, () => val(item.content)); }, [
Tag("div", { class: "tab-content-inner" }, () => val(item.content))
]);
elements.push(panel); elements.push(panel);
} }
return elements; return elements;

File diff suppressed because one or more lines are too long

View File

@@ -49,11 +49,14 @@ export const Tabs = (props) => {
} }
elements.push(button); elements.push(button);
// Contenido (tab-content) - debe ir inmediatamente después del botón // Contenido (tab-content) - borde exterior estático
const panel = Tag("div", { const panel = Tag("div", {
class: "tab-content bg-base-100 border-base-300 p-6", class: "tab-content bg-base-100 border-base-300 p-6",
style: () => isActive() ? "display: block" : "display: none" style: () => isActive() ? "display: block" : "display: none"
}, () => val(item.content)); }, [
// Contenedor interno con animación
Tag("div", { class: "tab-content-inner" }, () => val(item.content))
]);
elements.push(panel); elements.push(panel);
} }

View File

@@ -110,3 +110,32 @@
color: oklch(28% 0.01 260); /* Gris oscuro cuando tiene valor */ color: oklch(28% 0.01 260); /* Gris oscuro cuando tiene valor */
font-size: 1.1rem; /* Mantiene el mismo tamaño */ font-size: 1.1rem; /* Mantiene el mismo tamaño */
} }
.collapse .collapse-content {
transform: scaleY(0);
transform-origin: top;
transition: transform 0.3s cubic-bezier(0.4, 0, 0.2, 1);
height: 0;
opacity: 0;
}
.collapse:has(input:checked) .collapse-content {
transform: scaleY(1);
height: auto;
opacity: 1;
}
.tab-content-inner {
animation: tabFadeIn 0.3s cubic-bezier(0.4, 0, 0.2, 1);
transform-origin: top;
}
@keyframes tabFadeIn {
from {
opacity: 0;
transform: scaleY(0.95);
}
to {
opacity: 1;
transform: scaleY(1);
}
}

View File

@@ -34,6 +34,10 @@ const onUnmount = fn => {
if (activeOwner) (activeOwner._cleanups ||= new Set()).add(fn) if (activeOwner) (activeOwner._cleanups ||= new Set()).add(fn)
} }
const onMount = fn => {
if (activeOwner) (activeOwner._mounts ||= []).push(fn)
}
const createEffect = (fn, isComputed = false) => { const createEffect = (fn, isComputed = false) => {
const effect = () => { const effect = () => {
if (effect._disposed) return if (effect._disposed) return
@@ -99,20 +103,16 @@ const untrack = fn => {
try { return fn() } finally { activeEffect = prev } try { return fn() } finally { activeEffect = prev }
} }
const onMount = fn => { const $ = (initialValue, storageKey = null) => {
if (activeOwner) (activeOwner._mounts ||= []).push(fn)
}
const $ = (value, storageKey = null) => {
const subs = new Set() const subs = new Set()
if (isFunc(value)) { if (isFunc(initialValue)) {
let cache, dirty = true let cache, dirty = true
const computed = () => { const computed = () => {
if (dirty) { if (dirty) {
const prev = activeEffect const prev = activeEffect
activeEffect = computed activeEffect = computed
try { try {
const next = value() const next = initialValue()
if (!Object.is(cache, next)) { if (!Object.is(cache, next)) {
cache = next cache = next
dirty = false dirty = false
@@ -140,18 +140,18 @@ const $ = (value, storageKey = null) => {
if (activeOwner) onUnmount(computed.stop) if (activeOwner) onUnmount(computed.stop)
return computed return computed
} }
if (storageKey) try { value = JSON.parse(localStorage.getItem(storageKey)) ?? value } catch (e) {} if (storageKey) try { initialValue = JSON.parse(localStorage.getItem(storageKey)) ?? initialValue } catch (e) {}
return (...args) => { return (...args) => {
if (args.length) { if (args.length) {
const next = isFunc(args[0]) ? args[0](value) : args[0] const next = isFunc(args[0]) ? args[0](initialValue) : args[0]
if (!Object.is(value, next)) { if (!Object.is(initialValue, next)) {
value = next initialValue = next
if (storageKey) localStorage.setItem(storageKey, JSON.stringify(value)) if (storageKey) localStorage.setItem(storageKey, JSON.stringify(initialValue))
trackUpdate(subs, true) trackUpdate(subs, true)
} }
} }
trackUpdate(subs) trackUpdate(subs)
return value return initialValue
} }
} }
@@ -181,33 +181,29 @@ const cleanupNode = node => {
const DANGEROUS_PROTOCOL = /^\s*(javascript|data|vbscript):/i const DANGEROUS_PROTOCOL = /^\s*(javascript|data|vbscript):/i
const isDangerousAttr = key => key === 'src' || key === 'href' || key.startsWith('on') const isDangerousAttr = key => key === 'src' || key === 'href' || key.startsWith('on')
const validateAttr = (key, val) => { const applyProp = (elem, key, value, isSVG) => {
if (val == null || val === false) return null if (value == null || value === false) {
if (isDangerousAttr(key) && DANGEROUS_PROTOCOL.test(String(val))) return '#' if (key === 'class' || key === 'className') elem.className = ''
return val else if (key in elem && !isSVG) elem[key] = ''
} else elem.removeAttribute(key)
return
const setProperty = (elem, key, val, isSVG) => { }
val = validateAttr(key, val) if (key === 'class' || key === 'className') {
if (key === 'class' || key === 'className') elem.className = val || '' elem.className = value
else if (key === 'style' && typeof val === 'object') Object.assign(elem.style, val) } else if (key === 'style' && typeof value === 'object') {
else if (key in elem && !isSVG) elem[key] = val Object.assign(elem.style, value)
else if (isSVG) { } else if (key in elem && !isSVG) {
elem[key] = value
} else if (isSVG) {
if (key.startsWith('xlink:')) { if (key.startsWith('xlink:')) {
if (val == null || val === false) elem.removeAttributeNS('http://www.w3.org/1999/xlink', key.slice(6)) elem.setAttributeNS('http://www.w3.org/1999/xlink', key, value)
else elem.setAttributeNS('http://www.w3.org/1999/xlink', key, val)
} else if (key === 'xmlns' || key.startsWith('xmlns:')) { } else if (key === 'xmlns' || key.startsWith('xmlns:')) {
if (val == null || val === false) elem.removeAttributeNS('http://www.w3.org/2000/xmlns/', key) elem.setAttributeNS('http://www.w3.org/2000/xmlns/', key, value)
else elem.setAttributeNS('http://www.w3.org/2000/xmlns/', key, val)
} else { } else {
if (val == null || val === false) elem.removeAttribute(key) elem.setAttribute(key, value === true ? '' : value)
else if (val === true) elem.setAttribute(key, '')
else elem.setAttribute(key, val)
} }
} else { } else {
if (val == null || val === false) elem.removeAttribute(key) elem.setAttribute(key, value === true ? '' : value)
else if (val === true) elem.setAttribute(key, '')
else elem.setAttribute(key, val)
} }
} }
@@ -230,14 +226,14 @@ const Tag = (tag, props = {}, children = []) => {
ctx._mounts = effect._mounts || [] ctx._mounts = effect._mounts || []
ctx._cleanups = effect._cleanups || new Set() ctx._cleanups = effect._cleanups || new Set()
const result = effect._result const result = effect._result
const attachLifecycle = node => { const attach = node => {
if (node && typeof node === 'object' && !node._isRuntime) { if (node && typeof node === 'object' && !node._isRuntime) {
node._mounts = ctx._mounts node._mounts = ctx._mounts
node._cleanups = ctx._cleanups node._cleanups = ctx._cleanups
node._ownerEffect = effect node._ownerEffect = effect
} }
} }
isArr(result) ? result.forEach(attachLifecycle) : attachLifecycle(result) isArr(result) ? result.forEach(attach) : attach(result)
if (result == null) return null if (result == null) return null
if (result instanceof Node || (isArr(result) && result.every(n => n instanceof Node))) return result if (result instanceof Node || (isArr(result) && result.every(n => n instanceof Node))) return result
return doc.createTextNode(String(result)) return doc.createTextNode(String(result))
@@ -262,11 +258,9 @@ const Tag = (tag, props = {}, children = []) => {
onUnmount(off) onUnmount(off)
} else if (isFunc(value)) { } else if (isFunc(value)) {
const effect = createEffect(() => { const effect = createEffect(() => {
const val = validateAttr(key, value()) let val = value()
if (key === "class") elem.className = val || "" if (isDangerousAttr(key) && DANGEROUS_PROTOCOL.test(String(val))) val = '#'
else if (val == null) elem.removeAttribute(key) applyProp(elem, key, val, isSVG)
else if (key in elem && !isSVG) elem[key] = val
else elem.setAttribute(key, val === true ? "" : val)
}) })
effect() effect()
elem._cleanups.add(() => dispose(effect)) elem._cleanups.add(() => dispose(effect))
@@ -276,16 +270,14 @@ const Tag = (tag, props = {}, children = []) => {
elem.addEventListener(eventType, ev => value(ev.target[key])) elem.addEventListener(eventType, ev => value(ev.target[key]))
} }
} else { } else {
const val = validateAttr(key, value) let val = value
if (val != null) { if (isDangerousAttr(key) && DANGEROUS_PROTOCOL.test(String(val))) val = '#'
if (key in elem && !isSVG) elem[key] = val if (val != null) applyProp(elem, key, val, isSVG)
else elem.setAttribute(key, val === true ? "" : val)
}
} }
} }
const append = child => { const mountChild = child => {
if (isArr(child)) return child.forEach(append) if (isArr(child)) return child.forEach(mountChild)
if (isFunc(child)) { if (isFunc(child)) {
const anchor = doc.createTextNode("") const anchor = doc.createTextNode("")
elem.appendChild(anchor) elem.appendChild(anchor)
@@ -316,7 +308,7 @@ const Tag = (tag, props = {}, children = []) => {
if (node._mounts) node._mounts.forEach(fn => fn()) if (node._mounts) node._mounts.forEach(fn => fn())
} }
} }
append(children) mountChild(children)
return elem return elem
} }

1
src/sigpro.min.js vendored Normal file

File diff suppressed because one or more lines are too long