toc : add h4 support with nested structure and fix anchors in non-block mode

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
isUnknown 2026-03-27 19:18:32 +01:00
parent 4e5440a7ae
commit b724ed1244
6 changed files with 40 additions and 14 deletions

View file

@ -28,10 +28,16 @@
margin-bottom: calc(var(--unit--vertical) / 4); margin-bottom: calc(var(--unit--vertical) / 4);
} }
.toc ul { .toc > ul {
display: flex; display: flex;
flex-direction: column; flex-direction: column;
gap: calc(var(--unit--vertical) / 4); gap: calc(var(--unit--vertical) / 4);
> li {
> ul {
margin-left: var(--unit--horizontal);
}
}
} }
.toc li { .toc li {

View file

@ -1283,11 +1283,14 @@ body.full-width #main-content {
margin-bottom: calc(var(--unit--vertical) / 4); margin-bottom: calc(var(--unit--vertical) / 4);
} }
.toc ul { .toc > ul {
display: flex; display: flex;
flex-direction: column; flex-direction: column;
gap: calc(var(--unit--vertical) / 4); gap: calc(var(--unit--vertical) / 4);
} }
.toc > ul > li > ul {
margin-left: var(--unit--horizontal);
}
.toc li { .toc li {
margin-left: 0; margin-left: 0;

File diff suppressed because one or more lines are too long

View file

@ -1,6 +1,6 @@
<?php <?php
const H3_PATTERN = '/<h3>(.*?)<\/h3>/'; const HEADING_PATTERN = '/<(h[34])(.*?)>(.*?)<\/\1>/';
function getContent($page) { function getContent($page) {
if ($page->intendedTemplate() == 'grid') return $page->body()->toBlocks(); if ($page->intendedTemplate() == 'grid') return $page->body()->toBlocks();
@ -29,15 +29,23 @@ Kirby::plugin('actuel-inactuel/toc', [
'tocItems' => function(): array { 'tocItems' => function(): array {
$content = getTocContent($this); $content = getTocContent($this);
if (!$content) return []; if (!$content) return [];
preg_match_all(H3_PATTERN, $content, $matches); preg_match_all(HEADING_PATTERN, $content, $matches, PREG_SET_ORDER);
return array_map(fn($title) => [ $items = [];
'title' => $title, foreach ($matches as $m) {
'slug' => Str::slug($title) $entry = ['title' => $m[3], 'slug' => Str::slug($m[3])];
], $matches[1]);
if ($m[1] === 'h3') {
$entry['children'] = [];
$items[] = $entry;
} elseif ($m[1] === 'h4' && count($items) > 0) {
$items[count($items) - 1]['children'][] = $entry;
}
}
return $items;
}, },
'bodyWithAnchors' => function(): string { 'bodyWithAnchors' => function(): string {
@ -45,8 +53,8 @@ Kirby::plugin('actuel-inactuel/toc', [
if (!$content) return ''; if (!$content) return '';
return preg_replace_callback( return preg_replace_callback(
H3_PATTERN, HEADING_PATTERN,
fn($m) => '<h3 id="' . Str::slug($m[1]) . '">' . $m[1] . '</h3>', fn($m) => '<' . $m[1] . ' id="' . Str::slug($m[3]) . '"' . $m[2] . '>' . $m[3] . '</' . $m[1] . '>',
$content $content
); );
} }

View file

@ -2,7 +2,16 @@
<div class="light toc_label">table des matières</div> <div class="light toc_label">table des matières</div>
<ul> <ul>
<?php foreach ($page->tocItems() as $item): ?> <?php foreach ($page->tocItems() as $item): ?>
<li><a href="#<?= $item['slug'] ?>"><?= $item['title'] ?></a></li> <li>
<a href="#<?= $item['slug'] ?>"><?= $item['title'] ?></a>
<?php if (!empty($item['children'])): ?>
<ul>
<?php foreach ($item['children'] as $child): ?>
<li><a href="#<?= $child['slug'] ?>"><?= $child['title'] ?></a></li>
<?php endforeach ?>
</ul>
<?php endif ?>
</li>
<?php endforeach ?> <?php endforeach ?>
</ul> </ul>
</nav> </nav>

View file

@ -36,7 +36,7 @@
<?php elseif ($page->isBlockMode()->isTrue()): ?> <?php elseif ($page->isBlockMode()->isTrue()): ?>
<?= $page->bodyWithAnchors() ?> <?= $page->bodyWithAnchors() ?>
<?php else: ?> <?php else: ?>
<?= $page->body() ?> <?= $page->bodyWithAnchors() ?>
<?php endif ?> <?php endif ?>
</div> </div>
</article> </article>