添加系列栏
This commit is contained in:
1 parent
841d47019d
commit
3b4d3195bd
16 files changed
+90
-3
No files matched your search
@@ -0,0 +1,51 @@
|
||||
---
|
||||
import I18nKey from '../../i18n/i18nKey'
|
||||
import { i18n } from '../../i18n/translation'
|
||||
import { getPostSeries } from '../../utils/content-utils'
|
||||
import { getPostUrlBySlug } from '../../utils/url-utils'
|
||||
import WidgetLayout from './WidgetLayout.astro'
|
||||
|
||||
const COLLAPSED_HEIGHT = '7.5rem'
|
||||
|
||||
interface Props {
|
||||
class?: string
|
||||
style?: string
|
||||
series: string
|
||||
}
|
||||
const className = Astro.props.class
|
||||
const style = Astro.props.style
|
||||
const seriesName = Astro.props.series
|
||||
|
||||
const series = await getPostSeries(seriesName)
|
||||
|
||||
const isCollapsed = series.length >= 10
|
||||
---
|
||||
<WidgetLayout name={i18n(I18nKey.series) + " - " + series[0].data.series} id="series" isCollapsed={isCollapsed} collapsedHeight={COLLAPSED_HEIGHT} class={className} style={style}>
|
||||
<div class="flex flex-col gap-1">
|
||||
{series.map(t => (
|
||||
<a href={getPostUrlBySlug(t.slug)}
|
||||
aria-label={t.data.title}
|
||||
class="group btn-plain h-10 w-full rounded-lg hover:text-[initial]"
|
||||
>
|
||||
<!-- dot and line -->
|
||||
<div class="w-[15%] md:w-[10%] relative dash-line h-full flex items-center">
|
||||
<div class="transition-all mx-auto w-1 h-1 rounded group-hover:h-5
|
||||
bg-[oklch(0.5_0.05_var(--hue))] group-hover:bg-[var(--primary)]
|
||||
outline outline-4 z-50
|
||||
outline-[var(--card-bg)]
|
||||
group-hover:outline-[var(--btn-plain-bg-hover)]
|
||||
group-active:outline-[var(--btn-plain-bg-active)]
|
||||
"
|
||||
></div>
|
||||
</div>
|
||||
<!-- post title -->
|
||||
<div class="w-[85%] text-left font-bold
|
||||
group-hover:translate-x-1 transition-all group-hover:text-[var(--primary)]
|
||||
text-75 pr-15 whitespace-nowrap overflow-ellipsis overflow-hidden" title={t.data.title}
|
||||
>
|
||||
{t.data.title}
|
||||
</div>
|
||||
</a>
|
||||
))}
|
||||
</div>
|
||||
</WidgetLayout>
|
||||
@@ -3,19 +3,25 @@ import type { MarkdownHeading } from "astro";
|
||||
import Categories from "./Categories.astro";
|
||||
import Profile from "./Profile.astro";
|
||||
import Tag from "./Tags.astro";
|
||||
import Series from './Series.astro'
|
||||
|
||||
interface Props {
|
||||
class?: string;
|
||||
headings?: MarkdownHeading[];
|
||||
series?: string
|
||||
}
|
||||
|
||||
const className = Astro.props.class;
|
||||
const series = Astro.props.series
|
||||
---
|
||||
<div id="sidebar" class:list={[className, "w-full"]}>
|
||||
<div class="flex flex-col w-full gap-4 mb-4">
|
||||
<Profile></Profile>
|
||||
</div>
|
||||
<div id="sidebar-sticky" class="transition-all duration-700 flex flex-col w-full gap-4 top-4 sticky top-4">
|
||||
<div id="series" class="flex flex-col w-full gap-4">
|
||||
{ series && <Series series={ series }></Series> }
|
||||
</div>
|
||||
<Categories class="onload-animation" style="animation-delay: 150ms"></Categories>
|
||||
<Tag class="onload-animation" style="animation-delay: 200ms"></Tag>
|
||||
</div>
|
||||
|
||||
@@ -11,6 +11,7 @@ const postsCollection = defineCollection({
|
||||
tags: z.array(z.string()).optional().default([]),
|
||||
category: z.string().optional().nullable().default(""),
|
||||
lang: z.string().optional().default(""),
|
||||
series: z.string().optional(),
|
||||
|
||||
/* For internal use */
|
||||
prevTitle: z.string().default(""),
|
||||
|
||||
@@ -32,6 +32,7 @@ enum I18nKey {
|
||||
author = "author",
|
||||
publishedAt = "publishedAt",
|
||||
license = "license",
|
||||
series = 'series',
|
||||
}
|
||||
|
||||
export default I18nKey;
|
||||
@@ -35,4 +35,5 @@ export const en: Translation = {
|
||||
[Key.author]: "Author",
|
||||
[Key.publishedAt]: "Published at",
|
||||
[Key.license]: "License",
|
||||
[Key.series]: 'Series',
|
||||
};
|
||||
@@ -35,4 +35,5 @@ export const es: Translation = {
|
||||
[Key.author]: "Autor",
|
||||
[Key.publishedAt]: "Publicado el",
|
||||
[Key.license]: "Licencia",
|
||||
[Key.series]: 'Serie',
|
||||
};
|
||||
@@ -35,4 +35,5 @@ export const ja: Translation = {
|
||||
[Key.author]: "作者",
|
||||
[Key.publishedAt]: "公開日",
|
||||
[Key.license]: "ライセンス",
|
||||
[Key.series]: 'シリーズ',
|
||||
};
|
||||
@@ -35,4 +35,5 @@ export const ko: Translation = {
|
||||
[Key.author]: "저자",
|
||||
[Key.publishedAt]: "게시일",
|
||||
[Key.license]: "라이선스",
|
||||
[Key.series]: '시리즈',
|
||||
};
|
||||
@@ -35,4 +35,5 @@ export const th: Translation = {
|
||||
[Key.author]: "ผู้เขียน",
|
||||
[Key.publishedAt]: "เผยแพร่เมื่อ",
|
||||
[Key.license]: "สัญญาอนุญาต",
|
||||
[Key.series]: 'ชุด',
|
||||
};
|
||||
@@ -35,4 +35,5 @@ export const zh_CN: Translation = {
|
||||
[Key.author]: "作者",
|
||||
[Key.publishedAt]: "发布于",
|
||||
[Key.license]: "许可协议",
|
||||
[Key.series]: '系列',
|
||||
};
|
||||
@@ -35,4 +35,5 @@ export const zh_TW: Translation = {
|
||||
[Key.author]: "作者",
|
||||
[Key.publishedAt]: "發佈於",
|
||||
[Key.license]: "許可協議",
|
||||
[Key.series]: '系列',
|
||||
};
|
||||
@@ -21,6 +21,7 @@ interface Props {
|
||||
description?: string;
|
||||
lang?: string;
|
||||
setOGTypeArticle?: boolean;
|
||||
series?: string
|
||||
headings?: MarkdownHeading[];
|
||||
}
|
||||
|
||||
@@ -80,7 +81,7 @@ const mainPanelTop = siteConfig.banner.enable
|
||||
</a>}
|
||||
|
||||
|
||||
<SideBar class="mb-4 row-start-2 row-end-3 col-span-2 lg:row-start-1 lg:row-end-2 lg:col-span-1 lg:max-w-[17.5rem] onload-animation" headings={headings}></SideBar>
|
||||
<SideBar class="mb-4 row-start-2 row-end-3 col-span-2 lg:row-start-1 lg:row-end-2 lg:col-span-1 lg:max-w-[17.5rem] onload-animation" headings={headings} series={ series }></SideBar>
|
||||
|
||||
<main id="swup-container" class="transition-swup-fade col-span-2 lg:col-span-1 overflow-hidden">
|
||||
<div id="content-wrapper" class="onload-animation">
|
||||
|
||||
@@ -45,7 +45,7 @@ const jsonLd = {
|
||||
// TODO include cover image here
|
||||
};
|
||||
---
|
||||
<MainGridLayout banner={entry.data.image} title={entry.data.title} description={entry.data.description} lang={entry.data.lang} setOGTypeArticle={true} headings={headings}>
|
||||
<MainGridLayout banner={entry.data.image} title={entry.data.title} description={entry.data.description} lang={entry.data.lang} setOGTypeArticle={true} headings={headings} series={entry.data.series}>
|
||||
<script is:inline slot="head" type="application/ld+json" set:html={JSON.stringify(jsonLd)}></script>
|
||||
<div class="flex w-full rounded-[var(--radius-large)] overflow-hidden relative mb-4">
|
||||
<div id="post-container" class:list={["card-base z-10 px-6 md:px-9 pt-6 pb-4 relative w-full ",
|
||||
|
||||
@@ -98,6 +98,7 @@ export type BlogPostData = {
|
||||
draft?: boolean;
|
||||
image?: string;
|
||||
category?: string;
|
||||
series?: string
|
||||
prevTitle?: string;
|
||||
prevSlug?: string;
|
||||
nextTitle?: string;
|
||||
|
||||
@@ -112,3 +112,22 @@ export async function getCategoryList(): Promise<Category[]> {
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
export async function getPostSeries(
|
||||
seriesName: string,
|
||||
): Promise<{ body: string; data: BlogPostData; slug: string }[]> {
|
||||
const posts = (await getCollection('posts', ({ data }) => {
|
||||
return (
|
||||
(import.meta.env.PROD ? data.draft !== true : true) &&
|
||||
data.series === seriesName
|
||||
)
|
||||
})) as unknown as { body: string; data: BlogPostData; slug: string }[]
|
||||
|
||||
posts.sort((a, b) => {
|
||||
const dateA = new Date(a.data.published)
|
||||
const dateB = new Date(b.data.published)
|
||||
return dateA > dateB ? 1 : -1
|
||||
})
|
||||
|
||||
return posts
|
||||
}
|
||||
Reference in new issue
Block a user