From Arms of God Wiki

bot: render-time derivation refactor (phase 1)
 
bot: documentation pass — editor-facing docs (data descriptions, module comments, template usage, Help rewrite)
 
Line 1: Line 1:
-- Module:Navbox — per-category bottom-of-page navigation (Arms of God).
-- Module:Navbox — the collapsible navigation box at the bottom of
-- RENDER-TIME DERIVATION: reads the category's own source
-- detail pages.
-- Data:<Category>.json via Module:Core and groups members at render time.
-- No Data:Navbox_* pages.
--
--
--  {{#invoke:Navbox|render|Weapons}}   (wrapped by Template:Navbox_<Cat>)
-- WHAT IT DOES
--  Renders a category's full member list, bucketed into rows (by tier,
--   by type, or one flat row), with each member as icon + link. Rows
--  are computed at render time from the category's own Data page, so
--  a new record appears in every navbox on purge — no stored lists.
--
--
-- Grouping config is presentation, so it lives here (editable on-wiki).
-- HOW TO INVOKE
--  {{#invoke:Navbox|render|Weapons}}
--  ...but pages should normally use the wrapper template instead:
--  {{Navbox_Weapons}}, {{Navbox_Crux}}, etc.
--
-- SOURCE DATA IT READS (via Module:Core)
--  Data:<Category>.json for whichever category is named in the call.
--
-- EDITING NOTES
--  The CONFIG table below is the presentation contract per category:
--  group = record field to bucket by (nil = single row), order = bucket
--  order, label = row label format, icon = icon size in px. Categories
--   absent from CONFIG render nothing.
local Core = require('Module:Core')
local Core = require('Module:Core')
local p = {}
local p = {}

Latest revision as of 16:04, 10 June 2026

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

-- Module:Navbox — the collapsible navigation box at the bottom of
-- detail pages.
--
-- WHAT IT DOES
--   Renders a category's full member list, bucketed into rows (by tier,
--   by type, or one flat row), with each member as icon + link. Rows
--   are computed at render time from the category's own Data page, so
--   a new record appears in every navbox on purge — no stored lists.
--
-- HOW TO INVOKE
--   {{#invoke:Navbox|render|Weapons}}
--   ...but pages should normally use the wrapper template instead:
--   {{Navbox_Weapons}}, {{Navbox_Crux}}, etc.
--
-- SOURCE DATA IT READS (via Module:Core)
--   Data:<Category>.json for whichever category is named in the call.
--
-- EDITING NOTES
--   The CONFIG table below is the presentation contract per category:
--   group = record field to bucket by (nil = single row), order = bucket
--   order, label = row label format, icon = icon size in px. Categories
--   absent from CONFIG render nothing.
local Core = require('Module:Core')
local p = {}

local CONFIG = {
  Weapons    = {group = 'tier', order = {'1', '2', '3'}, label = 'Tier %s (%d)',
                row_header = 'Tier', items_header = 'Weapons', icon = 24},
  Characters = {group = nil, label = 'Characters (%d)',
                row_header = '', items_header = 'Characters', icon = 48},
  Blessings  = {group = 'tier', order = {'1', '2', '3'}, label = 'Tier %s (%d)',
                row_header = 'Tier', items_header = 'Blessings', icon = 24},
  Upgrades   = {group = 'tier', order = {'1', '2', '3'}, label = 'Tier %s (%d)',
                row_header = 'Tier', items_header = 'Upgrades', icon = 24},
  Passives   = {group = 'tier', order = {'1', '2', '3'}, label = 'Tier %s (%d)',
                row_header = 'Tier', items_header = 'Passives', icon = 24},
  Crux       = {group = 'type', order = {'Unique', 'Action', 'Buff', 'Debuff', 'Aura'},
                label = '%s (%d)', row_header = 'Type',
                items_header = 'Crux powers', icon = 24},
  Enemies    = {group = nil, label = 'Enemies (%d)',
                row_header = '', items_header = 'Enemies', icon = 32},
  Codex      = {group = 'type', order = {'Enemies', 'Weapons', 'Events', 'Tips'},
                label = '%s (%d)', row_header = 'Type',
                items_header = 'Entries', icon = 24},
}

local function chain(items, size)
  if #items == 0 then return '—' end
  local parts = {}
  for _, rec in ipairs(items) do
    parts[#parts + 1] = Core.iconLink(rec, size)
  end
  return table.concat(parts, ', ')
end

local function buildRows(cat, cfg)
  local recs = Core.load(cat)
  -- bucket
  local buckets, seen = {}, {}
  for _, r in ipairs(recs) do
    local key
    if cfg.group == nil then
      key = 'all'
    else
      key = tostring(r[cfg.group] or '')
    end
    if key ~= '' then
      if not buckets[key] then buckets[key] = {}; seen[#seen + 1] = key end
      local b = buckets[key]
      b[#b + 1] = r
    end
  end
  local keys = cfg.order or (cfg.group == nil and {'all'} or seen)
  if cfg.order then
    -- append unknown keys after the declared order
    local inOrder = {}
    for _, k in ipairs(cfg.order) do inOrder[k] = true end
    keys = {}
    for _, k in ipairs(cfg.order) do keys[#keys + 1] = k end
    for _, k in ipairs(seen) do
      if not inOrder[k] then keys[#keys + 1] = k end
    end
  end
  local rows = {}
  for _, k in ipairs(keys) do
    local b = buckets[k]
    if b and #b > 0 then
      table.sort(b, function(x, y)
        return mw.ustring.lower(x.name or '') < mw.ustring.lower(y.name or '')
      end)
      local label
      if cfg.group == nil then
        label = string.format(cfg.label, #b)
      else
        label = string.format(cfg.label, k, #b)
      end
      rows[#rows + 1] = {label = label, items = b}
    end
  end
  return rows
end

function p.render(frame)
  local cat = (frame.args[1] or frame.args.category or ''):gsub('^%s+', ''):gsub('%s+$', '')
  cat = Core.resolveCategory(cat) or cat
  local cfg = CONFIG[cat]
  if not cfg then return '' end
  local rows = buildRows(cat, cfg)
  if #rows == 0 then return '' end

  local hsty = '! style="background:var(--table-header-bg, #26272e);color:var(--infobox-header-fg, #f1e9d2);" | '
  local lines = {
    '{| class="wikitable mw-collapsible" style="font-size:90%; width:100%;background:var(--table-row-odd, #1b1c20);color:var(--table-text, #e6e6e6);border-color:var(--table-border, #3a3c44);"',
    '|-',
    hsty .. cfg.row_header .. ' !' .. hsty .. cfg.items_header,
  }
  for _, row in ipairs(rows) do
    lines[#lines + 1] = '|-'
    lines[#lines + 1] = "| '''" .. row.label .. "''' || " .. chain(row.items, cfg.icon)
  end
  lines[#lines + 1] = '|}'
  local body = table.concat(lines, '\n')

  local title_link = '[[' .. cat .. '|' .. cat .. ']]'
  return string.format(
    '\n----\n<div class="navbox" style="margin-top:1em; clear:both; border:1px solid var(--navbox-border, #3a3c44); background:var(--navbox-bg, #1c1d22); color:var(--navbox-text, #dcdcdc);">\n' ..
    '<div class="navbox-bar" style="padding:6px 10px; background:var(--navbox-bar-bg, #26272e); color:var(--navbox-bar-fg, #f1e9d2); font-weight:bold; text-align:center; border:1px solid var(--navbox-border, #3a3c44);">' ..
    '<span style="float:left; font-size:85%%; color:#eee;">[v · e]</span>%s</div>\n%s\n</div>',
    title_link, body
  )
end

return p