Files
blogflare/src/lib/components/admin/ArticleOutline.svelte
T

92 lines
2.1 KiB
Svelte

<script lang="ts">
import type { EditorOutline } from '$lib/editor/outline.svelte';
let { outline, onselect }: { outline: EditorOutline; onselect?: () => void } = $props();
let baseLevel = $derived(Math.min(...outline.headings.map((heading) => heading.level), 6));
</script>
<nav aria-label="正文大纲" class="article-outline">
{#if outline.headings.length}
<ul>
{#each outline.headings as heading, index (heading.from)}
<li>
<button
type="button"
class:active={outline.active === index}
aria-current={outline.active === index ? 'location' : undefined}
aria-label={`${heading.level} 级标题:${heading.text}`}
title={heading.text}
style:padding-left={`${10 + (heading.level - baseLevel) * 14}px`}
onclick={() => {
onselect?.();
outline.navigate(heading);
}}
>
<span>{heading.text}</span>
</button>
</li>
{/each}
</ul>
{:else}
<p class="empty">暂无标题。使用 #、## 等 Markdown 标题组织正文后,大纲会显示在这里。</p>
{/if}
</nav>
<style>
.article-outline {
min-width: 0;
}
.count,
.empty {
margin: 0 0 12px;
color: var(--text-muted);
font-size: 0.85rem;
}
ul {
list-style: none;
padding: 0;
margin: 0;
}
li {
margin: 0 0 4px;
}
button {
display: block;
width: 100%;
border: 0;
border-left: 2px solid transparent;
border-radius: 3px;
padding: 8px 10px;
background: transparent;
color: var(--text-color);
font: inherit;
font-size: 0.85rem;
text-align: left;
cursor: pointer;
}
button:hover {
background: rgba(65, 155, 56, 0.06);
}
button.active {
border-left-color: var(--accent-color, #419b38);
color: var(--accent-color, #419b38);
background: rgba(65, 155, 56, 0.1);
}
button:focus-visible {
outline: 2px solid var(--accent-color, #419b38);
outline-offset: -2px;
}
span {
display: -webkit-box;
-webkit-line-clamp: 2;
line-clamp: 2;
-webkit-box-orient: vertical;
overflow: hidden;
overflow-wrap: anywhere;
}
@media (pointer: coarse) {
button {
min-height: 44px;
}
}
</style>