Module:ListGeneration

Revision as of 17:29, 8 July 2025 by MarkWD (talk | contribs) (// via Wikitext Extension for VSCode)

Documentation for this module may be created at Module:ListGeneration/doc

-- Module:ListGeneration
-- A centralized and flexible module for generating various types of lists from delimited strings.
--
-- This module is designed to replace fragmented list generation logic across the wiki,
-- providing a single, powerful function to create multiple list formats.
--
-- Features:
--   * Handles semicolon-delimited strings automatically.
--   * Supports multiple list modes:
--     - 'bullet': Standard HTML unordered list (<ul>).
--     - 'bullet_custom': Unordered list with a custom Unicode character as the bullet.
--     - 'invisible': A bulleted list with the 'no-bullets' CSS class for an unstyled list.
--     - 'comma': A simple, comma-separated string of items.
--   * Allows for custom CSS classes on the list container.
--   * Provides an 'itemHook' for custom, per-item processing and formatting.
--
-- Usage:
--   local ListGeneration = require('Module:ListGeneration')
--   local options = {
--     mode = 'bullet',
--     listClass = 'my-custom-list',
--     itemHook = function(item) return '<b>' .. item .. '</b>' end
--   }
--   local htmlList = ListGeneration.createList('item1; item2; item3', options)

local p = {}

-- Dependencies
local NormalizationText = require('Module:NormalizationText')
local ConfigRepository

-- Core list creation function
-- @param input string The semicolon-delimited string of list items.
-- @param options table A table of configuration options.
--   - mode (string): 'bullet' (default), 'bullet_custom', 'invisible', 'comma'.
--   - bulletChar (string): The character to use for custom bullets.
--   - listClass (string): A custom CSS class for the <ul> element.
--   - itemHook (function): A function to process each list item.
-- @return string The formatted list as an HTML string or comma-separated text.
function p.createList(input, options)
    if not input or (type(input) == 'string' and input == '') or (type(input) == 'table' and #input == 0) then
        return ''
    end

    -- Initialize options with defaults
    options = options or {}
    local mode = options.mode or 'bullet'
    local bulletChar = options.bulletChar
    local listClass = options.listClass
    local itemHook = options.itemHook

    -- If input is a string, split it. Otherwise, assume it's a pre-split table.
    local items
    if type(input) == 'string' then
        items = NormalizationText.splitMultiValueString(input)
    else
        items = input
    end

    if #items == 0 then
        return ''
    end

    -- Process items with the hook if provided, allowing for custom formatting
    if type(itemHook) == 'function' then
        local processedItems = {}
        for i, item in ipairs(items) do
            local processed = itemHook(item)
            if processed then
                table.insert(processedItems, processed)
            end
        end
        items = processedItems
    end

    -- Handle comma-separated mode for simple text lists
    if mode == 'comma' then
        return table.concat(items, ', ')
    end

    -- Handle wikitext bullet list
    if mode == 'bullet' then
        ConfigRepository = ConfigRepository or require('Module:ConfigRepository')
        local bulletChar = ConfigRepository.p.default_bullet or '*'
        local listItems = {}
        for _, itemData in ipairs(items) do
            local content = type(itemData) == 'table' and itemData.content or itemData
            if content and content ~= '' then
                table.insert(listItems, bulletChar .. ' ' .. content)
            end
        end
        return table.concat(listItems, '\n')
    end

    -- Handle HTML list-based modes
    if mode == 'bullet_custom' or mode == 'invisible' then
        local listItems = {}
        local listStyle = ''

        -- Handle custom bullet character via inline style
        if mode == 'bullet_custom' and bulletChar and bulletChar ~= '' then
            listStyle = string.format(' style="list-style-type: \'%s\';"', bulletChar)
        end

        -- Build the individual list items
        for _, itemData in ipairs(items) do
            local content = ''
            local itemClass = ''
            if type(itemData) == 'table' then
                content = itemData.content or ''
                if itemData.class and itemData.class ~= '' then
                    itemClass = string.format(' class="%s"', itemData.class)
                end
            elseif type(itemData) == 'string' then
                content = itemData
            end

            if content ~= '' then
                table.insert(listItems, string.format('<li%s>%s</li>', itemClass, content))
            end
        end

        -- Build the final CSS class string for the <ul> element
        local finalClass = 'template-list'
        if mode == 'invisible' then
            finalClass = finalClass .. ' no-bullets'
        end
        if listClass and listClass ~= '' then
            finalClass = finalClass .. ' ' .. listClass
        end

        return string.format('<ul class="%s"%s>%s</ul>', finalClass, listStyle, table.concat(listItems, ''))
    end

    -- Fallback for a single item or an unknown mode
    if #items == 1 then
        return items[1]
    end

    -- Default fallback for multiple items with an unknown mode is a comma-separated list
    return table.concat(items, ', ')
end

return p