add router-hook

This commit is contained in:
2026-04-13 20:41:58 +02:00
parent 0729f00fe2
commit 1018c0bf9f

View File

@@ -11,6 +11,7 @@ let isFlushing = false
const effectQueue = new Set() const effectQueue = new Set()
const MOUNTED_NODES = new WeakMap() const MOUNTED_NODES = new WeakMap()
// effect cleanup
const dispose = eff => { const dispose = eff => {
if (!eff || eff._disposed) return if (!eff || eff._disposed) return
eff._disposed = true eff._disposed = true
@@ -32,6 +33,7 @@ const dispose = eff => {
} }
} }
// helpers
const onMount = fn => { const onMount = fn => {
if (activeOwner) (activeOwner._mounts ||= []).push(fn) if (activeOwner) (activeOwner._mounts ||= []).push(fn)
} }
@@ -40,6 +42,22 @@ const onUnmount = fn => {
if (activeOwner) (activeOwner._cleanups ||= new Set()).add(fn) if (activeOwner) (activeOwner._cleanups ||= new Set()).add(fn)
} }
const set = (signal, path, value) => {
if (value === undefined) return signal(isFunc(path) ? path(signal()) : path)
const keys = path.split('.'), root = { ...signal() }
let acc = root, k
for (k of keys.slice(0, -1)) acc = acc[k] = { ...(acc[k] || {}) }
acc[keys.at(-1)] = value
signal(root)
}
const untrack = fn => {
const p = activeEffect
activeEffect = null
try { return fn() } finally { activeEffect = p }
}
// effect creation
const createEffect = (fn, isComputed = false) => { const createEffect = (fn, isComputed = false) => {
const effect = () => { const effect = () => {
if (effect._disposed) return if (effect._disposed) return
@@ -99,12 +117,7 @@ const trackUpdate = (subs, trigger = false) => {
} }
} }
const untrack = fn => { // signal creation
const p = activeEffect
activeEffect = null
try { return fn() } finally { activeEffect = p }
}
const $ = (val, key = null) => { const $ = (val, key = null) => {
const subs = new Set() const subs = new Set()
if (isFunc(val)) { if (isFunc(val)) {
@@ -157,6 +170,7 @@ const $ = (val, key = null) => {
} }
} }
// create Watch
const Watch = (sources, cb) => { const Watch = (sources, cb) => {
if (cb === undefined) { if (cb === undefined) {
const effect = createEffect(sources) const effect = createEffect(sources)
@@ -195,6 +209,7 @@ const validateAttr = (key, val) => {
return val return val
} }
// create Tag
const Tag = (tag, props = {}, children = []) => { const Tag = (tag, props = {}, children = []) => {
if (props instanceof Node || isArr(props) || !isObj(props)) { if (props instanceof Node || isArr(props) || !isObj(props)) {
children = props children = props
@@ -307,6 +322,7 @@ const Tag = (tag, props = {}, children = []) => {
return el return el
} }
// create Render
const Render = renderFn => { const Render = renderFn => {
const cleanups = new Set() const cleanups = new Set()
const mounts = [] const mounts = []
@@ -349,6 +365,7 @@ const Render = renderFn => {
} }
} }
// create If
const If = (cond, ifYes, ifNot = null, trans = null) => { const If = (cond, ifYes, ifNot = null, trans = null) => {
const anchor = doc.createTextNode("") const anchor = doc.createTextNode("")
const root = Tag("div", { style: "display:contents" }, [anchor]) const root = Tag("div", { style: "display:contents" }, [anchor])
@@ -390,6 +407,7 @@ const If = (cond, ifYes, ifNot = null, trans = null) => {
return root return root
} }
// create For
const For = (src, itemFn, keyFn) => { const For = (src, itemFn, keyFn) => {
const anchor = doc.createTextNode("") const anchor = doc.createTextNode("")
const root = Tag("div", { style: "display:contents" }, [anchor]) const root = Tag("div", { style: "display:contents" }, [anchor])
@@ -420,13 +438,14 @@ const For = (src, itemFn, keyFn) => {
return root return root
} }
// create Router
const Router = routes => { const Router = routes => {
const getHash = () => window.location.hash.slice(1) || "/" const getHash = () => window.location.hash.slice(1) || "/"
const path = $(getHash()) const path = $(getHash())
const handler = () => path(getHash()) const handler = () => path(getHash())
window.addEventListener("hashchange", handler) window.addEventListener("hashchange", handler)
onUnmount(() => window.removeEventListener("hashchange", handler)) onUnmount(() => window.removeEventListener("hashchange", handler))
const outlet = Tag("div", { class: "router-outlet" }) const hook = Tag("div", { class: "router-hook" })
let currentView = null let currentView = null
Watch([path], () => { Watch([path], () => {
const cur = path() const cur = path()
@@ -443,10 +462,10 @@ const Router = routes => {
}) })
Router.params(params) Router.params(params)
currentView = Render(() => isFunc(route.component) ? route.component(params) : route.component) currentView = Render(() => isFunc(route.component) ? route.component(params) : route.component)
outlet.replaceChildren(currentView.container) hook.replaceChildren(currentView.container)
} }
}) })
return outlet return hook
} }
Router.params = $({}) Router.params = $({})
Router.to = p => window.location.hash = p.replace(/^#?\/?/, "#/") Router.to = p => window.location.hash = p.replace(/^#?\/?/, "#/")
@@ -463,15 +482,6 @@ const Mount = (comp, target) => {
return inst return inst
} }
const set = (signal, path, value) => {
if (value === undefined) return signal(isFunc(path) ? path(signal()) : path)
const keys = path.split('.'), root = { ...signal() }
let acc = root, k
for (k of keys.slice(0, -1)) acc = acc[k] = { ...(acc[k] || {}) }
acc[keys.at(-1)] = value
signal(root)
}
const SigPro = Object.freeze({ $, Watch, Tag, Render, If, For, Router, Mount, onMount, onUnmount, set }) const SigPro = Object.freeze({ $, Watch, Tag, Render, If, For, Router, Mount, onMount, onUnmount, set })
if (typeof window !== "undefined") { if (typeof window !== "undefined") {