Skip to content

Instantly share code, notes, and snippets.

@atusy
Created January 10, 2021 04:10
Show Gist options
  • Save atusy/4b076cc084d9e920b34d6d25f85e5176 to your computer and use it in GitHub Desktop.
Save atusy/4b076cc084d9e920b34d6d25f85e5176 to your computer and use it in GitHub Desktop.
Navigate tabsets from ToC (tabs must be named uniquely)
document.addEventListener('DOMContentLoaded', function() {
const anchors = Array.from(document.querySelectorAll("ul.nav.nav-tabs li a")).
filter(a => a.attributes.role.value === "tab").
reduce((hash, a) => {
hash["#" + a.innerText.replace(/ /, "_")] = a;
return hash;
}, {});
window.addEventListener('hashchange', function() {
const anchor = anchors[location.hash];
if (anchor !== undefined) {
anchor.click();
}
});
});
local tabset_level = 100
local tab_level = 101
local flag = false
local stringify = pandoc.utils.stringify
function is_tabset(classes)
local res = false
for _,v in ipairs(classes) do
res = res or (v == "tabset")
end
return res
end
function Block(block)
if block.tag == "Header" then
local level = block.level
if level <= tabset_level then
flag = is_tabset(block.classes)
tabset_level = flag and level or 100
tab_level = tabset_level + 1
elseif level == tab_level then
local id = block.identifier
block.identifier = id .. "-tab"
return {
block,
pandoc.RawBlock(
"html",
"<h" .. level .. " style='visibility: hidden'>" ..
stringify(block) ..
"</h" .. level .. ">"
)
}
end
end
end
---
title: "minidown::mini_document"
author: "Atsushi Yasumoto"
date: "`r Sys.Date()`"
output:
html_document:
toc: true
toc_float: true
self_contained: false
pandoc_args: ["--lua-filter", "tabset.lua"]
---
```{=html}
<script src='tabset.js'></script>
```
# A {.tabset}
## tab A-1
content A-1
## tab A-2
content A-2
# B {.tabset}
## tab B-1
content B-1
## tab B-2
content B-2
@cderv
Copy link

cderv commented Jan 11, 2021

That is great !
The trick is to have the header inserted in the tab content ? which you are doing using Lua here ?

Just posting below as a one file Rmd example so that it is easier to test. (for future me :) )

Toc and Tabset
---
title: "ToC and Tabset"
author: "Atsushi Yasumoto"
date: "`r Sys.Date()`"
output:
  html_document:
    toc: true
    toc_float: true
    self_contained: false
    pandoc_args: ["--lua-filter", "tabset.lua"]
---

```{js}
document.addEventListener('DOMContentLoaded', function() {
  const anchors = Array.from(document.querySelectorAll("ul.nav.nav-tabs li a")).
    filter(a => a.attributes.role.value === "tab").
    reduce((hash, a) => {
      hash["#" + a.innerText.replace(/ /, "_")] = a;
      return hash;
    }, {});
  window.addEventListener('hashchange', function() {
    const anchor = anchors[location.hash];
    if (anchor !== undefined) {
      anchor.click();
    }
  });
});
```

```{cat, engine.opts=list(file = "tabset.lua")}
local tabset_level = 100
local tab_level = 101
local flag = false
local stringify = pandoc.utils.stringify

function is_tabset(classes)

  local res = false

  for _,v in ipairs(classes) do
    res = res or (v == "tabset")
  end

  return res
end

function Block(block)
  if block.tag == "Header" then
    local level = block.level
    if level <= tabset_level then
      flag = is_tabset(block.classes)
      tabset_level = flag and level or 100
      tab_level = tabset_level + 1
    elseif level == tab_level then
      local id = block.identifier
      block.identifier = id .. "-tab"
      return {
        block,
        pandoc.RawBlock(
          "html",
          "<h" .. level .. " style='visibility: hidden'>" ..
          stringify(block) ..
          "</h" .. level .. ">"
        )
      }
    end
  end
end
```


# A {.tabset}

## tab A-1

content A-1

## tab A-2

content A-2

# B {.tabset}

## tab B-1

content B-1

## tab B-2

content B-2

@Lucius-Cesar
Copy link

Lucius-Cesar commented Apr 2, 2021

Hello, thank you for your script. It is really usefull.

I noticed that unfortunately, it doesn't work if we use html widgets in the rmarkdown file. It doens't work with plotly and DT::datatable.
Do you have an idea of where this problem comes from?

e.g:

  DT::datatable(head(iris)) 

library(plotly)
library(tidyr)
library(dplyr)


data(airquality)
tm <- plot_ly(airquality,x=~Month, y=~Temp, z=~Ozone,color=~Month, mode= "markers",type = "scatter3d") %>% layout(title = "Ozone, Temperature correlation by Month")
tm

@atusy
Copy link
Author

atusy commented Apr 2, 2021

tabset navigation with html_document is quite difficult and experimental.

I'd recommend https://github.com/atusy/minidown which support the feature in the development version.

---
output:
  minidown::mini_document:
    tabset: true
    toc: true
---

# foo {.tabset}

## DT

```{r}
DT::datatable(head(mtcars))
```

## plotly

```{r}
plotly::ggplotly(ggplot2::qplot(1, 1))
```

@Lucius-Cesar
Copy link

It works great with your package, thank you.

Unfortunately, I really need to keep html_output for my project. Do you think there is a way to avoid the conflict with html widgets?

@atusy
Copy link
Author

atusy commented Apr 4, 2021

hmmm, I have no ideas. It needs some investigations, but I am running out of time...

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment