feat: redesign TOC

This commit is contained in:
saicaca
2024-10-26 15:48:54 +08:00
parent b5fddf2096
commit a336f34ea4
14 changed files with 102 additions and 80 deletions

View File

@@ -120,6 +120,10 @@ color_set({
--admonitions-color-important: oklch(0.7 0.14 310) oklch(0.75 0.14 310)
--admonitions-color-warning: oklch(0.7 0.14 60) oklch(0.75 0.14 60)
--admonitions-color-caution: oklch(0.6 0.2 25) oklch(0.65 0.2 25)
--toc-badge-bg: oklch(0.9 0.045 var(--hue)) var(--btn-regular-bg)
--toc-btn-hover: oklch(0.92 0.015 var(--hue)) oklch(0.22 0.02 var(--hue))
--toc-btn-active: oklch(0.90 0.015 var(--hue)) oklch(0.25 0.02 var(--hue))
})
@@ -242,6 +246,23 @@ color_set({
hover:decoration-[var(--link-hover)] active:decoration-[var(--link-active)] underline-offset-[0.25rem]
}
.toc-hide,
.toc-not-ready {
@apply opacity-0 pointer-events-none
}
#toc-inner-wrapper {
mask-image: linear-gradient(to bottom, transparent 0%, black 2rem, black calc(100% - 2rem), transparent 100%);
}
.hide-scrollbar {
scrollbar-width: none;
-ms-overflow-style: none;
}
.hide-scrollbar::-webkit-scrollbar {
display: none;
}
.text-90 {
@apply text-black/90 dark:text-white/90
}

View File

@@ -25,7 +25,7 @@ import { Icon } from 'astro-icon/components'
font-weight: bold
border: none
position: fixed
bottom: 15rem
bottom: 10rem
opacity: 1
cursor: pointer
transform: translateX(5rem)

View File

@@ -18,13 +18,8 @@ const headings = Astro.props.headings
<div class="flex flex-col w-full gap-4 mb-4">
<Profile></Profile>
</div>
<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 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>
</div>

View File

@@ -1,30 +1,30 @@
---
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 { headings = [] } = Astro.props;
// generate random headings, for testing
/*
for (let i = 0; i < 50; i++) {
headings.push({
text: `Heading ${i + 1}`,
depth: Math.floor(Math.random() * 3) + 1,
slug: `heading-${i + 1}`
})
}
*/
let minDepth = 10;
for (const heading of headings) {
minDepth = Math.min(minDepth, heading.depth);
}
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('#');
@@ -35,14 +35,26 @@ const removeTailingHash = (text: string) => {
return text.substring(0, lastIndexOfHash);
}
let heading1Count = 1;
---
<WidgetLayout name={i18n(I18nKey.toc)} id="toc" isCollapsed={isCollapsed} collapsedHeight={COLLAPSED_HEIGHT}
class={className} style={style}
<div class:list={[className]}>
{headings.filter((heading) => heading.depth <= minDepth + 1).map((heading) =>
<a href={`#${heading.slug}`} class="px-2 transition flex w-full gap-2 h-9 rounded-xl items-center
hover:bg-[var(--toc-btn-hover)] active:bg-[var(--toc-btn-active)]
">
<div class:list={["w-5 h-5 rounded-lg text-xs flex items-center justify-center font-bold",
{
"bg-[var(--toc-badge-bg)] text-[var(--btn-content)]": heading.depth == minDepth,
}
]}
>
{headings.map((heading) =>
<div style={getMarginStyleFromHeading(heading)}>
<ButtonLink url={`#${heading.slug}`}>{removeTailingHash(heading.text)}</ButtonLink>
</div>
{heading.depth == minDepth && heading1Count++}
{heading.depth == minDepth + 1 && <div class="w-1.5 h-1.5 rounded-sm bg-black/5 dark:bg-white/5"></div>}
</div>
<div class:list={["text-sm", {
"text-50": heading.depth == minDepth,
"text-30": heading.depth == minDepth + 1,
}]}>{removeTailingHash(heading.text)}</div>
</a>
)}
</WidgetLayout>
</div>