From Arms of God Wiki
bot: operator iteration 2026-06-10 |
bot: operator iteration 2026-06-10d (hover tooltips / filter chips + DPS sort / damage-type hubs / stat reverse-lookup pages) |
||
| Line 49: | Line 49: | ||
local slug = rec.slug or rec.name or rec.id | local slug = rec.slug or rec.name or rec.id | ||
if _isEmpty(slug) then return '—' end | if _isEmpty(slug) then return '—' end | ||
return '[[' .. tostring(slug) .. '|' .. tostring(rec.name or slug) .. ']]' | return '<span class="wm-tip" data-tip-title="' .. tostring(slug) | ||
.. '">[[' .. tostring(slug) .. '|' .. tostring(rec.name or slug) .. ']]</span>' | |||
end | end | ||
-- Plan-declared infobox label first; raw record field as fallback | -- Plan-declared infobox label first; raw record field as fallback | ||
| Line 106: | Line 107: | ||
-- Body rows. | -- Body rows. | ||
for _, rec in ipairs(ordered) do | for _, rec in ipairs(ordered) do | ||
table.insert(rows, '|-') | local attrs = '' | ||
local tier = rec.infobox and rec.infobox.Tier | |||
if tier ~= nil and tier ~= '' then | |||
attrs = ' data-tier="' .. tostring(tier) .. '"' | |||
end | |||
table.insert(rows, '|-' .. attrs) | |||
local cells = {} | local cells = {} | ||
for _, col in ipairs(columns) do | for _, col in ipairs(columns) do | ||
Revision as of 06:08, 10 June 2026
Shared helper that renders sortable index tables from Data: pages.
Library module shipped by the publishing bot; shared across categories. Bot-published — edits are overwritten on re-publish.
-- Module:Index — sortable-wikitable renderer for category index pages.
-- Public API: Index.render(records, opts).
-- API contract:
-- records : list of Phase-4.5 flattened records (NOT a dict).
-- opts : { columns = {{name, source}, ...}, thumbnail_size, sort_default }
-- Column `source` semantics:
-- "thumbnail" → [[File:<rec.icon>|<thumbnail_size>px|alt=<rec.name>]] (em-dash on empty icon)
-- "name" → rec.name (bare)
-- "link" → [[<rec.slug>|<rec.name>]]
-- any other → rec.infobox[source], fallback rec[source], fallback em-dash
-- Maintainer don'ts: never recompute a slug here; never call Slugs.lookup;
-- never gsub a display name. Plans use the infobox `field` label as `source`,
-- not the raw entity-source field name.
local p = {}
local function _isEmpty(v)
return v == nil or v == ''
end
local function _flatten(v)
if type(v) ~= 'table' then return tostring(v) end
local parts = {}
for i = 1, #v do
local item = v[i]
if type(item) == 'table' and item.name then
table.insert(parts, tostring(item.name))
else
table.insert(parts, tostring(item))
end
end
return table.concat(parts, ', ')
end
local function _cell(rec, source, opts)
if source == 'thumbnail' then
local icon = rec.icon
if _isEmpty(icon) then return '—' end
return string.format('[[File:%s|%dpx|alt=%s]]',
tostring(icon),
opts.thumbnail_size or 48,
tostring(rec.name or rec.id or ''))
end
if source == 'name' then
if _isEmpty(rec.name) then return '—' end
return tostring(rec.name)
end
if source == 'link' then
local slug = rec.slug or rec.name or rec.id
if _isEmpty(slug) then return '—' end
return '<span class="wm-tip" data-tip-title="' .. tostring(slug)
.. '">[[' .. tostring(slug) .. '|' .. tostring(rec.name or slug) .. ']]</span>'
end
-- Plan-declared infobox label first; raw record field as fallback
-- (covers top-level scalars like `family_key`).
local v = (rec.infobox and rec.infobox[source])
if _isEmpty(v) then v = rec[source] end
if _isEmpty(v) then return '—' end
return _flatten(v)
end
-- Stable sort by the column whose name matches sort_default. Sorting
-- happens on the textual cell value (case-insensitive). Records that
-- render '—' sort last regardless of column.
local function _sort(records, columns, sort_default, opts)
if not sort_default or #records < 2 then return records end
local sort_col
for _, col in ipairs(columns or {}) do
if col.name == sort_default then sort_col = col; break end
end
if not sort_col then return records end
local source = sort_col.source
-- Case-folded sort key for the chosen column. NOTE: this case-fold
-- is for SORT ORDER only — it is NOT a slug operation. The library
-- never derives a link target from a display name (slugs come from
-- rec.slug). Using `string.lower(...)` (function-call form) rather
-- than `:lower()` keeps the F1 lint clean: the lint catches the
-- method form because that's the shape slug-computation code in
-- prior runs took.
local function key(rec)
local c = _cell(rec, source, opts)
if c == '—' then return string.char(255) end -- last
return string.lower(c)
end
local list = {}
for i, r in ipairs(records) do list[i] = r end
table.sort(list, function(a, b) return key(a) < key(b) end)
return list
end
function p.render(records, opts)
records = records or {}
opts = opts or {}
local columns = opts.columns or {}
if #columns == 0 or #records == 0 then return '' end
local ordered = _sort(records, columns, opts.sort_default, opts)
local rows = { '{| class="wikitable sortable" style="background:var(--table-row-odd, #1b1c20);color:var(--table-text, #e6e6e6);border-color:var(--table-border, #3a3c44);"' }
-- Header row. MediaWiki inline header separator is `!!`, not single `!` —
-- `! Icon ! Name` parses as ONE cell with literal `!` text. Use `!!`.
local hdr = {}
for _, col in ipairs(columns) do
table.insert(hdr, 'style="background:var(--table-header-bg, #26272e);color:var(--infobox-header-fg, #f1e9d2);" | ' .. tostring(col.name))
end
table.insert(rows, '! ' .. table.concat(hdr, ' !! '))
-- Body rows.
for _, rec in ipairs(ordered) do
local attrs = ''
local tier = rec.infobox and rec.infobox.Tier
if tier ~= nil and tier ~= '' then
attrs = ' data-tier="' .. tostring(tier) .. '"'
end
table.insert(rows, '|-' .. attrs)
local cells = {}
for _, col in ipairs(columns) do
table.insert(cells, _cell(rec, col.source, opts))
end
-- F2/F9-safe: column separators are plain `||` on a single `|`
-- line, OUTSIDE any #if. No conditional cells.
table.insert(rows, '| ' .. table.concat(cells, ' || '))
end
table.insert(rows, '|}')
-- F2 (SC-03): preprocess so any {{Template:...}} embedded in a cell
-- (rare under the Phase 4.5 contract — most cells are already plain
-- strings — but possible for caption-style cells) expands. Plain
-- `return` from #invoke does NOT trigger another template-expansion
-- pass.
return mw.getCurrentFrame():preprocess(table.concat(rows, '\n'))
end
return p