This commit is contained in:
dmc
2024-01-24 19:15:15 +01:00
parent fa3993e473
commit af088cfed6
4 changed files with 145 additions and 99 deletions

58
lib/app-router.js Normal file
View File

@@ -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)
}
})
}
})

View File

@@ -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'
}
})

View File

@@ -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

View File

@@ -1,6 +1,5 @@
<!DOCTYPE html>
<body onload="location.hash = location.hash || '#status'">
<script> addEventListener('hashchange', () => { document.documentElement.scrollTop = 0 }) </script>
<body>
<style>
@font-face {
font-family: 'overpass-mono';
@@ -41,8 +40,6 @@
background: #efeaea;
color: #151517;
}
a:visited, a:active, a { color: #B0D944; outline: none; text-decoration: none; }
#logo {
float: left;
@@ -65,7 +62,6 @@
section {
display: block;
padding-top: 156px;
}
pre {
@@ -93,6 +89,7 @@
transform: scale(0.92);
width: 72rem;
margin: 0 auto;
margin-top: .75rem;
user-select: none;
}
@@ -142,51 +139,61 @@
margin-left: 9px;
}
pear-ctrl[data-platform="dawrin"] { float: left; margin-top: 4px; }
system-status, docs-viewer { position: relative; top: 156px }
a:visited, a:active, a { color: #B0D944; outline: none; text-decoration: none; }
app-router a:visited, app-router a:active, app-router a {
color: #c8cdbb;
}
app-router[data-load="system-status"] a[href='/'], app-router[data-load="system-status"] a:active[href='/'], app-router[data-load="system-status"] a:visited[href='/'],
app-router[data-load="docs-viewer"] a[href='/documentation'], app-router[data-load="docs-viewer"] a:active[href='/documentation'], app-router[data-load="docs-viewer"] a:visited[href='/documentation'] {
color: #B0D944;
}
</style>
<div id="bar"><pear-ctrl></pear-ctrl></div>
<header>
<div id="headin">
<svg id="logo" width="102" height="146" viewBox="0 0 102 146" fill="none" xmlns="http://www.w3.org/2000/svg">
<g clip-path="url(#clip0_8912_10861)">
<path d="M47.4056 0.838379H54.5943V15.0361H47.4056V0.838379Z" fill="#B0D944"/>
<path d="M43.8113 19.5305V22.406H36.6226V26.0004H65.3774V22.406H58.1887V16.8347H51V19.5305H43.8113Z" fill="#B0D944"/>
<path d="M72.5662 27.7974H51V30.4931H29.4339V36.963H72.5662V27.7974Z" fill="#B0D944"/>
<path d="M79.7548 38.7593H51V41.455H22.2451V47.9249H79.7548V38.7593Z" fill="#B0D944"/>
<path d="M79.7548 49.7219H51V52.4177H22.2451V58.8875H79.7548V49.7219Z" fill="#B0D944"/>
<path d="M86.9436 60.6846H51V63.3803H15.0565V69.8502H86.9436V60.6846Z" fill="#B0D944"/>
<path d="M86.9436 71.6481H51V74.3438H15.0565V80.8137H86.9436V71.6481Z" fill="#B0D944"/>
<path d="M94.1323 82.61H51V85.3058H7.86774V91.7756H94.1323V82.61Z" fill="#B0D944"/>
<path d="M101.321 93.5726H51V96.2684H0.679016V102.738H101.321V93.5726Z" fill="#B0D944"/>
<path d="M101.321 104.536H51V107.232H0.679016V113.702H101.321V104.536Z" fill="#B0D944"/>
<path d="M101.321 115.499H51V118.195H0.679016V124.664H101.321V115.499Z" fill="#B0D944"/>
<path d="M86.9436 126.461H51V129.156H15.0565V135.626H86.9436V126.461Z" fill="#B0D944"/>
<path d="M72.5662 137.424H51V140.12H29.4339V144.613H72.5662V137.424Z" fill="#B0D944"/>
</g>
<defs>
<clipPath id="clip0_8912_10861">
<rect width="100.642" height="145.571" fill="white" transform="translate(0.679016 0.214233)"/>
</clipPath>
</defs>
</svg>
<img id="mode" src="/assets/light.svg" onclick="document.documentElement.classList.toggle('light');"></img>
<img id="back" src="/assets/arrow.svg" onclick="history.back()"></img>
<nav>
<a href="#status" style="text-indent: -1rem">System Status</a>
<a href="#documentation">Documentation</a>
</nav>
</div>
</header>
<section id="status">
<div class="col">
<system-status></system-status>
</div>
</section>
<script type='module' src='./lib/system-status.js'></script>
<section id="documentation">
<div class="col">
<docs-viewer></docs-viewer>
</div>
</section>
<script type='module' src='./lib/docs-viewer.js'></script>
<app-router docs-viewer="/documentation" system-status="/" data-load="system-status">
<header>
<div id="headin">
<svg id="logo" width="102" height="146" viewBox="0 0 102 146" fill="none" xmlns="http://www.w3.org/2000/svg">
<g clip-path="url(#clip0_8912_10861)">
<path d="M47.4056 0.838379H54.5943V15.0361H47.4056V0.838379Z" fill="#B0D944"/>
<path d="M43.8113 19.5305V22.406H36.6226V26.0004H65.3774V22.406H58.1887V16.8347H51V19.5305H43.8113Z" fill="#B0D944"/>
<path d="M72.5662 27.7974H51V30.4931H29.4339V36.963H72.5662V27.7974Z" fill="#B0D944"/>
<path d="M79.7548 38.7593H51V41.455H22.2451V47.9249H79.7548V38.7593Z" fill="#B0D944"/>
<path d="M79.7548 49.7219H51V52.4177H22.2451V58.8875H79.7548V49.7219Z" fill="#B0D944"/>
<path d="M86.9436 60.6846H51V63.3803H15.0565V69.8502H86.9436V60.6846Z" fill="#B0D944"/>
<path d="M86.9436 71.6481H51V74.3438H15.0565V80.8137H86.9436V71.6481Z" fill="#B0D944"/>
<path d="M94.1323 82.61H51V85.3058H7.86774V91.7756H94.1323V82.61Z" fill="#B0D944"/>
<path d="M101.321 93.5726H51V96.2684H0.679016V102.738H101.321V93.5726Z" fill="#B0D944"/>
<path d="M101.321 104.536H51V107.232H0.679016V113.702H101.321V104.536Z" fill="#B0D944"/>
<path d="M101.321 115.499H51V118.195H0.679016V124.664H101.321V115.499Z" fill="#B0D944"/>
<path d="M86.9436 126.461H51V129.156H15.0565V135.626H86.9436V126.461Z" fill="#B0D944"/>
<path d="M72.5662 137.424H51V140.12H29.4339V144.613H72.5662V137.424Z" fill="#B0D944"/>
</g>
<defs>
<clipPath id="clip0_8912_10861">
<rect width="100.642" height="145.571" fill="white" transform="translate(0.679016 0.214233)"/>
</clipPath>
</defs>
</svg>
<img id="mode" src="/assets/light.svg" onclick="document.documentElement.classList.toggle('light');">
<img id="back" src="/assets/arrow.svg" onclick="history.back()">
<nav>
<a href="/" style="text-indent: -1rem">System Status</a>
<a href="/documentation">Documentation</a>
</nav>
</div>
</header>
<section id="status">
<div class="col"><system-status></system-status></div>
</section>
<script type='module' src='./lib/system-status.js'></script>
<section id="documentation">
<div class="col"><docs-viewer></docs-viewer></div>
</section>
<script type='module' src='./lib/docs-viewer.js'></script>
</app-router>
<script type='module' src='./lib/app-router.js'></script>
</body>