From 08395d5eafc2ee76da13dad2859bc478b6ea1129 Mon Sep 17 00:00:00 2001 From: Max P Date: Thu, 30 Nov 2023 18:09:39 +0100 Subject: [PATCH 1/4] The option to set a configurable show/hide button has been added. Changes - Configuration: - toggle Activate the button or not. - initially_hide Hidden in initial state or not. - hide_text The text of the button to hide the menu. - show_text The text of the button to make the menu visible. - CSS: Corresponding classes created for the button (with ID) and for hiding or making visible. - PHP: Added logic to add the button. This also works without a heading text being set. --- TableOfContents.php | 43 ++++++++++++++++++++++++++++++++++++++----- js/toc.js | 21 +++++++++++++++++++++ style.css | 21 +++++++++++++++++++-- 3 files changed, 78 insertions(+), 7 deletions(-) create mode 100644 js/toc.js diff --git a/TableOfContents.php b/TableOfContents.php index 1fc5f2c..40b90d5 100644 --- a/TableOfContents.php +++ b/TableOfContents.php @@ -23,9 +23,17 @@ class TableOfContents extends AbstractPicoPlugin 'style' => 'none', // Heading text, if a heading for the table of contents is desired. 'heading' => null, + // Show/hide the table of contents by default. + 'toggle' => true, + // Hide toc initially + 'initially_hide' => true, + // Hide text + 'hide_text' => '▲', + // Show text + 'show_text' => '▼', ); - protected $min_headers, $min_level, $max_level, $tag, $style, $heading, $toc_element_xml; + protected $min_headers, $min_level, $max_level, $tag, $style, $toggle, $initially_hide, $hide_text, $show_text, $heading, $toc_element_xml; protected $available_tags = ['ol', 'ul']; protected $available_styles = ['numbers', 'bullets', 'none', 'default']; @@ -90,6 +98,10 @@ public function onMetaParsed(array &$meta) $this->tag = $this->getVal('tag', $meta); $this->style = $this->getVal('style', $meta); $this->heading = $this->getVal('heading', $meta); + $this->initially_hide = $this->getVal('initially_hide', $meta); + $this->toggle = $this->getVal('toggle', $meta); + $this->hide_text = $this->getVal('hide_text', $meta); + $this->show_text = $this->getVal('show_text', $meta); // Check if the tag is valid if (!in_array($this->tag, $this->available_tags)) { @@ -147,9 +159,21 @@ public function onContentParsed(&$content) $div_element->setAttribute('id', 'toc'); // Add heading element, if enabled - if (isset($this->heading)) { - $heading_element = $document->createElement('div', $this->heading); + if (isset($this->heading) || $this->toggle) { + $heading_element = $document->createElement('div', isset($this->heading) ? $this->heading : ' '); $heading_element->setAttribute('class', 'toc-heading'); + + // Create the toogle button element if enabled + if($this->toggle) + { + $button_element = $document->createElement('button', $this->initially_hide ? $this->show_text : $this->hide_text); + $button_element->setAttribute('id', 'toc-toggle'); + $button_element->setAttribute('data-show-text', $this->show_text); + $button_element->setAttribute('data-hide-text', $this->hide_text); + + $heading_element->appendChild($button_element); + } + $div_element->appendChild($heading_element); } @@ -208,7 +232,7 @@ private function getVal($key, $meta) * @param integer $index * @return DOMElement */ - private function getList($document, $headers, &$index = 0) + private function getList($document, $headers, &$index = 0, $isTopLevel = true) { // Initialize ordered list element $list_element = $document->createElement($this->tag); @@ -216,6 +240,15 @@ private function getList($document, $headers, &$index = 0) $list_element->setAttribute('class', "toc-$this->style"); } + if ($this->toggle && $isTopLevel) + { + if ($this->initially_hide === true) { + $list_element->setAttribute('class', 'toc-hide'); + } else { + $list_element->setAttribute('class', 'toc-show'); + } + } + for ($index; $index < $headers->length; $index++) { $curr_header = $headers[$index]; if (isset($curr_header->tagName) && $curr_header->tagName !== '') { @@ -237,7 +270,7 @@ private function getList($document, $headers, &$index = 0) if ($next_header && strtolower($curr_header->tagName) < strtolower($next_header->tagName)) { // The next header is at a lower level -> add nested headers $index++; - $nested_list_element = $this->getList($document, $headers, $index); + $nested_list_element = $this->getList($document, $headers, $index, false); $li_element->appendChild($nested_list_element); } diff --git a/js/toc.js b/js/toc.js new file mode 100644 index 0000000..2f50186 --- /dev/null +++ b/js/toc.js @@ -0,0 +1,21 @@ +var toggleTocElement = document.getElementById("toc-toggle"); + +if (toggleTocElement) { + toggleTocElement.addEventListener("click", function() { + var tocElement = document.getElementById("toc"); + if (tocElement) { + var xElement = tocElement.querySelector(".toc-hide, .toc-show"); + if (xElement) { + if (xElement.classList.contains("toc-hide")) { + xElement.classList.remove("toc-hide"); + xElement.classList.add("toc-show"); + this.innerText = this.getAttribute('data-show-text'); + } else if (xElement.classList.contains("toc-show")) { + xElement.classList.remove("toc-show"); + xElement.classList.add("toc-hide"); + this.innerText = this.getAttribute('data-hide-text'); + } + } + } + }); +} diff --git a/style.css b/style.css index 52e2206..00d67a5 100644 --- a/style.css +++ b/style.css @@ -1,7 +1,7 @@ #toc { border : 1px solid #cdd; background-color: #f5f5ff; - margin : 0; + margin : 20px 0 20px 0; padding : 1em; font-size : 80%; } @@ -29,7 +29,7 @@ .toc-numbers { counter-reset : item; - list-style-type: none; + list-style-type: none; } .toc-numbers li:before { content : counters(item, ". ") ". "; @@ -39,4 +39,21 @@ .toc-heading { font-size : larger; font-weight: bold; + position: relative; +} + +#toc-toggle { + position: absolute; + right: 10px; + top: 50%; + transform: translateY(-50%); + /* Weitere Stiloptionen nach Wunsch */ } + +.toc-hide { + display: none; +} + +.toc-show { + display: inherit; +} \ No newline at end of file From 6dc335ae0a77ab24ff800d74969b068c1986ce3e Mon Sep 17 00:00:00 2001 From: Max P Date: Thu, 30 Nov 2023 18:32:06 +0100 Subject: [PATCH 2/4] =?UTF-8?q?If=20there=20is=20no=20ID=20for=20a=20headi?= =?UTF-8?q?ng,=20an=20ID=20is=20generated=20from=20the=20content=20of=20th?= =?UTF-8?q?e=20heading.=20In=20the=20case=20of=20repeated=20headings=20(e.?= =?UTF-8?q?g.=20=E2=80=9CSummary=E2=80=9D),=20this=20leads=20to=20an=20amb?= =?UTF-8?q?iguous=20ID.?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The problem is solved by adding the index of all headings to the ID generated in this case. Changes to - PHP: If the ID does not exist, the given index is appended to the generated ID. --- TableOfContents.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/TableOfContents.php b/TableOfContents.php index 1fc5f2c..68b1abc 100644 --- a/TableOfContents.php +++ b/TableOfContents.php @@ -222,7 +222,7 @@ private function getList($document, $headers, &$index = 0) // Add missing id's to the h tags $id = $curr_header->getAttribute('id'); if ($id === "") { - $id = $this->slugify($curr_header->nodeValue); + $id = $this->slugify($curr_header->nodeValue) . '-' . $index; $curr_header->setAttribute('id', $id); } From 67cc54062e2b4ec2223d23b2c5984d196fb6c370 Mon Sep 17 00:00:00 2001 From: Max P Date: Thu, 30 Nov 2023 18:46:28 +0100 Subject: [PATCH 3/4] Consider >>unlisted<< classes for headings and do not add them to the table of contents. Changes to - PHP: Logic for noting any unlisted classes for the headings running through. If such a class is found, the corresponding heading is not added. --- TableOfContents.php | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/TableOfContents.php b/TableOfContents.php index 1fc5f2c..a0b2826 100644 --- a/TableOfContents.php +++ b/TableOfContents.php @@ -219,6 +219,9 @@ private function getList($document, $headers, &$index = 0) for ($index; $index < $headers->length; $index++) { $curr_header = $headers[$index]; if (isset($curr_header->tagName) && $curr_header->tagName !== '') { + $header_classes = explode(' ', $curr_header->getAttribute('class')); + $is_unlisted = in_array('unlisted', $header_classes); + // Add missing id's to the h tags $id = $curr_header->getAttribute('id'); if ($id === "") { @@ -241,7 +244,9 @@ private function getList($document, $headers, &$index = 0) $li_element->appendChild($nested_list_element); } - $list_element->appendChild($li_element); + if (!$is_unlisted) { + $list_element->appendChild($li_element); + } // Refresh next_header with the updated index $next_header = ($index + 1 < $headers->length) ? $headers[$index + 1] : null; From e9b712156c706d7484f628c51ec4ef4658f21c76 Mon Sep 17 00:00:00 2001 From: Max P Date: Thu, 30 Nov 2023 19:06:19 +0100 Subject: [PATCH 4/4] Added a check that no empty elements are added due to the consideration of the >>unlisted<< class. --- TableOfContents.php | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/TableOfContents.php b/TableOfContents.php index a9f9d9d..0c2f075 100644 --- a/TableOfContents.php +++ b/TableOfContents.php @@ -274,7 +274,9 @@ private function getList($document, $headers, &$index = 0, $isTopLevel = true) // The next header is at a lower level -> add nested headers $index++; $nested_list_element = $this->getList($document, $headers, $index, false); - $li_element->appendChild($nested_list_element); + if ($nested_list_element->hasChildNodes()) { + $li_element->appendChild($nested_list_element); + }; } if (!$is_unlisted) {