Module:Tabs: Difference between revisions
Jump to navigation
Jump to search
Want an adless experience? Log in or Create an account.
(determine default content based on default tabs, rather than specifying it. might add support for 'nojs' content later.) |
(split contentId into separate attributes for each selector) |
||
Line 181: | Line 181: | ||
:attr( 'data-tab-content', self.contentId ) | :attr( 'data-tab-content', self.contentId ) | ||
:wikitext( '\n' .. self.content ) | :wikitext( '\n' .. self.content ) | ||
for k, v in ipairs( mw.text.split( self.contentId ) ) do | |||
content:attr( 'data-tab-content-' .. (k - 1), v ) | |||
end | |||
if self.default then | if self.default then |
Revision as of 05:55, June 27, 2020
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 and (tonumber(args.selector) or error('invalid arg: selector must be a number')) 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 ) 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 ) if args.left then local left = tabs:leftTabs( args.left ) for _, leftArgs in ipairs( args.left ) do leftArgs = Args.getTable( leftArgs ) leftArgs.selection = Args.getValue( leftArgs ) left:addTab( leftArgs ) end end if args.top then local topArgs = args.top if args.left and not topArgs.selector then topArgs.selector = 1 end -- default to 2D behavior if both sets are present local top = tabs:topTabs( topArgs ) for _, topArgs in ipairs( args.top ) do topArgs = Args.getTable( topArgs ) topArgs.selection = Args.getValue( topArgs ) top:addTab( topArgs ) 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