hele-router
Version:
Declarative routing for hele.
158 lines (148 loc) • 4.43 kB
JavaScript
import { Component, Context, createElement, Fragment } from 'hele';
const _last = (arr) => arr[arr.length - 1];
const defaultRouterName = 'router';
class Router extends Component {
constructor(props, context) {
super(props, context);
this.name = props.name;
}
render() {
return (createElement(Context, { value: { [this.name]: this } }, this.props.children));
}
}
Router.defaultProps = {
name: defaultRouterName
};
class HistoryRouter extends Router {
constructor(props, context) {
super(props, context);
this.onpopstate = (e) => {
this.update(location.pathname);
};
this.state = location.pathname;
}
onDidMount() {
window.addEventListener('popstate', this.onpopstate);
}
onWillUnmount() {
window.removeEventListener('popstate', this.onpopstate);
}
push(path) {
if (!this.props.noHash && path.lastIndexOf('#') === -1) {
path += location.hash;
}
history.pushState(null, '', path);
this.update(location.pathname);
}
pop() {
history.back();
}
}
const hashHistory = new Array(location.hash);
class HashRouter extends Router {
constructor(props, context) {
super(props, context);
this.onhashchange = () => {
this.update(location.hash.slice(1));
};
this.state = _last(hashHistory).slice(1);
}
onDidMount() {
window.addEventListener('hashchange', this.onhashchange);
}
onWillUnmount() {
window.removeEventListener('hashchange', this.onhashchange);
}
push(path) {
const hash = '#' + path;
if (_last(hashHistory) !== hash) {
location.hash = hash;
hashHistory.push(hash);
}
}
pop() {
if (hashHistory.length > 1) {
hashHistory.pop();
const hash = _last(hashHistory);
location.hash = hash;
}
}
}
class Switch extends Router {
constructor(props, context) {
super(props, context);
this._history = new Array();
this._history.push(this.state = props.path);
}
push(path) {
const { _history } = this;
if (_last(_history) !== path) {
_history.push(path);
this.update(path);
}
}
pop() {
const { _history } = this;
if (_history.length > 1) {
_history.pop();
const path = _last(_history);
this.update(path);
}
}
}
Switch.defaultProps = {
...Router.defaultProps,
path: '/'
};
function testRoute(pathname, path, exact = false) {
return exact ? pathname === path : pathname.indexOf(path) >= 0;
}
class Route extends Component {
constructor(props, context) {
super(props, context);
this.state = testRoute(context[props.router].state, props.path, props.exact);
}
render() {
const { props, state } = this;
return props.render ? props.render(state) : (state && (props.component ?
createElement(props.component) :
createElement(Fragment, null, this.props.children)));
}
}
Route.defaultProps = {
router: defaultRouterName
};
class Link extends Component {
constructor(props, context) {
super(props, context);
this.onclick = this.onclick.bind(this);
}
render() {
const { router, back, onclick, ...attrs } = this.props;
return createElement("a", Object.assign({}, attrs, { onclick: this.onclick }), this.props.children);
}
onclick(event) {
const { props } = this;
let returnValue;
if (props.onclick) {
returnValue = props.onclick(event);
}
if (!event.defaultPrevented) {
event.preventDefault();
const router = this.context[props.router];
if (props.back) {
router.pop();
}
else if (props.href) {
router.push(props.href);
}
}
return returnValue;
}
}
Link.defaultProps = {
href: 'javascript:;',
back: false,
router: defaultRouterName
};
export { _last, defaultRouterName, Router, HistoryRouter, HashRouter, Switch, testRoute, Route, Link };