92 lines
2.1 KiB
Svelte
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>
|