diff --git a/lib/app-router.js b/lib/app-router.js new file mode 100644 index 0000000..7eeaaf9 --- /dev/null +++ b/lib/app-router.js @@ -0,0 +1,58 @@ +/* eslint-env browser */ + +import { config } from 'pear' + +customElements.define('app-router', class AppRouter extends HTMLElement { + constructor () { + super() + this.routes = {} + } + + unload () { + for (const element of Object.values(this.routes)) element?.unload && element.unload() + } + + async load (pathname = '/', opts = {}) { + for (const [route, element] of Object.entries(this.routes)) { + if (pathname.startsWith(route)) { + const page = pathname.slice(route.length) || '/' + this.unload() + document.documentElement.scrollTop = 0 + this.dataset.load = element.tagName.toLowerCase() + await element.load(page) + document.documentElement.scrollTop = 0 + if (!opts.back) history.pushState({ pathname }, null, pathname) + break + } + } + } + + link (evt) { + if (evt.target?.tagName !== 'A') return + evt.preventDefault() + if (evt.target.origin !== location.origin) return window.open(evt.target.href) + const { tagName } = evt.target.getRootNode().host || {} + const route = tagName ? this.getAttribute(tagName) : '' + this.load(route + evt.target.pathname).catch(console.error) + } + + connectedCallback () { + for (const { name, value } of Array.from(this.attributes)) { + if (name.startsWith('data-')) continue + this.routes[value] = this.querySelector(name) + this.routes[value].router = this + } + + this.addEventListener('click', (evt) => this.link(evt)) + + window.addEventListener('popstate', (evt) => { + this.load(evt.state?.pathname, { back: true }).catch(console.error) + }) + + window.addEventListener('load', () => { + if (config.link.indexOf('pear://pulse') === 0) { + this.load(config.link.slice(12)).catch(console.error) + } + }) + } +}) diff --git a/lib/docs-viewer.js b/lib/docs-viewer.js index 480bdad..2f0d79d 100644 --- a/lib/docs-viewer.js +++ b/lib/docs-viewer.js @@ -10,6 +10,13 @@ /* eslint-enable */ customElements.define('docs-viewer', class extends HTMLElement { + router = null + connectedCallback () { + this.root.addEventListener('click', (evt) => { + this.router.link(evt) + }) + } + constructor () { super() const lightMode = document.documentElement.classList.contains('light') @@ -52,66 +59,24 @@ customElements.define('docs-viewer', class extends HTMLElement { this.panel = this.root.querySelector('#panel') - this.status = document.querySelector('#status') - - this.page = '/readme.md' + this.entry = '/readme.md' const observer = new MutationObserver(() => { if (document.documentElement.classList.contains('light')) this.panel.classList.add('light') else this.panel.classList.remove('light') }) observer.observe(document.documentElement, { attributes: true, attributeFilter: ['class'] }) - - this.panel.addEventListener('click', async (evt) => { - evt.preventDefault() - if (evt.target?.tagName !== 'A') return - const href = evt.target.href - const { origin } = new URL(location.href) - const url = new URL(href, location.href) - if (url.origin !== origin) return window.open(href) - this.load(evt.target.pathname) - }) - - window.addEventListener('click', async (evt) => { - evt.preventDefault() - if (evt.target?.tagName !== 'A') return - const targetUrl = new URL(evt.target.href) - if (targetUrl.hash === '#documentation') { - const docsViewer = document.querySelector('docs-viewer') - await docsViewer.load('/readme.md') - } - if (targetUrl.hash === '#status') { - this.panel.style.display = 'none' - this.status.style.display = '' - } - }) - - window.addEventListener('popstate', async (evt) => { - console.log(evt) - if (evt.state === null) { - this.panel.style.display = 'none' - this.status.style.display = '' - return - } - const docsViewer = document.querySelector('docs-viewer') - await docsViewer.load(evt.state.page, { back: true }) - }) - - const config = global[Symbol.for('pear.config')] || {} - if (config.link.indexOf('pear://pulse') === 0) { - window.addEventListener('load', async () => { - await this.load(config.link.slice(12)) - }) - } } - async load (page, opts = {}) { - if (page.length === 0) return + async load (page = '/') { + if (page === '/') page = this.entry const html = await fetch(page) const text = await marked.parse(await html.text()) this.panel.querySelector('slot').innerHTML = text this.panel.style.display = '' - this.status.style.display = 'none' - if (!opts.back) history.pushState({ page }, null) + } + + unload () { + this.panel.style.display = 'none' } }) diff --git a/lib/system-status.js b/lib/system-status.js index ca89be3..2bcc2df 100644 --- a/lib/system-status.js +++ b/lib/system-status.js @@ -48,6 +48,22 @@ customElements.define('pear-welcome', class extends HTMLElement { }) customElements.define('system-status', class extends HTMLElement { + router = null + + connectedCallback () { + this.root.addEventListener('click', (evt) => { + this.router.onclick(evt) + }) + } + + load () { + this.style.display = '' + } + + unload () { + this.style.display = 'none' + } + constructor () { super() this.zsh = false diff --git a/pulse.html b/pulse.html index f9af692..40fd55e 100644 --- a/pulse.html +++ b/pulse.html @@ -1,6 +1,5 @@ - - +
- -
-
- - - - -
-
-
-
- -
-
- -
-
- -
-
- + +
+
+ + + + +
+
+
+
+
+ +
+
+
+ +
+