模块:Navbox:修订间差异
来自星砂岛百科
更多操作
同步更新物品导航框 |
调整物品导航框样式与排序 |
||
| 第136行: | 第136行: | ||
end | end | ||
local chip = container:tag('span'):addClass(' | local chip = container:tag('span'):addClass('navbox__chip') | ||
if is_current_link(link, current_targets) then | if is_current_link(link, current_targets) then | ||
chip:addClass('is-current') | chip:addClass('is-current') | ||
| 第163行: | 第163行: | ||
end | end | ||
local row = container:tag('div'):addClass(' | local row = container:tag('div'):addClass('navbox__group') | ||
local label = trim(group.label) | local label = trim(group.label) | ||
if label ~= '' then | if label ~= '' then | ||
row:tag('div'):addClass(' | row:tag('div'):addClass('navbox__group-label'):wikitext(label) | ||
else | else | ||
row:tag('div'):addClass(' | row:tag('div'):addClass('navbox__group-label') | ||
end | end | ||
local links = row:tag('div'):addClass(' | local links = row:tag('div'):addClass('navbox__links') | ||
for _, link in ipairs(group.links) do | for _, link in ipairs(group.links) do | ||
render_link(links, link, current_targets) | render_link(links, link, current_targets) | ||
| 第177行: | 第177行: | ||
end | end | ||
local function render_box(box, target, | 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 | if type(box) ~= 'table' or type(box.groups) ~= 'table' then | ||
return '' | return '' | ||
| 第193行: | 第211行: | ||
local root = mw.html.create('div') | local root = mw.html.create('div') | ||
root:addClass(' | root:addClass('navbox') | ||
root:attr('role', 'navigation') | root:attr('role', 'navigation') | ||
root:attr('aria-label', trim(box.title) ~= '' and trim(box.title) or '导航') | root:attr('aria-label', trim(box.title) ~= '' and trim(box.title) or '导航') | ||
local custom_class = trim(extra_class) | local custom_class = trim(options.extra_class) | ||
if custom_class ~= '' then | if custom_class ~= '' then | ||
root:addClass(custom_class) | root:addClass(custom_class) | ||
end | end | ||
local panel = root:tag('div'):addClass(' | local panel = root:tag('div'):addClass('navbox__panel') | ||
local header = panel:tag('div'):addClass(' | local header = panel:tag('div'):addClass('navbox__header') | ||
local heading = header:tag('div'):addClass(' | 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) | local title = trim(box.title) | ||
if title ~= '' then | if title ~= '' then | ||
title_row:tag('h2'):addClass('navbox__title'):wikitext(title) | |||
end | end | ||
local subtitle = trim(box.subtitle) | local subtitle = trim(box.subtitle) | ||
if subtitle ~= '' then | if subtitle ~= '' then | ||
heading:tag('div'):addClass(' | heading:tag('div'):addClass('navbox__subtitle'):wikitext(subtitle) | ||
end | end | ||
local badge = trim(box.badge) | local badge = trim(box.badge) | ||
if badge ~= '' then | if badge ~= '' then | ||
header:tag('div'):addClass(' | header:tag('div'):addClass('navbox__badge'):wikitext(badge) | ||
end | end | ||
local body = panel:tag('div'):addClass(' | local body = panel:tag('div'):addClass('navbox__body') | ||
local current_targets = build_current_targets(target) | local current_targets = build_current_targets(target) | ||
for _, group in ipairs(box.groups) do | for _, group in ipairs(box.groups) do | ||
| 第229行: | 第250行: | ||
local footer = trim(box.footer) | local footer = trim(box.footer) | ||
if footer ~= '' then | if footer ~= '' then | ||
panel:tag('div'):addClass(' | panel:tag('div'):addClass('navbox__footer'):wikitext(footer) | ||
end | end | ||
| 第243行: | 第264行: | ||
local box_key = common.getArg(frame, 'box', common.getArg(frame, 2, '')) | local box_key = common.getArg(frame, 'box', common.getArg(frame, 2, '')) | ||
local target = common.getArg(frame, 'target', common.getArg(frame, 3, common.getCurrentTitleText())) | local target = common.getArg(frame, 'target', common.getArg(frame, 3, common.getCurrentTitleText())) | ||
local extra_class = common.getArg(frame, 'class', '') | 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 data = load_data(data_page) | ||
| 第251行: | 第276行: | ||
end | end | ||
local content = render_box(box, target, | local content = render_box(box, target, options) | ||
if content == '' then | if content == '' then | ||
return '' | return '' | ||
2026年4月2日 (四) 15:22的版本
概述
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')
if is_current_link(link, current_targets) then
chip:addClass('is-current')
chip:wikitext(label)
return
end
if label == page then
chip:wikitext('[[' .. page .. ']]')
else
chip: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