diff --git a/src/Link.js b/src/Link.js deleted file mode 100644 index 8f977dc..0000000 --- a/src/Link.js +++ /dev/null @@ -1,28 +0,0 @@ -import { h } from "hyperapp" - -export function Link(props, children) { - var to = props.to - var location = props.location || window.location - - props.href = to - props.onclick = function(e) { - if ( - e.button !== 0 || - e.altKey || - e.metaKey || - e.ctrlKey || - e.shiftKey || - props.target === "_blank" || - e.currentTarget.origin !== location.origin - ) { - } else { - e.preventDefault() - - if (to !== location.pathname) { - history.pushState(location.pathname, "", to) - } - } - } - - return h("a", props, children) -} diff --git a/src/Route.js b/src/Route.js deleted file mode 100644 index 5ca625a..0000000 --- a/src/Route.js +++ /dev/null @@ -1,16 +0,0 @@ -import { parseRoute } from "./parseRoute" - -export function Route(props) { - var location = props.location || window.location - var match = parseRoute(props.path, location.pathname, { - exact: !props.parent - }) - - return ( - match && - props.render({ - match: match, - location: location - }) - ) -} diff --git a/src/createLink.js b/src/createLink.js new file mode 100644 index 0000000..d6086ab --- /dev/null +++ b/src/createLink.js @@ -0,0 +1,28 @@ +import { h } from "hyperapp" + +export var createLink = function(provider) { + return function(props, children) { + var to = props.to + props.href = to + props.onclick = function(e) { + if ( + e.button !== 0 || + e.altKey || + e.metaKey || + e.ctrlKey || + e.shiftKey || + props.target === "_blank" || + e.currentTarget.origin !== window.location.origin + ) { + } else { + e.preventDefault() + + if (to !== provider.get()) { + provider.go(to) + } + } + } + + return h("a", props, children) + } +} diff --git a/src/createRoute.js b/src/createRoute.js new file mode 100644 index 0000000..4aea03d --- /dev/null +++ b/src/createRoute.js @@ -0,0 +1,18 @@ +import { parseRoute } from "./parseRoute" + +export function createRoute(provider) { + return function(props) { + var location = provider.get() + var match = parseRoute(props.path, location, { + exact: !props.parent + }) + + return ( + match && + props.render({ + match: match, + location: location + }) + ) + } +} diff --git a/src/index.js b/src/index.js index 32ae51b..c4f5f34 100644 --- a/src/index.js +++ b/src/index.js @@ -1,5 +1,9 @@ -export { Link } from "./Link" -export { Route } from "./Route" export { Switch } from "./Switch" export { Redirect } from "./Redirect" -export { location } from "./location" +export { location, hash } from "./location" + +import { locationProvider } from './locationProvider' +import { createLink } from "./createLink" +import { createRoute } from "./createRoute" +export var Link = createLink(locationProvider) +export var Route = createRoute(locationProvider) diff --git a/src/location.js b/src/location.js index 1bd704e..316cad8 100644 --- a/src/location.js +++ b/src/location.js @@ -1,51 +1,31 @@ -function wrapHistory(keys) { - return keys.reduce(function(next, key) { - var fn = history[key] +import {locationProvider, hashProvider} from './locationProvider' +import {createLink} from './createLink' +import {createRoute} from './createRoute' - history[key] = function(data, title, url) { - fn.call(this, data, title, url) - dispatchEvent(new CustomEvent("pushstate", { detail: data })) - } - - return function() { - history[key] = fn - next && next() - } - }, null) -} - -export var location = { - state: { - pathname: window.location.pathname, - previous: window.location.pathname - }, - actions: { - go: function(pathname) { - history.pushState(null, "", pathname) - }, - set: function(data) { - return data - } - }, - subscribe: function(actions) { - function handleLocationChange(e) { - actions.set({ - pathname: window.location.pathname, - previous: e.detail - ? (window.location.previous = e.detail) - : window.location.previous - }) - } - - var unwrap = wrapHistory(["pushState", "replaceState"]) - - addEventListener("pushstate", handleLocationChange) - addEventListener("popstate", handleLocationChange) - - return function() { - removeEventListener("pushstate", handleLocationChange) - removeEventListener("popstate", handleLocationChange) - unwrap() - } - } +function createPreset(provider) { + return { + state: { + pathname: provider.get(), + previous: provider.get(), + }, + actions: { + go: function(pathname) { + provider.go(pathname) + }, + set: function(data) { + return data + }, + }, + subscribe: function(actions) { + return provider.subscribe(actions) + }, + } } +export var location = Object.assign(createPreset(locationProvider), { + Link: createLink(locationProvider), + Route: createRoute(locationProvider), +}) +export var hash = Object.assign(createPreset(hashProvider), { + Link: createLink(hashProvider), + Route: createRoute(hashProvider), +}) diff --git a/src/locationProvider.js b/src/locationProvider.js new file mode 100644 index 0000000..cc18eba --- /dev/null +++ b/src/locationProvider.js @@ -0,0 +1,77 @@ +function wrapHistory(keys) { + return keys.reduce(function(next, key) { + var fn = history[key] + + history[key] = function(data, title, url) { + fn.call(this, data, title, url) + dispatchEvent(new CustomEvent('pushstate', {detail: data})) + } + + return function() { + history[key] = fn + next && next() + } + }, null) +} + +function createProvider(methods) { + var _listeners = [] + var provider = { + _trigger: function(pathname, previous) { + for (var i = 0; i < _listeners.length; ++i) { + _listeners[i].set({pathname: pathname, previous: previous}) + } + }, + get: function() { + return methods.get() + }, + go: function(pathname) { + return methods.go(pathname, provider) + }, + subscribe: function(actions) { + function handleLocationChange(e) { + actions.set({ + pathname: window.location.pathname, + previous: e.detail + ? (window.location.previous = e.detail) + : window.location.previous, + }) + } + + var unwrap = wrapHistory(['pushState', 'replaceState']) + + addEventListener('pushstate', handleLocationChange) + addEventListener('popstate', handleLocationChange) + addEventListener('hashchange', handleLocationChange) + + return function() { + removeEventListener('pushstate', handleLocationChange) + removeEventListener('popstate', handleLocationChange) + removeEventListener('hashchange', handleLocationChange) + unwrap() + } + }, + } + return provider +} + +export var locationProvider = createProvider({ + get: function() { + return window.location.pathname + }, + go: function(pathname, provider) { + var prev = provider.get() + history.pushState(null, '', pathname) + provider._trigger(pathname, prev) + }, +}) +export var hashProvider = createProvider({ + get: function() { + return (window.location.hash || '#/').replace(/^#/, '') + }, + go: function(pathname, provider) { + var prev = provider.get() + window.location.hash = '#' + pathname + provider._trigger(pathname, prev) + }, +})