模块:Navbox
来自星砂岛百科
更多操作
概述
Navbox 用于渲染通用导航框,适合在条目底部输出“按分类浏览”的导航内容。
用法
{{Navbox|数据:Item/item_navbox_index.json}}
{{Navbox
| data_page = 数据:Item/item_navbox_index.json
| box = tool
| target = DIY核心
}}
数据格式
{
"boxes": {
"tool": {
"title": "工具导航",
"subtitle": "按用途和类型快速浏览已创建的工具条目",
"badge": "66 项",
"groups": [
{
"label": "钓竿",
"links": [
{ "page": "木制钓竿", "label": "木制钓竿" }
]
}
]
}
},
"lookup": {
"diy核心": "tool",
"item.diycore": "tool"
}
}
参数
data_page/ 第 1 参数:数据页标题。box/ 第 2 参数:可选,指定具体导航框 key。target/ 第 3 参数:可选,用于从lookup自动匹配导航框,并高亮当前条目。class:可选,给外层追加 class。
local common = require('Module:Common')
local css = require('Module:CSS')
local p = {}
local data_cache = {}
local function trim(value)
return common.trim(value)
end
local function normalize_key(value)
return common.normalizeKey(value)
end
local function load_data(title_text)
local page_name = trim(title_text)
if page_name == '' then
return nil
end
if data_cache[page_name] ~= nil then
return data_cache[page_name] or nil
end
local data = common.loadJsonData(page_name)
if type(data) ~= 'table' then
data_cache[page_name] = false
return nil
end
data_cache[page_name] = data
return data
end
local function get_box_by_key(boxes, box_key)
if type(boxes) ~= 'table' then
return nil
end
local resolved = trim(box_key)
if resolved == '' then
return nil
end
if type(boxes[resolved]) == 'table' then
return boxes[resolved]
end
local normalized = normalize_key(resolved)
if type(boxes[normalized]) == 'table' then
return boxes[normalized]
end
return nil
end
local function resolve_box(data, box_key, target)
if type(data) ~= 'table' then
return nil
end
local boxes = data.boxes
if type(boxes) ~= 'table' then
return nil
end
local explicit = get_box_by_key(boxes, box_key)
if explicit then
return explicit
end
local lookup = data.lookup
if type(lookup) == 'table' then
local normalized_target = normalize_key(target)
if normalized_target ~= '' then
local lookup_key = lookup[normalized_target]
local matched = get_box_by_key(boxes, lookup_key)
if matched then
return matched
end
end
end
return get_box_by_key(boxes, target)
end
local function build_current_targets(target)
local normalized = {}
local function append(value)
local key = normalize_key(value)
if key ~= '' then
normalized[key] = true
end
end
append(target)
append(common.getCurrentTitleText())
return normalized
end
local function is_current_link(link, current_targets)
if type(link) ~= 'table' then
return false
end
for _, value in ipairs({
link.page,
link.title,
link.label,
}) do
local normalized = normalize_key(value)
if normalized ~= '' and current_targets[normalized] then
return true
end
end
return false
end
local function render_link(container, link, current_targets)
if type(link) ~= 'table' then
return
end
local page = trim(link.page or link.title or link.label)
local label = trim(link.label or link.title or link.page)
if page == '' then
return
end
if label == '' then
label = page
end
local chip = container:tag('span'):addClass('navbox__chip')
local icon_file = trim(link.icon_file)
if icon_file ~= '' then
chip:tag('span')
:addClass('navbox__chip-icon')
:wikitext('[[File:' .. icon_file .. '|18x18px|link=]]')
end
if is_current_link(link, current_targets) then
chip:addClass('is-current')
chip:tag('span'):addClass('navbox__chip-label'):wikitext(label)
return
end
if label == page then
chip:tag('span'):addClass('navbox__chip-label'):wikitext('[[' .. page .. ']]')
else
chip:tag('span'):addClass('navbox__chip-label'):wikitext('[[' .. page .. '|' .. label .. ']]')
end
end
local function render_group(container, group, current_targets)
if type(group) ~= 'table' or type(group.links) ~= 'table' then
return
end
local link_count = 0
for _, _ in ipairs(group.links) do
link_count = link_count + 1
end
if link_count == 0 then
return
end
local row = container:tag('div'):addClass('navbox__group')
local label = trim(group.label)
if label ~= '' then
row:tag('div'):addClass('navbox__group-label'):wikitext(label)
else
row:tag('div'):addClass('navbox__group-label')
end
local links = row:tag('div'):addClass('navbox__links')
for _, link in ipairs(group.links) do
render_link(links, link, current_targets)
end
end
local function render_icon(title_row, options)
local icon_markup = trim(options.icon)
local icon_file = trim(options.icon_file)
if icon_markup == '' and icon_file == '' then
return
end
local icon = title_row:tag('div'):addClass('navbox__icon')
if icon_markup ~= '' then
icon:wikitext(icon_markup)
return
end
if common.filePageExists(icon_file) then
icon:wikitext('[[File:' .. icon_file .. '|44x44px|link=]]')
end
end
local function render_box(box, target, options)
if type(box) ~= 'table' or type(box.groups) ~= 'table' then
return ''
end
local group_count = 0
for _, group in ipairs(box.groups) do
if type(group) == 'table' and type(group.links) == 'table' and group.links[1] ~= nil then
group_count = group_count + 1
end
end
if group_count == 0 then
return ''
end
local root = mw.html.create('div')
root:addClass('navbox')
root:attr('role', 'navigation')
root:attr('aria-label', trim(box.title) ~= '' and trim(box.title) or '导航')
local custom_class = trim(options.extra_class)
if custom_class ~= '' then
root:addClass(custom_class)
end
local panel = root:tag('div'):addClass('navbox__panel')
local header = panel:tag('div'):addClass('navbox__header')
local heading = header:tag('div'):addClass('navbox__heading')
local title_row = heading:tag('div'):addClass('navbox__title-row')
render_icon(title_row, options)
local title = trim(box.title)
if title ~= '' then
title_row:tag('h2'):addClass('navbox__title'):wikitext(title)
end
local subtitle = trim(box.subtitle)
if subtitle ~= '' then
heading:tag('div'):addClass('navbox__subtitle'):wikitext(subtitle)
end
local badge = trim(box.badge)
if badge ~= '' then
header:tag('div'):addClass('navbox__badge'):wikitext(badge)
end
local body = panel:tag('div'):addClass('navbox__body')
local current_targets = build_current_targets(target)
for _, group in ipairs(box.groups) do
render_group(body, group, current_targets)
end
local footer = trim(box.footer)
if footer ~= '' then
panel:tag('div'):addClass('navbox__footer'):wikitext(footer)
end
return tostring(root)
end
function p.render(frame)
local data_page = common.getArg(frame, 'data_page', common.getArg(frame, 1, ''))
if data_page == '' then
return ''
end
local box_key = common.getArg(frame, 'box', common.getArg(frame, 2, ''))
local target = common.getArg(frame, 'target', common.getArg(frame, 3, common.getCurrentTitleText()))
local options = {
extra_class = common.getArg(frame, 'class', ''),
icon = common.getArg(frame, 'icon', ''),
icon_file = common.getArg(frame, 'icon_file', ''),
}
local data = load_data(data_page)
local box = resolve_box(data, box_key, target)
if not box then
return ''
end
local content = render_box(box, target, options)
if content == '' then
return ''
end
return (css.quickCall('Navbox') or '') .. content
end
return p