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);
}
.toc ul {
.toc > ul {
display: flex;
flex-direction: column;
gap: calc(var(--unit--vertical) / 4);
> li {
> ul {
margin-left: var(--unit--horizontal);
}
}
}
.toc li {

View file

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

File diff suppressed because one or more lines are too long

View file

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

View file

@ -2,7 +2,16 @@
<div class="light toc_label">table des matières</div>
<ul>
<?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 ?>
</ul>
</nav>

View file

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