Module:Card
From ARC Raiders Wiki
More actions
Engine that renders specific card types defined as wrapper modules. Each wrapper supplies a configuration to Card.main, which returns a rendered module bound to that configuration.
local Card = {}
local BASE_STYLES = 'Template:Card/styles.css'
local BASE_BLOCK = 'card'
local function nilIfBlank(value)
local trimmed = value and mw.text.trim(value)
return trimmed ~= '' and trimmed or nil
end
Card.slots = {}
Card.slots.media = function(opts)
opts = opts or {}
local iconSize = opts.iconSize or 128
local imageSize = opts.imageSize or 200
return {
args = {'image', 'icon'},
render = function(parent, args, config)
if args.icon or args.image then
local div = parent:tag('div')
:addClass(BASE_BLOCK .. '__media')
:addClass(config.block .. '__media')
if args.icon then
div:addClass(BASE_BLOCK .. '__media--icon')
:addClass(config.block .. '__media--icon')
:wikitext(string.format('[[File:%s|%dpx|link=]]',
args.icon, iconSize))
else
div:wikitext(string.format('[[File:%s|%dpx|link=]]',
args.image, imageSize))
end
end
end,
}
end
Card.slots.title = function()
return {
args = {'title'},
render = function(parent, args, config)
if not args.title then return end
parent:tag('div')
:addClass(BASE_BLOCK .. '__title')
:addClass(config.block .. '__title')
:wikitext(args.title)
end,
}
end
Card.slots.description = function()
return {
args = {'description'},
render = function(parent, args, config)
if not args.description then return end
parent:tag('div')
:addClass(BASE_BLOCK .. '__description')
:addClass(config.block .. '__description')
:wikitext(args.description)
end,
}
end
Card.slots.interaction = function()
return {
args = {'link', 'title'},
render = function(parent, args, config)
local linkTarget = args.link or args.title
if not linkTarget then return end
parent:wikitext(string.format('[[%s|<span></span>]]', linkTarget))
end,
}
end
Card.modifiers = {}
Card.modifiers.titleOnly = function()
return {
name = 'title-only',
when = function(args) return args.title and not args.description end,
}
end
local function validateConfig(rawConfig)
for _, name in ipairs({'block', 'styles'}) do
if rawConfig[name] == nil then
error(string.format('Module:Card: missing required field "%s"', name), 2)
end
end
for i, slot in ipairs(rawConfig.slots or {}) do
if type(slot) ~= 'table' or type(slot.render) ~= 'function' then
error(string.format('Module:Card: slot %d must be a table with a render function', i), 2)
end
end
end
local function buildConfig(rawConfig)
local argNames = {}
local seen = {}
for _, slot in ipairs(rawConfig.slots or {}) do
for _, name in ipairs(slot.args or {}) do
if not seen[name] then
table.insert(argNames, name)
seen[name] = true
end
end
end
return {
block = rawConfig.block,
styles = rawConfig.styles,
slots = rawConfig.slots or {},
modifiers = rawConfig.modifiers or {},
argNames = argNames,
}
end
local function normalizeArgs(rawArgs, config)
local args = {}
for _, name in ipairs(config.argNames) do
args[name] = nilIfBlank(rawArgs[name])
end
return args
end
local function loadStyles(frame, config)
local baseStyles = frame:extensionTag{
name = 'templatestyles',
args = {src = BASE_STYLES}
}
local typeStyles = frame:extensionTag{
name = 'templatestyles',
args = {src = config.styles}
}
return baseStyles .. typeStyles
end
local function buildCard(args, config)
local block = config.block
local html = mw.html.create('div')
:addClass(BASE_BLOCK)
:addClass(block)
for _, modifier in ipairs(config.modifiers) do
if modifier.when(args) then
html:addClass(BASE_BLOCK .. '--' .. modifier.name)
html:addClass(block .. '--' .. modifier.name)
end
end
for _, slot in ipairs(config.slots) do
slot.render(html, args, config)
end
return html
end
function Card.main(rawConfig)
validateConfig(rawConfig)
local config = buildConfig(rawConfig)
return {
render = function(frame)
local args = normalizeArgs(frame:getParent().args, config)
local html = buildCard(args, config)
return loadStyles(frame, config) .. tostring(html)
end
}
end
return Card