Module:UtilsLayout/Tabs: Difference between revisions

From Zelda Wiki, the Zelda encyclopedia
Jump to navigation Jump to search
No edit summary
(refining API)
Line 4: Line 4:
function p.tabs(data, options)
function p.tabs(data, options)
local options = options or {}
local options = options or {}
local format = options.format or "top"
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"
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
if #data == 1 and tabOptions.collapse then
return data[1].content
return data[1].content
end
end
local tabContainer = h.tabContainer(data, defaultTab, format, align, columns, stretch)
local tabContainer = h.tabContainer(data, defaultTab, align, tabOptions, labelOptions)
local tabContents = h.tabContents(data, defaultTab, align, alignVertical, fixedContentWidth, fixedContentHeight)
local tabContents = h.tabContents(data, defaultTab, align, contentOptions)
local html = mw.html.create("div")
local html = mw.html.create("div")
if tabOptions.position == "top" then
if format == "top" then
html:node(tabContainer)
html:node(tabContainer)
:node(tabContents)
:node(tabContents)
Line 32: Line 29:
end
end


function h.tabContainer(data, defaultTab, format, align, columns, stretch)
function h.tabContainer(data, defaultTab, align, tabOptions, labelOptions)
local tabContainer = mw.html.create("div"):addClass("tabcontainer tabcontainer-" .. format)
local position = tabOptions.position or "top"
local stretch = tabOptions.stretch
if align then
local columns = tabOptions.columns
tabContainer:addClass("tabcontainer--"..align)
local labelAlignVertical = labelOptions.alignVertical or "center"
end
local tabContainer = mw.html.create("div")
:addClass("tabcontainer tabcontainer-"..position)
:addClass("tabcontainer--align-x-"..align)
if stretch then
if stretch then
tabContainer:addClass("tabcontainer--stretch")
tabContainer:addClass("tabcontainer--stretch")
Line 47: Line 48:
for i, tabData in ipairs(data) do
for i, tabData in ipairs(data) do
local tab = mw.html.create("span")
local tab = mw.html.create("span")
:addClass("tab explain")
:addClass("tab")
:addClass("explain")
:addClass("tab--label-align-y-"..labelAlignVertical)
:attr("title", tabData.tooltip)
:attr("title", tabData.tooltip)
:wikitext(tabData.label)
:wikitext(tabData.label)
Line 63: Line 66:
end
end


function h.tabContents(data, defaultTab, align, alignVertical, fixedContentWidth, fixedContentHeight)
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")
local tabContents = mw.html.create("div")
:addClass("tabcontents")
:addClass("tabcontents")
:addClass("tabcontents--align-x-"..align)
if align then
:addClass("tabcontents--align-y-"..alignVertical)
tabContents:addClass("tabcontents--align-x-"..align)
 
end
if fixedWidth then
if alignVertical then
tabContents:addClass("tabcontents--align-y-"..alignVertical)
end
if fixedContentWidth then
tabContents:addClass("tabcontents--fixed-width")
tabContents:addClass("tabcontents--fixed-width")
if type(fixedContentWidth) == "number" then
if type(fixedWidth) == "number" then
tabContents:css("width", fixedContentWidth .. "px")
tabContents:css("width", fixedWidth .. "px")
end
end
end
end
if fixedContentHeight then
if fixedHeight then
tabContents:addClass("tabcontents--fixed-height")
tabContents:addClass("tabcontents--fixed-height")
if type(fixedContentHeight) == "number" then
if type(fixedHeight) == "number" then
tabContents:css("height", fixedContentHeight .. "px")
tabContents:css("height", fixedHeight .. "px")
end
end
end
end
Line 135: Line 137:
default = 1,
default = 1,
desc = "Index of default tab.",
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 <code>top</code>, the tabs are placed above their content. If <code>bottom</code>, 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 <code>stretch</code>."
},
},
{
{
Line 166: Line 146:
},
},
{
{
name = "alignVertical",
name = "tabOptions",
type = "string",
type = "record",
enum = {"top", "center", "bottom"},
desc = "Display options for the tabs themselves.",
default = mw.dumpObject("top"),
properties = {
desc = "Vertical alignment of tab contents with respect to the tab container. Useful only alonside <code>fixedHeight</code>."
{
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 = "fixedContentWidth",
name = "labelOptions",
oneOf = {
type = "record",
{ type = "boolean" },
desc = "Display options for the tab labels.",
{ type = "number" },
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>.",
},
},
},
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 = "fixedContentHeight",
name = "contentOptions",
oneOf = {
type = "record",
{ type = "boolean" },
desc = "Display options for the tab contents.",
{ type = "number" },
properties = {
{
name = "fixedContentWidth",
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 = "fixedContentHeight",
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>."
},
},
},
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.",
},
}
},
}
},
},
}
}
Line 226: Line 256:
},
},
{
{
stretch = true,
tabOptions = {
stretch = true,
}
},
},
},
},
Line 250: Line 282:
},
},
{
{
format = "bottom",
default = 2,
align = "center",
align = "center",
default = 2,
tabOptions = {
position = "bottom",
},
}
}
}
}
Line 265: Line 299:
args = {
args = {
{{ label = "Single Tab", content = "Content" }},
{{ label = "Single Tab", content = "Content" }},
{ collapse = true }
{  
tabOptions = {
collapse = true
}
},
}
}
},
},
Line 285: Line 323:
{ label = "Tall tab", content = "m\ne\ne\ne\ne\np" },
{ label = "Tall tab", content = "m\ne\ne\ne\ne\np" },
},
},
{ fixedContentWidth = true },
{
contentOptions = {
fixedWidth = true,
},
},
  },
  },
},
},
Line 295: Line 337:
{ label = "Tall tab", content = "m\ne\ne\ne\ne\np" },
{ label = "Tall tab", content = "m\ne\ne\ne\ne\np" },
},
},
{ fixedContentHeight = true },
{
contentOptions = {
fixedHeight = true,
},
},
  },
  },
},
},
Line 306: Line 352:
},
},
{  
{  
fixedContentWidth = true,
contentOptions = {
fixedContentHeight = true,
fixedWidth = true,
alignVertical = "center",
fixedHeight = true,
alignVertical = "center",
},
},
},
  },
  },
Line 320: Line 368:
},
},
{  
{  
fixedContentWidth = 80,
contentOptions = {
fixedContentHeight = 80,
fixedWidth = 80,
alignVertical = "center",
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",
},
},
},
},
},
},
},

Revision as of 13:37, 26 September 2020

Lua error in Module:Documentation/Module at line 351: attempt to call field 'Documentation' (a table value).


local p = {}
local h = {}

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")
	if tabOptions.position == "top" then
		html:node(tabContainer)
			:node(tabContents)
	else
		html:node(tabContents)
			:node(tabContainer)
	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)
	
	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")
			:addClass("explain")
			:addClass("tab--label-align-y-"..labelAlignVertical)
			: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, 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")
			: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 = "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 = "fixedContentWidth",
							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 = "fixedContentHeight",
							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>."
						},
					},
				},
			},
		},
	}
}

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"
						},
					},
					{
						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>fixedContentWidth</code> and <code>fixedContentHeight</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",
						},
					},
				},
			},
		},
	},
}

return p