Module:Tabs: Difference between revisions

Want an adless experience? Log in or Create an account.
2,933 bytes added ,  July 26, 2020
I knew the logic was bad but not that bad. breaking it into two ifs for clarity
(replace arg vtabwidth with left_width)
(I knew the logic was bad but not that bad. breaking it into two ifs for clarity)
 
(24 intermediate revisions by the same user not shown)
Line 47: Line 47:
function TabContainer:addTabLeftWithContent( args )
function TabContainer:addTabLeftWithContent( args )
   -- normalize args so they can just be passed to everything. definitely a bad idea but...
   -- normalize args so they can just be passed to everything. definitely a bad idea but...
   args.selection = getRequiredArg( args, 'contentId', 'TabContainer:addTabLeft' )
   args.selection = getRequiredArg( args, 'contentId', 'TabContainer:addTabLeftWithContent' )
   self:leftTabs( args ):addTab( args )
   self:leftTabs( args ):addTab( args )
   self:addContent( args )
   self:addContent( args )
Line 54: Line 54:
function TabContainer:addTabTopWithContent( args )
function TabContainer:addTabTopWithContent( args )
   -- normalize args so they can just be passed to everything. definitely a bad idea but...
   -- normalize args so they can just be passed to everything. definitely a bad idea but...
   args.selection = getRequiredArg( args, 'contentId', 'TabContainer:addTabTop' )
   args.selection = getRequiredArg( args, 'contentId', 'TabContainer:addTabTopWithContent' )
   self:topTabs( args ):addTab( args )
   self:topTabs( args ):addTab( args )
   self:addContent( args )
   self:addContent( args )
Line 74: Line 74:
     container:addClass( 'zdw-tabcontainer--hastabsleft' )
     container:addClass( 'zdw-tabcontainer--hastabsleft' )
     container:css( 'margin-left', tostring( width ) .. 'px' )
     container:css( 'margin-left', tostring( width ) .. 'px' )
     container:tag( 'div' )
     local left = container:tag( 'div' )
       :addClass( 'zdw-tabcontainer__tabset--left' )
       :addClass( 'zdw-tabcontainer__tabset--left' )
       :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:render() )
       :wikitext( self.tabsLeft:render() )
    if self.args.height then left:css( 'height', self.args.height .. 'px' ) end
   end
   end


   if self.tabsTop then
   if self.tabsTop then
     container:addClass( 'zdw-tabcontainer--hastabstop' )
     container:addClass( 'zdw-tabcontainer--hastabstop' )
     container:tag( 'div' )
     local top = container:tag( 'div' )
       :addClass( 'zdw-tabcontainer__tabset--top' )
       :addClass( 'zdw-tabcontainer__tabset--top' )
       :wikitext( self.tabsTop:render() )
       :wikitext( self.tabsTop:render() )
    if self.args.width then top:css( 'width', self.args.width .. 'px' ) end
   end
   end


   for _, c in ipairs( self.contents ) do
   for _, c in ipairs( self.contents ) do
    if type( c.selection ) == 'string'
        and (self.tabsLeft and self.tabsLeft.defaultSelection == c.selection:gsub( "%s", "" )
        or self.tabsTop and self.tabsTop.defaultSelection == c.selection:gsub( "%s", "" )) then
      c.default = true
    elseif type( c.selection ) == 'table'
        and self.tabsLeft and self.tabsLeft.defaultSelection == c.selection.left:gsub( "%s", "" )
        and self.tabsTop and self.tabsTop.defaultSelection == c.selection.top:gsub( "%s", "" ) then
      c.default = true
    end
     container:wikitext( c:render() )
     container:wikitext( c:render() )
   end
   end
Line 102: Line 113:
   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,
     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 111: Line 122:
function TabSet:addTab( args )
function TabSet:addTab( args )
   local index = #self.tabs + 1
   local index = #self.tabs + 1
   if index == self.defaultTab then args.default = true end
   if index == self.defaultTab then
    self.defaultSelection = args.selection:gsub( "%s", "" )
    args.default = true
  end
   self.tabs[index] = Tab.new( args )
   self.tabs[index] = Tab.new( args )
end
end
Line 118: Line 132:
   local tabSet = mw.html.create( 'ul' )
   local tabSet = mw.html.create( 'ul' )
     :addClass( 'zdw-tabset' )
     :addClass( 'zdw-tabset' )
    :attr( 'data-tab-selector', self.selector )
     :attr( 'data-tab-type', self.activation )
     :attr( 'data-tab-type', self.activation )
   if self.target then tabSet:attr( 'data-tab-target', self.target ) end
   if self.target then tabSet:attr( 'data-tab-target', self.target ) end
  if self.selector then tabSet:attr( 'data-tab-selector', self.selector ) end


   for _, tab in ipairs( self.tabs ) do
   for _, tab in ipairs( self.tabs ) do
