Module:ListGeneration
Appearance
Documentation for this module may be created at Module:ListGeneration/doc
--[[
* Name: ListGeneration
* Author: Mark W. Datysgeld
* Description: Centralized and flexible module for generating various types of lists from delimited strings, designed to replace fragmented list generation logic
* Notes: Handles semicolon-delimited strings automatically; supports multiple list modes (bullet, bullet_custom, invisible, comma); allows custom CSS classes on list container; provides itemHook for custom per-item processing and formatting; accepts both string input (auto-split) and pre-split table input
]]
local p = {}
-- Dependencies
local NormalizationText = require('Module:NormalizationText')
-- 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
-- Default to splitting only by semicolon. This is a safer default than also splitting by "and", which can break up valid entity names.
-- Callers that need more complex splitting logic should pre-split the string and pass a table instead.
local semicolonOnlyPattern = {{pattern = ";%s*", replacement = ";"}}
items = NormalizationText.splitMultiValueString(input, semicolonOnlyPattern)
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
-- If there's only one item and no special formatting is needed, return it directly without list formatting
if #items == 1 then
local singleItem = items[1]
local hasCustomBullet = (mode == 'bullet_custom' and bulletChar and bulletChar ~= '')
local hasSpecialClass = (type(singleItem) == 'table' and singleItem.class and singleItem.class ~= '')
-- Only bypass list formatting if there's no custom bullet AND no special class
if not hasCustomBullet and not hasSpecialClass then
if type(singleItem) == 'table' then
return singleItem.content or ''
else
return singleItem
end
end
-- If we have custom bullets or special classes, continue to list formatting below
end
-- Handle comma-separated mode for simple text lists
if mode == 'comma' then
return table.concat(items, ', ')
end
-- Handle all HTML list-based modes
if mode == 'bullet' or 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 .. ' list-style-none'
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