Module:Tabs: Difference between revisions

Want an adless experience? Log in or Create an account.
1,536 bytes added ,  June 23, 2020
refactored with a single entrypoint accepting flattened-tree args
(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 = getRequiredArg( args, 'id', 'TabContainer.new' ),
     id = args.id or Guid(),
     content = args[1],
     contents = {},
    tabsTop = args.top,
    tabsLeft = args.left,
     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:wikitext( self.content )
 
   container:tag( 'div' )
   container:tag( 'div' )
     :css( 'clear', 'both' )
     :css( 'clear', 'both' )
Line 44: Line 84:
   return tostring( container )
   return tostring( container )
end
end
local TabSet = {}
TabSet.__index = TabSet


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',
     tabs = getRequiredArg( args, 1, 'TabSet.new' )
     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( self.tabs )
 
  for _, tab in ipairs( self.tabs ) do
     tabSet:wikitext( tab:render() )
  end


   return tostring( tabSet )
   return tostring( tabSet )
end
end
local Tab = {}
Tab.__index = Tab


function Tab.new( args )
function Tab.new( args )
   return setmetatable( {
   return setmetatable( {
     selection = getRequiredArg( args, 1, 'Tab.new' ),
     selection = getRequiredArg( args, 'selection', 'Tab.new' ),
     label = args[2] or args[1],
     label = args.label or args.selection,
     args = args
     args = args
   }, Tab )
   }, Tab )
Line 91: Line 135:
   return tostring( tab )
   return tostring( tab )
end
end
local TabContent = {}
TabContent.__index = TabContent


function TabContent.new( args )
function TabContent.new( args )
   return setmetatable( {
   return setmetatable( {
     contentId = getRequiredArg( args, 1, 'TabContent.new' ),
     contentId = getRequiredArg( args, 'contentId', 'TabContent.new' ),
     content = args[2] or args[1],
     content = args.content or args.contentId,
     args = args
     args = args
   }, TabContent )
   }, TabContent )
Line 122: Line 163:
local p = {}
local p = {}


p.TabContainer = TabContainer
p.Tabs = TabContainer
p.TabSet = TabSet
p.Tab = Tab
p.TabContent = TabContent


function p.tabs( frame )
function p.tabs( frame )
   local tabs = TabContainer.new( frame.args )
   local args = Args.parse( frame.args )
   return tabs:render()
   local tabs = TabContainer.new( args:values() )
end


function p.tabset( frame )
  if args.left then
  local tabset = TabSet.new( frame.args )
    local left = tabs:leftTabs( args.left:values() )
  return tabset:render()
    for _, leftArgs in ipairs( args.left ) do
end
      tabArgs = leftArgs:values()
      tabArgs.selection = leftArgs:val()
      left:tab( tabArgs )
    end
  end


function p.tab( frame )
  if args.top then
   local tab = Tab.new( frame.args )
    local topArgs = args.top:values()
  return tab:render()
    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


function p.content( frame )
   return tabs:render()
  local content = TabContent.new( frame.args )
   return content:render()
end
end


return p
return p