Files
blog-frontend-v2/src/layout/FullLayoutV1.astro
passthem 43a85ac056
All checks were successful
continuous-integration/drone/push Build is passing
完成博客索引页
2026-04-05 19:05:31 +08:00

329 lines
7.5 KiB
Plaintext

---
import BaseLayout from './BaseLayout.astro'
import { Icon } from 'astro-icon/components'
interface Props {
title?: string
withGap?: boolean
}
const { title = '小帕的小窝', withGap = false } = Astro.props
---
<BaseLayout title={title}>
<div class="nav-mobile" aria-hidden="true" transition:persist>
<button
id="nav-mobile-button"
onclick="document.getElementById('nav-main').classList.remove('nav-container-hidden')"
><Icon name="mdi:menu-close" /></button
>
<a href="/">小帕的小窝</a>
</div>
<div
id="nav-main"
class="nav-container nav-container-hidden"
transition:persist
>
<nav>
<ul class="left">
<li class="website-logo">
<a href="/">小帕的小窝</a>
</li>
<li>
<a href="/blogs"><Icon name="mdi:invoice-text-outline" />文章</a>
</li>
<li>
<a href="/about"
><Icon name="mdi:information-slab-circle-outline" />关于</a
>
</li>
</ul>
<ul class="right">
<li>
<a href="https://legacy.passthem.top"
><Icon name="mdi:history" /> 旧博客系统*</a
>
</li>
<li>
<a href="/rss.xml">
<Icon name="material-symbols:rss-feed" /> 博客 RSS
</a>
</li>
</ul>
</nav>
</div>
<div
class="loading-indicator hidden"
id="loading-indicator"
aria-hidden="true"
transition:persist
>
</div>
<main>
{withGap && <div aria-hidden="true" class="layout-gap" />}
<slot />
</main>
</BaseLayout>
<script>
const navClose = () => {
const navMain = document.getElementById('nav-main')
navMain?.classList.add('nav-container-hidden')
}
const navOpen = () => {
const navMain = document.getElementById('nav-main')
navMain?.classList.remove('nav-container-hidden')
}
document.getElementById('nav-main')?.addEventListener('click', (e) => {
const target = e.target as HTMLElement
if (target.id == 'nav-main') {
document.getElementById('nav-main')?.classList.add('nav-container-hidden')
}
})
document.getElementById('nav-main')?.addEventListener('focusin', navOpen)
document.getElementById('nav-main')?.addEventListener('focusout', (e) => {
if (
e.relatedTarget &&
!document
.getElementById('nav-main')
?.contains(e.relatedTarget as HTMLElement)
) {
navClose()
}
})
document.addEventListener('astro:before-preparation', () => {
document.getElementById('loading-indicator')?.classList.remove('hidden')
})
document.addEventListener('astro:after-swap', () => {
requestAnimationFrame(navClose)
document.getElementById('loading-indicator')?.classList.add('hidden')
})
document.addEventListener('astro:page-load', () => {
document.querySelectorAll('#nav-main li:has(a)').forEach((li) => {
const child = li.querySelector('a')
if (!child) {
li.classList.add('url-here')
return
}
if (
window.location.href.replace(/\/$/, '') == child.href.replace(/\/$/, '')
) {
li.classList.add('url-here')
} else {
li.classList.remove('url-here')
}
})
})
</script>
<style>
.nav-container {
inline-size: 100dvi;
block-size: 3rem;
position: fixed;
z-index: 10000;
backdrop-filter: blur(4px);
background-color: rgb(from var(--color-bg-n) r g b / 0.5);
@media (width < 768px) {
block-size: 100dvh;
background-color: #00000033;
transition:
background-color ease 0.2s,
backdrop-filter ease 0.2s;
}
&.nav-container-hidden {
@media (width < 768px) {
backdrop-filter: blur(0px);
background-color: transparent;
pointer-events: none;
}
}
& > nav {
inline-size: 100%;
block-size: 100%;
padding-inline: 40px;
display: flex;
flex-direction: row;
align-items: center;
justify-content: space-between;
max-inline-size: 1400px;
margin-inline: auto;
font-weight: 400;
@media (width < 768px) {
flex-direction: column;
padding-block: 5rem;
padding-inline: 2rem;
align-items: start;
inline-size: 70%;
background-color: var(--color-bg-n);
margin-inline-start: 0;
font-size: larger;
transition: transform cubic-bezier(0.4, 1, 0.4, 1) 0.4s;
}
.nav-container-hidden & {
@media (width < 768px) {
transform: translateX(-80dvi);
}
}
& > ul {
display: flex;
flex-direction: row;
@media (width < 768px) {
flex-direction: column;
gap: 0.25rem;
inline-size: 100%;
}
}
& li {
height: 2rem;
@media (width < 768px) {
height: 3rem;
inline-size: 100%;
}
&:hover {
background-color: rgb(from var(--color-fg-0) r g b / 0.1);
}
&.url-here {
color: var(--color-blue);
background-color: rgb(from var(--color-blue) r g b / 0.1);
}
& > a {
padding-inline: 0.75rem;
inline-size: 100%;
block-size: 100%;
display: flex;
align-items: center;
gap: 0.25rem;
& > svg {
font-size: 1.5rem;
}
}
}
& .website-logo > a {
gap: 0.5rem;
font-weight: 600;
/* & > .logo { */
/* --size: 2.5rem; */
/**/
/* width: var(--size); */
/* height: var(--size); */
/* display: inline-block; */
/* } */
}
}
}
.nav-mobile {
inline-size: 100dvw;
block-size: 4rem;
position: fixed;
z-index: 100;
backdrop-filter: blur(4px);
background-color: rgb(from var(--color-bg-n) r g b / 0.5);
font-size: larger;
display: flex;
flex-direction: row;
align-items: center;
padding-inline: 1rem;
gap: 0.5rem;
& > button {
inline-size: 3rem;
aspect-ratio: 1;
display: flex;
align-items: center;
justify-content: center;
font-size: 2rem;
cursor: pointer;
&:active,
&:hover {
background-color: rgb(from var(--color-fg-0) r g b / 0.1);
}
}
& > a {
font-weight: 600;
&:active,
&:hover {
background-color: rgb(from var(--color-fg-0) r g b / 0.1);
}
}
@media (width >= 768px) {
display: none;
}
}
.loading-indicator {
--color1: oklch(from var(--color-blue) 0.7 0.17 h);
--color2: oklch(from var(--color-blue) 0.57 0.25 h);
--loading-x-size: 2rem;
--loading-height: 0.25rem;
position: fixed;
height: var(--loading-height);
background-color: var(--color1);
inset-inline-start: 0;
inset-inline-end: 0;
inset-block-start: 0;
z-index: 10001;
background-image: linear-gradient(
-45deg,
transparent 0%,
transparent 25%,
var(--color2) 25%,
var(--color2) 75%,
transparent 75%,
transparent 100%
);
background-repeat: repeat;
background-size: 2rem 100%;
animation: loading-animation infinite 0.5s linear;
transition: inset-block-start cubic-bezier(0.4, 1, 0.4, 1) 0.2s;
&.hidden {
inset-block-start: calc(-1 * var(--loading-height));
}
}
@keyframes loading-animation {
0% {
background-position: 0 0;
}
100% {
background-position: var(--loading-x-size) 0;
}
}
.layout-gap {
height: var(--size-nav);
}
</style>