0
Fork 0
mirror of https://github.com/withastro/astro.git synced 2025-03-17 23:11:29 -05:00

chore(examples): router

This commit is contained in:
Nate Moore 2022-03-28 12:00:10 -05:00
parent 5ec589f220
commit bdc549b178
20 changed files with 258 additions and 71 deletions

View file

@ -1,9 +1,4 @@
import { defineConfig } from 'astro/config';
import spa from "@astrojs/spa";
// https://astro.build/config
export default defineConfig({
integrations: [
spa()
]
});
export default defineConfig({});

View file

@ -10,6 +10,7 @@
},
"devDependencies": {
"@astrojs/spa": "^0.0.1",
"@astrojs/tailwind": "^0.0.2",
"astro": "^0.25.2"
},
"dependencies": {

View file

@ -5,10 +5,11 @@ const { color = "red" } = Astro.props;
<div class="box" />
<style define:vars={{ color }}>
.box {
--size: 400px;
width: var(--size);
height: var(--size);
background: var(--color);
}
div {
display: block;
--size: 256px;
width: var(--size);
height: var(--size);
background: var(--color);
}
</style>

View file

@ -0,0 +1,36 @@
---
import { Link } from '@astrojs/router';
---
<nav>
<ul role="list">
<li><Link class="navlink" for="main" to="/dashboard">Dashboard</Link></li>
<li><Link class="navlink" for="main" to="/projects">Projects</Link></li>
<li><Link class="navlink" for="main" to="/team">Team</Link></li>
</ul>
</nav>
<style>
nav {
padding: 1rem;
background: rgb(31 41 55 / 1);
width: 100%;
}
ul {
display: flex;
gap: 0.5em;
}
.navlink {
color: rgb(209 213 219 / 1);
padding: 0.25em 0.67em;
border-radius: 4px;
}
.navlink:is(:hover, :focus) {
background: rgb(55 65 81 / 1);
color: white;
}
.navlink[aria-current="true"] {
background: rgb(17 24 39 / 1);
color: white;
}
</style>

View file

@ -0,0 +1,15 @@
---
import { Link } from '@astrojs/router';
---
<div class="titlebar">
<h1><slot /></h1>
</div>
<style>
.titlebar {
padding: 1.5em 1rem;
background: white;
box-shadow: 0 1px 3px 0 rgb(0 0 0 / 0.1),0 1px 2px -1px rgb(0 0 0 / 0.1);
}
</style>

View file

@ -1,28 +1,52 @@
---
import { Link, Outlet } from '@astrojs/router';
import { Outlet } from '@astrojs/router';
import Nav from '../components/Nav.astro';
---
<html lang="en">
<head>
<title>Router</title>
<style>
main {
body {
display: flex;
flex-flow: column wrap;
min-height: 100vh;
}
header {
display: flex;
align-items: center;
width: 100vw;
}
html {
background: rgb(243 244 246 / 1);
}
</style>
<style global>
* {
box-sizing: border-box;
margin: 0;
padding: 0;
}
html {
font-family: system-ui, sans-serif;
}
ul[role="list"] {
list-style: none;
}
a {
text-decoration: none;
}
</style>
<script type="module" hoist src="@astrojs/router/client.js" />
</head>
<body>
<h1>Router</h1>
<header>
<Nav />
</header>
<main>
<ul>
<li><Link for="tabs" to="/dashboard">Dashboard</Link></li>
<li><Link for="tabs" to="/about">About</Link></li>
</ul>
<Outlet id="tabs" default="/dashboard" />
<Outlet id="main" default="/dashboard" />
</main>
</body>
</html>

View file

@ -0,0 +1,16 @@
<h3><slot name="title" /></h3>
<div>
<slot />
</div>
<style>
div {
display: flex;
flex-direction: column;
gap: 1rem;
}
h3 {
margin-bottom: 1rem;
}
</style>

View file

@ -1,7 +1,9 @@
---
import Box from '../../../components/Box.astro'
import Layout from './_layout.astro';
---
<h3>Two</h3>
<Box color="red" />
<Layout>
<Fragment slot="title">One</Fragment>
<Box color="red" />
</Layout>

View file

@ -1,7 +1,9 @@
---
import Box from '../../../components/Box.astro'
import Layout from './_layout.astro';
---
<h3>Two</h3>
<Box color="blue" />
<Layout>
<Fragment slot="title">Three</Fragment>
<Box color="blue" />
</Layout>

View file

@ -1,7 +1,9 @@
---
import Box from '../../../components/Box.astro'
import Layout from './_layout.astro';
---
<h3>Two</h3>
<Box color="green" />
<Layout>
<Fragment slot="title">Two</Fragment>
<Box color="green" />
</Layout>

View file

@ -0,0 +1,18 @@
---
import Titlebar from '../../../components/Titlebar.astro';
---
<Titlebar>
<slot name="title" />
</Titlebar>
<div class="content">
<slot />
</div>
<style>
.content {
padding: 1rem;
display: flex;
}
</style>

View file

@ -0,0 +1,36 @@
---
import { Link, Outlet } from '@astrojs/router';
import Layout from './_layout.astro';
---
<Layout>
<Fragment slot="title">Dashboard</Fragment>
<div class="main">
<ul role="list">
<li><Link class="dash-link" for="dashboard" to="/one">One</Link></li>
<li><Link class="dash-link" for="dashboard" to="/two">Two</Link></li>
<li><Link class="dash-link" for="dashboard" to="/three">Three</Link></li>
</ul>
<Outlet id="dashboard" default="/one" />
</div>
</Layout>
<style>
.main {
display: grid;
width: 100%;
grid-template-columns: 1fr 3fr;
}
ul {
display: flex;
flex-flow: column nowrap;
gap: 0.5rem;
}
.dash-link {
color: black;
}
.dash-link[aria-current] {
font-weight: bold;
}
</style>

View file

@ -0,0 +1,17 @@
---
import Layout from './_layout.astro';
---
<Layout>
<Fragment slot="title">Projects</Fragment>
<Fragment>
<p>Lorem ipsum dolor sit amet, consectetur adipisicing elit. Ipsa assumenda porro dolorem tenetur, accusantium accusamus reprehenderit eligendi harum aliquam libero maxime placeat ea quidem inventore possimus perferendis doloremque? Esse, libero!</p>
</Fragment>
</Layout>
<style>
p {
max-width: 70ch;
}
</style>

View file

@ -0,0 +1,17 @@
---
import Layout from './_layout.astro';
---
<Layout>
<Fragment slot="title">Team</Fragment>
<Fragment>
<p>Hello world!</p>
</Fragment>
</Layout>
<style>
p {
max-width: 70ch;
}
</style>

View file

@ -1,3 +0,0 @@
<h2>About</h2>
<p>Lorem ipsum dolor, sit amet consectetur adipisicing elit. Sed saepe iste ex laboriosam? A quasi tempora provident voluptate itaque ea laboriosam numquam adipisci quidem porro. Nulla saepe illo odit assumenda.</p>

View file

@ -1,15 +0,0 @@
---
import { Link, Outlet } from '@astrojs/router';
---
<h2>Dashboard</h2>
<div>
<ul>
<li><Link for="dashboard" to="/one">One</Link></li>
<li><Link for="dashboard" to="/two">Two</Link></li>
<li><Link for="dashboard" to="/three">Three</Link></li>
</ul>
<Outlet id="dashboard" default="/one" />
</div>

View file

@ -2,4 +2,4 @@
const { for: htmlFor, to, ...props } = Astro.props;
---
<router-link {...props} for={htmlFor} to={to}><a href="#"><slot /></a></router-link>
<a {...props} href="#"><router-link for={htmlFor} to={to}><slot /></router-link></a>

View file

@ -1,24 +1,24 @@
---
const { default: route, id } = Astro.props;
function getRoutePath(...args) {
return args.map(arg => arg.replace(/^\/|\/$/g, '')).join('/');
function getRoutePath(...parts: string[]) {
return parts.map(part => part.replace(/^\/|\/$/g, '')).join('/');
}
const routes = import.meta.glob('/src/pages/routes/**/*');
const components = new Map();
for (let [key, getComponent] of Object.entries(routes)) {
key = key.replace(`/src/pages/routes/`, '').split('.').slice(0, -1).join('').replace(/\/index$/, '');
components.set(key, getComponent)
}
let Child = Fragment;
if (route) {
try {
const { default: Component } = await import(`/src/pages/routes/${getRoutePath(id, route)}.astro`);
if (Component) {
Child = Component;
}
} catch (e) {}
try {
const { default: Component } = await import(`/src/pages/routes/${getRoutePath(id, route)}/index.astro`);
if (Component) {
Child = Component;
}
} catch (e) {}
const target = getRoutePath(id, route);
const fn = components.get(target)
if (fn) {
Child = fn().then((mod) => mod.default);
}
}
---

View file

@ -4,16 +4,36 @@ function getRoutePath(...args) {
return args.map(arg => arg.replace(/^\/|\/$/, '')).join('/');
}
const s = new XMLSerializer();
const p = new DOMParser();
const initialChildren = new Set(Array.from(document.head.children).map(child => s.serializeToString(child)));
class RouterOutlet extends HTMLElement {
constructor() {
super();
}
connectedCallback() {
this.isUpdating = false;
this.updateLinks();
}
updateLinks() {
document.querySelectorAll(`router-link[for="${this.getAttribute('id')}"]`).forEach(link => {
link.ariaCurrent = (link.to === this.route);
})
const current = this.getAttribute('route');
const links = document.querySelectorAll(`router-link[for="${this.getAttribute('id')}"]`);
for (const link of links) {
if (current === link.getAttribute('to')) {
link.parentElement.setAttribute("aria-current", "true");
} else {
link.parentElement.removeAttribute("aria-current");
}
}
}
mergeHead(newHead) {
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&amp;type=script&amp;index=0'))));
for (const [key, child] of currentChildren.entries()) {
if (initialChildren.has(key) || newChildren.has(key)) continue;
child.remove();
}
document.head.append(...newChildren.values());
}
static get observedAttributes() { return ['route']; }
async attributeChangedCallback(_, oldValue, newValue) {
@ -22,9 +42,10 @@ class RouterOutlet extends HTMLElement {
if (oldValue === newValue) return;
this.isUpdating = true;
const text = await fetch(`/routes/${getRoutePath(this.getAttribute('id'), newValue)}`).then(res => res.text());
const children = p.parseFromString(text, 'text/html').body.children;
const clone = this.cloneNode(true)
clone.replaceChildren(...children);
const { head, body } = p.parseFromString(text, 'text/html');
const clone = this.cloneNode(true);
clone.replaceChildren(...body.children);
this.mergeHead(head);
await morph(this, clone);
this.updateLinks();
this.isUpdating = false;
@ -44,11 +65,11 @@ class RouterLink extends HTMLElement {
}
connectedCallback() {
this.target = document.querySelector(`router-outlet#${this.getAttribute('for')}`);
this.addEventListener('click', this.handleClick);
this.parentElement.addEventListener('click', this.handleClick);
}
disconnectedCallback() {
this.target = null;
this.removeEventListener('click', this.handleClick);
this.parentElement.removeEventListener('click', this.handleClick);
}
}
customElements.define('router-link', RouterLink);

2
pnpm-lock.yaml generated
View file

@ -288,11 +288,13 @@ importers:
specifiers:
'@astrojs/router': ^0.0.1
'@astrojs/spa': ^0.0.1
'@astrojs/tailwind': ^0.0.2
astro: ^0.25.2
dependencies:
'@astrojs/router': link:../../packages/router
devDependencies:
'@astrojs/spa': link:../../packages/integrations/spa
'@astrojs/tailwind': link:../../packages/integrations/tailwind
astro: link:../../packages/astro
examples/spa: