Module:UtilsLayout/Tabs: Difference between revisions
Jump to navigation
Jump to search
PhantomCaleb (talk | contribs) No edit summary |
PhantomCaleb (talk | contribs) (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 | 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" | ||
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 | local tabContainer = h.tabContainer(data, defaultTab, align, tabOptions, labelOptions) | ||
local tabContents = h.tabContents(data, defaultTab, align, | 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 | |||
html:node(tabContainer) | html:node(tabContainer) | ||
:node(tabContents) | :node(tabContents) | ||
Line 32: | Line 29: | ||
end | end | ||
function h.tabContainer(data, defaultTab | function h.tabContainer(data, defaultTab, align, tabOptions, labelOptions) | ||
local tabContainer = mw.html.create("div"):addClass("tabcontainer tabcontainer-" .. | 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 | 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 | 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) | |||
:addClass("tabcontents--align-y-"..alignVertical) | |||
if fixedWidth then | |||
if | |||
tabContents:addClass("tabcontents--fixed-width") | tabContents:addClass("tabcontents--fixed-width") | ||
if type( | if type(fixedWidth) == "number" then | ||
tabContents:css("width", | tabContents:css("width", fixedWidth .. "px") | ||
end | end | ||
end | end | ||
if | if fixedHeight then | ||
tabContents:addClass("tabcontents--fixed-height") | tabContents:addClass("tabcontents--fixed-height") | ||
if type( | if type(fixedHeight) == "number" then | ||
tabContents:css("height", | 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.", | ||
}, | }, | ||
{ | { | ||
Line 166: | Line 146: | ||
}, | }, | ||
{ | { | ||
name = " | name = "tabOptions", | ||
type = " | 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 = " | name = "labelOptions", | ||
type = "record", | |||
{ type = " | 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 = " | name = "contentOptions", | ||
oneOf = { | type = "record", | ||
{ type = "boolean" }, | desc = "Display options for the tab contents.", | ||
{ type = " | 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>." | |||
}, | |||
}, | }, | ||
}, | |||
} | }, | ||
} | |||
}, | }, | ||
} | } | ||
Line 226: | Line 256: | ||
}, | }, | ||
{ | { | ||
stretch = true, | tabOptions = { | ||
stretch = true, | |||
} | |||
}, | }, | ||
}, | }, | ||
Line 250: | Line 282: | ||
}, | }, | ||
{ | { | ||
default = 2, | |||
align = "center", | align = "center", | ||
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" }, | ||
}, | }, | ||
{ | { | ||
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" }, | ||
}, | }, | ||
{ | { | ||
contentOptions = { | |||
fixedHeight = true, | |||
}, | |||
}, | |||
}, | }, | ||
}, | }, | ||
Line 306: | Line 352: | ||
}, | }, | ||
{ | { | ||
contentOptions = { | |||
fixedWidth = true, | |||
fixedHeight = true, | |||
alignVertical = "center", | |||
}, | |||
}, | }, | ||
}, | }, | ||
Line 320: | Line 368: | ||
}, | }, | ||
{ | { | ||
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", | |||
}, | |||
}, | |||
}, | |||
}, | }, | ||
}, | }, |
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