Implement tab views (#386)

Tab views from @swenzel
This commit is contained in:
Swen Wenzel 2021-03-16 18:21:57 +01:00 committed by GitHub
parent 0c0b906ae1
commit 0b5bb6e7b3
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
6 changed files with 252 additions and 3 deletions

View file

@ -0,0 +1,119 @@
---
title: Tabbed views
description : "Synchronize selection of content in different tabbed views"
---
Choose which content to see across the page. Very handy for providing code
snippets for multiple languages or providing configuration in different formats.
## Code example
{{</* tabs */>}}
{{%/* tab name="python" */%}}
```python
print("Hello World!")
```
{{%/* /tab */%}}
{{%/* tab name="R" */%}}
```R
> print("Hello World!")
```
{{%/* /tab */%}}
{{%/* tab name="Bash" */%}}
```Bash
echo "Hello World!"
```
{{%/* /tab */%}}
{{</* /tabs */>}}
Renders as:
{{< tabs >}}
{{% tab name="python" %}}
```python
print("Hello World!")
```
{{% /tab %}}
{{% tab name="R" %}}
```R
> print("Hello World!")
```
{{% /tab %}}
{{% tab name="Bash" %}}
```Bash
echo "Hello World!"
```
{{% /tab %}}
{{< /tabs >}}
Tab views with the same tabs that belong to the same group sychronize their selection:
{{< tabs >}}
{{% tab name="python" %}}
```python
print("Hello World!")
```
{{% /tab %}}
{{% tab name="R" %}}
```R
> print("Hello World!")
```
{{% /tab %}}
{{% tab name="Bash" %}}
```Bash
echo "Hello World!"
```
{{% /tab %}}
{{< /tabs >}}
## Config example
{{</* tabs groupId="config" */>}}
{{%/* tab name="json" */%}}
```json
{
"Hello": "World"
}
```
{{%/* /tab */%}}
{{%/* tab name="XML" */%}}
```xml
<Hello>World</Hello>
```
{{%/* /tab */%}}
{{%/* tab name="properties" */%}}
```properties
Hello = World
```
{{%/* /tab */%}}
{{</* /tabs */>}}
Renders as:
{{< tabs groupId="config" >}}
{{% tab name="json" %}}
```json
{
"Hello": "World"
}
```
{{% /tab %}}
{{% tab name="XML" %}}
```xml
<Hello>World</Hello>
```
{{% /tab %}}
{{% tab name="properties" %}}
```properties
Hello = World
```
{{% /tab %}}
{{< /tabs >}}
{{% notice warning %}}
When using tab views with different content sets, make sure to use a common `groupId` for equal sets but distinct
`groupId` for different sets. The `groupId` defaults to `'default'`.
**Take this into account across the whole site!**
The tab selection is restored automatically based on the `groupId` and if it cannot find a tab item because it came
from the `'default'` group on a different page then all tabs will be empty at first.
{{% /notice %}}

View file

@ -17,6 +17,7 @@
<link href="{{"css/auto-complete.css" | relURL}}{{ if $assetBusting }}?{{ now.Unix }}{{ end }}" rel="stylesheet">
<link href="{{"css/atom-one-dark-reasonable.css" | relURL}}{{ if $assetBusting }}?{{ now.Unix }}{{ end }}" rel="stylesheet">
<link href="{{"css/theme.css" | relURL}}{{ if $assetBusting }}?{{ now.Unix }}{{ end }}" rel="stylesheet">
<link href="{{"css/tabs.css" | relURL}}{{ if $assetBusting }}?{{ now.Unix }}{{ end }}" rel="stylesheet">
<link href="{{"css/hugo-theme.css" | relURL}}{{ if $assetBusting }}?{{ now.Unix }}{{ end }}" rel="stylesheet">
{{with .Site.Params.themeVariant}}
<link href="{{(printf "css/theme-%s.css" .) | relURL}}{{ if $assetBusting }}?{{ now.Unix }}{{ end }}" rel="stylesheet">

View file

@ -0,0 +1,12 @@
{{ if .Parent }}
{{ $name := trim (.Get "name") " " }}
{{ if not (.Parent.Scratch.Get "tabs") }}
{{ .Parent.Scratch.Set "tabs" slice }}
{{ end }}
{{ with .Inner }}
{{ $.Parent.Scratch.Add "tabs" (dict "name" $name "content" . ) }}
{{ end }}
{{ else }}
{{- errorf "[%s] %q: tab shortcode missing its parent" site.Language.Lang .Page.Path -}}
{{ end}}

View file

@ -0,0 +1,21 @@
{{ with .Inner }}{{/* don't do anything, just call it */}}{{ end }}
{{ $groupId := default "default" (.Get "groupId") }}
<div class="tab-panel">
<div class="tab-nav">
{{ range $idx, $tab := .Scratch.Get "tabs" }}
<button
data-tab-item="{{ .name }}"
data-tab-group="{{ $groupId }}"
class="tab-nav-button btn {{ cond (eq $idx 0) "active" ""}}"
onclick="switchTab('{{ $groupId }}','{{ .name }}')"
>{{ .name }}</button>
{{ end }}
</div>
<div class="tab-content">
{{ range $idx, $tab := .Scratch.Get "tabs" }}
<div data-tab-item="{{ .name }}" data-tab-group="{{ $groupId }}" class="tab-item {{ cond (eq $idx 0) "active" ""}}">
{{ .content }}
</div>
{{ end }}
</div>
</div>

43
static/css/tabs.css Normal file
View file

@ -0,0 +1,43 @@
#body .tab-nav-button {
border-width: 1px 1px 1px 1px !important;
border-color: #ccc !important;
border-radius: 4px 4px 0 0 !important;
background-color: #ddd !important;
float: left;
display: block;
position: relative;
margin-left: 4px;
bottom: -1px;
}
#body .tab-nav-button:first-child {
margin-left: 0px;
}
#body .tab-nav-button.active {
background-color: #fff !important;
border-bottom-color: #fff !important;
}
#body .tab-panel {
margin-top: 32px;
margin-bottom: 32px;
}
#body .tab-content {
display: block;
clear: both;
padding: 8px;
border-width: 1px;
border-style: solid;
border-color: #ccc;
}
#body .tab-content .tab-item{
display: none;
}
#body .tab-content .tab-item.active{
display: block;
}
#body .tab-item pre{
margin-bottom: 0;
margin-top: 0;
}

