Skip to content

Instantly share code, notes, and snippets.

@janwirth
Created January 6, 2020 06:19
Show Gist options
  • Save janwirth/4865ebf9eaec7bb0c88a003c91ae77a1 to your computer and use it in GitHub Desktop.
Save janwirth/4865ebf9eaec7bb0c88a003c91ae77a1 to your computer and use it in GitHub Desktop.
hyperapp router
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