Module:InfoboxNPC: Difference between revisions
Jump to navigation
Jump to search
mNo edit summary |
mNo edit summary Tag: Reverted |
||
| Line 2: | Line 2: | ||
-- Define the CSS variables/hex codes as Lua constants for use in inline styles | -- Define the CSS variables/hex codes as Lua constants for use in inline styles | ||
local STYLE_FLOATING = 'float: right !important; clear: right !important; | -- Cleaner, more modern design | ||
local STYLE_HEADER = 'background: linear-gradient(135deg, # | local STYLE_FLOATING = 'float: right !important; clear: right !important; width: 280px !important; margin: 0 0 16px 16px !important; border: 1px solid #cbd5e0 !important; border-radius: 8px !important; background: linear-gradient(180deg, #ffffff 0%, #f8fafc 100%) !important; font-size: 14px !important; box-shadow: 0 4px 12px rgba(0, 0, 0, 0.08) !important;' | ||
local STYLE_DATAROW = 'display: flex !important; justify-content: space-between !important; padding: | local STYLE_HEADER = 'background: linear-gradient(135deg, #2c5282 0%, #3182ce 100%) !important; color: white !important; padding: 12px 15px !important; text-align: center !important; font-weight: 600 !important; font-size: 1.4em !important; letter-spacing: 0.5px !important; border-top-left-radius: 7px !important; border-top-right-radius: 7px !important;' | ||
local STYLE_DATALABEL = 'font-weight: 600 !important; color: # | local STYLE_DATAROW = 'display: flex !important; justify-content: space-between !important; padding: 8px 12px !important; border-bottom: 1px solid #e2e8f0 !important;' | ||
local STYLE_DATAVALUE = 'color: # | local STYLE_DATALABEL = 'font-weight: 600 !important; color: #4a5568 !important; width: 35% !important; text-align: left !important;' | ||
local STYLE_DATAVALUE = 'color: #2d3748 !important; text-align: right !important; width: 65% !important; line-height: 1.4;' | |||
-- Helper function to properly handle wikilinks | -- Helper function to properly handle wikilinks | ||
| Line 14: | Line 15: | ||
end | end | ||
local frame = mw.getCurrentFrame() | local frame = mw.getCurrentFrame() | ||
| Line 44: | Line 43: | ||
return '' | return '' | ||
else | else | ||
return '' | |||
return | |||
end | end | ||
end | end | ||
-- Function to create a section divider | -- Function to create a section divider (only used for schedule) | ||
local function make_section(title) | local function make_section(title) | ||
return string.format( | return string.format( | ||
'<div | '<div style="padding: 8px 12px; background-color: #edf2f7 !important; color: #2d3748 !important; font-weight: 600; font-size: 0.95em; border-top: 1px solid #e2e8f0; border-bottom: 1px solid #e2e8f0; margin-top: 0; text-transform: uppercase; letter-spacing: 0.5px;">%s</div>', | ||
title | title | ||
) | ) | ||
| Line 81: | Line 73: | ||
if image and image ~= '' then | if image and image ~= '' then | ||
local image_link = string.format('[[File:%s|250px|alt=%s|class=npc-portrait]]', image, name) | local image_link = string.format('[[File:%s|250px|alt=%s|class=npc-portrait]]', image, name) | ||
table.insert(html, string.format('<div | table.insert(html, string.format('<div style="padding: 15px !important; text-align: center !important; background-color: #ffffff; border-bottom: 1px solid #e2e8f0;">%s</div>', image_link)) | ||
end | end | ||
-- Start Quick Facts Section | -- Start Quick Facts Section - NO SECTION HEADER, just start with data | ||
-- Core Character Data Rows | |||
local has_core_data = false | |||
-- | -- Check which core data fields exist | ||
local core_fields = { | |||
{label = 'Gender', value = args.gender}, | |||
{label = 'Age', value = args.age}, | |||
{label = 'Race', value = args.race}, | |||
{label = 'Role', value = args.role}, | |||
{label = 'Location', value = args.location} | |||
} | |||
for _, field in ipairs(core_fields) do | |||
if field.value and field.value ~= '' then | |||
if not has_core_data then | |||
has_core_data = true | |||
end | |||
table.insert(html, make_data_row(field.label, field.value, false)) | |||
end | |||
end | end | ||
| Line 104: | Line 101: | ||
local has_schedule = args.schedule1 or args.schedule2 or args.schedule3 | local has_schedule = args.schedule1 or args.schedule2 or args.schedule3 | ||
if has_schedule then | if has_schedule then | ||
-- Add a divider before schedule | |||
table.insert(html, make_section('Daily Schedule')) | table.insert(html, make_section('Daily Schedule')) | ||
-- Create | -- Create schedule items | ||
local schedule_html = '<div style="padding: | local schedule_html = '<div style="padding: 0 12px 8px 12px; font-size: 0.9em;">' | ||
local schedules = {} | |||
if args.schedule1 and args.schedule1 ~= '' then | if args.schedule1 and args.schedule1 ~= '' then | ||
table.insert(schedules, args.schedule1) | |||
end | end | ||
if args.schedule2 and args.schedule2 ~= '' then | if args.schedule2 and args.schedule2 ~= '' then | ||
table.insert(schedules, args.schedule2) | |||
end | end | ||
if args.schedule3 and args.schedule3 ~= '' then | if args.schedule3 and args.schedule3 ~= '' then | ||
schedule_html = schedule_html .. '<div style="padding: | table.insert(schedules, args.schedule3) | ||
end | |||
for i, schedule in ipairs(schedules) do | |||
schedule_html = schedule_html .. string.format( | |||
'<div style="padding: 6px 0; border-bottom: %s;">' .. | |||
'<div style="font-weight: 600; color: #4a5568; font-size: 0.85em;">%s</div>' .. | |||
'<div style="color: #718096; margin-top: 2px;">%s</div>' .. | |||
'</div>', | |||
i == #schedules and 'none' or '1px dotted #e2e8f0', | |||
schedule:match('%[(.-)%]') or 'Time', -- Extract time from [time] | |||
schedule:gsub('%[.-%]%s*', '') or schedule -- Remove time brackets | |||
) | |||
end | end | ||
| Line 123: | Line 134: | ||
end | end | ||
-- Metadata note (for internal references) | -- Metadata note (for internal references) - only if exists | ||
if args.note and args.note ~= '' then | if args.note and args.note ~= '' then | ||
table.insert(html, make_section('Notes')) | table.insert(html, make_section('Notes')) | ||
table.insert(html, string.format('<div style="padding: | table.insert(html, string.format( | ||
'<div style="padding: 10px 12px; font-size: 0.85em; color: #718096; font-style: italic; background-color: #f7fafc; border-radius: 4px; margin: 0 12px 10px 12px; line-height: 1.5;">%s</div>', | |||
mw.text.nowiki(args.note) | |||
)) | |||
end | end | ||
Revision as of 10:40, 5 February 2026
Documentation for this module may be created at Module:InfoboxNPC/doc
local p = {}
-- Define the CSS variables/hex codes as Lua constants for use in inline styles
-- Cleaner, more modern design
local STYLE_FLOATING = 'float: right !important; clear: right !important; width: 280px !important; margin: 0 0 16px 16px !important; border: 1px solid #cbd5e0 !important; border-radius: 8px !important; background: linear-gradient(180deg, #ffffff 0%, #f8fafc 100%) !important; font-size: 14px !important; box-shadow: 0 4px 12px rgba(0, 0, 0, 0.08) !important;'
local STYLE_HEADER = 'background: linear-gradient(135deg, #2c5282 0%, #3182ce 100%) !important; color: white !important; padding: 12px 15px !important; text-align: center !important; font-weight: 600 !important; font-size: 1.4em !important; letter-spacing: 0.5px !important; border-top-left-radius: 7px !important; border-top-right-radius: 7px !important;'
local STYLE_DATAROW = 'display: flex !important; justify-content: space-between !important; padding: 8px 12px !important; border-bottom: 1px solid #e2e8f0 !important;'
local STYLE_DATALABEL = 'font-weight: 600 !important; color: #4a5568 !important; width: 35% !important; text-align: left !important;'
local STYLE_DATAVALUE = 'color: #2d3748 !important; text-align: right !important; width: 65% !important; line-height: 1.4;'
-- Helper function to properly handle wikilinks
local function format_wikitext(value)
if not value or value == '' then
return ''
end
local frame = mw.getCurrentFrame()
-- Check if it looks like a wikilink
if string.find(value, '%[%[') and string.find(value, '%]%]') then
-- It's a wikilink, expand it through the parser
return frame:preprocess(value)
else
-- Plain text, escape it
return mw.text.nowiki(value)
end
end
-- Function to generate a single data row
local function make_data_row(label, value, is_optional)
if value and value ~= '' then
local formatted_value = format_wikitext(value)
return string.format(
'<div style="%s"><span style="%s">%s:</span> <span style="%s">%s</span></div>',
STYLE_DATAROW,
STYLE_DATALABEL,
label,
STYLE_DATAVALUE,
formatted_value
)
elseif is_optional then
return ''
else
return ''
end
end
-- Function to create a section divider (only used for schedule)
local function make_section(title)
return string.format(
'<div style="padding: 8px 12px; background-color: #edf2f7 !important; color: #2d3748 !important; font-weight: 600; font-size: 0.95em; border-top: 1px solid #e2e8f0; border-bottom: 1px solid #e2e8f0; margin-top: 0; text-transform: uppercase; letter-spacing: 0.5px;">%s</div>',
title
)
end
-- Main function to create the infobox HTML
function p.infobox(frame)
local args = frame:getParent().args
local name = args.name or 'Unknown Character'
local image = args.image
local html = {}
-- Start the main infobox container
table.insert(html, string.format('<div class="npc-infobox" style="%s">', STYLE_FLOATING))
-- Header/Title
table.insert(html, string.format('<div class="npc-header" style="%s">%s</div>', STYLE_HEADER, name))
-- Image Section
if image and image ~= '' then
local image_link = string.format('[[File:%s|250px|alt=%s|class=npc-portrait]]', image, name)
table.insert(html, string.format('<div style="padding: 15px !important; text-align: center !important; background-color: #ffffff; border-bottom: 1px solid #e2e8f0;">%s</div>', image_link))
end
-- Start Quick Facts Section - NO SECTION HEADER, just start with data
-- Core Character Data Rows
local has_core_data = false
-- Check which core data fields exist
local core_fields = {
{label = 'Gender', value = args.gender},
{label = 'Age', value = args.age},
{label = 'Race', value = args.race},
{label = 'Role', value = args.role},
{label = 'Location', value = args.location}
}
for _, field in ipairs(core_fields) do
if field.value and field.value ~= '' then
if not has_core_data then
has_core_data = true
end
table.insert(html, make_data_row(field.label, field.value, false))
end
end
-- Start Schedule Section (if schedule data exists)
local has_schedule = args.schedule1 or args.schedule2 or args.schedule3
if has_schedule then
-- Add a divider before schedule
table.insert(html, make_section('Daily Schedule'))
-- Create schedule items
local schedule_html = '<div style="padding: 0 12px 8px 12px; font-size: 0.9em;">'
local schedules = {}
if args.schedule1 and args.schedule1 ~= '' then
table.insert(schedules, args.schedule1)
end
if args.schedule2 and args.schedule2 ~= '' then
table.insert(schedules, args.schedule2)
end
if args.schedule3 and args.schedule3 ~= '' then
table.insert(schedules, args.schedule3)
end
for i, schedule in ipairs(schedules) do
schedule_html = schedule_html .. string.format(
'<div style="padding: 6px 0; border-bottom: %s;">' ..
'<div style="font-weight: 600; color: #4a5568; font-size: 0.85em;">%s</div>' ..
'<div style="color: #718096; margin-top: 2px;">%s</div>' ..
'</div>',
i == #schedules and 'none' or '1px dotted #e2e8f0',
schedule:match('%[(.-)%]') or 'Time', -- Extract time from [time]
schedule:gsub('%[.-%]%s*', '') or schedule -- Remove time brackets
)
end
schedule_html = schedule_html .. '</div>'
table.insert(html, schedule_html)
end
-- Metadata note (for internal references) - only if exists
if args.note and args.note ~= '' then
table.insert(html, make_section('Notes'))
table.insert(html, string.format(
'<div style="padding: 10px 12px; font-size: 0.85em; color: #718096; font-style: italic; background-color: #f7fafc; border-radius: 4px; margin: 0 12px 10px 12px; line-height: 1.5;">%s</div>',
mw.text.nowiki(args.note)
))
end
-- End container
table.insert(html, '</div>') -- End npc-infobox
return table.concat(html, '\n')
end
return p