mirror of
https://github.com/KazooTTT/kazoottt-blog.git
synced 2025-06-23 10:41:31 +08:00
201 lines
5.6 KiB
Plaintext
201 lines
5.6 KiB
Plaintext
---
|
|
import type { CollectionEntry } from 'astro:content'
|
|
|
|
import BlogHero from '@/components/blog/Hero.astro'
|
|
import TOC from '@/components/blog/TOC.astro'
|
|
import Button from '@/components/Button.astro'
|
|
import PageLayout from './BaseLayout.astro'
|
|
import GiscusComment from '@/components/GiscusComment'
|
|
|
|
interface Props {
|
|
post: CollectionEntry<'post'>
|
|
simple?: boolean
|
|
backHref: string
|
|
}
|
|
|
|
const { post, simple = false, backHref = '/blog' } = Astro.props
|
|
const {
|
|
data: { description, ogImage, title, date },
|
|
slug
|
|
} = post
|
|
|
|
const socialImage = ogImage ?? `/og-image/${slug}.png`
|
|
const articleDate = date?.toISOString()
|
|
const { headings } = await post.render()
|
|
---
|
|
|
|
<PageLayout
|
|
meta={{
|
|
articleDate,
|
|
description: description ?? '',
|
|
ogImage: socialImage,
|
|
title
|
|
}}
|
|
>
|
|
<div class='w-full'>
|
|
<Button title='Back' href={backHref} style='button'>
|
|
<svg
|
|
xmlns='http://www.w3.org/2000/svg'
|
|
width='14'
|
|
height='14'
|
|
viewBox='0 0 24 24'
|
|
slot='icon-before'
|
|
>
|
|
<path
|
|
fill='currentColor'
|
|
d='m6.921 12.5l5.792 5.792L12 19l-7-7l7-7l.713.708L6.921 11.5H19v1z'
|
|
>
|
|
</path>
|
|
</svg>
|
|
</Button>
|
|
<div class='mt-8 gap-x-10 lg:flex lg:items-start'>
|
|
{!!headings.length && <TOC headings={headings} />}
|
|
<article class='flex-1 flex-grow break-words' data-pagefind-body>
|
|
<div id='blog-hero'>
|
|
<BlogHero content={post} simple={simple} />
|
|
</div>
|
|
<div
|
|
id='blog-gallery'
|
|
class='prose prose-base prose-zinc mt-12 w-full text-muted-foreground dark:prose-invert
|
|
prose-headings:scroll-mt-20 prose-headings:font-bold
|
|
prose-headings:text-foreground prose-headings:underline prose-headings:decoration-foreground
|
|
prose-headings:underline-offset-4 prose-headings:before:absolute prose-headings:before:-ms-4
|
|
prose-p:leading-relaxed prose-p:text-muted-foreground
|
|
prose-a:text-primary prose-a:decoration-green-400/50 prose-a:decoration-wavy prose-a:underline-offset-4
|
|
prose-a:transition-all hover:prose-a:text-primary hover:prose-a:decoration-green-400
|
|
prose-blockquote:border-primary prose-blockquote:bg-muted/50
|
|
prose-blockquote:px-4 prose-blockquote:py-1 prose-li:leading-relaxed
|
|
prose-li:text-muted-foreground
|
|
prose-th:before:content-none prose-img:my-8 prose-img:rounded-lg
|
|
prose-img:shadow-md prose-hr:border-border
|
|
[&>:not(pre)>code]:rounded-md [&>:not(pre)>code]:bg-green-100/50 [&>:not(pre)>code]:px-1.5
|
|
[&>:not(pre)>code]:py-0.5 [&>:not(pre)>code]:font-bold [&>:not(pre)>code]:text-green-900
|
|
[&>:not(pre)>code]:before:content-none [&>:not(pre)>code]:after:content-none
|
|
dark:[&>:not(pre)>code]:bg-green-950 dark:[&>:not(pre)>code]:text-green-400'
|
|
>
|
|
<slot />
|
|
</div>
|
|
<GiscusComment client:load />
|
|
</article>
|
|
</div>
|
|
<button
|
|
aria-label='Back to Top'
|
|
class='z-90 fixed bottom-8 end-4 flex h-8 w-8 translate-y-28 items-center justify-center rounded-full border-2 border-border/50 bg-primary-foreground text-3xl opacity-0 transition-all duration-300 hover:border-border/75 data-[show=true]:translate-y-0 data-[show=true]:opacity-100 sm:end-8 sm:h-12 sm:w-12'
|
|
data-show='false'
|
|
id='to-top-btn'
|
|
><svg
|
|
aria-hidden='true'
|
|
class='h-4 w-4'
|
|
fill='none'
|
|
focusable='false'
|
|
stroke='currentColor'
|
|
stroke-width='2'
|
|
viewBox='0 0 24 24'
|
|
xmlns='http://www.w3.org/2000/svg'
|
|
>
|
|
<path d='M4.5 15.75l7.5-7.5 7.5 7.5' stroke-linecap='round' stroke-linejoin='round'></path>
|
|
</svg>
|
|
</button>
|
|
<div id='image-modal' class='modal'>
|
|
<span class='close'>
|
|
<svg
|
|
xmlns='http://www.w3.org/2000/svg'
|
|
class='h-6 w-6'
|
|
fill='none'
|
|
viewBox='0 0 24 24'
|
|
stroke='currentColor'
|
|
>
|
|
<path
|
|
stroke-linecap='round'
|
|
stroke-linejoin='round'
|
|
stroke-width='2'
|
|
d='M6 18L18 6M6 6l12 12'></path>
|
|
</svg>
|
|
</span>
|
|
<img class='modal-content' id='modal-image' />
|
|
</div>
|
|
</div>
|
|
</PageLayout>
|
|
|
|
<script>
|
|
const scrollBtn = document.getElementById('to-top-btn') as HTMLButtonElement
|
|
const targetHeader = document.getElementById('blog-hero') as HTMLDivElement
|
|
|
|
function callback(entries: IntersectionObserverEntry[]) {
|
|
entries.forEach((entry) => {
|
|
// only show the scroll to top button when the heading is out of view
|
|
scrollBtn.dataset.show = (!entry.isIntersecting).toString()
|
|
})
|
|
}
|
|
|
|
scrollBtn.addEventListener('click', () => {
|
|
document.documentElement.scrollTo({ behavior: 'smooth', top: 0 })
|
|
})
|
|
|
|
const observer = new IntersectionObserver(callback)
|
|
observer.observe(targetHeader)
|
|
|
|
const modal = document.getElementById('image-modal')
|
|
const modalImg = document.getElementById('modal-image') as HTMLImageElement
|
|
const images = document.querySelectorAll('#blog-gallery img') as NodeListOf<HTMLImageElement>
|
|
images.forEach(function (img) {
|
|
img.style.cursor = 'pointer'
|
|
img.addEventListener('click', () => {
|
|
if (modal && modalImg) {
|
|
modal.style.display = 'flex'
|
|
document.body.style.overflow = 'hidden'
|
|
|
|
modalImg.src = img.src
|
|
}
|
|
})
|
|
})
|
|
|
|
const span = document.getElementsByClassName('close')[0]
|
|
span?.addEventListener('click', () => {
|
|
if (modal) {
|
|
modal.style.display = 'none'
|
|
document.body.style.overflow = 'auto'
|
|
}
|
|
})
|
|
</script>
|
|
|
|
<style>
|
|
.modal {
|
|
display: none;
|
|
position: fixed;
|
|
z-index: 100;
|
|
margin: auto;
|
|
padding: 50px;
|
|
inset: 0;
|
|
width: 100%;
|
|
height: 100%;
|
|
overflow: auto;
|
|
background-color: rgba(0, 0, 0, 0.9);
|
|
align-items: center;
|
|
justify-content: center;
|
|
}
|
|
|
|
.modal-content {
|
|
margin: auto;
|
|
display: block;
|
|
width: 80%;
|
|
}
|
|
|
|
.close {
|
|
position: fixed;
|
|
top: 15px;
|
|
right: 35px;
|
|
color: #f1f1f1;
|
|
font-size: 40px;
|
|
font-weight: bold;
|
|
transition: 0.3s;
|
|
}
|
|
|
|
.close:hover,
|
|
.close:focus {
|
|
color: #bbb;
|
|
text-decoration: none;
|
|
cursor: pointer;
|
|
}
|
|
</style>
|