From d1bbb2381f16dd892767b7cfeb1ba150004e1d5d Mon Sep 17 00:00:00 2001 From: Dimitrios Theodorakis Date: Fri, 2 Feb 2024 10:52:59 +0000 Subject: [PATCH 1/6] Add tabs First attempt at adding tabs to the lua code --- inst/rmarkdown/lua/lesson.lua | 182 +++++++++++++++++++++++++++++++++- 1 file changed, 180 insertions(+), 2 deletions(-) diff --git a/inst/rmarkdown/lua/lesson.lua b/inst/rmarkdown/lua/lesson.lua index 5de76169f..c1d6150df 100644 --- a/inst/rmarkdown/lua/lesson.lua +++ b/inst/rmarkdown/lua/lesson.lua @@ -21,7 +21,8 @@ local blocks = { ["discussion"] = "message-circle", ["testimonial"] = "heart", ["keypoints"] = "key", - ["instructor"] = "edit-2" + ["instructor"] = "edit-2", + ["tab"] = "none" } local block_counts = { @@ -36,7 +37,9 @@ local block_counts = { ["discussion"] = 0, ["testimonial"] = 0, ["keypoints"] = 0, - ["instructor"] = 0 + ["instructor"] = 0, + ["tab"] = 0, + ["tab_collapse"] = 0, } -- get the timing elements from the metadata and store them in a global var @@ -192,6 +195,10 @@ local button_headings = {
{{title}} ]], + ["tab"] = [[ +

+ {{title}} +

]], } local accordion_titles = { @@ -268,6 +275,169 @@ accordion = function(el, class) return(main_div) end +local tab_button_num = 1 +local tabpanel_tab_button_num = 0 +local tabpanel_current_tab_button_num = 1 + +local tabpanels = {} +local empty_list = pandoc.List:new() + +local test = {} + +local tab_button = [[ +]] + +local tab_button_selected = [[ +]] + +wrap_tab_panel = function(el) + if tabpanel_current_tab_button_num == tabpanel_tab_button_num then + print(tabpanel_current_tab_button_num) + print(tabpanel_tab_button_num) + print(el) + table.insert(tabpanels, el) + return empty_list + else + print("WRAPPING") + print(tabpanel_current_tab_button_num) + print(tabpanel_tab_button_num) + print(el) + + block_counts["tab_collapse"] = block_counts["tab_collapse"] + 1 + local id = block_counts["tab_collapse"] + local CLASS = upper_case("tab") + + collapse_id = "tabpanel"..CLASS..id + tab_div_id = "tab"..id + + -- return the content + local tab_collapse = pandoc.Div(tabpanels) + -- n.b. in pandoc 2.17, the attributes must be set after the classes + if tabpanel_current_tab_button_num == 1 then + tab_collapse.attributes = { + ['aria-selected'] = 'True', + } + else + tab_collapse.classes = {"tab-hidden"} + tab_collapse.attributes = { + ['aria-selected'] = 'False', + } + end + tab_collapse.identifier = collapse_id + tab_collapse.attributes = { + ['role'] = 'tabpanel', + ['aria-labelledby'] = tab_div_id, + } + tabpanels = {} + table.insert(tabpanels, el) + tabpanel_current_tab_button_num = tabpanel_current_tab_button_num + 1 + -- print(tab_collapse) + table.insert(test, tab_collapse) + return tab_collapse + end +end + +tab_button_filter = { + Header = function(el) + block_counts["tab"] = block_counts["tab"] + 1 + local id = block_counts["tab"] + local CLASS = upper_case("tab") + local label = CLASS..id + + if el.level == 1 then + -- return the header button + local this_button + + if tab_button_num == 1 then + this_button = tab_button_selected + tab_button_num = tab_button_num + 1 + else + this_button = tab_button + end + + this_button = this_button:gsub("{{heading}}", button_headings["tab"]) + this_button = this_button:gsub("{{title}}", pandoc.utils.stringify(el)) + -- this_button = this_button:gsub("{{class}}", "tab") + this_button = this_button:gsub("{{id}}", label) + + local button = pandoc.RawBlock("html", this_button) + + return button + else + return empty_list + end + end, + Para = function(el) + return empty_list + end, + Div = function(el) + return empty_list + end, + CodeBlock = function(el) + return empty_list + end +} + +tab_panel_filter = { + Header = function(el) + if el.level == 1 then + tabpanel_tab_button_num = tabpanel_tab_button_num + 1 + end + return empty_list + end, + Para = function(el) + local para_div = wrap_tab_panel(el) + return para_div + end, + Div = function(el) + local div_div = wrap_tab_panel(el) + return div_div + end, + CodeBlock = function(el) + local code_div = wrap_tab_panel(el) + return code_div + end +} + +tab_block = function(el) + + buttons = pandoc.walk_block(el,tab_button_filter) + panels = pandoc.walk_block(el,tab_panel_filter) + + local button_div = pandoc.Div(buttons.content) + button_div.classes = {"automatic"} + button_div.attributes = { + ['role'] = 'tablist', + } + + print('END') + print(panels) + -- trick into thinking there is a final header + tabpanel_tab_button_num = tabpanel_tab_button_num + 1 + local last_panel = wrap_tab_panel(tabpanels) + -- table.insert(test, last_panel) + table.insert(test, 1, button_div) + -- print(last_panel) + -- local tabs = pandoc.Div({button_div, panels, last_panel}) + local tabs = pandoc.Div(test) + tabs.classes = {"tabs"} + + tab_button_num = 1 + tabpanel_tab_button_num = 0 + tabpanel_current_tab_button_num = 1 + tabpanels = {} + + return tabs +end + callout_block = function(el) local classes = el.classes:map(pandoc.utils.stringify) local this_icon = blocks[classes[1]] @@ -402,6 +572,14 @@ 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 + return(tab_block(el)) + end + -- All other Div tags should have at most level 3 headers level_head(el, 3) return(callout_block(el)) From 15709d88e408fc3ce8cd01f2866448880caf6a64 Mon Sep 17 00:00:00 2001 From: Dimitrios Theodorakis Date: Sat, 3 Feb 2024 11:00:43 +0000 Subject: [PATCH 2/6] Use bootstrap in tab admonition --- inst/rmarkdown/lua/lesson.lua | 222 ++++++++++++++++++---------------- 1 file changed, 117 insertions(+), 105 deletions(-) diff --git a/inst/rmarkdown/lua/lesson.lua b/inst/rmarkdown/lua/lesson.lua index c1d6150df..dd79ba951 100644 --- a/inst/rmarkdown/lua/lesson.lua +++ b/inst/rmarkdown/lua/lesson.lua @@ -38,8 +38,7 @@ local block_counts = { ["testimonial"] = 0, ["keypoints"] = 0, ["instructor"] = 0, - ["tab"] = 0, - ["tab_collapse"] = 0, + ["tab"] = 0 } -- get the timing elements from the metadata and store them in a global var @@ -275,164 +274,177 @@ accordion = function(el, class) return(main_div) end -local tab_button_num = 1 -local tabpanel_tab_button_num = 0 -local tabpanel_current_tab_button_num = 1 - +-- 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 titles +local tab_titles = {} +-- 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 = {} -local empty_list = pandoc.List:new() - -local test = {} local tab_button = [[ -]] -local tab_button_selected = [[ -]] -wrap_tab_panel = function(el) - if tabpanel_current_tab_button_num == tabpanel_tab_button_num then - print(tabpanel_current_tab_button_num) - print(tabpanel_tab_button_num) - print(el) - table.insert(tabpanels, el) - return empty_list +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 - print("WRAPPING") - print(tabpanel_current_tab_button_num) - print(tabpanel_tab_button_num) - print(el) + local title_no_spaces = tab_titles[tabpanel_tab_button_num]:gsub("%s+", "-") + local id = block_counts["tab"] + local collapse_id = "nav-"..title_no_spaces..id.."-"..tabpanel_tab_button_num + local tabpanel_div_id = collapse_id.."-tab"..id.."-"..tabpanel_tab_button_num - block_counts["tab_collapse"] = block_counts["tab_collapse"] + 1 - local id = block_counts["tab_collapse"] - local CLASS = upper_case("tab") - - collapse_id = "tabpanel"..CLASS..id - tab_div_id = "tab"..id - - -- return the content - local tab_collapse = pandoc.Div(tabpanels) + -- Wrap the tabpanel contents in a div + local tab_collapse = pandoc.Div(this_tab_tabpanel) -- n.b. in pandoc 2.17, the attributes must be set after the classes - if tabpanel_current_tab_button_num == 1 then - tab_collapse.attributes = { - ['aria-selected'] = 'True', - } + if tabpanel_tab_button_num == 1 then + tab_collapse.classes = {"tab-pane show active"} else - tab_collapse.classes = {"tab-hidden"} - tab_collapse.attributes = { - ['aria-selected'] = 'False', - } + tab_collapse.classes = {"tab-pane"} end tab_collapse.identifier = collapse_id tab_collapse.attributes = { ['role'] = 'tabpanel', - ['aria-labelledby'] = tab_div_id, + ['aria-labelledby'] = tabpanel_div_id, } - tabpanels = {} - table.insert(tabpanels, el) - tabpanel_current_tab_button_num = tabpanel_current_tab_button_num + 1 - -- print(tab_collapse) - table.insert(test, tab_collapse) - return tab_collapse + + -- Store the div for the tab_block function + table.insert(tabpanels, tab_collapse) + + -- 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_button_filter = { +tab_filter = { Header = function(el) - block_counts["tab"] = block_counts["tab"] + 1 - local id = block_counts["tab"] - local CLASS = upper_case("tab") - local label = CLASS..id - + -- Level 1 headers mark the tab titles + -- all other headers in a tab block are ignored if el.level == 1 then - -- return the header button + local id = block_counts["tab"] + local label = id.."-"..tabpanel_tab_button_num + + -- Found another button so increment the + -- current tab_button_num + tab_button_num = tab_button_num + 1 + -- Insert the title for the add_to_tabpanel to access + local title = pandoc.utils.stringify(el) + table.insert(tab_titles, title) + + -- 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_selected - tab_button_num = tab_button_num + 1 + this_button = tab_button_active else this_button = tab_button end + -- Substitute in the button information + local title_no_spaces = title:gsub("%s+", "-") this_button = this_button:gsub("{{heading}}", button_headings["tab"]) - this_button = this_button:gsub("{{title}}", pandoc.utils.stringify(el)) - -- this_button = this_button:gsub("{{class}}", "tab") + this_button = this_button:gsub("{{title}}", title) + this_button = this_button:gsub("{{title_no_spaces}}", title_no_spaces) this_button = this_button:gsub("{{id}}", label) + -- Convert the tab button to a raw block and store local button = pandoc.RawBlock("html", this_button) - - return button - else - return empty_list + table.insert(tab_buttons, button) end end, + -- for all other elements process them using + -- the add_to_tabpanel function Para = function(el) - return empty_list + _ = add_to_tabpanel(el) end, Div = function(el) - return empty_list + _ = add_to_tabpanel(el) end, - CodeBlock = function(el) - return empty_list - end -} - -tab_panel_filter = { - Header = function(el) - if el.level == 1 then - tabpanel_tab_button_num = tabpanel_tab_button_num + 1 - end - return empty_list - end, - Para = function(el) - local para_div = wrap_tab_panel(el) - return para_div - end, - Div = function(el) - local div_div = wrap_tab_panel(el) - return div_div + Figure = function(el) + _ = add_to_tabpanel(el) end, CodeBlock = function(el) - local code_div = wrap_tab_panel(el) - return code_div + _ = add_to_tabpanel(el) end } tab_block = function(el) - buttons = pandoc.walk_block(el,tab_button_filter) - panels = pandoc.walk_block(el,tab_panel_filter) + -- Increment the tab count + block_counts["tab"] = block_counts["tab"] + 1 + + -- Walk the tab elements and process them + _ = pandoc.walk_block(el,tab_filter) - local button_div = pandoc.Div(buttons.content) - button_div.classes = {"automatic"} + -- Wraps the tab buttons to create the tablist div + local button_div_id = "nav-tab-"..block_counts["tab"] + local button_div = pandoc.Div(tab_buttons) + button_div.identifier = button_div_id + button_div.classes = {"nav", "nav-tabs"} button_div.attributes = { ['role'] = 'tablist', } - print('END') - print(panels) - -- trick into thinking there is a final header - tabpanel_tab_button_num = tabpanel_tab_button_num + 1 - local last_panel = wrap_tab_panel(tabpanels) - -- table.insert(test, last_panel) - table.insert(test, 1, button_div) - -- print(last_panel) - -- local tabs = pandoc.Div({button_div, panels, last_panel}) - local tabs = pandoc.Div(test) + -- 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-"..block_counts["tab"] + 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"} - tab_button_num = 1 - tabpanel_tab_button_num = 0 - tabpanel_current_tab_button_num = 1 + -- Reset counters for the next tab block + tab_button_num = 0 + tabpanel_tab_button_num = 1 + tab_titles = {} + tab_buttons = {} + this_tab_tabpanel = {} tabpanels = {} return tabs From 44a309499e5f01f6813e31fadc24a93803c8fc76 Mon Sep 17 00:00:00 2001 From: Dimitrios Theodorakis Date: Mon, 5 Feb 2024 19:14:30 +0000 Subject: [PATCH 3/6] Update lesson.lua Fix the id's of the buttons and the tabpanels so that the buttons control the correct tabpanel. --- inst/rmarkdown/lua/lesson.lua | 38 ++++++++++++++--------------------- 1 file changed, 15 insertions(+), 23 deletions(-) diff --git a/inst/rmarkdown/lua/lesson.lua b/inst/rmarkdown/lua/lesson.lua index dd79ba951..be496f25f 100644 --- a/inst/rmarkdown/lua/lesson.lua +++ b/inst/rmarkdown/lua/lesson.lua @@ -195,7 +195,7 @@ local button_headings = { {{title}} ]], ["tab"] = [[ -

+

]], } @@ -281,8 +281,6 @@ local tab_button_num = 0 -- element thinks it is on local tabpanel_tab_button_num = 1 --- Stores the tab titles -local tab_titles = {} -- Stores the tab nav buttons local tab_buttons = {} -- Stores the elements to form the tabpanel @@ -292,13 +290,13 @@ local this_tab_tabpanel = {} local tabpanels = {} local tab_button = [[ -]] -- The first tab button is active local tab_button_active = [[ -]] -- The first tab button is active local tab_button_active = [[ -]] add_to_tabpanel = function(el) @@ -313,8 +317,14 @@ add_to_tabpanel = function(el) -- wrap the tabpanel content we stored in the -- this_tab_tabpanel table for the previous button else - local tab_id = block_counts["tab"] - local id = tab_id.."-"..tabpanel_tab_button_num + 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) @@ -349,14 +359,27 @@ tab_filter = { -- Level 1 headers mark the tab titles -- all other headers in a tab block are ignored if el.level == 1 then - local tab_id = block_counts["tab"] - local id = tab_id.."-"..tab_button_num+1 + + -- 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 + name = 'name="'..title_no_spaces..'"' + table.insert(group_tab_titles, title_no_spaces) + else + local tab_id = block_counts["tab"] + id = tab_id.."-"..tab_button_num+1 + name = "" + end -- Found another button so increment the -- current tab_button_num tab_button_num = tab_button_num + 1 - -- Insert the title for the add_to_tabpanel to access - local title = pandoc.utils.stringify(el) -- Create the button, if this is the first -- button it needs to be active @@ -371,6 +394,7 @@ tab_filter = { 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) @@ -396,13 +420,20 @@ tab_filter = { tab_block = function(el) -- Increment the tab count - block_counts["tab"] = block_counts["tab"] + 1 + 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-"..block_counts["tab"] + 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"} @@ -420,7 +451,7 @@ tab_block = function(el) -- Wraps the tabpanels local tab_content_div = pandoc.Div(tabpanels) - local tab_content_div_id = "nav-tabContent-"..block_counts["tab"] + local tab_content_div_id = "nav-tabContent-"..count tab_content_div.identifier = tab_content_div_id tab_content_div.classes = {"tab-content"} @@ -581,6 +612,16 @@ handle_our_divs = function(el) -- 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 From 9986a93b225175158e232bf3ad3880e5054c187a Mon Sep 17 00:00:00 2001 From: Dimitrios Theodorakis Date: Tue, 6 Feb 2024 22:27:58 +0000 Subject: [PATCH 5/6] Update lesson.lua Add in extra comments for the group tab filter. --- inst/rmarkdown/lua/lesson.lua | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/inst/rmarkdown/lua/lesson.lua b/inst/rmarkdown/lua/lesson.lua index c9730fbaf..7990c9367 100644 --- a/inst/rmarkdown/lua/lesson.lua +++ b/inst/rmarkdown/lua/lesson.lua @@ -369,11 +369,16 @@ tab_filter = { 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 From 7b3c7413d35cc49486fb22f123161a7017db0d45 Mon Sep 17 00:00:00 2001 From: Dimitrios Theodorakis Date: Wed, 6 Mar 2024 11:11:07 +0000 Subject: [PATCH 6/6] Change tab component header syntax to match other components and add support for lists --- inst/rmarkdown/lua/lesson.lua | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/inst/rmarkdown/lua/lesson.lua b/inst/rmarkdown/lua/lesson.lua index 7990c9367..0326cbb0c 100644 --- a/inst/rmarkdown/lua/lesson.lua +++ b/inst/rmarkdown/lua/lesson.lua @@ -356,9 +356,9 @@ end tab_filter = { Header = function(el) - -- Level 1 headers mark the tab titles + -- Level 3 headers mark the tab titles -- all other headers in a tab block are ignored - if el.level == 1 then + if el.level == 3 then -- Insert the title for the add_to_tabpanel to access local title = pandoc.utils.stringify(el) @@ -419,6 +419,12 @@ tab_filter = { 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 }