From 7b2774315999631e0e6aa35dc01f7a6d0c182e46 Mon Sep 17 00:00:00 2001 From: Xin <5097752+imfing@users.noreply.github.com> Date: Mon, 21 Jul 2025 21:16:44 +0100 Subject: [PATCH] feat(tabs): implement synchronized tabs switching (#700) * Sync tabs across groups * feat(tabs): add optional synchronization * Move tabs sync setting under page params * fix: spacing between title and site title (#704) * docs: document configure opengraph image (#706) * [Docs] document using og:image * Make example title page match others * clarify wording * chore: update tailwind css to latest version 4.1.8 (#703) * fix: wrong SRI hash for katex.css (#702) * Correct URL given in 'dev.toml' * stylesheet 'katex.css': fix SRI hash * fix(build): run npm update to fix postcss complaint * feat(tags): improve usability of tags (#698) * feat(tags): improve usability of tags * Tags can be shown also at docs * Documented tag-related config flags * Added example tags to the site * Made rendered tags active * Move tags listing to ToC * Hide tags section on no tags * feat(math): add optional MathJax support (#707) * feat: add MathJax option * docs: move math engine note * refactor: update LaTeX documentation and improve MathJax integration - Adjusted LaTeX documentation for clarity and formatting. - Enhanced MathJax configuration in the templates to support both KaTeX and MathJax rendering. - Removed deprecated comments and streamlined the script loading process for MathJax. - Updated the passthrough extension settings in the Hugo configuration for better compatibility with LaTeX math expressions. * docs: simplify LaTeX documentation and clarify configuration steps - Updated LaTeX documentation to reflect that KaTeX is enabled by default, removing the need for manual activation. - Added examples for using LaTeX math expressions and clarified the configuration for the passthrough extension in Hugo. - Enhanced MathJax section to emphasize its use as an alternative rendering engine. * fix(tabs): add null check for panels container and update example items * fix(tabs): improve tab group key handling and add validation for items parameter * refactor(tabs): comment out sync option in configuration and adjust tab formatting in documentation --------- Co-authored-by: hobobandy <30026704+hobobandy@users.noreply.github.com> Co-authored-by: Matt Dodson <47385188+MattDodsonEnglish@users.noreply.github.com> Co-authored-by: Andreas Deininger Co-authored-by: yuri <1969yuri1969@gmail.com> --- assets/js/tabs.js | 65 ++++++++++++++----- .../content/docs/guide/shortcodes/tabs.md | 40 ++++++++++-- exampleSite/hugo.yaml | 10 +-- layouts/_shortcodes/tabs.html | 16 ++++- 4 files changed, 104 insertions(+), 27 deletions(-) diff --git a/assets/js/tabs.js b/assets/js/tabs.js index b8d7937..12a7d64 100644 --- a/assets/js/tabs.js +++ b/assets/js/tabs.js @@ -1,20 +1,51 @@ -document.querySelectorAll('.hextra-tabs-toggle').forEach(function (button) { - button.addEventListener('click', function (e) { - // set parent tabs to unselected - const tabs = Array.from(e.target.parentElement.querySelectorAll('.hextra-tabs-toggle')); - tabs.map(tab => tab.dataset.state = ''); - - // set current tab to selected - e.target.dataset.state = 'selected'; - - // set all panels to unselected - const panelsContainer = e.target.parentElement.parentElement.nextElementSibling; - Array.from(panelsContainer.children).forEach(function (panel) { - panel.dataset.state = ''; +(function () { + function updateGroup(container, index) { + const tabs = Array.from(container.querySelectorAll('.hextra-tabs-toggle')); + tabs.forEach((tab, i) => { + tab.dataset.state = i === index ? 'selected' : ''; + if (i === index) { + tab.setAttribute('aria-selected', 'true'); + tab.tabIndex = 0; + } else { + tab.removeAttribute('aria-selected'); + tab.removeAttribute('tabindex'); + } }); + const panelsContainer = container.parentElement.nextElementSibling; + if (!panelsContainer) return; + Array.from(panelsContainer.children).forEach((panel, i) => { + panel.dataset.state = i === index ? 'selected' : ''; + if (i === index) { + panel.tabIndex = 0; + } else { + panel.removeAttribute('tabindex'); + } + }); + } - const panelId = e.target.getAttribute('aria-controls'); - const panel = panelsContainer.querySelector(`#${panelId}`); - panel.dataset.state = 'selected'; + const groups = document.querySelectorAll('[data-tab-group]'); + + groups.forEach((group) => { + const key = encodeURIComponent(group.dataset.tabGroup); + const saved = localStorage.getItem('hextra-tab-' + key); + if (saved !== null) { + updateGroup(group, parseInt(saved, 10)); + } }); -}); + + document.querySelectorAll('.hextra-tabs-toggle').forEach((button) => { + button.addEventListener('click', function (e) { + const container = e.target.parentElement; + const index = Array.from(container.querySelectorAll('.hextra-tabs-toggle')).indexOf( + e.target + ); + const key = encodeURIComponent(container.dataset.tabGroup); + document + .querySelectorAll('[data-tab-group="' + container.dataset.tabGroup + '"]') + .forEach((grp) => updateGroup(grp, index)); + if (key) { + localStorage.setItem('hextra-tab-' + key, index.toString()); + } + }); + }); +})(); diff --git a/exampleSite/content/docs/guide/shortcodes/tabs.md b/exampleSite/content/docs/guide/shortcodes/tabs.md index c63547a..1d37cc5 100644 --- a/exampleSite/content/docs/guide/shortcodes/tabs.md +++ b/exampleSite/content/docs/guide/shortcodes/tabs.md @@ -5,11 +5,11 @@ next: /docs/guide/deploy-site ## Example -{{< tabs items="JSON,YAML,TOML" >}} +{{< tabs items="macOS,Linux,Windows" >}} -{{< tab >}}**JSON**: JavaScript Object Notation (JSON) is a standard text-based format for representing structured data based on JavaScript object syntax.{{< /tab >}} -{{< tab >}}**YAML**: YAML is a human-readable data serialization language.{{< /tab >}} -{{< tab >}}**TOML**: TOML aims to be a minimal configuration file format that's easy to read due to obvious semantics.{{< /tab >}} + {{< tab >}}**macOS**: A desktop operating system by Apple.{{< /tab >}} + {{< tab >}}**Linux**: An open-source operating system.{{< /tab >}} + {{< tab >}}**Windows**: A desktop operating system by Microsoft.{{< /tab >}} {{< /tabs >}} @@ -91,3 +91,35 @@ Markdown syntax including code block is also supported: {{< /tab >}} {{< /tabs >}} + + +### Sync Tabs + +Tabs with the same list of `items` can be synchronized. When enabled, selecting a tab updates all other tabs with the same `items` and remembers the selection across pages. + +Enable globally in your `hugo.yaml` under the `page` section: + +```yaml {filename="hugo.yaml"} +params: + page: + tabs: + sync: true +``` + +With this enabled the following two tab blocks will always display the same selected item: + +```markdown +{{}} + + {{}}A content{{}} + {{}}B content{{}} + +{{}} + +{{}} + + {{}}Second A content{{}} + {{}}Second B content{{}} + +{{}} +``` diff --git a/exampleSite/hugo.yaml b/exampleSite/hugo.yaml index 453dc90..25a3d5b 100644 --- a/exampleSite/hugo.yaml +++ b/exampleSite/hugo.yaml @@ -132,10 +132,6 @@ params: # link: / width: wide - page: - # full (100%), wide (90rem), normal (80rem) - width: normal - theme: # light | dark | system default: system @@ -187,6 +183,12 @@ params: # hover | always display: hover + page: + # full (100%), wide (90rem), normal (80rem) + width: normal + # tabs: + # sync: true + comments: enable: false type: giscus diff --git a/layouts/_shortcodes/tabs.html b/layouts/_shortcodes/tabs.html index b068790..22992be 100644 --- a/layouts/_shortcodes/tabs.html +++ b/layouts/_shortcodes/tabs.html @@ -1,12 +1,24 @@ {{- $items := split (.Get "items") "," -}} {{- $defaultIndex := int ((.Get "defaultIndex") | default "0") -}} +{{- $enableSync := site.Params.page.tabs.sync | default false -}} + +{{- if not (.Get "items") -}} + {{ errorf "tabs shortcode: 'items' parameter is required" }} +{{- end -}} + {{- if not $items -}} - {{ errorf "no items provided" }} + {{ errorf "tabs shortcode: 'items' parameter cannot be empty" }} +{{- end -}} + +{{- range $items -}} + {{- if eq (trim . " ") "" -}} + {{ errorf "tabs shortcode: empty item found in 'items' parameter" }} + {{- end -}} {{- end -}}
-
+
{{- range $i, $item := $items -}}