Documentation for this module may be created at Module:UtilsLayout/Tabs/doc
local p = {} local h = {} local CLASS_TOOLTIP = require("Module:Constants/class/tooltip") function p.tabs(data, options) local options = options or {} local tabOptions = options.tabOptions or {} local labelOptions = options.labelOptions or {} local contentOptions = options.contentOptions or {} local defaultTab = options.default or 1 local align = options.align or "left" if #data == 1 and tabOptions.collapse then return data[1].content end local tabContainer = h.tabContainer(data, defaultTab, align, tabOptions, labelOptions) local tabContents = h.tabContents(data, defaultTab, align, contentOptions) local html = mw.html.create("div") :addClass("zw-tabs") :addClass(tabOptions.stretch and "zw-tabs--stretch" or nil) :addClass(tabOptions.columns and "zw-tabs--columns" or nil) if tabOptions.position == "bottom" then html:node(tabContents) :node(tabContainer) else html:node(tabContainer) :node(tabContents) end return tostring(html) end function h.tabContainer(data, defaultTab, align, tabOptions, labelOptions) local position = tabOptions.position or "top" local stretch = tabOptions.stretch local columns = tabOptions.columns local labelAlignVertical = labelOptions.alignVertical or "center" local tabContainer = mw.html.create("div") :addClass("tabcontainer tabcontainer-"..position) :addClass("tabcontainer--align-x-"..align) :attr("role", "tablist") for i, tabData in ipairs(data) do local tab = mw.html.create("span") :addClass("tab") :addClass(CLASS_TOOLTIP) :addClass("tab--label-align-y-"..labelAlignVertical) :attr("title", tabData.tooltip) :attr("role", "tab") :attr("tabindex", "0") :wikitext(tabData.label) if i == defaultTab then tab:addClass("active") end if columns then tab:css({ ["max-width"] = "calc(100%/"..columns.." - 2*"..(columns-1).."px)" -- the subtraction is to account for tab margins }) end tabContainer:node(tab) end return tabContainer end function h.tabContents(data, defaultTab, align, contentOptions) local alignVertical = contentOptions.alignVertical or "top" local fixedWidth = contentOptions.fixedWidth local fixedHeight = contentOptions.fixedHeight local tabContents = mw.html.create("div") :addClass("tabcontents") :addClass("tabcontents--align-x-"..align) :addClass("tabcontents--align-y-"..alignVertical) if fixedWidth then tabContents:addClass("tabcontents--fixed-width") if type(fixedWidth) == "number" then tabContents:css("width", fixedWidth .. "px") end end if fixedHeight then tabContents:addClass("tabcontents--fixed-height") if type(fixedHeight) == "number" then tabContents:css("height", fixedHeight .. "px") end end for i, tabData in ipairs(data) do local content = mw.html.create("div") :addClass("content") :attr("role", "tabpanel") :wikitext("\n", tabData.content) if i == defaultTab then content:addClass("content--active") end tabContents:node(content) end return tabContents end function p.Schemas() return { tabs = { data = { type = "array", required = true, items = { type = "record", properties = { { name = "label", type = "string", required = true, desc = "Button text for the tab." }, { name = "tooltip", type = "string", desc = "Tooltip for the tab button", }, { name = "content", type = "string", required = true, desc = "Content for the tab.", }, } } }, options = { type = "record", properties = { { name = "default", type = "number", default = 1, desc = "Index of default tab.", }, { name = "align", type = "string", enum = {"left", "center", "right"}, default = mw.dumpObject("left"), desc = "Horizontal alignment for tabs and their content.", }, { name = "tabOptions", type = "record", desc = "Display options for the tabs themselves.", properties = { { name = "position", type = "string", enum = {"top", "bottom"}, default = mw.dumpObject("top"), desc = "If <code>top</code> (default), the tabs are placed above their content. If <code>bottom</code>, then the opposite." }, { name = "collapse", type = "boolean", desc = "If truthy, tabs will not be rendered if there is only one tab. The content of that tab will simply be rendered instead." }, { name = "stretch", type = "boolean", desc = "If true, then tabs will stretch to fill the available space in their container.", }, { name = "columns", type = "number", desc = "If specified, the tabs will attempt to arrange themselves in N columns of equal width. This option is not compatible with <code>stretch</code>." }, }, }, { name = "labelOptions", type = "record", desc = "Display options for the tab labels.", properties = { { name = "alignVertical", type = "string", enum = {"center", "bottom"}, default = mw.dumpObject("center"), desc = "Vertical alignment of the label with respect to its tab. Options are: <code>center</code> (default) and <code>bottom</code>.", }, }, }, { name = "contentOptions", type = "record", desc = "Display options for the tab contents.", properties = { { name = "fixedWidth", oneOf = { { type = "boolean" }, { type = "number" }, }, desc = "Width for the tab container in <code>px</code>. Or, if set to <code>true</code>, the tab container will assume the width of the largest tab. By default, the tab container assumes the width of the current tab." }, { name = "fixedHeight", oneOf = { { type = "boolean" }, { type = "number" }, }, desc = "Height for the tab container in <code>px</code>. Or, if set to <code>true</code>, the tab container will assume the height of the largest tab. By default, the tab container assumes the height of the current tab.", }, { name = "alignVertical", type = "string", enum = {"top", "center", "bottom"}, default = mw.dumpObject("top"), desc = "Vertical alignment of tab contents with respect to the tab container. Useful only alonside <code>fixedHeight</code>." }, }, }, }, }, } } end function p.Documentation() return { tabs = { params = {"data", "options"}, returns = "HTML markup rendering tabs.", cases = { resultOnly = true, { args = { { { label = "Tab1", content = "Content1", }, { label = "Tab2", content = "Content2" }, }, } }, { args = { { { label = "Tab1", content = "Content1", }, { label = "Tab2", content = "Content2" }, }, { tabOptions = { stretch = true, } }, }, }, { args = { { { label = "Tab1", tooltip = "This is the first tab.", content = "Content1" }, { label = "Tab2", tooltip = "This is the second tab.", content = "Content2" }, { label = "Tab3", tooltip = "This is the third tab.", content = "Content3" } }, { default = 2, align = "center", tabOptions = { position = "bottom", }, } } }, { desc = "Tabs display even for a single tab unless <code>collapse</code> is set to true.", args = { {{ label = "Single Tab", content = "Content" }} } }, { args = { {{ label = "Single Tab", content = "Content" }}, { tabOptions = { collapse = true } }, } }, { desc = "<code>fixedtWidth</code> and <code>fixedHeight</code> determine how the overall tab container is sized.", args = { { { label = "Small Tab", content = "meep" }, { label = "Wide Tab", content = "meeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeep" }, { label = "Tall tab", content = "m\ne\ne\ne\ne\np" }, }, }, }, { args = { { { label = "Small Tab", content = "meep" }, { label = "Wide Tab", content = "meeeeeeeeeeeeeeeeeeeeeeeeeeep" }, { label = "Tall tab", content = "m\ne\ne\ne\ne\np" }, }, { contentOptions = { fixedWidth = true, }, }, }, }, { args = { { { label = "Small Tab", content = "meep" }, { label = "Wide Tab", content = "meeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeep" }, { label = "Tall tab", content = "m\ne\ne\ne\ne\np" }, }, { contentOptions = { fixedHeight = true, }, }, }, }, { args = { { { label = "Small Tab", content = "meep" }, { label = "Wide Tab", content = "meeeeeeeeeeeeeeeeeeeeeeeeeeep" }, { label = "Tall tab", content = "m\ne\ne\ne\ne\np" }, }, { contentOptions = { fixedWidth = true, fixedHeight = true, alignVertical = "center", }, }, }, }, { args = { { { label = "Small Tab", content = "meep" }, { label = "Wide Tab", content = "meeeeeeeeeeeeeeeeeeeeeeeeeeep" }, { label = "Tall tab", content = "m\ne\ne\ne\ne\np" }, }, { contentOptions = { fixedWidth = 80, fixedHeight = 80, alignVertical = "center", }, }, }, }, { desc = "When images are used as tab labels, the label alignment options can be useful.", args = { { { label = "[[File:ST Engine Icon.png|link=]]", content = "Engines" }, { label = "[[File:ST Passenger Car Icon.png|link=]]", content = "Passenger Cars" }, }, { labelOptions = { alignVertical = "bottom", }, }, }, }, }, }, } end return p