Bootstrap Tabs as Hugo Shortcodes

1k views Asked by At

I am trying to create a shortcode for bootstrap tabs. I have following HTML structure,

<nav class="nav nav-tabs" id="myTab" role="tablist">
  <a class="nav-item nav-link" id="{{ .Get `id` }}-tab" data-toggle="tab" href="#{{ .Get `id` }}" role="tab" aria-controls="{{ .Get `id` }}">
      {{ .Get "title" }}
  </a>
</nav>
<div class="tab-content" id="nav-tabContent">
  <div class="tab-pane fade" id="{{ .Get `id` }}" role="tabpanel" aria-labelledby="{{ .Get `id` }}-tab">
    {{ .Inner }}
  </div>
</div>

The structure also contains the Hugo placeholder. But the inner part of the code, which is,

  <a class="nav-item nav-link" id="{{ .Get `id` }}-tab" data-toggle="tab" href="#{{ .Get `id` }}" role="tab" aria-controls="{{ .Get `id` }}">
      {{ .Get "title" }}
  </a>

is required multiple times along with the corresponding content div which is,

  <div class="tab-pane fade" id="{{ .Get `id` }}" role="tabpanel" aria-labelledby="{{ .Get `id` }}-tab">
    {{ .Inner }}
  </div>

Now the problem is that I have to pass the id as an argument of inner structure and should be detached from its parent. I should be able to use shortcode in following way:

{% tabs %}
    {% tab id = "tab-1" title = "Tab One" %}
        Content in Tab 1
    {% \tab %}
    {% tab id = "tab-2" title = "Tab Two" %}
        Content in Tab 2
    {% \tab %}
{% \tabs %}

How can I achieve this?

2

There are 2 answers

0
Scriptonomy On

It's not possible to iterate over the child shortcodes and extract their values in order to include them in the parent shortcode construct.

I suggest you create a custom param in the front matter (call it 'tabs' for instance) and iterate over its values in your 'tags' shortcode: range .Page.Params.tabs

I know it's redundant and hacky but it's the simplest way to get it working. The other way I know is much more involved and complicated and I am not sure it's worth the effort, but it is fully automated. It involves creating a content page for every tab, ugh.

0
Andreas Deininger On

The following two shortcodes will achieve what you want:

tabs.html

<!-- Scratchpad gets populated through call to .Inner -->  
{{- .Inner -}}

<nav class="nav nav-tabs" id="tabs-{{- $.Ordinal -}}" role="tablist">
  {{- range $index, $element := $.Scratch.Get "tabs" -}}
    <!-- Generate the IDs for the <a> and the <div> elements -->
    {{- $tabid := printf "tab-%v-%v-tab" $.Ordinal $index | anchorize -}}
    {{- $entryid := printf "tab-%v-%v" $.Ordinal $index | anchorize -}}
    <a class="nav-item nav-link{{ if eq $index "0" }} active{{ end }}"
      id="{{ $tabid }}" data-toggle="tab" href="#{{ $entryid }}" role="tab"
      aria-controls="{{ $tabid }}" aria-selected="{{- cond (eq $index "0") "true" "false" -}}">
      {{ index . "title" }}
    </a>
  {{- end -}}
  </nav>

<!-- Inner content -->
<div class="tab-content" id="tabs-{{- $.Ordinal -}}-content">
  {{- range $index, $element := $.Scratch.Get "tabs" -}}
    {{- $tabid := printf "tab-%v-%v-tab" $.Ordinal $index | anchorize -}}
    {{- $entryid := printf "tab-%v-%v" $.Ordinal $index | anchorize -}}
    <div class="tab-pane fade{{ if eq $index "0" }} show active{{ end }}"
        id="{{ $entryid }}" role="tabpanel" aria-labelled-by="{{ $tabid }}">
    {{- highlight (index . "content") "" "" -}}
     </div>
  {{ end }}
</div>

tab.html

<!-- Prefill title if not given as parameter -->
{{ $title := default (printf "Tab %v" ( add $.Ordinal 1)) (.Get "title") }}

<!-- store all tab info in dict tab -->
{{ $tab := dict "title" $title }}
{{ $tab = merge $tab (dict "content" (trim $.Inner "\n")) }}

<!-- add dict tab to parent's scratchpad -->
{{- $.Parent.Scratch.SetInMap "tabs" (printf "%v" $.Ordinal) $tab -}}

You can then use the shortcodes inside your page as shown below:

PageWithTabbedPane.md

## Tabbed pane

{{< tabs >}}
    {{< tab title = "Tab One" >}}
Content in Tab 1
    {{< /tab >}}
    {{< tab title = "Tab Two" >}}
Content in Tab 2
    {{< /tab >}}
{{< /tabs >}}

With the presented solution, there is no need to specify an id inside the markup of the tabbed pane. Also, if you omit the title parameter of the tab, the title defaults to Tab n.

Please note that this is the bare-bone version of the shortcodes. You may extend the shortcodes with named parameters lang and/or highlight. The values of these optional parameters could be passed on as second LANG and third OPTIONS arguments to Hugo’s built-in highlight function which is used to render the code blocks of the individual tabs.

You may have a look at the advanced shortcodes tabpane.html and tab.html, together with their according description.