Модуль:ТранспортнаяСхема: различия между версиями
Внешний вид
Tringoli (обсуждение | вклад) мНет описания правки |
Tringoli (обсуждение | вклад) м Пока вырежу картинки |
||
| Строка 1: | Строка 1: | ||
local p = {} | local p = {} | ||
local function getArgs(frame) | local function getArgs(frame) | ||
local args = {} | local args = {} | ||
| Строка 16: | Строка 15: | ||
end | end | ||
local function loadData(args) | local function loadData(args) | ||
local dataParam = args['данные'] or args[1] | local dataParam = args['данные'] or args[1] | ||
| Строка 51: | Строка 49: | ||
error('Страница данных [[' .. dataPageTitle .. ']] не найдена.') | error('Страница данных [[' .. dataPageTitle .. ']] не найдена.') | ||
end | end | ||
| Строка 103: | Строка 63: | ||
local args = getArgs(frame) | local args = getArgs(frame) | ||
local jsonData = loadData(args) | local jsonData = loadData(args) | ||
local root = mw.html.create('div') | local root = mw.html.create('div') | ||
:addClass('ts-transport-scheme') | :addClass('ts-transport-scheme') | ||
:css({ | :css({ | ||
['clear'] = | ['clear'] = 'both', | ||
['margin'] = | ['margin'] = '1.2em auto', | ||
['width'] = '100%', | ['width'] = '100%', | ||
['text-align'] = 'center' | ['text-align'] = 'center' | ||
}) | }) | ||
-- | -- 1. Легенда линий и маршрутов | ||
local | if jsonData.lines and #jsonData.lines > 0 then | ||
local legend = root:tag('div') | |||
:addClass('ts-transport-lines-legend') | |||
:css({ | |||
['margin'] = '12px auto', | |||
['max-width'] = '950px', | |||
['padding'] = '8px 12px', | |||
['background'] = '#f6f8fa', | |||
['border'] = '1px solid #d0d7de', | |||
['border-radius'] = '8px' | |||
}) | |||
legend:tag('div') | |||
:css({ | |||
['font-size'] = '11px', | |||
['font-weight'] = 'bold', | |||
['color'] = '#57606a', | |||
['text-transform'] = 'uppercase', | |||
['margin-bottom'] = '6px' | |||
}) | |||
:wikitext(jsonData.legendTitle or 'Линии скоростного транспорта') | |||
local legendBadges = legend:tag('div') | |||
:css({ | :css({ | ||
[' | ['display'] = 'flex', | ||
[' | ['flex-wrap'] = 'wrap', | ||
['justify-content'] = 'center', | |||
[' | ['gap'] = '5px' | ||
[' | |||
}) | }) | ||
for _, line in ipairs(jsonData.lines) do | |||
local badge = legendBadges:tag('span') | |||
local | |||
:css({ | :css({ | ||
[' | ['display'] = 'inline-flex', | ||
[' | ['align-items'] = 'center', | ||
['padding'] = '8px | ['padding'] = '2px 8px 2px 4px', | ||
['background'] = '# | ['background'] = '#ffffff', | ||
['border'] = '1px solid #d0d7de', | ['border'] = '1px solid #d0d7de', | ||
['border-radius'] = ' | ['border-radius'] = '20px', | ||
['font-size'] = '12px' | |||
}) | }) | ||
badge:tag('span') | |||
:css({ | :css({ | ||
['font-size'] = ' | ['display'] = 'inline-flex', | ||
['align-items'] = 'center', | |||
['justify-content'] = 'center', | |||
['min-width'] = '16px', | |||
['height'] = '16px', | |||
['padding'] = '0 4px', | |||
['border-radius'] = '999px', | |||
['background-color'] = line.color or '#888888', | |||
['color'] = line.textColor or '#ffffff', | |||
['font-size'] = '9.5px', | |||
['font-weight'] = 'bold', | ['font-weight'] = 'bold', | ||
['margin-right'] = '6px' | |||
['margin- | |||
}) | }) | ||
:wikitext( | :wikitext(line.symbol or line.number or '•') | ||
badge:tag('span') | |||
: | :wikitext('[[' .. (line.article or '') .. '|' .. (line.displayName or line.name or line.article or '') .. ']]') | ||
end | |||
end | |||
-- 2. Сворачиваемый каталог станций | |||
if jsonData.lines and #jsonData.lines > 0 then | |||
local listCard = root:tag('div') | |||
:addClass('mw-collapsible toccolours') | |||
:css({ | |||
['margin'] = '12px auto 0 auto', | |||
['max-width'] = '950px', | |||
['text-align'] = 'left', | |||
['background'] = 'var(--background-color-neutral-subtle, #f8f9fa)', | |||
['border'] = '1px solid #d0d7de', | |||
['border-radius'] = '8px', | |||
['overflow'] = 'hidden' | |||
}) | |||
listCard:tag('div') | |||
:css({ | |||
['font-weight'] = 'bold', | |||
['font-size'] = '13.5px', | |||
['background'] = 'linear-gradient(180deg, #f6f8fa 0%, #eaeef2 100%)', | |||
['padding'] = '9px 14px', | |||
['border-bottom'] = '1px solid #d0d7de' | |||
}) | |||
:wikitext(jsonData.listTitle or '📋 Реестр станций') | |||
local listContent = listCard:tag('div') | |||
:addClass('mw-collapsible-content') | |||
:css({ | |||
['padding'] = '14px 18px', | |||
['column-width'] = '20em', | |||
['column-gap'] = '2em', | |||
['column-rule'] = '1px solid #e1e4e8', | |||
['font-size'] = '13px', | |||
['line-height'] = '1.7' | |||
}) | |||
local currentMode = nil | |||
local listWikitext = '' | |||
for _, line in ipairs(jsonData.lines) do | |||
local mode = line.mode or 'other' | |||
if mode ~= currentMode then | |||
currentMode = mode | |||
listWikitext = listWikitext .. "\n; " .. getModeLabel(mode) .. "\n" | |||
end | |||
listWikitext = listWikitext .. "* '''[[" .. (line.article or '') .. '|' .. (line.name or line.article or '') .. "]]'''\n" | |||
for _, station in ipairs(line.stations or {}) do | |||
local badge = "<span style='display:inline-flex;align-items:center;justify-content:center;min-width:14px;height:14px;padding:0 3px;border-radius:999px;background:" .. (line.color or '#888') .. ";color:" .. (line.textColor or '#fff') .. ";font-size:9px;font-weight:bold;margin-right:4px;'>" .. (line.symbol or line.number or '•') .. "</span>" | |||
listWikitext = listWikitext .. '** ' .. badge .. ' [[' .. station.article .. '|' .. (station.name or station.article) .. ']]' | |||
if station.transfer then | |||
listWikitext = listWikitext .. " <small style='color:#57606a;'>(" .. station.transfer .. ")</small>" | |||
if | |||
listWikitext = listWikitext .. " | |||
end | end | ||
if station.future then | |||
listWikitext = listWikitext .. " <small style='color:#8c959f; font-style:italic;'>(строит.)</small>" | |||
end | end | ||
listWikitext = listWikitext .. '\n' | |||
end | end | ||
end | |||
listContent:wikitext(listWikitext) | |||
end | end | ||
Версия от 13:05, 22 августа 2026
Для документации этого модуля может быть создана страница Модуль:ТранспортнаяСхема/doc
local p = {}
local function getArgs(frame)
local args = {}
if frame.getParent then
local parent = frame:getParent()
if parent and parent.args then
for k, v in pairs(parent.args) do args[k] = v end
end
end
if frame.args then
for k, v in pairs(frame.args) do args[k] = v end
end
return args
end
local function loadData(args)
local dataParam = args['данные'] or args[1]
if not dataParam or dataParam == '' then
error('Не указано имя базы данных или страница JSON (параметр 1 или |данные=)')
end
local dataPageTitle = dataParam
if not mw.ustring.find(dataParam, ':') then
dataPageTitle = 'Шаблон:Интерактивная схема ' .. dataParam .. '/data.json'
end
local success, jsonData = pcall(mw.loadJsonData, dataPageTitle)
if success and jsonData then
return jsonData
end
local titleObj = mw.title.new(dataPageTitle)
if titleObj and titleObj.exists then
local rawText = titleObj:getContent()
if rawText and rawText ~= '' then
rawText = mw.ustring.gsub(rawText, '<noinclude>.-</noinclude>', '')
rawText = mw.ustring.gsub(rawText, '<!--.--->', '')
rawText = mw.text.trim(rawText)
local decodeSuccess, decodedData = pcall(mw.text.jsonDecode, rawText)
if decodeSuccess and decodedData then
return decodedData
else
error('Ошибка в синтаксисе JSON на странице [[' .. dataPageTitle .. ']]!')
end
end
end
error('Страница данных [[' .. dataPageTitle .. ']] не найдена.')
end
local function getModeLabel(mode)
local modes = {
metro = '🚇 Линии Муроморского метрополитена',
train = '🚆 Направления Мурской железной дороги (МЖД)',
other = '🚍 Прочие линии'
}
return modes[mode] or modes.other
end
function p.render(frame)
local args = getArgs(frame)
local jsonData = loadData(args)
local root = mw.html.create('div')
:addClass('ts-transport-scheme')
:css({
['clear'] = 'both',
['margin'] = '1.2em auto',
['width'] = '100%',
['text-align'] = 'center'
})
-- 1. Легенда линий и маршрутов
if jsonData.lines and #jsonData.lines > 0 then
local legend = root:tag('div')
:addClass('ts-transport-lines-legend')
:css({
['margin'] = '12px auto',
['max-width'] = '950px',
['padding'] = '8px 12px',
['background'] = '#f6f8fa',
['border'] = '1px solid #d0d7de',
['border-radius'] = '8px'
})
legend:tag('div')
:css({
['font-size'] = '11px',
['font-weight'] = 'bold',
['color'] = '#57606a',
['text-transform'] = 'uppercase',
['margin-bottom'] = '6px'
})
:wikitext(jsonData.legendTitle or 'Линии скоростного транспорта')
local legendBadges = legend:tag('div')
:css({
['display'] = 'flex',
['flex-wrap'] = 'wrap',
['justify-content'] = 'center',
['gap'] = '5px'
})
for _, line in ipairs(jsonData.lines) do
local badge = legendBadges:tag('span')
:css({
['display'] = 'inline-flex',
['align-items'] = 'center',
['padding'] = '2px 8px 2px 4px',
['background'] = '#ffffff',
['border'] = '1px solid #d0d7de',
['border-radius'] = '20px',
['font-size'] = '12px'
})
badge:tag('span')
:css({
['display'] = 'inline-flex',
['align-items'] = 'center',
['justify-content'] = 'center',
['min-width'] = '16px',
['height'] = '16px',
['padding'] = '0 4px',
['border-radius'] = '999px',
['background-color'] = line.color or '#888888',
['color'] = line.textColor or '#ffffff',
['font-size'] = '9.5px',
['font-weight'] = 'bold',
['margin-right'] = '6px'
})
:wikitext(line.symbol or line.number or '•')
badge:tag('span')
:wikitext('[[' .. (line.article or '') .. '|' .. (line.displayName or line.name or line.article or '') .. ']]')
end
end
-- 2. Сворачиваемый каталог станций
if jsonData.lines and #jsonData.lines > 0 then
local listCard = root:tag('div')
:addClass('mw-collapsible toccolours')
:css({
['margin'] = '12px auto 0 auto',
['max-width'] = '950px',
['text-align'] = 'left',
['background'] = 'var(--background-color-neutral-subtle, #f8f9fa)',
['border'] = '1px solid #d0d7de',
['border-radius'] = '8px',
['overflow'] = 'hidden'
})
listCard:tag('div')
:css({
['font-weight'] = 'bold',
['font-size'] = '13.5px',
['background'] = 'linear-gradient(180deg, #f6f8fa 0%, #eaeef2 100%)',
['padding'] = '9px 14px',
['border-bottom'] = '1px solid #d0d7de'
})
:wikitext(jsonData.listTitle or '📋 Реестр станций')
local listContent = listCard:tag('div')
:addClass('mw-collapsible-content')
:css({
['padding'] = '14px 18px',
['column-width'] = '20em',
['column-gap'] = '2em',
['column-rule'] = '1px solid #e1e4e8',
['font-size'] = '13px',
['line-height'] = '1.7'
})
local currentMode = nil
local listWikitext = ''
for _, line in ipairs(jsonData.lines) do
local mode = line.mode or 'other'
if mode ~= currentMode then
currentMode = mode
listWikitext = listWikitext .. "\n; " .. getModeLabel(mode) .. "\n"
end
listWikitext = listWikitext .. "* '''[[" .. (line.article or '') .. '|' .. (line.name or line.article or '') .. "]]'''\n"
for _, station in ipairs(line.stations or {}) do
local badge = "<span style='display:inline-flex;align-items:center;justify-content:center;min-width:14px;height:14px;padding:0 3px;border-radius:999px;background:" .. (line.color or '#888') .. ";color:" .. (line.textColor or '#fff') .. ";font-size:9px;font-weight:bold;margin-right:4px;'>" .. (line.symbol or line.number or '•') .. "</span>"
listWikitext = listWikitext .. '** ' .. badge .. ' [[' .. station.article .. '|' .. (station.name or station.article) .. ']]'
if station.transfer then
listWikitext = listWikitext .. " <small style='color:#57606a;'>(" .. station.transfer .. ")</small>"
end
if station.future then
listWikitext = listWikitext .. " <small style='color:#8c959f; font-style:italic;'>(строит.)</small>"
end
listWikitext = listWikitext .. '\n'
end
end
listContent:wikitext(listWikitext)
end
return tostring(root)
end
return p