Skip to content
This repository was archived by the owner on Apr 6, 2025. It is now read-only.

Commit 215cb52

Browse files
committed
Hyperapp#V2 Router API Update
- Path-to-regexp usage (with cached paths) - Router context passing - Redirect as subscription - Location 'back' and 'go' methods - Browser history as subscription
1 parent 446431e commit 215cb52

File tree

9 files changed

+100
-98
lines changed

9 files changed

+100
-98
lines changed

package.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -50,6 +50,7 @@
5050
"babel-preset-env": "^1.6.1",
5151
"hyperapp": "^2.0.0",
5252
"jest": "^22.4.3",
53+
"path-to-regexp": "^2.4.0",
5354
"prettier": "^1.11.1",
5455
"rollup": "^0.57.1",
5556
"uglify-js": "^3.3.16",

src/Link.js

Lines changed: 12 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -1,27 +1,25 @@
11
import { h } from 'hyperapp'
2-
import { location } from './location'
2+
import { location } from './index'
33

4-
function onClick(state, link, e){
5-
if(
4+
const onClick = (state, link, e) => {
5+
const { pathname } = state.location
6+
if(!(
67
e.defaultPrevented ||
78
e.button !== 0 ||
89
e.altKey ||
910
e.metaKey ||
1011
e.ctrlKey ||
1112
e.shiftKey ||
12-
e.target.getAttribute("target") === "_blank" ||
13-
link.href === state.location.pathname
14-
){
15-
return state
16-
} else {
13+
e.target.getAttribute('target') === '_blank'
14+
)){
1715
e.preventDefault()
18-
return location(state, link.href)
19-
}
16+
return link.to !== pathname
17+
? [location, link.to] : state
18+
} else return state
2019
}
2120

22-
export function Link(props, childrens){
21+
export const Link = (props, children) => {
2322
props.onClick = props.onClick || [ onClick, props ]
2423
props.href = props.to
25-
delete props.to
26-
return h('a', props, childrens)
27-
}
24+
return h('a', props, children)
25+
}

src/Redirect.js

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
import { location, Route } from './index'
2+
3+
const fake = () => () => {}
4+
export const Redirect = (props) => {
5+
const { from, to } = props
6+
const render = typeof to === 'function' ? to : () => to
7+
const match = Route({...props, path: from, render })
8+
return { effect: match ? (props, dispatch) => {
9+
dispatch([location, match])
10+
return () => {}
11+
} : fake }
12+
}

src/Route.js

Lines changed: 31 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -1,23 +1,34 @@
1-
import { Switch } from './Switch'
2-
import { parseRoute } from './parseRoute'
1+
import pathToRegexp from 'path-to-regexp'
32

4-
export function Route(props, childrens){
5-
var location = props.location
6-
var path = props.path || ''
7-
var render = props.render || Switch
8-
var match = parseRoute(path, location.pathname, {
9-
exact: !props.parent
3+
const cache = {}
4+
const compile = (path, { exact: end, strict }, keys = []) => {
5+
const id = `${path}/${end}/${strict}`
6+
return cache[id] ? cache[id] : (cache[id] = {
7+
regexp: pathToRegexp(path, keys, { end, strict }), keys
108
})
11-
return (match && render(
12-
function(){
13-
var args = Array.prototype.slice.call(arguments, 0)
14-
args[0] = Object.assign({}, args[0], {
15-
location: location,
16-
path: path + args[0].path || '',
17-
})
18-
return Route.apply(this, args)
19-
},
20-
Object.assign({}, props, { match: match, location: location } ),
21-
childrens)
22-
)
9+
}
10+
11+
export const Route = (context, child) => {
12+
const {
13+
path = '',
14+
exact = false,
15+
strict = false,
16+
render = () => child,
17+
location = window.location
18+
} = context
19+
20+
const compiled = compile(path, { exact, strict })
21+
const match = compiled.regexp.exec(location.pathname)
22+
const [ url, ...values ] = match || []
23+
return match ? render({ route: {
24+
params: compiled.keys.reduce((params, key, index) =>
25+
Object.assign(params, {[ key.name ]: values[index]}), {}),
26+
context,
27+
path,
28+
url
29+
}}, (props, ...args) => Route.call(this, {
30+
...context, ...props,
31+
render: props.render || undefined,
32+
path: path + (props.path || ''),
33+
}, ...args)) : null
2334
}

src/Switch.js

Lines changed: 0 additions & 3 deletions
This file was deleted.

src/history.js

Lines changed: 24 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,25 @@
1-
export function history(location){
2-
if(location && window.location.pathname !== location.pathname){
3-
window.history.pushState(location, '', location.pathname)
4-
}
1+
import { location } from './index'
2+
3+
const effect = (_, dispatch) => {
4+
const handleLocationChange = () =>
5+
dispatch([ location, window.location.pathname ])
6+
addEventListener('popstate', handleLocationChange)
7+
8+
return ['pushState', 'replaceState'].reduce((next, key) => {
9+
const fn = history[key]
10+
history[key] = (data, tittle, url) => {
11+
!data.ignore && dispatch([location, url])
12+
return fn.call(history, data, tittle, url)
13+
}
14+
return () => {
15+
history[key] = fn
16+
next()
17+
}
18+
}, () => removeEventListener("popstate", handleLocationChange))
19+
}
20+
21+
export default location => {
22+
if(location && window.location.pathname !== location.pathname)
23+
history.pushState({ location, ignore: true }, '', location.pathname)
24+
return { effect }
525
}

src/index.js

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
export { location } from './location'
2-
export { history } from './history'
3-
export { Switch } from './Switch'
2+
export { default as history } from './history'
3+
export { Redirect } from './Redirect'
44
export { Route } from './Route'
55
export { Link } from './Link'

src/location.js

Lines changed: 18 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,19 @@
1-
export function location(state, path){
2-
return Object.assign({}, state, {
3-
location: {
4-
pathname: path || window.location.pathname,
5-
previous: state && state.location
6-
? state.location.pathname
7-
: window.location.pathname
8-
}
9-
})
1+
export const location = (state, path) => ({
2+
...state,
3+
location: {
4+
pathname: path,
5+
previous: state && state.location ? state.location : null
6+
}
7+
})
8+
9+
location.back = (state, n = 1) => ({
10+
...state,
11+
location: new Array(n).fill(n).reduce(location =>
12+
location.previous || location, state.location)
13+
})
14+
15+
location.go = url => (state, event) => {
16+
event.preventDefault()
17+
event.stopPropagation()
18+
return [location, url]
1019
}

src/parseRoute.js

Lines changed: 0 additions & 46 deletions
This file was deleted.

0 commit comments

Comments
 (0)