UNPKG

@logux/state

Version:

A tiny (152 bytes) state manager for React/Preact/Vue/Svelte with many atomic tree-shakable stores

112 lines (100 loc) 2.97 kB
import { createStore } from '../create-store/index.js' export function createRouter(routes) { let normalized = Object.keys(routes).map(name => { let value = routes[name] if (typeof value === 'string') { value = value.replace(/\/$/g, '') || '/' let names = (value.match(/\/:\w+/g) || []).map(i => i.slice(2)) let pattern = value .replace(/[\s!#$()+,.:<=?[\\\]^{|}]/g, '\\$&') .replace(/\/\\:\w+/g, '/([^/]+)') return [ name, RegExp('^' + pattern + '$', 'i'), (...matches) => matches.reduce((params, match, index) => { params[names[index]] = match return params }, {}), value ] } else { return [name, ...value] } }) let prev let parse = path => { path = path.replace(/\/$/, '') || '/' if (prev === path) return false prev = path for (let [route, pattern, cb] of normalized) { let match = path.match(pattern) if (match) { return { path, route, params: cb(...match.slice(1)) } } } } let click = event => { let link = event.target.closest('a') if ( !event.defaultPrevented && link && event.button === 0 && link.target !== '_blank' && link.dataset.noRouter == null && link.rel !== 'external' && !link.download && !event.metaKey && !event.ctrlKey && !event.shiftKey && !event.altKey ) { let url = new URL(link.href) if (url.origin === location.origin) { event.preventDefault() let changed = location.hash !== url.hash router.open(url.pathname) if (changed) { location.hash = url.hash if (url.hash === '' || url.hash === '#') { window.dispatchEvent(new HashChangeEvent('hashchange')) } } } } } let popstate = () => { let page = parse(location.pathname) if (page !== false) router.set(page) } let router = createStore(() => { let page = parse(location.pathname) if (page !== false) router.set(page) document.body.addEventListener('click', click) window.addEventListener('popstate', popstate) return () => { prev = undefined document.body.removeEventListener('click', click) window.removeEventListener('popstate', popstate) } }) router.routes = normalized router.open = path => { let page = parse(path) if (page !== false) { history.pushState(null, null, path) router.set(page) } } return router } export function getPagePath(router, name, params) { let route = router.routes.find(i => i[0] === name) if (process.env.NODE_ENV !== 'production') { if (!route[3]) throw new Error('RegExp routes are not supported') } return route[3].replace(/\/:\w+/g, i => '/' + params[i.slice(2)]) } export function openPage(router, name, params) { router.open(getPagePath(router, name, params)) }