mirror of
https://github.com/saicaca/fuwari.git
synced 2026-01-11 23:02:53 +01:00
feat: added TOC (#198)
This commit is contained in:
@@ -2,15 +2,29 @@
|
||||
import Profile from './Profile.astro'
|
||||
import Tag from './Tags.astro'
|
||||
import Categories from './Categories.astro'
|
||||
import type { MarkdownHeading } from 'astro'
|
||||
import TOC from './TOC.astro'
|
||||
|
||||
interface Props {
|
||||
class? : string
|
||||
headings? : MarkdownHeading[]
|
||||
}
|
||||
|
||||
const className = Astro.props.class
|
||||
const headings = Astro.props.headings
|
||||
|
||||
---
|
||||
<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">
|
||||
<Categories class="onload-animation" style="animation-delay: 150ms"></Categories>
|
||||
<Tag class="onload-animation" style="animation-delay: 200ms"></Tag>
|
||||
<div id="sidebar-sticky" class="sticky top-4">
|
||||
<div id="toc" class="transition-all duration-700 flex flex-col w-full gap-4">
|
||||
{headings && headings.length > 0 && <TOC class="mb-4 onload-animation" style="animation-delay: 150ms" headings={headings}/>}
|
||||
</div>
|
||||
<div class="transition-all duration-700 flex flex-col w-full gap-4">
|
||||
<Categories class="onload-animation" style="animation-delay: 150ms"></Categories>
|
||||
<Tag class="onload-animation" style="animation-delay: 200ms"></Tag>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
48
src/components/widget/TOC.astro
Normal file
48
src/components/widget/TOC.astro
Normal file
@@ -0,0 +1,48 @@
|
||||
---
|
||||
import WidgetLayout from './WidgetLayout.astro'
|
||||
import type { MarkdownHeading } from 'astro';
|
||||
|
||||
import { i18n } from '../../i18n/translation'
|
||||
import I18nKey from '../../i18n/i18nKey'
|
||||
import ButtonLink from '../control/ButtonLink.astro'
|
||||
|
||||
interface Props {
|
||||
class?: string
|
||||
style?: string
|
||||
headings: MarkdownHeading[]
|
||||
}
|
||||
|
||||
const { headings } = Astro.props;
|
||||
|
||||
const className = Astro.props.class
|
||||
const style = Astro.props.style
|
||||
|
||||
const COLLAPSED_HEIGHT = '7.5rem'
|
||||
const COLLAPSE_THRESHOLD = 5
|
||||
|
||||
const isCollapsed = headings.length >= COLLAPSE_THRESHOLD
|
||||
|
||||
const getMarginStyleFromHeading = (heading: MarkdownHeading) => {
|
||||
return `margin-left: ${(heading.depth - 1) / 2}rem`;
|
||||
}
|
||||
|
||||
const removeTailingHash = (text: string) => {
|
||||
let lastIndexOfHash = text.lastIndexOf('#');
|
||||
if (lastIndexOfHash != text.length - 1) {
|
||||
return text;
|
||||
}
|
||||
|
||||
return text.substring(0, lastIndexOfHash);
|
||||
}
|
||||
|
||||
---
|
||||
|
||||
<WidgetLayout name={i18n(I18nKey.toc)} id="toc" isCollapsed={isCollapsed} collapsedHeight={COLLAPSED_HEIGHT}
|
||||
class={className} style={style}
|
||||
>
|
||||
{headings.map((heading) =>
|
||||
<div style={getMarginStyleFromHeading(heading)}>
|
||||
<ButtonLink url={`#${heading.slug}`}>{removeTailingHash(heading.text)}</ButtonLink>
|
||||
</div>
|
||||
)}
|
||||
</WidgetLayout>
|
||||
@@ -8,6 +8,7 @@ enum I18nKey {
|
||||
categories = 'categories',
|
||||
recentPosts = 'recentPosts',
|
||||
|
||||
toc = 'toc',
|
||||
comments = 'comments',
|
||||
|
||||
untitled = 'untitled',
|
||||
|
||||
@@ -11,6 +11,7 @@ export const en: Translation = {
|
||||
[Key.categories]: 'Categories',
|
||||
[Key.recentPosts]: 'Recent Posts',
|
||||
|
||||
[Key.toc]: 'TOC',
|
||||
[Key.comments]: 'Comments',
|
||||
|
||||
[Key.untitled]: 'Untitled',
|
||||
|
||||
@@ -11,6 +11,7 @@ export const es: Translation = {
|
||||
[Key.categories]: 'Categorías',
|
||||
[Key.recentPosts]: 'Publicaciones recientes',
|
||||
|
||||
[Key.toc]: 'Índice',
|
||||
[Key.comments]: 'Comentarios',
|
||||
|
||||
[Key.untitled]: 'Sin título',
|
||||
|
||||
@@ -11,6 +11,7 @@ export const ja: Translation = {
|
||||
[Key.categories]: 'カテゴリ',
|
||||
[Key.recentPosts]: '最近の投稿',
|
||||
|
||||
[Key.toc]: '目次',
|
||||
[Key.comments]: 'コメント',
|
||||
|
||||
[Key.untitled]: 'タイトルなし',
|
||||
|
||||
@@ -11,6 +11,7 @@ export const ko: Translation = {
|
||||
[Key.categories]: '카테고리',
|
||||
[Key.recentPosts]: '최근 게시물',
|
||||
|
||||
[Key.toc]: '목차',
|
||||
[Key.comments]: '댓글',
|
||||
|
||||
[Key.untitled]: '제목 없음',
|
||||
|
||||
@@ -11,6 +11,7 @@ export const zh_CN: Translation = {
|
||||
[Key.categories]: '分类',
|
||||
[Key.recentPosts]: '最新文章',
|
||||
|
||||
[Key.toc]: '目录',
|
||||
[Key.comments]: '评论',
|
||||
|
||||
[Key.untitled]: '无标题',
|
||||
|
||||
@@ -11,6 +11,7 @@ export const zh_TW: Translation = {
|
||||
[Key.categories]: '分類',
|
||||
[Key.recentPosts]: '最新文章',
|
||||
|
||||
[Key.toc]: '目錄',
|
||||
[Key.comments]: '評論',
|
||||
|
||||
[Key.untitled]: '無標題',
|
||||
|
||||
@@ -6,6 +6,7 @@ import SideBar from '@components/widget/SideBar.astro'
|
||||
import Layout from './Layout.astro'
|
||||
import { Icon } from 'astro-icon/components'
|
||||
import { siteConfig } from '../config'
|
||||
import type { MarkdownHeading } from 'astro'
|
||||
|
||||
interface Props {
|
||||
title?: string
|
||||
@@ -13,9 +14,10 @@ interface Props {
|
||||
description?: string
|
||||
lang?: string
|
||||
setOGTypeArticle?: boolean;
|
||||
headings? : MarkdownHeading[]
|
||||
}
|
||||
|
||||
const { title, banner, description, lang, setOGTypeArticle } = Astro.props
|
||||
const { title, banner, description, lang, setOGTypeArticle, headings } = Astro.props
|
||||
const hasBannerCredit =
|
||||
siteConfig.banner.enable && siteConfig.banner.credit.enable
|
||||
const hasBannerLink = !!siteConfig.banner.credit.url
|
||||
@@ -48,7 +50,7 @@ const hasBannerLink = !!siteConfig.banner.credit.url
|
||||
</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"></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}></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">
|
||||
|
||||
@@ -25,7 +25,7 @@ export async function getStaticPaths() {
|
||||
}
|
||||
|
||||
const { entry } = Astro.props
|
||||
const { Content } = await entry.render()
|
||||
const { Content, headings } = await entry.render()
|
||||
|
||||
const { remarkPluginFrontmatter } = await entry.render()
|
||||
|
||||
@@ -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}>
|
||||
<MainGridLayout banner={entry.data.image} title={entry.data.title} description={entry.data.description} lang={entry.data.lang} setOGTypeArticle={true} headings={headings}>
|
||||
<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 ",
|
||||
|
||||
Reference in New Issue
Block a user