Created
January 6, 2020 06:19
-
-
Save janwirth/4865ebf9eaec7bb0c88a003c91ae77a1 to your computer and use it in GitHub Desktop.
hyperapp router
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import { fx } from "./utils/utils-fx.js"; | |
/** | |
* Give it your handlers for url and it will reward you with an installation FX and a pushUrl function to update the history | |
* @param {{onLinkClicked: (url: URL) => any, onUrlChanged: (url: URL) => any}} handlers | |
* returns [any, (url: URL) => any] | |
*/ | |
export const install = handlers => { | |
// bake the function manually pushing a new entry into the user's browser history | |
const pushUrl = fx((dispatch, url) => { | |
// notify the route changed event handler | |
dispatch(handlers.onUrlChanged, new URL(url)); | |
return history.pushState({}, "", url); | |
}); | |
// bake the installation FX to be passed to the hyperapp instance | |
const installRouter = fx((dispatch, props) => { | |
const { onLinkClicked, onUrlChanged } = props; | |
window.addEventListener("popstate", () => | |
dispatch(onUrlChanged, new URL(window.location.href)) | |
); | |
window.addEventListener("hashchange", () => | |
dispatch(onUrlChanged, new URL(window.location.href)) | |
); | |
document.addEventListener( | |
"click", | |
handleLinkClick(url => dispatch(onLinkClicked, url)) | |
); | |
})(handlers); | |
return { installRouter, pushUrl }; | |
}; | |
/** | |
* @param {HTMLElement} el | |
* @returns HTMLElement | undefined | |
* The purpose of this recursive function is to determine whether an element is either itself an anchor or | |
* if it is wrapped in a an anchor. | |
* The question is: When I click this element, will we trigger a location (URL) change? | |
* Here is part of the answer. | |
*/ | |
function getClosestAnchorInHierarchy(el) { | |
// is the current element an anchor? | |
if (el.tagName == "A") { | |
return el; | |
} else if (el.parentElement) { | |
// recursion exit condition | |
return getClosestAnchorInHierarchy(el.parentElement); | |
} else { | |
return undefined; | |
} | |
} | |
/** | |
* This kicks in whenever a user clicks a link on the page that actually triggers navigation inside the same tab | |
*/ | |
const handleLinkClick = handler => event => { | |
// check if element is wrapped inside anchor or is an anchor itself | |
const closestAnchor = getClosestAnchorInHierarchy(event.target); | |
// event.preventDefault() | |
if (!closestAnchor) { | |
return; | |
} else { | |
if ( | |
!event.ctrlKey && | |
!event.metaKey && | |
!event.shiftKey && | |
event.button < 1 && | |
!closestAnchor.target && | |
!closestAnchor.hasAttribute("download") | |
) { | |
event.preventDefault(); | |
var href = new URL(closestAnchor.href); | |
var curr = new URL(location.href); | |
var next = new URL(closestAnchor.href); | |
handler( | |
next && | |
curr.protocol === next.protocol && | |
curr.host === next.host && | |
curr.port === next.port | |
? /* _Internal */ next | |
: /* _External */ href | |
); | |
} | |
} | |
}; |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment