Module:Tabs: Difference between revisions

Want an adless experience? Log in or Create an account.
2,235 bytes added ,  July 26, 2020
I knew the logic was bad but not that bad. breaking it into two ifs for clarity
(split contentId into separate attributes for each selector)
(I knew the logic was bad but not that bad. breaking it into two ifs for clarity)
 
(20 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 69: Line 69:
   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
  local defaults = {}


   if self.tabsLeft then
   if self.tabsLeft then
Line 76: 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
     if self.tabsLeft.defaultTab > 0 then
      defaults[self.tabsLeft.selector] = self.tabsLeft.tabs[self.tabsLeft.defaultTab].selection
    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
     if self.tabsTop.defaultTab > 0 then
      defaults[self.tabsTop.selector] = self.tabsTop.tabs[self.tabsTop.defaultTab].selection
    end
   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
   for _, c in ipairs( self.contents ) do
     if c.contentId == defaultContent then c.default = true end
     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 117: 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 126: 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 133: 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 147: 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 156: 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 )


Line 170: 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 179: 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( '\n' .. self.content )


   for k, v in ipairs( mw.text.split( self.contentId ) ) do
   if type( self.selection ) == 'table' then
    content:attr( 'data-tab-content-' .. (k - 1), v )
    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
   end


Line 204: 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