1.3.6 • Published 3 months ago
rutter v1.3.6
About
Rutter is a framework-agnostic, lightweight router. Built with URLPattern & History API. Internal reactivity is powered by Signal.
This library doesn't ship polyfill for
URLPattern
. You may consider installing urlpattern-polyfill.
Usage
VanillaJS
import { CreateHistory } from 'rutter'
const router = new CreateHistory({
routes: {
index: {
pathname: ''
},
about: {
pathname: '/about'
},
blog: {
pathname: '/blog'
},
blogDetail: {
pathname: '/blog/:id'
}
}
})
router.on('index') // boolean
router.onOneOf(['index', 'about']) // boolean
React bindings: via useState
/context
// router.(tsx|jsx)
import {
FC,
PropsWithChildren,
createContext,
useContext,
useEffect,
useState
} from 'react'
import { CreateHistory } from 'rutter'
export const {
redirect,
on,
summaryState,
routeState,
watchSummaryState,
watchRouteState
} = new CreateHistory({
routes: {
index: {
pathname: ''
},
about: {
pathname: '/about'
},
blog: {
pathname: '/blog'
},
blogDetail: {
pathname: '/blog/:id'
}
}
})
/**
* Although using with `context` is recommended for performance reason, you can directly use this hook if you don't want to store all the states in `context` tree.
*/
export const useRouterValues = () => {
const [routeStateValue, setRouteStateState] = useState(routeState)
const [summaryStateValue, setSummaryStateState] = useState(summaryState)
useEffect(() => watchRouteState(setRouteStateState), [])
useEffect(() => watchSummaryState(setSummaryStateState), [])
return {
routeState: routeStateValue,
summaryState: summaryStateValue
}
}
const context = createContext({
routeState,
summaryState
})
const useRouterContext = () => useContext(context)
export const RouterProvider: FC<PropsWithChildren> = ({ children }) => {
const value = useRouterValues()
return <context.Provider value={value}>{children}</context.Provider>
}
export const useRoute = () => {
const { routeState } = useRouterContext()
return routeState
}
// app.(tsx|jsx)
import { FC } from 'react'
import { on, redirect, useRoute, RouterProvider } from './router'
const Routing: FC = () => {
const { is404, ...restStates } = useRoute()
return (
<>
<nav>
<button onClick={() => redirect('index')}>Index</button>
<button onClick={() => redirect('blog')}>Blog</button>
<a href="/invalid-url">
<button>404</button>
</a>
</nav>
<fieldset>
<legend>Body:</legend>
<div>
{is404 ? (
<h1>404 Page</h1>
) : (
<>
{on('index') && <h1>Index Page</h1>}
{on('about') && <h1>About Page</h1>}
{on('blog') && (
<>
<h1>Blog Page</h1>
<button
onClick={() =>
redirect('blogDetail', {
params: {
id: 123
}
})
}
>
Blog Detail
</button>
</>
)}
{on('blogDetail') && <h1>Blog Detail Page</h1>}
</>
)}
</div>
</fieldset>
<fieldset>
<legend>Current route detail:</legend>
<code>
<pre>{JSON.stringify(restStates, null, 2)}</pre>
</code>
</fieldset>
</>
)
}
const App: FC = () => (
<RouterProvider>
<Routing />
</RouterProvider>
)
Vue bindings: via shallowRef
/computed
// router.(ts|js)
import { computed, shallowRef } from 'vue'
import { CreateHistory } from 'rutter'
import { mapValues } from 'lodash-es'
const router = new CreateHistory({
routes: {
index: {
pathname: ''
},
about: {
pathname: '/about'
},
blog: {
pathname: '/blog'
},
blogDetail: {
pathname: '/blog/:id'
}
}
})
const {
//
summaryState,
routeState,
watchSummaryState,
watchRouteState,
on
} = router
export const { redirect } = router
export const routerState = shallowRef(summaryState)
export const route = shallowRef(routeState)
export const is404 = computed(() => route.value.is404)
export const matches = computed(() => {
const { details } = routerState.value
type RouteNames = keyof typeof details
return mapValues(details, (_, name) => on(name as RouteNames))
})
watchSummaryState(state => {
routerState.value = state
})
watchRouteState(state => {
route.value = state
})
<script setup lang="ts">
// app.vue
import { redirect, route, matches, is404 } from './router'
</script>
<template>
<nav>
<button @click="() => redirect('index')">Index</button>
<button @click="() => redirect('blog')">Blog</button>
<a href="/invalid-url">
<button>404</button>
</a>
</nav>
<fieldset>
<legend>Body:</legend>
<div>
<h1 v-if="is404">404 Page</h1>
<template v-else>
<h1 v-if="matches.index">Index Page</h1>
<h1 v-if="matches.about">About Page</h1>
<template v-if="matches.blog">
<h1>Blog Page</h1>
<button
@click="() => redirect('blogDetail', { params: { id: 123 } })"
>
Blog Detail
</button>
</template>
<h1 v-if="matches.blogDetail">Blog Detail Page</h1>
</template>
</div>
</fieldset>
<fieldset>
<legend>Current route detail:</legend>
<code>
<pre>{{ route }}</pre>
</code>
</fieldset>
</template>
Svelte bindings: via readable
/derived
// router.(ts|js)
import { readable, derived } from 'svelte/store'
import { CreateHistory } from 'rutter'
import { mapValues } from 'lodash-es'
const router = new CreateHistory({
routes: {
index: {
pathname: ''
},
about: {
pathname: '/about'
},
blog: {
pathname: '/blog'
},
blogDetail: {
pathname: '/blog/:id'
}
}
})
const { summaryState, routeState, watchSummaryState, watchRouteState } = router
export const { redirect, on, onOneOf } = router
export const route = readable(routeState, watchRouteState)
export const routerState = readable(summaryState, watchSummaryState)
export const matches = derived(routerState, ({ details }) =>
mapValues(details, (_, name) => on(name as keyof typeof details))
)
<script lang="ts">
// app.svelte
import { redirect, route, matches } from './router'
$: ({ is404, ...restState } = $route)
$: data = JSON.stringify(restState, null, 2)
</script>
<nav>
<button on:click={() => redirect('index')}>Index</button>
<button on:click={() => redirect('blog')}>Blog</button>
<a href="/invalid-url">
<button>404</button>
</a>
</nav>
<fieldset>
<legend>Body:</legend>
<div>
{#if is404}
<h1>404 Page</h1>
{:else}
{#if $matches.index}
<h1>Index Page</h1>
{/if}
{#if $matches.about}
<h1>About Page</h1>
{/if}
{#if $matches.blog}
<h1>Blog Page</h1>
<button
on:click={() => redirect('blogDetail', { params: { id: 123 } })}
>
Blog Detail
</button>
{/if}
{#if $matches.blogDetail}
<h1>Blog Detail Page</h1>
{/if}
{/if}
</div>
</fieldset>
<fieldset>
<legend>Current route detail:</legend>
<code>
<pre>{data}</pre>
</code>
</fieldset>
Documentation
Type API: https://paka.dev/npm/rutter/api
Development
pnpm i
pnpm dev
1.3.6
3 months ago
1.3.5
4 months ago
1.3.4
6 months ago
1.3.3
6 months ago
1.3.2
7 months ago
1.3.1
7 months ago
1.2.0
12 months ago
1.2.7
11 months ago
1.2.6
11 months ago
1.2.5
11 months ago
1.2.4
12 months ago
1.2.3
12 months ago
1.2.2
12 months ago
1.3.0
10 months ago
1.2.1
12 months ago
1.1.2
1 year ago
1.1.1
1 year ago
1.1.0
1 year ago
1.0.3
1 year ago
1.0.2
1 year ago
1.0.1
1 year ago
1.0.0
1 year ago