feat: 优化文章系列导航栏的展开收缩动画与交互效果
continuous-integration/drone/push Build is passing

This commit is contained in:
seahi committed 2026-08-17 12:20:53 +08:00
1 parent 2a04f9a226
commit 37b8ffc7fe
1 file changed
+57 -27
+57 -27
View File
@@ -20,6 +20,40 @@
: null
);
let isSeriesOpen = $state(true);
let seriesContentEl: HTMLElement | null = $state(null);
function toggleSeries(e: MouseEvent) {
const target = e.target as HTMLElement;
if (target.closest('a')) return;
const contentEl = seriesContentEl;
if (!contentEl) {
isSeriesOpen = !isSeriesOpen;
return;
}
if (isSeriesOpen) {
if (contentEl.style.maxHeight === 'none' || contentEl.style.maxHeight === '') {
contentEl.style.maxHeight = contentEl.scrollHeight + 'px';
void contentEl.offsetHeight;
}
contentEl.style.maxHeight = '0px';
isSeriesOpen = false;
} else {
isSeriesOpen = true;
contentEl.style.maxHeight = contentEl.scrollHeight + 'px';
const transitionEndHandler = (ev: Event) => {
if (ev.target === contentEl && isSeriesOpen) {
contentEl.style.maxHeight = 'none';
}
contentEl.removeEventListener('transitionend', transitionEndHandler);
};
contentEl.addEventListener('transitionend', transitionEndHandler);
}
}
onMount(() => {
const headers = document.querySelectorAll(
'.js-toc-content h1, .js-toc-content h2, .js-toc-content h3, .js-toc-content h4, .js-toc-content h5, .js-toc-content h6'
@@ -61,6 +95,10 @@
};
});
if (seriesContentEl && isSeriesOpen) {
seriesContentEl.style.maxHeight = 'none';
}
return () => {
if (hasToc) tocbot.destroy();
};
@@ -88,8 +126,14 @@
{/if}
{#if data.series && data.seriesPosts.length > 0}
<details class="series-nav" open>
<summary class="series-title" id="series-navigation-title">
<div class="details series-nav" class:open={isSeriesOpen}>
<!-- svelte-ignore a11y_click_events_have_key_events -->
<!-- svelte-ignore a11y_no_static_element_interactions -->
<div
class="details-summary series-title"
id="series-navigation-title"
onclick={toggleSeries}
>
<span
>{$page.data.aliases?.series || '系列'} -
<a href={resolve('/series/[slug]', { slug: data.series.slug })}>{data.series.name}</a
@@ -110,8 +154,8 @@
></path></svg
>
</span>
</summary>
<div class="series-content">
</div>
<div bind:this={seriesContentEl} class="details-content series-content">
<nav>
<ul>
{#each data.seriesPosts as seriesPost (seriesPost.id)}
@@ -142,7 +186,7 @@
{/if}
</nav>
</div>
</details>
</div>
{/if}
<MarkdownRenderer content={data.post.content} />
@@ -204,11 +248,7 @@
padding: 0.25rem 0.75rem;
background: #e6e6e6;
cursor: pointer;
list-style: none;
}
.series-nav .series-title::-webkit-details-marker {
display: none;
user-select: none;
}
/* .details-icon — 对应 DoIt .details .details-icon > svg */
@@ -222,35 +262,25 @@
transition: transform 0.2s ease;
}
.series-nav[open] .details-icon > :global(svg) {
.series-nav.open .details-icon > :global(svg) {
transform: rotate(90deg);
}
/* .series-content — 展开收缩动画 */
.series-nav .series-content {
/* .series-content — 展开收缩动画,与 admonition 完全一致 */
.series-nav .details-content {
font-size: 0.875rem;
background-color: #f5f5f5;
display: grid;
grid-template-rows: 0fr;
transition: grid-template-rows 0.3s ease-out;
max-height: 0;
overflow: hidden;
transition: max-height 0.3s ease-out;
}
.series-nav[open] .series-content {
grid-template-rows: 1fr;
}
.series-nav .series-content > nav {
min-height: 0;
overflow: hidden;
}
.series-nav .series-content > nav > ul {
.series-nav .details-content > nav > ul {
margin: 0;
padding: 0.4rem 1rem 0.4rem 1.8rem;
}
.series-nav .series-content ul {
.series-nav .details-content ul {
text-indent: -0.85rem;
padding-left: 0.8rem;
list-style: none;