View file

@ -47,6 +47,57 @@ function fallbackMessage(action) {
return actionMsg;
}
function switchTab(tabGroup, tabId) {
allTabItems = jQuery("[data-tab-group='"+tabGroup+"']");
targetTabItems = jQuery("[data-tab-group='"+tabGroup+"'][data-tab-item='"+tabId+"']");
// if event is undefined then switchTab was called from restoreTabSelection
// so it's not a button event and we don't need to safe the selction or
// prevent page jump
var isButtonEvent = event != undefined;
if(isButtonEvent){
// save button position relative to viewport
var yposButton = event.target.getBoundingClientRect().top;
}
allTabItems.removeClass("active");
targetTabItems.addClass("active");
if(isButtonEvent){
// reset screen to the same position relative to clicked button to prevent page jump
var yposButtonDiff = event.target.getBoundingClientRect().top - yposButton;
window.scrollTo(window.scrollX, window.scrollY+yposButtonDiff);
// Store the selection to make it persistent
if(window.localStorage){
var selectionsJSON = window.localStorage.getItem("tabSelections");
if(selectionsJSON){
var tabSelections = JSON.parse(selectionsJSON);
}else{
var tabSelections = {};
}
tabSelections[tabGroup] = tabId;
window.localStorage.setItem("tabSelections", JSON.stringify(tabSelections));
}
}
}
function restoreTabSelections() {
if(window.localStorage){
var selectionsJSON = window.localStorage.getItem("tabSelections");
if(selectionsJSON){
var tabSelections = JSON.parse(selectionsJSON);
}else{
var tabSelections = {};
}
Object.keys(tabSelections).forEach(function(tabGroup) {
var tabItem = tabSelections[tabGroup];
switchTab(tabGroup, tabItem);
});
}
}
// for the window resize
$(window).resize(function() {
setMenuHeight();
@ -83,6 +134,8 @@ $(window).resize(function() {
jQuery(document).ready(function() {
restoreTabSelections();
jQuery('#sidebar .category-icon').on('click', function() {
$( this ).toggleClass("fa-angle-down fa-angle-right") ;
$( this ).parent().parent().children('ul').toggle() ;