diff --git a/inst/rmarkdown/lua/lesson.lua b/inst/rmarkdown/lua/lesson.lua
index 5de76169..0326cbb0 100644
--- a/inst/rmarkdown/lua/lesson.lua
+++ b/inst/rmarkdown/lua/lesson.lua
@@ -21,7 +21,9 @@ local blocks = {
["discussion"] = "message-circle",
["testimonial"] = "heart",
["keypoints"] = "key",
- ["instructor"] = "edit-2"
+ ["instructor"] = "edit-2",
+ ["tab"] = "none",
+ ["group-tab"] = "none"
}
local block_counts = {
@@ -36,7 +38,9 @@ local block_counts = {
["discussion"] = 0,
["testimonial"] = 0,
["keypoints"] = 0,
- ["instructor"] = 0
+ ["instructor"] = 0,
+ ["tab"] = 0,
+ ["group-tab"] = 0
}
-- get the timing elements from the metadata and store them in a global var
@@ -192,6 +196,10 @@ local button_headings = {
{{title}}
]],
+ ["tab"] = [[
+
+ {{title}}
+
]],
}
local accordion_titles = {
@@ -268,6 +276,214 @@ accordion = function(el, class)
return(main_div)
end
+-- For a single tab block:
+-- Store the current tab button number
+local tab_button_num = 0
+-- Store the tab button number a tabpanel
+-- element thinks it is on
+local tabpanel_tab_button_num = 1
+
+-- Stores the tab nav buttons
+local tab_buttons = {}
+-- Stores the elements to form the tabpanel
+-- content for the tab currently being processed
+local this_tab_tabpanel = {}
+-- Stores a tab blocks, tabpanel content
+local tabpanels = {}
+
+-- Are we processing a group-tab?
+local group_tab = false
+local group_tab_titles = {}
+
+local tab_button = [[
+]]
+
+-- The first tab button is active
+local tab_button_active = [[
+]]
+
+add_to_tabpanel = function(el)
+ -- If the tabpanel_button_number is the same
+ -- as the nav tab_button_number this element
+ -- belongs with the current nav button so store
+ -- it for later
+ if tabpanel_tab_button_num == tab_button_num then
+ table.insert(this_tab_tabpanel, el)
+ -- Else we have hit the next tab button and should
+ -- wrap the tabpanel content we stored in the
+ -- this_tab_tabpanel table for the previous button
+ else
+ local id
+ if group_tab then
+ local tab_id = block_counts["group-tab"]
+ id = tab_id.."-"..group_tab_titles[tabpanel_tab_button_num]
+ else
+ local tab_id = block_counts["tab"]
+ id = tab_id.."-"..tabpanel_tab_button_num
+ end
+
+ -- Wrap the tabpanel contents in a div
+ local tabpanel_div = pandoc.Div(this_tab_tabpanel)
+ -- n.b. in pandoc 2.17, the attributes must be set after the classes
+ if tabpanel_tab_button_num == 1 then
+ tabpanel_div.classes = {"tab-pane show active"}
+ else
+ tabpanel_div.classes = {"tab-pane"}
+ end
+ tabpanel_div.identifier = "nav-tabpanel-"..id
+ tabpanel_div.attributes = {
+ ['role'] = 'tabpanel',
+ ['aria-labelledby'] = "nav-tab-"..id,
+ }
+
+ -- Store the div for the tab_block function
+ table.insert(tabpanels, tabpanel_div)
+
+ -- We move onto the next button having processed
+ -- the previous buttons tabpanel content
+ tabpanel_tab_button_num = tabpanel_tab_button_num + 1
+
+ -- The current element belongs to the new button
+ -- so empty out the this_tab_tabpanel table and store the el
+ this_tab_tabpanel = {}
+ table.insert(this_tab_tabpanel, el)
+ end
+end
+
+tab_filter = {
+ Header = function(el)
+ -- Level 3 headers mark the tab titles
+ -- all other headers in a tab block are ignored
+ if el.level == 3 then
+
+ -- Insert the title for the add_to_tabpanel to access
+ local title = pandoc.utils.stringify(el)
+
+ local id
+ local name
+ if group_tab then
+ local tab_id = block_counts["group-tab"]
+ local title_no_spaces = title:gsub("%s+", "")
+ id = tab_id.."-"..title_no_spaces
+ -- The JS for the group tabs selects buttons
+ -- to show based on the name attribute.
+ -- Here we set it to the button title.
+ name = 'name="'..title_no_spaces..'"'
+ -- Store the title so it can be used in the tabpanel id
+ table.insert(group_tab_titles, title_no_spaces)
+ else
+ local tab_id = block_counts["tab"]
+ id = tab_id.."-"..tab_button_num+1
+ -- Non group tabs don't need a name attribute.
+ name = ""
+ end
+
+ -- Found another button so increment the
+ -- current tab_button_num
+ tab_button_num = tab_button_num + 1
+
+ -- Create the button, if this is the first
+ -- button it needs to be active
+ local this_button
+ if tab_button_num == 1 then
+ this_button = tab_button_active
+ else
+ this_button = tab_button
+ end
+
+ -- Substitute in the button information
+ this_button = this_button:gsub("{{heading}}", button_headings["tab"])
+ this_button = this_button:gsub("{{title}}", title)
+ this_button = this_button:gsub("{{id}}", id)
+ this_button = this_button:gsub("{{name}}", name)
+
+ -- Convert the tab button to a raw block and store
+ local button = pandoc.RawBlock("html", this_button)
+ table.insert(tab_buttons, button)
+ end
+ end,
+ -- for all other elements process them using
+ -- the add_to_tabpanel function
+ Para = function(el)
+ _ = add_to_tabpanel(el)
+ end,
+ Div = function(el)
+ _ = add_to_tabpanel(el)
+ end,
+ Figure = function(el)
+ _ = add_to_tabpanel(el)
+ end,
+ CodeBlock = function(el)
+ _ = add_to_tabpanel(el)
+ end,
+ OrderedList = function(el)
+ _ = add_to_tabpanel(el)
+ end,
+ BulletList = function(el)
+ _ = add_to_tabpanel(el)
+ end
+}
+
+tab_block = function(el)
+
+ -- Increment the tab count
+ local count
+ if group_tab then
+ block_counts["group-tab"] = block_counts["group-tab"] + 1
+ count = block_counts["group-tab"]
+ else
+ block_counts["tab"] = block_counts["tab"] + 1
+ count = block_counts["tab"]
+ end
+
+ -- Walk the tab elements and process them
+ _ = pandoc.walk_block(el,tab_filter)
+
+ -- Wraps the tab buttons to create the tablist div
+ local button_div_id = "nav-tab-"..count
+ local button_div = pandoc.Div(tab_buttons)
+ button_div.identifier = button_div_id
+ button_div.classes = {"nav", "nav-tabs"}
+ button_div.attributes = {
+ ['role'] = 'tablist',
+ }
+
+ -- The tab_filter uses the current tab number
+ -- to determine whether we have reached the next tab
+ -- This tricks the add_to_tabpanel function into thinking
+ -- it has hit the number of tabs + 1 so it wraps
+ -- the last tabpanel in a div
+ tab_button_num = tab_button_num + 1
+ _ = add_to_tabpanel(tabpanels)
+
+ -- Wraps the tabpanels
+ local tab_content_div = pandoc.Div(tabpanels)
+ local tab_content_div_id = "nav-tabContent-"..count
+ tab_content_div.identifier = tab_content_div_id
+ tab_content_div.classes = {"tab-content"}
+
+ -- Create the nav html tags
+ local nav_start = pandoc.RawBlock("html", "")
+
+ -- Put everything in a tabs div
+ local tabs = pandoc.Div({nav_start, button_div, nav_end, tab_content_div})
+ tabs.classes = {"tabs"}
+
+ -- Reset counters for the next tab block
+ tab_button_num = 0
+ tabpanel_tab_button_num = 1
+ tab_buttons = {}
+ this_tab_tabpanel = {}
+ tabpanels = {}
+
+ return tabs
+end
+
callout_block = function(el)
local classes = el.classes:map(pandoc.utils.stringify)
local this_icon = blocks[classes[1]]
@@ -402,6 +618,24 @@ handle_our_divs = function(el)
return(challenge_block(el))
end
+ -- Tab blocks:
+ --
+ -- Toggleable Tab blocks.
+ v,i = el.classes:find("tab")
+ if i ~= nil then
+ group_tab = false
+ return(tab_block(el))
+ end
+
+ -- Group Tab blocks:
+ --
+ -- Toggleable Group Tab blocks.
+ v,i = el.classes:find("group-tab")
+ if i ~= nil then
+ group_tab = true
+ return(tab_block(el))
+ end
+
-- All other Div tags should have at most level 3 headers
level_head(el, 3)
return(callout_block(el))