Module:Tabs
Want an adless experience? Log in or Create an account.
Documentation for this module may be created at Module:Tabs/doc
local Args = require( 'Module:Args' ) function getRequiredArg( args, argName, fnName ) return assert( args[argName], 'missing required arg for ' .. fnName .. ': ' .. argName ) end local TabContainer = {} TabContainer.__index = TabContainer local TabSet = {} TabSet.__index = TabSet local Tab = {} Tab.__index = Tab local TabContent = {} TabContent.__index = TabContent function TabContainer.new( args ) args = args or {} return setmetatable( { contents = {}, args = args }, TabContainer ) end function TabContainer:leftTabs( args ) if not self.tabsLeft then args.target = args.target or self.args.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.args.id args.activation = args.activation or self.args.activation self.tabsTop = TabSet.new( args ) end return self.tabsTop end function TabContainer:addTabLeftWithContent( args ) -- normalize args so they can just be passed to everything. definitely a bad idea but... args.selection = getRequiredArg( args, 'contentId', 'TabContainer:addTabLeft' ) self:leftTabs( args ):addTab( args ) self:addContent( args ) end function TabContainer:addTabTopWithContent( args ) -- normalize args so they can just be passed to everything. definitely a bad idea but... args.selection = getRequiredArg( args, 'contentId', 'TabContainer:addTabTop' ) self:topTabs( args ):addTab( args ) self:addContent( args ) end function TabContainer:addContent( args ) self.contents[#self.contents + 1] = TabContent.new( args ) end function TabContainer:render() local container = mw.html.create( 'div' ) :addClass( 'zdw-tabcontainer zdw-box' ) if self.args.id then container:attr( 'id', self.args.id ) 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 local defaults = {} if self.tabsLeft then local width = tonumber( self.args.left and self.args.left.width ) or 60 container:addClass( 'zdw-tabcontainer--hastabsleft' ) container:css( 'margin-left', tostring( width ) .. 'px' ) container:tag( 'div' ) :addClass( 'zdw-tabcontainer__tabset--left' ) :css( 'width', tostring( width ) .. 'px' ) :css( 'margin-left', '-' .. tostring( width + 10 ) .. 'px' ) :wikitext( self.tabsLeft:render() ) if self.tabsLeft.defaultTab > 0 then defaults[self.tabsLeft.selector] = self.tabsLeft.tabs[self.tabsLeft.defaultTab].selection end end if self.tabsTop then container:addClass( 'zdw-tabcontainer--hastabstop' ) container:tag( 'div' ) :addClass( 'zdw-tabcontainer__tabset--top' ) :wikitext( self.tabsTop:render() ) if self.tabsTop.defaultTab > 0 then defaults[self.tabsTop.selector] = self.tabsTop.tabs[self.tabsTop.defaultTab].selection end end local defaultContent = '' if defaults[0] then defaultContent = defaultContent .. defaults[0] end if defaults[1] then defaultContent = defaultContent .. ' ' .. defaults[1] end for _, c in ipairs( self.contents ) do if c.contentId == defaultContent then c.default = true end container:wikitext( c:render() ) end container:tag( 'div' ) :css( 'clear', 'both' ) return tostring( container ) end function TabSet.new( args ) args = args or {} return setmetatable( { target = args.target, selector = args.selector or '0', 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 ) end function TabSet:addTab( args ) local index = #self.tabs + 1 if index == self.defaultTab then args.default = true end self.tabs[index] = Tab.new( args ) end function TabSet:render() local tabSet = mw.html.create( 'ul' ) :addClass( 'zdw-tabset' ) :attr( 'data-tab-selector', self.selector ) :attr( 'data-tab-type', self.activation ) if self.target then tabSet:attr( 'data-tab-target', self.target ) end for _, tab in ipairs( self.tabs ) do tabSet:wikitext( tab:render() ) end return tostring( tabSet ) end function Tab.new( args ) args = args or {} return setmetatable( { selection = getRequiredArg( args, 'selection', 'Tab.new' ), label = args.label or args.selection, args = args }, Tab ) end function Tab:render() local tab = mw.html.create( 'li' ) :addClass( 'zdw-tab' ) :attr( 'data-tab-selection', self.selection:gsub( "%s", "" ) ) :wikitext( self.label ) if self.args.default then tab:addClass( 'active' ) tab:attr( 'data-tab-default', 'true' ) end return tostring( tab ) end function TabContent.new( args ) args = args or {} return setmetatable( { contentId = getRequiredArg( args, 'contentId', 'TabContent.new' ), content = args.content or args.contentId, args = args }, TabContent ) end function TabContent:render() local content = mw.html.create( 'div' ) :addClass( 'zdw-tabcontent' ) :attr( 'data-tab-content', self.contentId ) :wikitext( '\n' .. self.content ) -- newline is needed for tables, lists, etc. for k, v in ipairs( mw.text.split( self.contentId, ' ' ) ) do content:attr( 'data-tab-content-' .. (k - 1), v ) end if self.default then content:addClass( 'default' ) end if self.args.width then content:css( 'width', self.args.width .. 'px' ) end return tostring( content ) end local p = {} p.Tabs = TabContainer function p.tabs( frame ) local args = Args.fromFrame( frame ) local tabs = TabContainer.new( args ) -- set selectors if args.left then args.left.selector = '0' end if args.top then args.top.selector = args.combine and '0' or '1' end -- fix default tabs if combined -- l\t nil 0 # -- nil nil\0 nil\0 0\# -- 0 0\nil 0\0 0\# -- # #\0 #\0 #\0 if args.left and args.top and args.combine then if args.left.default and args.left.default ~= '0' then args.top.default = '0' end if not args.left.default then if args.top.default and args.top.default ~= '0' then args.left.default = 0 else args.top.default = 0 end end end if args.left then local left = tabs:leftTabs( args.left ) for _, tabArgs in ipairs( args.left ) do tabArgs = Args.getTable( tabArgs ) tabArgs.selection = Args.getValue( tabArgs ) left:addTab( tabArgs ) end end if args.top then local top = tabs:topTabs( args.top ) for _, tabArgs in ipairs( args.top ) do tabArgs = Args.getTable( tabArgs ) tabArgs.selection = Args.getValue( tabArgs ) top:addTab( tabArgs ) end end for contentId, contentArgs in pairs( args.content or {} ) do -- order doesn't matter here since only one is displayed at a time contentArgs = Args.getTable( contentArgs ) contentArgs.contentId = contentId contentArgs.content = Args.getValue( contentArgs ) tabs:addContent( contentArgs ) end return tabs:render() end return p