Skip to content

Commit

Permalink
Adding shortcode for accordion component (Bootstrap 5)
Browse files Browse the repository at this point in the history
  • Loading branch information
deining committed Feb 3, 2023
1 parent a497c22 commit 04aaba9
Show file tree
Hide file tree
Showing 6 changed files with 158 additions and 8 deletions.
2 changes: 1 addition & 1 deletion assets/js/anchor.js
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ limitations under the License.
if (!article) {
return;
}
var headings = article.querySelectorAll('h1, h2, h3, h4, h5, h6');
var headings = article.querySelectorAll('h1, h2:not(.accordion-header), h3, h4, h5, h6');
headings.forEach(function (heading) {
if (heading.id) {
var a = document.createElement('a');
Expand Down
1 change: 1 addition & 0 deletions assets/scss/shortcodes.scss
Original file line number Diff line number Diff line change
@@ -1,2 +1,3 @@
@import "shortcodes/tabbed-pane.scss";
@import "shortcodes/cards-pane.scss";
@import "shortcodes/accordion.scss";
32 changes: 32 additions & 0 deletions assets/scss/shortcodes/accordion.scss
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
.accordion {
--bs-accordion-bg: #fff important;
max-width: 80%;
}

.accordion-button:not(.collapsed) {
background-color: #e7f1ff;
}

.accordion-button:focus {
box-shadow: none;
}

.accordion-collapse.show, .accordion-collapse.open
{
--bs-accordion-border-width: 0px important;
border-left: 1px solid #dee2e6;
border-right: 1px solid #dee2e6;
}

.accordion-item {
--bs-accordion-border-width: 0px important;
}

.accordion-item:last-of-type {
border-bottom: 1px solid #dee2e6;
}

.accordion-button {
--bs-accordion-border-width: 1px important;
border: 1px solid #dee2e6;
}
32 changes: 32 additions & 0 deletions layouts/shortcodes/accordion-item.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
{{- /* Make sure that we are enclosed within a accordion shortcode block */ -}}
{{ with $.Parent -}}
{{ if ne $.Parent.Name "accordion" -}}
{{ errorf "shortcode 'accordion-item' must be used within an 'accordion' block" -}}
{{ end -}}
{{ end -}}

{{ $header := "Header" -}}
{{ if and (not .IsNamedParams) (.Get 0) -}}
{{ $header = (.Get 0) -}}
{{ else -}}
{{/* Prefill header if not given as named or unnamed parameter */ -}}
{{ $header = default (printf "Header %v" ( add $.Ordinal 1)) (.Get "header") -}}
{{ end -}}

{{/* store all tab info in dict item */ -}}
{{ $item := dict "header" $header -}}
{{ with $.Get "open" -}}
{{ if ne ( printf "%T" . ) "bool" -}}
{{ errorf "shortcode 'accordion-item': parameter 'open' must be either true or false" -}}
{{ end -}}
{{ $item = merge $item (dict "open" ($.Get "open")) -}}
{{ end -}}

{{ with $.Inner -}}
{{ $item = merge $item (dict "content" $.Inner) -}}
{{ end -}}

{{/* add dict tab to parent's scratchpad */ -}}
{{ with .Parent -}}
{{ $.Parent.Scratch.SetInMap "items" (printf "%02v" $.Ordinal) $item -}}
{{ end -}}
39 changes: 39 additions & 0 deletions layouts/shortcodes/accordion.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
{{/* Check parameter type */ -}}
{{ with .Get "alwaysOpen" -}}
{{ warnf "Always open given" }}
{{ if ne ( printf "%T" . ) "bool" -}}
{{ errorf "shortcode accordion: parameter 'alwaysOpen' must be either true or false" -}}
{{ end -}}
{{ end -}}

{{ $alwaysOpen := default false ($.Get "alwaysOpen") -}}

{{- /* Scratchpad gets populated through call to .Inner */ -}}
{{- .Inner -}}

{{/* Accordion markup */ -}}
<div id="accordion-{{- $.Ordinal -}}" class="accordion mb-4">
{{ range $index, $element := $.Scratch.Get "items" -}}
{{ $itemid := printf "-%02v-%v" $.Ordinal $index | anchorize -}}

{{ $open := false -}}
{{ with $element.open -}}
{{ $open = . -}}
{{ end -}}

<div class="accordion-item">
<h2 class="accordion-header" id="heading{{- $itemid -}}">
<button class="accordion-button {{- cond ( eq $index "00" ) "" " collapsed" -}}" type="button" data-bs-toggle="collapse" data-bs-target="#collapse{{- $itemid -}}"
aria-controls="collapse{{- $itemid -}}" aria-expanded="{{- cond ( eq $index "00" ) "true" "false" -}}">
{{ index . "header" | markdownify }}
</button>
</h2>
<div id="collapse{{- $itemid -}}" class="accordion-collapse {{ cond $open "open" "collapse" }} {{- cond ( eq $index "00" ) " show" "" -}}"
aria-labelledby="heading{{- $itemid -}}" {{ if not $alwaysOpen -}}data-bs-parent="#accordion-{{- $.Ordinal -}}"{{- end }}>
<div class="accordion-body">
{{ index . "content" }}
</div>
</div>
</div>
{{- end }}
</div>
60 changes: 53 additions & 7 deletions userguide/content/en/docs/adding-content/shortcodes/index.md
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ Rather than writing all your site pages from scratch, Hugo lets you define and u

## Shortcode blocks

The theme comes with a set of custom **Page Block** shortcodes that can be used to compose landing pages, about pages, and similar.
The theme comes with a set of custom **Page Block** shortcodes that can be used to compose landing pages, about pages, and similar.

These blocks share some common parameters:

Expand Down Expand Up @@ -82,7 +82,7 @@ Runs on **bare metal** in the **cloud**!
{{%/* /blocks/lead */%}}
```

| Parameter | Default | Description |
| Parameter | Default | Description |
| ---------------- |------------| ------------|
| height | | See above.
| color | | See above.
Expand All @@ -109,11 +109,11 @@ For announcement of latest features etc.
{{</* /blocks/section */>}}
```

| Parameter | Default | Description |
| Parameter | Default | Description |
| ---------------- |------------| ------------|
| height | | See above.
| color | | See above.
| type | | Specify "section" if you want a general container, omit this parameter if you want this section to contain a horizontal row of features.
| type | | Specify "section" if you want a general container, omit this parameter if you want this section to contain a horizontal row of features.

### blocks/feature

Expand All @@ -125,7 +125,7 @@ We do a [Pull Request](https://github.com/gohugoio/hugo/pulls) contributions wor
```

| Parameter | Default | Description |
| Parameter | Default | Description |
| ---------------- |------------| ------------|
| title | | The title to use.
| url | | The URL to link to.
Expand All @@ -143,7 +143,7 @@ The **blocks/link-down** shortcode creates a navigation link down to the next se
</div>
```

| Parameter | Default | Description |
| Parameter | Default | Description |
| ---------------- |------------| ------------|
| color | info | See above.

Expand Down Expand Up @@ -435,7 +435,7 @@ This code translates to the right aligned tabbed pane below, showing a `Welcome!
Tabbed panes are implemented using two shortcodes:

* The `tabpane` shortcode, which is the container element for the tabs. This shortcode can hold the optional named parameters `lang`, `highlight` and `right`. The value of the optional parameters `lang` and `highlight` are passed on as second `LANG` and third `OPTIONS` arguments to Hugo's built-in [`highlight`](https://gohugo.io/functions/highlight/) function which is used to render the code blocks of the individual tabs. Specify `right=true` if you want to right align your tabs. In case the header text of the tab equals the language used in the tab's code block (as in the first tabbed pane example above), you may specify `langEqualsHeader=true` in the surrounding `tabpane` shortcode. Then, the header text of the individual tab is automatically set as `lang` parameter of the respective tab.
* The various `tab` shortcodes represent the tabs you would like to show. Specify the named parameter `header` for each tab in order to set the header text of the tab. If the `header` parameter is the only parameter inside your tab shortcode, you can specify the header as unnamed parameter, something like `{ tab "My header" }} … {{ /tab }}`. If your `tab` shortcode does not have any parameters, the header of the tab will default to `Tab n`. To split the panes into a left aligned and a right aligned tab group, specify `right=true` in the dividing tab. By giving `right=true` several times, you can even render multiple tab groups. You can disable a tab by specifying the parameter `disabled=true`. For enabled tabs, there are two modes for content display, `code` representation and _textual_ representation:
* The various `tab` shortcodes represent the tabs you would like to show. Specify the named parameter `header` for each tab in order to set the header text of the tab. If the `header` parameter is the only parameter inside your tab shortcode, you can specify the header as unnamed parameter, something like `{{</* tab "My header" */>}} … {{</* /tab */>}}`. If your `tab` shortcode does not have any parameters, the header of the tab will default to `Tab n`. To split the panes into a left aligned and a right aligned tab group, specify `right=true` in the dividing tab. By giving `right=true` several times, you can even render multiple tab groups. You can disable a tab by specifying the parameter `disabled=true`. For enabled tabs, there are two modes for content display, `code` representation and _textual_ representation:
* By default, the tab's content is rendered as `code block`. In order to get proper syntax highlighting, specify the named parameter `lang` --and optionally the parameter `highlight`-- for each tab. Parameters set in the parent `tabpane` shortcode will be overwritten.
* If the contents of your tabs should be rendered as text with different styles and with optional images, specify `text=true` as parameter of your `tabpane` (or your `tab`). If your content is markdown, use the percent sign `%` as outermost delimiter of your `tab` shortcode, your markup should look like `{{%/* tab */%}}`Your \*\*markdown\*\* content`{{%/* /tab */%}}`. In case of HTML content, use square brackets `<>` as outermost delimiters: `{{</* tab */>}}`Your &lt;b&gt;HTML&lt;/b&gt; content`{{</* /tab */>}}`.

Expand Down Expand Up @@ -700,3 +700,49 @@ This text does not appear in the output if `buildCondition = "bar" is set in you
```

If you are using this shortcode, note that when evaluating the conditions, substring matches are matches as well. That means, if you set `include-if="foobar"`, and `buildcondition = "foo"`, you have a match!

## Accordion

Sometimes it's very useful to have an accordion element at hand when authoring content. One common use-case is FAQ section where only one section (= accordion item) will be open at any time. The following sample showcases a accordion presenting the four principles of object-oriented programming:

### Sample accordion: Object-oriented programming

{{< accordion alwaysOpen=false >}}
{{% accordion-item header="Principle 1:&nbsp;**Abstraction**" %}}
**Abstraction** is the process of showing only essential/necessary features of an entity/object to the outside world and hide the other irrelevant information. For example to shut on/off your TV you only have a on/off method (≙ power button). It is not required to understand what internal processes are triggered in order to power on/off your TV device.
{{% /accordion-item %}}
{{% accordion-item header="Principle 2:&nbsp;**Encapsulation**" %}}
**Encapsulation** means wrapping up data and member functions (methods) together into a single unit, commonly referred to as class. One aspect of encapsulation is the concept of data hiding. We can hide data by marking variables as private and expose only needed properties (getters and setters) to the public. Internally, these properties make use of the private data not exposed to the public.
{{% /accordion-item %}}
{{% accordion-item header="Principle 3:&nbsp;**Inheritance**" %}}
**Inheritance** is the ability of creating a new class from an existing class. A child object acquires the property of a parent object. By means of inheritance, we achieve that a child class (subclass) acquires the properties and behavior of another parent class (super-class).
{{% /accordion-item %}}
{{% accordion-item header="Principle 4:&nbsp;**Polymorphism**" %}}
**Polymorphism** is derived from two Greek words: _πολύς_ (`polús`) translates to `many` and _μορφή_ (`morphḗ`) to `form`. So polymorphism means `many forms`. A child/subclass can define its own unique behavior and still share the same functionalities or behavior of its parent/base class. A subclass can have their own behavior and share some of its behavior from its parent class not the other way around. A parent class cannot have the behavior of its subclass.
{{% /accordion-item %}}
{{% /accordion %}}

The Docsy template provides two shortcodes `accordion` and `accordion-item` that let you easily create an accordion component. To see how to use them, have a look at the following code block, which renders to the accordion shown above:

```go-html-template
{{%/* accordion alwaysOpen=false */%}}
{{%/* accordion-item header="Principle 1: Abstraction" */%}}
**Abstraction** is the process of ...
{{%/* /accordion-item */%}}
{{%/* accordion-item header="Principle 2: Encapsulation" */%}}
**Encapsulation** means wrapping up data ...
{{%/* /accordion-item */%}}
{{%/* accordion-item Principle 3: Inheritance */%}}
**Inheritance** is the ability of creating a new class ...
{{%/* /accordion-item */%}}
... fourth item omitted
{{%/* /accordion %}}
```

### Shortcode details

An accordion component is implemented using two shortcodes:

* The `accordion` shortcode, which is the container element for the collapsible items of the accordion. This shortcode can hold the optional named parameters `alwaysOpen`, and `flush`. Specify `alwaysOpen=true` if you want to make accordion items stay open even when another item is opened.

* The various `accordion-item` shortcodes represent the collapsible items of your accordion. Specify the named parameter `header` for each `accordion-item` in order to set the header text of the item. If the `header` parameter is the only parameter inside your `accordion-item` shortcode, you can specify the header as unnamed parameter, something like `{{</* accordion-item "My header" */>}} … {{</* /accordion-item */>}}`. If your `accordion-item` shortcode does not have any parameters, the header of the item will default to `Header n`. When specifying `open=true` for an `accordion-item`, this item will be initially rendered opened (as opposed to an default collapsed item). If your item content is markdown, use the percent sign `%` as outermost delimiter of your `accordion-item` shortcode, your markup should look like `{{%/* accordion-item */%}}`Your \*\*markdown\*\* content`{{%/* /accordion-item */%}}`. In case of HTML content, use square brackets `<>` as outermost delimiters: `{{</* accordion-item */>}}`Your &lt;b&gt;HTML&lt;/b&gt; content`{{</* /accordion-item */>}}`.

0 comments on commit 04aaba9

Please sign in to comment.