0
Fork 0
mirror of https://github.com/withastro/astro.git synced 2024-12-30 22:03:56 -05:00

wip(router): handle hash routing

This commit is contained in:
Nate Moore 2022-03-29 10:58:55 -05:00
parent bdc549b178
commit 8fdf8331d5

View file

@ -3,29 +3,63 @@ import morph from 'micromorph';
function getRoutePath(...args) { function getRoutePath(...args) {
return args.map(arg => arg.replace(/^\/|\/$/, '')).join('/'); return args.map(arg => arg.replace(/^\/|\/$/, '')).join('/');
} }
function syncAttributes() {
let hash = window.location.hash.split('/').slice(1);
let n = document;
while (n = n.querySelector('router-outlet')) {
const route = hash[0];
if (route) {
n.setAttribute('data-skip', '');
n.setAttribute('route', route);
hash = hash.slice(1);
}
}
}
window.addEventListener('popstate', () => syncAttributes());
const s = new XMLSerializer(); const s = new XMLSerializer();
const p = new DOMParser(); const p = new DOMParser();
const initialChildren = new Set(Array.from(document.head.children).map(child => s.serializeToString(child))); const initialChildren = new Set(Array.from(document.head.children).map(child => s.serializeToString(child)));
class RouterOutlet extends HTMLElement { class RouterOutlet extends HTMLElement {
constructor() {
super();
}
connectedCallback() { connectedCallback() {
this.isUpdating = false; this.isUpdating = false;
this.update();
}
update() {
if (this.hasAttribute('data-skip')) {
this.removeAttribute('data-skip');
} else {
this.updateLocation();
}
this.updateLinks(); this.updateLinks();
} }
updateLinks() { updateLinks() {
const current = this.getAttribute('route'); const current = this.getAttribute('route').replace(/^\/|\/$/g, '');
const links = document.querySelectorAll(`router-link[for="${this.getAttribute('id')}"]`); const links = document.querySelectorAll(`router-link[for="${this.getAttribute('id')}"]`);
for (const link of links) { for (const link of links) {
if (current === link.getAttribute('to')) { if (current === link.getAttribute('to').replace(/^\/|\/$/g, '')) {
link.parentElement.setAttribute("aria-current", "true"); link.parentElement.setAttribute("aria-current", "true");
} else { } else {
link.parentElement.removeAttribute("aria-current"); link.parentElement.removeAttribute("aria-current");
} }
} }
} }
updateLocation() {
// any parent route should skip this
if (this.querySelector('router-outlet')) return;
// this will be the deepest router-outlet on the page
const path = [];
let n = this;
while (n = n.closest('router-outlet')) {
path.splice(-1, 0, n.getAttribute('route').replace(/^\/|\/$/g, ''));
n = n.parentElement;
}
const pathname = `/${path.join('/')}`
const url = new URL(window.location.toString().replace(/\/$/, '') + '/');
if (url.hash.endsWith(pathname)) return;
url.hash = pathname;
window.history.pushState({}, '', url)
}
mergeHead(newHead) { mergeHead(newHead) {
const currentChildren = new Map(Array.from(document.head.children).map(child => [s.serializeToString(child), child])); const currentChildren = new Map(Array.from(document.head.children).map(child => [s.serializeToString(child), child]));
const newChildren = new Map(Array.from(newHead.children).map(child => [s.serializeToString(child), child]).filter(([key]) => !currentChildren.has(key) && !initialChildren.has(key) && !(!import.meta.env.PROD && key.includes('astro&type=script&index=0')))); const newChildren = new Map(Array.from(newHead.children).map(child => [s.serializeToString(child), child]).filter(([key]) => !currentChildren.has(key) && !initialChildren.has(key) && !(!import.meta.env.PROD && key.includes('astro&type=script&index=0'))));
@ -47,7 +81,7 @@ class RouterOutlet extends HTMLElement {
clone.replaceChildren(...body.children); clone.replaceChildren(...body.children);
this.mergeHead(head); this.mergeHead(head);
await morph(this, clone); await morph(this, clone);
this.updateLinks(); this.update();
this.isUpdating = false; this.isUpdating = false;
} }
} }
@ -61,7 +95,7 @@ class RouterLink extends HTMLElement {
handleClick(event) { handleClick(event) {
event.preventDefault(); event.preventDefault();
event.stopPropagation(); event.stopPropagation();
this.target.setAttribute('route', this.getAttribute('to')); this.target.setAttribute('route', this.getAttribute('to').replace(/^\/|\/$/g, ''));
} }
connectedCallback() { connectedCallback() {
this.target = document.querySelector(`router-outlet#${this.getAttribute('for')}`); this.target = document.querySelector(`router-outlet#${this.getAttribute('for')}`);