Module:Tabs: Difference between revisions

From Zelda Dungeon Wiki
Jump to navigation Jump to search
Want an adless experience? Log in or Create an account.
No edit summary
(use the right variables...)
Line 117: Line 117:
   return setmetatable( {
   return setmetatable( {
     target = args.target,
     target = args.target,
     selector = args.selector and (tonumber(args.selector) or error('invalid arg: selector must be a number')) or 0,
     selector = args.selector 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,
     defaultTab = args.default and (tonumber(args.default) or error('invalid arg: default must be a number')) or 1,
Line 180: Line 180:
     :addClass( 'zdw-tabcontent' )
     :addClass( 'zdw-tabcontent' )
     :attr( 'data-tab-content', self.contentId )
     :attr( 'data-tab-content', self.contentId )
     :wikitext( '\n' .. self.content )
     :wikitext( '\n' .. self.content ) -- newline is needed for tables, lists, etc.


   for k, v in ipairs( mw.text.split( self.contentId, ' ' ) ) do
   for k, v in ipairs( mw.text.split( self.contentId, ' ' ) ) do
Line 205: Line 205:
   local tabs = TabContainer.new( args )
   local tabs = TabContainer.new( args )


   -- fix default tabs if left and top share the same selector
   -- set selectors
   if args.left and args.top and (args.left.selector or '0') == (args.top.selector or '0') then
   if args.left then args.left.selector = '0' end
     if args.left.selector and args.left.selector ~= '0' then args.top.selector = 0 end
  if args.top then args.top.selector = args.combine and '0' or '1' end
     if not args.left.selector then
 
       if args.top.selector and args.top.selector ~= '0' then
  -- fix default tabs if combined
         args.left.selector = 0
  -- 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
       else
         args.top.selector = 0
         args.top.default = 0
       end
       end
     end
     end
Line 227: Line 235:


   if args.top then
   if args.top then
    local topArgs = args.top
     local top = tabs:topTabs( args.top )
    if args.left and not topArgs.selector then topArgs.selector = 1 end -- default to 2D behavior if both sets are present
    if args.left and args.left.selector == topArgs.selector and args.left.default ~= 0 then topArgs.default = 0 end
     local top = tabs:topTabs( topArgs )
     for _, tabArgs in ipairs( args.top ) do
     for _, tabArgs in ipairs( args.top ) do
       tabArgs = Args.getTable( tabArgs )
       tabArgs = Args.getTable( tabArgs )

Revision as of 07:34, 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 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