mirror of
https://github.com/thiloho/aurora.git
synced 2025-11-22 11:31:36 +01:00
Initial commit
This commit is contained in:
60
src/components/ArticleSearch.svelte
Normal file
60
src/components/ArticleSearch.svelte
Normal file
@@ -0,0 +1,60 @@
|
||||
<script lang="ts">
|
||||
import type { CollectionEntry } from "astro:content";
|
||||
|
||||
export let articles: CollectionEntry<"blog">[] = [];
|
||||
|
||||
import Dropdown from "./Dropdown.svelte";
|
||||
|
||||
let searchTerm = "";
|
||||
|
||||
$: filteredArticles = articles.filter(article => {
|
||||
const searchTermWords = searchTerm.trim().replace(/\s+/g, " ").toLowerCase().split(' ');
|
||||
const articleTitle = article.data.title.trim().replace(/\s+/g, " ").toLowerCase();
|
||||
|
||||
return searchTermWords.every(word => articleTitle.includes(word)) && searchTerm.trim() !== "";
|
||||
});
|
||||
</script>
|
||||
|
||||
<Dropdown shortcut="S">
|
||||
<svg slot="icon" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="currentColor">
|
||||
<title>Search for an article (Shift + s)</title>
|
||||
<path fill-rule="evenodd" d="M10.5 3.75a6.75 6.75 0 100 13.5 6.75 6.75 0 000-13.5zM2.25 10.5a8.25 8.25 0 1114.59 5.28l4.69 4.69a.75.75 0 11-1.06 1.06l-4.69-4.69A8.25 8.25 0 012.25 10.5z" clip-rule="evenodd" />
|
||||
</svg>
|
||||
<div class="article-search">
|
||||
<label for="article-search">Search for an article:</label>
|
||||
<input type="search" id="article-search" placeholder="first po..." bind:value={searchTerm} />
|
||||
{#if filteredArticles.length > 0}
|
||||
<p>Results: <strong>{filteredArticles.length}</strong></p>
|
||||
<ul>
|
||||
{#each filteredArticles as article}
|
||||
<li>
|
||||
<a href="/{article.slug}">{article.data.title}</a>
|
||||
</li>
|
||||
{/each}
|
||||
</ul>
|
||||
{:else if searchTerm.trim() !== ""}
|
||||
<p>No results found</p>
|
||||
{/if}
|
||||
</div>
|
||||
</Dropdown>
|
||||
|
||||
<style>
|
||||
.article-search {
|
||||
padding: 1rem;
|
||||
position: absolute;
|
||||
inset-inline: 0;
|
||||
border: var(--standard-border);
|
||||
inset-block-start: var(--nav-height);
|
||||
background-color: var(--background-color);
|
||||
display: grid;
|
||||
overflow-x: auto;
|
||||
}
|
||||
|
||||
ul {
|
||||
margin-block-start: 0;
|
||||
}
|
||||
|
||||
input {
|
||||
max-inline-size: 100%;
|
||||
}
|
||||
</style>
|
||||
10
src/components/ArticleSearchWrapper.astro
Normal file
10
src/components/ArticleSearchWrapper.astro
Normal file
@@ -0,0 +1,10 @@
|
||||
---
|
||||
import { getCollection } from "astro:content";
|
||||
import ArticleSearch from "./ArticleSearch.svelte";
|
||||
|
||||
const articles = (await getCollection("blog")).sort(
|
||||
(a, b) => b.data.publicationDate.valueOf() - a.data.publicationDate.valueOf()
|
||||
);
|
||||
---
|
||||
|
||||
<ArticleSearch client:load {articles} />
|
||||
49
src/components/Dropdown.svelte
Normal file
49
src/components/Dropdown.svelte
Normal file
@@ -0,0 +1,49 @@
|
||||
<script lang="ts">
|
||||
export let shortcut: string;
|
||||
|
||||
const possibleShortcuts = ["S", "T"];
|
||||
|
||||
let detailsElement: HTMLDetailsElement;
|
||||
let summaryElement: HTMLElement;
|
||||
|
||||
function openMenu() {
|
||||
detailsElement.setAttribute("open", "");
|
||||
}
|
||||
|
||||
export function closeMenu() {
|
||||
detailsElement.removeAttribute("open");
|
||||
}
|
||||
|
||||
function handleKeydown(event: KeyboardEvent) {
|
||||
if (event.key === "Escape") closeMenu();
|
||||
|
||||
if (event.key === shortcut) {
|
||||
summaryElement.focus();
|
||||
openMenu();
|
||||
} else if (possibleShortcuts.includes(event.key) && event.key !== shortcut) {
|
||||
closeMenu();
|
||||
}
|
||||
}
|
||||
|
||||
function handleClick(event: MouseEvent) {
|
||||
if (!detailsElement.contains(event.target as Node)) {
|
||||
closeMenu();
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<svelte:window on:keydown={handleKeydown} on:click={handleClick} />
|
||||
|
||||
<details bind:this={detailsElement}>
|
||||
<summary bind:this={summaryElement}>
|
||||
<slot name="icon" />
|
||||
</summary>
|
||||
<slot />
|
||||
</details>
|
||||
|
||||
<style>
|
||||
summary {
|
||||
display: grid;
|
||||
padding: 0.5rem;
|
||||
}
|
||||
</style>
|
||||
43
src/components/Footer.astro
Normal file
43
src/components/Footer.astro
Normal file
@@ -0,0 +1,43 @@
|
||||
---
|
||||
const currentDateYear = new Date().getFullYear();
|
||||
---
|
||||
|
||||
<footer>
|
||||
<div class="container">
|
||||
<ul>
|
||||
<li>
|
||||
<a href="#">GitHub</a>
|
||||
</li>
|
||||
<li>
|
||||
<a href="#">Twitter</a>
|
||||
</li>
|
||||
<li>
|
||||
<a href="#">More</a>
|
||||
</li>
|
||||
</ul>
|
||||
<p>
|
||||
<small>Copyright © {currentDateYear} Your Name</small>
|
||||
</p>
|
||||
</div>
|
||||
</footer>
|
||||
|
||||
<style>
|
||||
footer {
|
||||
margin-block-start: 4rem;
|
||||
padding-block: 1rem;
|
||||
background: linear-gradient(0deg, var(--tertiary-background-color) 0%, var(--background-color) 100%);
|
||||
}
|
||||
|
||||
.container {
|
||||
display: grid;
|
||||
place-content: center;
|
||||
}
|
||||
|
||||
ul {
|
||||
padding: 0;
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
list-style: none;
|
||||
gap: 1rem;
|
||||
}
|
||||
</style>
|
||||
14
src/components/Head.astro
Normal file
14
src/components/Head.astro
Normal file
@@ -0,0 +1,14 @@
|
||||
---
|
||||
const { title, description } = Astro.props;
|
||||
---
|
||||
|
||||
<head>
|
||||
<meta charset="utf-8" />
|
||||
<link rel="icon" type="image/svg+xml" href="/favicon.svg" />
|
||||
<meta name="viewport" content="width=device-width" />
|
||||
<meta name="generator" content={Astro.generator} />
|
||||
<meta name="color-scheme" content="light">
|
||||
<title>{title}</title>
|
||||
<meta name="author" content="Your Name" />
|
||||
<meta name="description" content={description} />
|
||||
</head>
|
||||
16
src/components/Header.astro
Normal file
16
src/components/Header.astro
Normal file
@@ -0,0 +1,16 @@
|
||||
---
|
||||
const { title } = Astro.props;
|
||||
---
|
||||
|
||||
<header>
|
||||
<div class="container">
|
||||
<h1>{title}</h1>
|
||||
<slot />
|
||||
</div>
|
||||
</header>
|
||||
|
||||
<style>
|
||||
header {
|
||||
margin-block-end: 4rem;
|
||||
}
|
||||
</style>
|
||||
33
src/components/Nav.astro
Normal file
33
src/components/Nav.astro
Normal file
@@ -0,0 +1,33 @@
|
||||
---
|
||||
import ArticleSearchWrapper from "../components/ArticleSearchWrapper.astro";
|
||||
---
|
||||
|
||||
<nav>
|
||||
<div class="container">
|
||||
<a href="/">
|
||||
Logo
|
||||
</a>
|
||||
<slot />
|
||||
<ArticleSearchWrapper />
|
||||
</div>
|
||||
</nav>
|
||||
|
||||
<style>
|
||||
nav {
|
||||
position: sticky;
|
||||
inset-block-start: 0;
|
||||
background-color: var(--secondary-background-color);
|
||||
border-block-end: var(--standard-border);
|
||||
}
|
||||
|
||||
.container {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
block-size: var(--nav-height);
|
||||
position: relative;
|
||||
}
|
||||
|
||||
a {
|
||||
margin-inline-end: auto;
|
||||
}
|
||||
</style>
|
||||
13
src/components/PublicationDate.astro
Normal file
13
src/components/PublicationDate.astro
Normal file
@@ -0,0 +1,13 @@
|
||||
---
|
||||
const { publicationDate } = Astro.props;
|
||||
---
|
||||
|
||||
<time datetime={publicationDate.toISOString()}>
|
||||
{
|
||||
publicationDate.toLocaleString("en-us", {
|
||||
year: "numeric",
|
||||
month: "long",
|
||||
day: "numeric",
|
||||
})
|
||||
}
|
||||
</time>
|
||||
36
src/components/TableOfContents.svelte
Normal file
36
src/components/TableOfContents.svelte
Normal file
@@ -0,0 +1,36 @@
|
||||
<script lang="ts">
|
||||
import type { MarkdownHeading } from "astro";
|
||||
|
||||
export let headings: MarkdownHeading[] = [];
|
||||
|
||||
import Dropdown from "./Dropdown.svelte";
|
||||
|
||||
let dropdown;
|
||||
</script>
|
||||
|
||||
<Dropdown shortcut="T" bind:this={dropdown}>
|
||||
<svg slot="icon" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="currentColor">
|
||||
<title>Table of contents (Shift + t)</title>
|
||||
<path fill-rule="evenodd" d="M2.625 6.75a1.125 1.125 0 112.25 0 1.125 1.125 0 01-2.25 0zm4.875 0A.75.75 0 018.25 6h12a.75.75 0 010 1.5h-12a.75.75 0 01-.75-.75zM2.625 12a1.125 1.125 0 112.25 0 1.125 1.125 0 01-2.25 0zM7.5 12a.75.75 0 01.75-.75h12a.75.75 0 010 1.5h-12A.75.75 0 017.5 12zm-4.875 5.25a1.125 1.125 0 112.25 0 1.125 1.125 0 01-2.25 0zm4.875 0a.75.75 0 01.75-.75h12a.75.75 0 010 1.5h-12a.75.75 0 01-.75-.75z" clip-rule="evenodd" />
|
||||
</svg>
|
||||
<ul>
|
||||
{#each headings.filter(({ depth }) => depth === 2) as { slug, text }}
|
||||
<li>
|
||||
<a href="#{slug}" on:click={dropdown.closeMenu}>{text}</a>
|
||||
</li>
|
||||
{/each}
|
||||
</ul>
|
||||
</Dropdown>
|
||||
|
||||
<style>
|
||||
ul {
|
||||
position: absolute;
|
||||
border: var(--standard-border);
|
||||
padding: 1rem;
|
||||
padding-inline-start: 2rem;
|
||||
background-color: var(--background-color);
|
||||
inset-block-start: var(--nav-height);
|
||||
margin: 0;
|
||||
inset-inline-end: 0;
|
||||
}
|
||||
</style>
|
||||
Reference in New Issue
Block a user