mirror of
https://github.com/aljazceru/pear-docs.git
synced 2025-12-17 14:34:19 +01:00
routing
This commit is contained in:
58
lib/app-router.js
Normal file
58
lib/app-router.js
Normal 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)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
})
|
||||||
@@ -10,6 +10,13 @@
|
|||||||
/* eslint-enable */
|
/* eslint-enable */
|
||||||
|
|
||||||
customElements.define('docs-viewer', class extends HTMLElement {
|
customElements.define('docs-viewer', class extends HTMLElement {
|
||||||
|
router = null
|
||||||
|
connectedCallback () {
|
||||||
|
this.root.addEventListener('click', (evt) => {
|
||||||
|
this.router.link(evt)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
constructor () {
|
constructor () {
|
||||||
super()
|
super()
|
||||||
const lightMode = document.documentElement.classList.contains('light')
|
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.panel = this.root.querySelector('#panel')
|
||||||
|
|
||||||
this.status = document.querySelector('#status')
|
this.entry = '/readme.md'
|
||||||
|
|
||||||
this.page = '/readme.md'
|
|
||||||
|
|
||||||
const observer = new MutationObserver(() => {
|
const observer = new MutationObserver(() => {
|
||||||
if (document.documentElement.classList.contains('light')) this.panel.classList.add('light')
|
if (document.documentElement.classList.contains('light')) this.panel.classList.add('light')
|
||||||
else this.panel.classList.remove('light')
|
else this.panel.classList.remove('light')
|
||||||
})
|
})
|
||||||
observer.observe(document.documentElement, { attributes: true, attributeFilter: ['class'] })
|
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 = {}) {
|
async load (page = '/') {
|
||||||
if (page.length === 0) return
|
if (page === '/') page = this.entry
|
||||||
const html = await fetch(page)
|
const html = await fetch(page)
|
||||||
const text = await marked.parse(await html.text())
|
const text = await marked.parse(await html.text())
|
||||||
this.panel.querySelector('slot').innerHTML = text
|
this.panel.querySelector('slot').innerHTML = text
|
||||||
this.panel.style.display = ''
|
this.panel.style.display = ''
|
||||||
this.status.style.display = 'none'
|
}
|
||||||
if (!opts.back) history.pushState({ page }, null)
|
|
||||||
|
unload () {
|
||||||
|
this.panel.style.display = 'none'
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|||||||
@@ -48,6 +48,22 @@ customElements.define('pear-welcome', class extends HTMLElement {
|
|||||||
})
|
})
|
||||||
|
|
||||||
customElements.define('system-status', 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 () {
|
constructor () {
|
||||||
super()
|
super()
|
||||||
this.zsh = false
|
this.zsh = false
|
||||||
|
|||||||
39
pulse.html
39
pulse.html
@@ -1,6 +1,5 @@
|
|||||||
<!DOCTYPE html>
|
<!DOCTYPE html>
|
||||||
<body onload="location.hash = location.hash || '#status'">
|
<body>
|
||||||
<script> addEventListener('hashchange', () => { document.documentElement.scrollTop = 0 }) </script>
|
|
||||||
<style>
|
<style>
|
||||||
@font-face {
|
@font-face {
|
||||||
font-family: 'overpass-mono';
|
font-family: 'overpass-mono';
|
||||||
@@ -42,8 +41,6 @@
|
|||||||
color: #151517;
|
color: #151517;
|
||||||
}
|
}
|
||||||
|
|
||||||
a:visited, a:active, a { color: #B0D944; outline: none; text-decoration: none; }
|
|
||||||
|
|
||||||
#logo {
|
#logo {
|
||||||
float: left;
|
float: left;
|
||||||
margin-right: 2rem;
|
margin-right: 2rem;
|
||||||
@@ -65,7 +62,6 @@
|
|||||||
|
|
||||||
section {
|
section {
|
||||||
display: block;
|
display: block;
|
||||||
padding-top: 156px;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pre {
|
pre {
|
||||||
@@ -93,6 +89,7 @@
|
|||||||
transform: scale(0.92);
|
transform: scale(0.92);
|
||||||
width: 72rem;
|
width: 72rem;
|
||||||
margin: 0 auto;
|
margin: 0 auto;
|
||||||
|
margin-top: .75rem;
|
||||||
user-select: none;
|
user-select: none;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -142,9 +139,21 @@
|
|||||||
margin-left: 9px;
|
margin-left: 9px;
|
||||||
}
|
}
|
||||||
pear-ctrl[data-platform="dawrin"] { float: left; margin-top: 4px; }
|
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>
|
</style>
|
||||||
<div id="bar"><pear-ctrl></pear-ctrl></div>
|
<div id="bar"><pear-ctrl></pear-ctrl></div>
|
||||||
|
<app-router docs-viewer="/documentation" system-status="/" data-load="system-status">
|
||||||
<header>
|
<header>
|
||||||
<div id="headin">
|
<div id="headin">
|
||||||
<svg id="logo" width="102" height="146" viewBox="0 0 102 146" fill="none" xmlns="http://www.w3.org/2000/svg">
|
<svg id="logo" width="102" height="146" viewBox="0 0 102 146" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||||
@@ -169,24 +178,22 @@
|
|||||||
</clipPath>
|
</clipPath>
|
||||||
</defs>
|
</defs>
|
||||||
</svg>
|
</svg>
|
||||||
<img id="mode" src="/assets/light.svg" onclick="document.documentElement.classList.toggle('light');"></img>
|
<img id="mode" src="/assets/light.svg" onclick="document.documentElement.classList.toggle('light');">
|
||||||
<img id="back" src="/assets/arrow.svg" onclick="history.back()"></img>
|
<img id="back" src="/assets/arrow.svg" onclick="history.back()">
|
||||||
<nav>
|
<nav>
|
||||||
<a href="#status" style="text-indent: -1rem">System Status</a>
|
<a href="/" style="text-indent: -1rem">System Status</a>
|
||||||
<a href="#documentation">Documentation</a>
|
<a href="/documentation">Documentation</a>
|
||||||
</nav>
|
</nav>
|
||||||
</div>
|
</div>
|
||||||
</header>
|
</header>
|
||||||
<section id="status">
|
<section id="status">
|
||||||
<div class="col">
|
<div class="col"><system-status></system-status></div>
|
||||||
<system-status></system-status>
|
|
||||||
</div>
|
|
||||||
</section>
|
</section>
|
||||||
<script type='module' src='./lib/system-status.js'></script>
|
<script type='module' src='./lib/system-status.js'></script>
|
||||||
<section id="documentation">
|
<section id="documentation">
|
||||||
<div class="col">
|
<div class="col"><docs-viewer></docs-viewer></div>
|
||||||
<docs-viewer></docs-viewer>
|
|
||||||
</div>
|
|
||||||
</section>
|
</section>
|
||||||
<script type='module' src='./lib/docs-viewer.js'></script>
|
<script type='module' src='./lib/docs-viewer.js'></script>
|
||||||
|
</app-router>
|
||||||
|
<script type='module' src='./lib/app-router.js'></script>
|
||||||
</body>
|
</body>
|
||||||
|
|||||||
Reference in New Issue
Block a user