Module:UtilsLayout/Tabs

local p = {} local h = {}

function p.tabs(data, options) local options = options or {} local format = options.format or "top" local align = options.align or "left" local alignVertical = options.alignVertical or "top" local columns = options.columns local collapse = options.collapse local defaultTab = options.default or 1 local stretch = options.stretch local fixedContentWidth = options.fixedContentWidth local fixedContentHeight = options.fixedContentHeight if #data == 1 and collapse then return data[1].content end local tabContainer = h.tabContainer(data, defaultTab, format, align, columns, stretch) local tabContents = h.tabContents(data, defaultTab, align, alignVertical, fixedContentWidth, fixedContentHeight) local html = mw.html.create("div") if format == "top" then html:node(tabContainer) :node(tabContents) else html:node(tabContents) :node(tabContainer) end return tostring(html) end

function h.tabContainer(data, defaultTab, format, align, columns, stretch) local tabContainer = mw.html.create("div"):addClass("tabcontainer tabcontainer-" .. format) if align then tabContainer:addClass("tabcontainer--"..align) end if stretch then tabContainer:addClass("tabcontainer--stretch") end if columns then tabContainer:addClass("tabcontainer--columns") end for i, tabData in ipairs(data) do		local tab = mw.html.create("span") :addClass("tab explain") :attr("title", tabData.tooltip) :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, alignVertical, fixedContentWidth, fixedContentHeight) local tabContents = mw.html.create("div") :addClass("tabcontents") if align then tabContents:css("text-align", align) -- TODO: add a class here instead of inline CSS end if alignVertical == "center" then tabContents:addClass("tabcontents--align-y-center") elseif alignVertical == "bottom" then tabContents:addClass("tabcontents--align-y-bottom") end if fixedContentWidth then tabContents:addClass("tabcontents--fixed-width") if type(fixedContentWidth) == "number" then tabContents:css("width", fixedContentWidth .. "px") end end if fixedContentHeight then tabContents:addClass("tabcontents--fixed-height") if type(fixedContentHeight) == "number" then tabContents:css("height", fixedContentHeight .. "px") end end for i, tabData in ipairs(data) do		local content = mw.html.create("div") :addClass("content") :wikitext(tabData.content) if i == defaultTab then content:addClass("content--active") end tabContents:node(content) end return tabContents end

p.Schemas = { 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 = "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 = "format", type = "string", enum = {"top", "bottom"}, default = mw.dumpObject("top"), desc = "If, the tabs are placed above their content. If  , then the opposite." },				{					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 ." },				{					name = "align", type = "string", enum = {"left", "center", "right"}, default = mw.dumpObject("left"), desc = "Horizontal alignment for tabs and their content.", },				{					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 ." },				{					name = "fixedContentWidth", oneOf = { { type = "boolean" }, { type = "number" }, },					desc = "Width for the tab container in . Or, if set to , the tab container will  assume the width of the largest tab. By default, the tab container assumes the width of the current tab." },				{					name = "fixedContentHeight", oneOf = { { type = "boolean" }, { type = "number" }, },					desc = "Height for the tab container in . Or, if set to , the tab container will  assume the height of the largest tab. By default, the tab container assumes the height of the current tab.", }			}		},	} }

p.Documentation = { 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" },					},					{						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" }					},					{						format = "bottom", align = "center", default = 2, }				}			},			{				desc = "Tabs display even for a single tab unless  is set to true.", args = { }			},			{				args = { ,					{ collapse = true } }			},			{				desc = " and   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" }, },					{ fixedContentWidth = true }, },			},			{				args = { {						{ label = "Small Tab", content = "meep" }, { label = "Wide Tab", content = "meeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeep" }, { label = "Tall tab", content = "m\ne\ne\ne\ne\np" }, },					{ fixedContentHeight = true }, },			},			{				args = { {						{ label = "Small Tab", content = "meep" }, { label = "Wide Tab", content = "meeeeeeeeeeeeeeeeeeeeeeeeeeep" }, { label = "Tall tab", content = "m\ne\ne\ne\ne\np" }, },					{ 						fixedContentWidth = true, fixedContentHeight = true, alignVertical = "center", }, 				},			},			{				args = { {						{ label = "Small Tab", content = "meep" }, { label = "Wide Tab", content = "meeeeeeeeeeeeeeeeeeeeeeeeeeep" }, { label = "Tall tab", content = "m\ne\ne\ne\ne\np" }, },					{ 						fixedContentWidth = 80, fixedContentHeight = 80, alignVertical = "center", }, 				},			},		},	}, }

return p