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:
parent
bdc549b178
commit
8fdf8331d5
1 changed files with 41 additions and 7 deletions
|
@ -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')}`);
|
||||||
|
|
Loading…
Reference in a new issue