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 : 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(() => { onMount(() => {
const headers = document.querySelectorAll( 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' '.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 () => { return () => {
if (hasToc) tocbot.destroy(); if (hasToc) tocbot.destroy();
}; };
@@ -88,8 +126,14 @@
{/if} {/if}
{#if data.series && data.seriesPosts.length > 0} {#if data.series && data.seriesPosts.length > 0}
<details class="series-nav" open> <div class="details series-nav" class:open={isSeriesOpen}>
<summary class="series-title" id="series-navigation-title"> <!-- 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 <span
>{$page.data.aliases?.series || '系列'} - >{$page.data.aliases?.series || '系列'} -
<a href={resolve('/series/[slug]', { slug: data.series.slug })}>{data.series.name}</a <a href={resolve('/series/[slug]', { slug: data.series.slug })}>{data.series.name}</a
@@ -110,8 +154,8 @@
></path></svg ></path></svg
> >
</span> </span>
</summary> </div>
<div class="series-content"> <div bind:this={seriesContentEl} class="details-content series-content">
<nav> <nav>
<ul> <ul>
{#each data.seriesPosts as seriesPost (seriesPost.id)} {#each data.seriesPosts as seriesPost (seriesPost.id)}
@@ -142,7 +186,7 @@
{/if} {/if}
</nav> </nav>
</div> </div>
</details> </div>
{/if} {/if}
<MarkdownRenderer content={data.post.content} /> <MarkdownRenderer content={data.post.content} />
@@ -204,11 +248,7 @@
padding: 0.25rem 0.75rem; padding: 0.25rem 0.75rem;
background: #e6e6e6; background: #e6e6e6;
cursor: pointer; cursor: pointer;
list-style: none; user-select: none;
}
.series-nav .series-title::-webkit-details-marker {
display: none;
} }
/* .details-icon — 对应 DoIt .details .details-icon > svg */ /* .details-icon — 对应 DoIt .details .details-icon > svg */
@@ -222,35 +262,25 @@
transition: transform 0.2s ease; transition: transform 0.2s ease;
} }
.series-nav[open] .details-icon > :global(svg) { .series-nav.open .details-icon > :global(svg) {
transform: rotate(90deg); transform: rotate(90deg);
} }
/* .series-content — 展开收缩动画 */ /* .series-content — 展开收缩动画,与 admonition 完全一致 */
.series-nav .series-content { .series-nav .details-content {
font-size: 0.875rem; font-size: 0.875rem;
background-color: #f5f5f5; background-color: #f5f5f5;
display: grid; max-height: 0;
grid-template-rows: 0fr;
transition: grid-template-rows 0.3s ease-out;
overflow: hidden; overflow: hidden;
transition: max-height 0.3s ease-out;
} }
.series-nav[open] .series-content { .series-nav .details-content > nav > ul {
grid-template-rows: 1fr;
}
.series-nav .series-content > nav {
min-height: 0;
overflow: hidden;
}
.series-nav .series-content > nav > ul {
margin: 0; margin: 0;
padding: 0.4rem 1rem 0.4rem 1.8rem; padding: 0.4rem 1rem 0.4rem 1.8rem;
} }
.series-nav .series-content ul { .series-nav .details-content ul {
text-indent: -0.85rem; text-indent: -0.85rem;
padding-left: 0.8rem; padding-left: 0.8rem;
list-style: none; list-style: none;