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

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