Line 132: Line 146:
   args = args or {}
   args = args or {}
   return setmetatable( {
   return setmetatable( {
     selection = getRequiredArg( args, 'selection', 'Tab.new' ),
     selection = getRequiredArg( args, 'selection', 'Tab.new' ):gsub( "%s", "" ),
     label = args.label or args.selection,
     label = args.label or args.selection,
     args = args
     args = args
Line 141: Line 155:
   local tab = mw.html.create( 'li' )
   local tab = mw.html.create( 'li' )
     :addClass( 'zdw-tab' )
     :addClass( 'zdw-tab' )
     :attr( 'data-tab-selection', self.selection:gsub( "%s", "" ) )
     :attr( 'data-tab-selection', self.selection )
     :wikitext( self.label )
     :wikitext( self.label )


   if self.args.default then
   if self.args.default then
    tab:addClass( 'active' )
     tab:attr( 'data-tab-default', 'true' )
     tab:attr( 'data-tab-default', 'true' )
   end
   end
Line 154: Line 169:
   args = args or {}
   args = args or {}
   return setmetatable( {
   return setmetatable( {
     contentId = getRequiredArg( args, 'contentId', 'TabContent.new' ),
     selection = getRequiredArg( args, 'selection', 'TabContent.new' ),
     content = args.content or args.contentId,
     content = args.content or args.selection,
     args = args
     args = args
   }, TabContent )
   }, TabContent )
Line 163: Line 178:
   local content = mw.html.create( 'div' )
   local content = mw.html.create( 'div' )
     :addClass( 'zdw-tabcontent' )
     :addClass( 'zdw-tabcontent' )
     :attr( 'data-tab-content', self.contentId )
     :wikitext( '\n' .. self.content ) -- newline is needed for tables, lists, etc.
     :wikitext( self.content )
 
  if type( self.selection ) == 'table' then
    for k, v in pairs( self.selection ) do
      content:attr( 'data-tab-content-' .. k, v:gsub( "%s", "" ) )
     end
  else
    content:attr( 'data-tab-content', self.selection:gsub( "%s", "" ) )
  end


   if self.contentId == 'default' then
   if self.default then
     content:addClass( 'default' )
     content:addClass( 'default' )
   end
   end
Line 184: Line 206:
   local args = Args.fromFrame( frame )
   local args = Args.fromFrame( frame )
   local tabs = TabContainer.new( args )
   local tabs = TabContainer.new( args )
  -- set selectors
  if args.left then args.left.selector = args.top and not args.combine and 'left' or nil end
  if args.top then args.top.selector = args.left and not args.combine and 'top' or nil 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
   if args.left then
     local left = tabs:leftTabs( args.left )
     local left = tabs:leftTabs( args.left )
     for _, leftArgs in ipairs( args.left ) do
     for _, tabArgs in ipairs( args.left ) do
       leftArgs = Args.getTable( leftArgs )
       tabArgs = Args.getTable( tabArgs )
       leftArgs.selection = Args.getValue( leftArgs )
       tabArgs.selection = Args.getValue( tabArgs )
       left:addTab( leftArgs )
       left:addTab( tabArgs )
     end
     end
   end
   end


   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
     for _, tabArgs in ipairs( args.top ) do
     local top = tabs:topTabs( topArgs )
       tabArgs = Args.getTable( tabArgs )
     for _, topArgs in ipairs( args.top ) do
       tabArgs.selection = Args.getValue( tabArgs )
       topArgs = Args.getTable( topArgs )
       top:addTab( tabArgs )
       topArgs.selection = Args.getValue( topArgs )
       top:addTab( topArgs )
     end
     end
   end
   end
    
    
   for contentId, contentArgs in pairs( args.content or {} ) do -- order doesn't matter here since only one is displayed at a time
   -- Special case where content is transcluded from subpages rather than provided directly
    contentArgs = Args.getTable( contentArgs )
  -- For now, assumes 2D mode (both left and top tabs)
    contentArgs.contentId = contentId
  if Args.getValue( args.content or {} ) == 'transclude' then
    contentArgs.content = Args.getValue( contentArgs )
    for _, leftArgs in ipairs( args.left ) do
    tabs:addContent( contentArgs )
      local leftSelection = Args.getValue( leftArgs )
      for _, topArgs in ipairs( args.top ) do
        local topSelection = Args.getValue( topArgs )
        local template = mw.title.getCurrentTitle().text .. '/' .. leftSelection .. '/' .. topSelection
 
        -- add edit link, aligned to transcluded table's header
        local editlink = mw.html.create( 'span' )
          :addClass( 'edit plainlinks' )
          :css( 'position', 'absolute' ) -- css to align with table header
          :css( 'top', '12px' )
          :css( 'right', '15px' )
          :wikitext( '[' .. tostring( mw.uri.fullUrl( template, { action = 'edit' } ) ) .. ' [edit]]' )
 
        -- add the transcluded content
        tabs:addContent{
          selection = { left = leftSelection, top = topSelection },
          content = mw.getCurrentFrame():expandTemplate{ title = ':' .. template } .. tostring( editlink )
        }
      end
    end
  else -- content is provided by the caller
    for contentSelection, contentArgs in pairs( args.content or {} ) do -- order doesn't matter here since only one is displayed at a time
      local leftSelection, topSelection = string.match( contentSelection, '(.+) (.+)' )
      contentArgs = Args.getTable( contentArgs )
      contentArgs.selection = leftSelection and { left = leftSelection, top = topSelection } or contentSelection
      contentArgs.content = Args.getValue( contentArgs )
      tabs:addContent( contentArgs )
    end
   end
   end