17,260
edits
(for tabcontent, read default from the id arg since it doesn't make sense to have an id in that case) |
(refactored with a single entrypoint accepting flattened-tree args) |
||
Line 1: | Line 1: | ||
local Args = require( 'Module:Args' ) | |||
local Guid = require( 'Module:Guid' ) | |||
function getRequiredArg( args, argName, fnName ) | function getRequiredArg( args, argName, fnName ) | ||
return assert( args[argName], 'missing required arg for ' .. fnName .. ': ' .. argName ) | return assert( args[argName], 'missing required arg for ' .. fnName .. ': ' .. argName ) | ||
Line 5: | Line 8: | ||
local TabContainer = {} | local TabContainer = {} | ||
TabContainer.__index = TabContainer | TabContainer.__index = TabContainer | ||
local TabSet = {} | |||
TabSet.__index = TabSet | |||
local Tab = {} | |||
Tab.__index = Tab | |||
local TabContent = {} | |||
TabContent.__index = TabContent | |||
function TabContainer.new( args ) | function TabContainer.new( args ) | ||
return setmetatable( { | return setmetatable( { | ||
id = | id = args.id or Guid(), | ||
contents = {}, | |||
args = args | args = args | ||
}, TabContainer ) | }, TabContainer ) | ||
end | |||
function TabContainer:leftTabs( args ) | |||
if not self.tabsLeft then | |||
args.target = args.target or self.id | |||
args.activation = args.activation or self.args.activation | |||
self.tabsLeft = TabSet.new( args ) | |||
end | |||
return self.tabsLeft | |||
end | |||
function TabContainer:topTabs( args ) | |||
if not self.tabsTop then | |||
args.target = args.target or self.id | |||
args.activation = args.activation or self.args.activation | |||
self.tabsTop = TabSet.new( args ) | |||
end | |||
return self.tabsTop | |||
end | |||
function TabContainer:content( args ) | |||
self.contents[#self.contents + 1] = TabContent.new( args ) | |||
end | end | ||
Line 22: | Line 56: | ||
if self.args.width then container:css( 'width', self.args.width .. 'px' ) end | if self.args.width then container:css( 'width', self.args.width .. 'px' ) end | ||
if self.args.height then container:css( 'height', self.args.height .. 'px' ) end | if self.args.height then container:css( 'height', self.args.height .. 'px' ) end | ||
if self.tabsLeft then | if self.tabsLeft then | ||
local width = tonumber( self.args.vtabwidth ) or 60 | local width = tonumber( self.args.vtabwidth ) or 60 | ||
Line 30: | Line 65: | ||
:css( 'width', tostring( width ) .. 'px' ) | :css( 'width', tostring( width ) .. 'px' ) | ||
:css( 'margin-left', '-' .. tostring( width + 10 ) .. 'px' ) | :css( 'margin-left', '-' .. tostring( width + 10 ) .. 'px' ) | ||
:wikitext( self.tabsLeft ) | :wikitext( self.tabsLeft:render() ) | ||
end | end | ||
if self.tabsTop then | if self.tabsTop then | ||
container:addClass( 'zdw-tabcontainer--hastabstop' ) | container:addClass( 'zdw-tabcontainer--hastabstop' ) | ||
container:tag( 'div' ) | container:tag( 'div' ) | ||
:addClass( 'zdw-tabcontainer__tabset--top' ) | :addClass( 'zdw-tabcontainer__tabset--top' ) | ||
:wikitext( self.tabsTop ) | :wikitext( self.tabsTop:render() ) | ||
end | |||
for _, c in ipairs( self.contents ) do | |||
container:wikitext( c:render() ) | |||
end | end | ||
container:tag( 'div' ) | container:tag( 'div' ) | ||
:css( 'clear', 'both' ) | :css( 'clear', 'both' ) | ||
Line 44: | Line 84: | ||
return tostring( container ) | return tostring( container ) | ||
end | end | ||
function TabSet.new( args ) | function TabSet.new( args ) | ||
Line 53: | Line 90: | ||
selector = args.selector and (tonumber(args.selector) or error('invalid arg: selector must be a number')) or 0, | selector = args.selector and (tonumber(args.selector) or error('invalid arg: selector must be a number')) or 0, | ||
activation = args.activation or 'click', | activation = args.activation or 'click', | ||
defaultTab = args.default and (tonumber(args.default) or error('invalid arg: default must be a number')) or 1, | |||
tabs = {} | |||
}, TabSet ) | }, TabSet ) | ||
end | |||
function TabSet:tab( args ) | |||
local index = #self.tabs + 1 | |||
if index == self.defaultTab then args.default = true end | |||
self.tabs[index] = Tab.new( args ) | |||
end | end | ||
Line 63: | Line 107: | ||
:attr( 'data-tab-selector', self.selector ) | :attr( 'data-tab-selector', self.selector ) | ||
:attr( 'data-tab-type', self.activation ) | :attr( 'data-tab-type', self.activation ) | ||
:wikitext( | |||
for _, tab in ipairs( self.tabs ) do | |||
tabSet:wikitext( tab:render() ) | |||
end | |||
return tostring( tabSet ) | return tostring( tabSet ) | ||
end | end | ||
function Tab.new( args ) | function Tab.new( args ) | ||
return setmetatable( { | return setmetatable( { | ||
selection = getRequiredArg( args, | selection = getRequiredArg( args, 'selection', 'Tab.new' ), | ||
label = args | label = args.label or args.selection, | ||
args = args | args = args | ||
}, Tab ) | }, Tab ) | ||
Line 91: | Line 135: | ||
return tostring( tab ) | return tostring( tab ) | ||
end | end | ||
function TabContent.new( args ) | function TabContent.new( args ) | ||
return setmetatable( { | return setmetatable( { | ||
contentId = getRequiredArg( args, | contentId = getRequiredArg( args, 'contentId', 'TabContent.new' ), | ||
content = args | content = args.content or args.contentId, | ||
args = args | args = args | ||
}, TabContent ) | }, TabContent ) | ||
Line 122: | Line 163: | ||
local p = {} | local p = {} | ||
p. | p.Tabs = TabContainer | ||
function p.tabs( frame ) | function p.tabs( frame ) | ||
local | local args = Args.parse( frame.args ) | ||
local tabs = TabContainer.new( args:values() ) | |||
if args.left then | |||
local left = tabs:leftTabs( args.left:values() ) | |||
for _, leftArgs in ipairs( args.left ) do | |||
end | tabArgs = leftArgs:values() | ||
tabArgs.selection = leftArgs:val() | |||
left:tab( tabArgs ) | |||
end | |||
end | |||
if args.top then | |||
local | local topArgs = args.top:values() | ||
if args.left and not topArgs.selector then topArgs.selector = 1 end -- default to 2D behavior if both sets are present | |||
end | local top = tabs:topTabs( topArgs ) | ||
for _, topArgs in ipairs( args.top ) do | |||
tabArgs = topArgs:values() | |||
tabArgs.selection = topArgs:val() | |||
top:tab( tabArgs ) | |||
end | |||
end | |||
for k, v in pairs( args.content or {} ) do -- order doesn't matter here since only one is displayed at a time | |||
local contentArgs = v:values() | |||
contentArgs.contentId = k | |||
contentArgs.content = v:val() | |||
tabs:content( contentArgs ) | |||
end | |||
return tabs:render() | |||
return | |||
end | end | ||
return p | return p |