If you now add a
(toc)
placeholder to a page that contains headlines of the given level, you should see a ToC appear where you added the placeholder.
I don’t understand where sould I add (toc) placeholder, is it in note.yml ?
Thanks for your time
If you now add a
(toc)
placeholder to a page that contains headlines of the given level, you should see a ToC appear where you added the placeholder.
I don’t understand where sould I add (toc) placeholder, is it in note.yml ?
Thanks for your time
The toc
placeholder would not go into the blueprint but into a textarea
field. But as I already mentioned above, the Starterkit doesn’t use any textarea fields anywhere. So if you want to use it, you have to add such a field to the blueprint where you want to use it, e.g. note.yml.
Currently, in note.yml
a blocks field is used for long form text. If you want to create a toc from a blocks field: Of anchors and ToCs, part 1 | Kirby CMS
Thanks @texnixe
For the blocks should I replace $headlines
by :
// assuming a blocks field called text
$headlines = $page->text()->toBlocks()->filterBy('type', 'heading')->filterby('level', 'h2');
And remove 'kirbytext:after' => [
?
<?php
use Kirby\Toolkit\Collection;
use Kirby\Toolkit\Str;
use Kirby\Toolkit\Obj;
Kirby::plugin('k-cookbook/toc', [
'hooks' => [
//
// Premiere fonction pour ancrer les titres
//
function($text) {
// assuming a blocks field called text
$headlines = $page->text()->toBlocks()->filterBy('type', 'heading')->filterby('level', 'h2');
// create the regex pattern to be used as first argument in `preg_replace_callback()`
$headlinesPattern = is_array($headlines) ? implode('|', $headlines) : $headlines;
// use `preg_replace_callback()` to replace matches with anchors
$text = preg_replace_callback('!<(' . $headlinesPattern . ')>(.*?)</\\1>!s', function ($match) {
// create the id from the headline text
$id = Str::slug(Str::unhtml($match[2]));
// return the modified headline:
// $match[1] contains the match for the first subpattern, i.e. `h2`, `h3` etc.
// $match[2] contains the match for the second subpattern, i.e. the actual headline text
return '<' . $match[1] . ' id="' . $id . '"><a href="#' . $id . '">' . $match[2] . '</a></' . $match[1] . '>';
}, $text);
return $text;
},
//
// Deuxième fonction pour générer le ToC
//
function($texto) {
// the pattern allows passing an optional headline level `(toc: h3)`
$pattern = '!\(toc(?::\s?(h[1-6]))?\)!';
$texto = preg_replace_callback($pattern, function($match) use($texto) {
// get the headline level from the match
$headline = $match[1] ?? 'h2';
// find all headline matches
preg_match_all('!<' . $headline . '.*?>(.*?)</' . $headline . '>!s', $texto, $matches);
// create a new collection for the headlines…
$headlines = new Collection();
// …and add all matches
foreach ($matches[1] as $texto) {
$headline = new Obj([
'id' => $id = '#' . Str::slug(Str::unhtml($texto)),
'url' => $id,
'texto' => trim(strip_tags($texto)),
]);
$headlines->append($headline->url(), $headline);
}
// return the html for the ToC
return snippet('toc', ['headlines' => $headlines], false);
}, $texto);
return $texto;
},
],
'snippets' => [
'toc' => __DIR__ . '/snippets/toc.php'
],
]);
Yes, you don’t need the kirbytext hooks for the blocks field.
I’ve got an error
Undefined variable: headlines
But I don’t understand why it don’t found $headlines
index.php
<?php
use Kirby\Toolkit\Collection;
use Kirby\Toolkit\Str;
use Kirby\Toolkit\Obj;
Kirby::plugin('k-cookbook/toc', [
'hooks' => [
//
// Premiere fonction pour ancrer les titres
//
function($text) {
// assuming a blocks field called text
$headlines = $page->text()->toBlocks()->filterBy('type', 'heading')->filterby('level', 'h2');
// create the regex pattern to be used as first argument in `preg_replace_callback()`
$headlinesPattern = is_array($headlines) ? implode('|', $headlines) : $headlines;
// use `preg_replace_callback()` to replace matches with anchors
$text = preg_replace_callback('!<(' . $headlinesPattern . ')>(.*?)</\\1>!s', function ($match) {
// create the id from the headline text
$id = Str::slug(Str::unhtml($match[2]));
// return the modified headline:
// $match[1] contains the match for the first subpattern, i.e. `h2`, `h3` etc.
// $match[2] contains the match for the second subpattern, i.e. the actual headline text
return '<' . $match[1] . ' id="' . $id . '"><a href="#' . $id . '">' . $match[2] . '</a></' . $match[1] . '>';
}, $text);
return $text;
},
//
// Deuxième fonction pour générer le ToC
//
function($texto) {
// the pattern allows passing an optional headline level `(toc: h3)`
$pattern = '!\(toc(?::\s?(h[1-6]))?\)!';
$texto = preg_replace_callback($pattern, function($match) use($texto) {
// get the headline level from the match
$headline = $match[1] ?? 'h2';
// find all headline matches
preg_match_all('!<' . $headline . '.*?>(.*?)</' . $headline . '>!s', $texto, $matches);
// create a new collection for the headlines…
$headlines = new Collection();
// …and add all matches
foreach ($matches[1] as $texto) {
$headline = new Obj([
'id' => $id = '#' . Str::slug(Str::unhtml($texto)),
'url' => $id,
'texto' => trim(strip_tags($texto)),
]);
$headlines->append($headline->url(), $headline);
}
// return the html for the ToC
return snippet('toc', ['headlines' => $headlines], false);
}, $texto);
return $texto;
},
],
'snippets' => [
'toc' => __DIR__ . '/snippets/toc.php'
],
]);
toc.php
<nav class="toc">
<h2>Table of Contents</h2>
<ol>
<?php foreach($headlines as $headline): ?>
<li><a href="<?= $page->url() . '/#' . Str::slug($headline->text()) ?>"><?= $headline->text() ?></a></li>
<?php endforeach ?>
</ol>
</nav>
<?php endif ?>
type or paste code here
And <?php snippet('toc') ?>
in note.php
I also add the line of code in /site/snippets/blocks/heading.php
for overwrite the default heading block snippet and add an id
attribute
Ok I replace <?php snippet('toc') ?
by <?php snippet('toc', ['headlines' => $page->text()->headlines('h2')]) ?>
and I don’t have the error anymore
But now I have the error
Object of class Kirby\Cms\Field could not be converted to int
But you now have to pass the filtered blocks as headlines_
<?php snippet('toc', ['headlines' => $page->text()->toBlocks->filterBy('type', 'heading')->filterby('level', 'h2')]) ?>
Ok I understand better now, thank you @texnixe !
I now have an error for this line;
syntax error, unexpected ‘->’ (T_OBJECT_OPERATOR), expecting identifier (T_STRING) or variable (T_VARIABLE) or ‘{’ or ‘$’
I added an arrow too many when copy/pasting, corrected above.
Tip: Learn to understand PHP error messages. Saves a lot of time.
sorry for that ! I should have seen it
I now have an error about the variable $headlines (Undefined variable: headlines), don’t found from where it come, probably from index.php…
You don’t actually need the plugin if you only want to use the toc with blocks.
Put the toc.php snippet into /site/snippets and call the snippet with like above. That’s it.
Thank you a lot @texnine !
I spent lot of time between code for textarea and blocks for finaly so simple and logical lines of code.
It looks so easier to do it for other headings now, thanks!