Module:Collapsible list

This template produces a collapsible list. It is possible to set CSS styles for the "frame" (the <div>...</div> tags surrounding the list), for the list title, and for the list items. The template supports an unlimited number of list items.

Syntax

{{Collapsible list
 | expand = 
 | framestyle = 
 | titlestyle = 
 | title = 
 | liststyle = 
 | hlist = 
 | bullets = 
 | <!-- 1 = -->    <!--(First item in list; the "1 =" is usually not required)-->
 | <!-- 2 = -->    <!--(Second item in list; ditto)-->
 | <!-- 3 = -->    <!--(Third item in list; etc.)-->
 | <!-- etc -->
}}

Parameters

expand Include as |expand=on, |expand=true, etc to set the list's default state to expanded rather than collapsed.
framestyle Custom CSS styling applied the template overall (title and list).
titlestyle Custom CSS styling applied to the title.
title The list's title (always on view beside the list's [show/hide] link).
liststyle Custom CSS styling applied to the list (specifically, to the <ul>...</ul> tags delimiting the list).
hlist Include as |hlist=on, |hlist=true, etc to produce a horizontal rather than vertical list.
bullets Include as |bullets=on, |bullets=true, etc to place a bullet point before each list item.
Unnamed parameters
(first, second, third...)
The list items (in the order in which they will appear). If none are supplied, the template outputs nothing.

Examples

{{Collapsible list 
 | title = [[European Free Trade Association]] members 
 | [[Iceland]] 
 | [[Liechtenstein]] 
 | [[Norway]] 
 | [[Switzerland]]
}}

Example of a list without borders because it's within an infobox

In these examples, the fields leader_name2 and leader_name3 have been changed to use collapsible list.

City of Hamilton
Motto: Together Aspire - Together Achieve
Location in the province of Ontario, Canada
Location in the province of Ontario, Canada
Country Canada
Province Ontario
Incorporated June 9, 1846
Stjórn
 • Mayor Fred Eisenberger
 • City Council Hamilton, Ontario City Council
 • MPs
 • MPPs
{{Infobox settlement
|official_name     = City of Hamilton
 ...truncated... 
|leader_title      = [[Mayor]]
|leader_name       = [[Fred Eisenberger]]
|leader_title1     = [[City Council]]
|leader_name1      = [[Hamilton City Council]]
|leader_title2     = [[Member of Parliament (Canada)|MPs]]
|leader_name2      =
 {{Collapsible list
  |framestyle=border:none; padding:0; <!--Hides borders and improves row spacing-->
  |title=List of MPs
  |1=[[Dean Allison]] |2=[[Chris Charlton]] |3=[[David Christopherson]] |4=[[Wayne Marston]] |5=[[David Sweet]]
 }}
|leader_title3     = [[Member of Provincial Parliament (Ontario)|MPPs]]
|leader_name3      =
 {{Collapsible list
  |framestyle=border:none; padding:0; <!--as above-->
  |title=List of MPPs
  |1=[[Marie Bountrogianni]] |2=[[Andrea Horwath]] |3=[[Judy Marsales]] |4=[[Ted McMeekin]] |5=[[Jennifer Mossop]]
 }}
|established_title = [[Municipal corporation|Incorporated]]
|established_date  = June 9, 1846
 (...etc...)
}}

-- This module implements {{collapsible list}}.

local p = {}

local function getListItem( data )
    if not type( data ) == 'string' then
        return ''
    end
    return mw.ustring.format( '<li style="line-height: inherit; margin: 0">%s</li>', data )
end

-- Returns an array containing the keys of all positional arguments
-- that contain data (i.e. non-whitespace values).
local function getArgNums( args )
    local nums = {}
    for k, v in pairs( args ) do
        if type( k ) == 'number' and
            k >= 1 and
            math.floor( k ) == k and
            type( v ) == 'string' and
            mw.ustring.match( v, '%S' ) then
                table.insert( nums, k )
        end
    end
    table.sort( nums )
    return nums
end

-- Formats a list of classes, styles or other attributes.
local function formatAttributes( attrType, ... )
    local attributes = { ... }
    local nums = getArgNums( attributes )
    local t = {}
    for i, num in ipairs( nums ) do
        table.insert( t, attributes[ num ] )
    end
    if #t == 0 then
        return '' -- Return the blank string so concatenation will work.
    end
    return mw.ustring.format( ' %s="%s"', attrType, table.concat( t, ' ' ) )
end

local function buildList( args )
    -- Get the list items.
    local listItems = {}
    local argNums = getArgNums( args )
    for i, num in ipairs( argNums ) do
        table.insert( listItems, getListItem( args[ num ] ) )
    end
    if #listItems == 0 then
        return ''
    end
    listItems = table.concat( listItems )
    
    -- Get class, style and title data.
    local div1class = formatAttributes( 'class', 'NavFrame', not args.expand and 'collapsed' )
    local div1style = formatAttributes(
        'style',
        args.frame_style,
        args.framestyle,
        not ( args.frame_style or args.framestyle ) and 'border: none; padding: 0;'
    )
    local div2class = formatAttributes( 'class', 'NavHead' )
    local div2style = formatAttributes(
        'style',
        'font-size: 105%;',
        args.title_style,
        args.titlestyle,
        not ( args.title_style or args.titlestyle ) and 'background: transparent; text-align: left;'
    )
    local title = args.title or 'List'
    local ulclass = formatAttributes( 'class', 'NavContent', args.hlist and 'hlist' )
    local ulstyle = formatAttributes( 
        'style',
        not args.bullets and 'list-style: none none; margin-left: 0;',
        args.list_style,
        args.liststyle,
        not ( args.list_style or args.liststyle ) and 'text-align: left;',
        'font-size: 105%; margin-top: 0; margin-bottom: 0; line-height: inherit;'
    )
    
    -- Build the list.
    return mw.ustring.format( 
        '<div%s%s>\n<div%s%s>%s</div>\n<ul%s%s>%s</ul>\n</div>',
        div1class, div1style, div2class, div2style, title, ulclass, ulstyle, listItems
    )
end

function p.main( frame )
    local origArgs
    if frame == mw.getCurrentFrame() then
        origArgs = frame:getParent().args
        for k, v in pairs( frame.args ) do
            origArgs = frame.args
            break
        end
    else
        origArgs = frame
    end
    
    local args = {}
    for k, v in pairs( origArgs ) do
        if type( k ) == 'number' or v ~= '' then
            args[ k ] = v
        end
    end
    return buildList( args )
end

return p