Открыть меню
Открыть персональное меню
Вы не представились системе
Your IP address will be publicly visible if you make any edits.

Модуль:Tabs: различия между версиями

Материал из wiki.iccup.org
Новая страница: «--- -- @iCCup -- wiki=commons -- page=Module:Tabs -- -- This module is adapted for iCCup. -- local Array = require('Module:Array') local Class = require('Module:Class') local Logic = require('Module:Logic') local Operator = require('Module:Operator') local Page = require('Module:Page') local Table = require('Module:Table') local Tabs = {} ---Creates static tabs. ---Entry point of Template:Tabs static ---@param args table? ---@return Html? function Tabs.st...»
 
Нет описания правки
 
(не показаны 2 промежуточные версии этого же участника)
Строка 19: Строка 19:
---Entry point of Template:Tabs static
---Entry point of Template:Tabs static
---@param args table?
---@param args table?
---@return Html?
---@return Html|string?
function Tabs.static(args)
function Tabs.static(args)
args = args or {}
    args = args or {}


local tabArgs = Tabs._readArguments(args, {allowThis2 = true})
    -- Читаем аргументы и проверяем их корректность
local tabCount = #tabArgs
    local tabArgs = Tabs._readArguments(args, {allowThis2 = true})
if tabCount == 0 then return end
    local tabCount = #tabArgs
    if tabCount == 0 then
        return '<div class="error">Ошибка: Не переданы табы.</div>'
    end


Tabs._setThis(tabArgs)
    -- Устанавливаем активные табы
    Tabs._setThis(tabArgs)


local tabs = mw.html.create('ul')
    -- Создаём список табов
:attr('class', 'nav nav-tabs navigation-not-searchable tabs tabs' .. tabCount)
    local tabs = mw.html.create('ul')
:attr('data-nosnippet')
        :addClass('nav nav-tabs navigation-not-searchable tabs tabs' .. tabCount)
        :attr('data-nosnippet', '')


local subTabs = mw.html.create()
    local subTabs = mw.html.create()


Array.forEach(tabArgs, function(tab)
    for _, tab in ipairs(tabArgs) do
-- if tab.name is unset tab.link is set as per `Tabs._readArguments`
        local name = tab.name or (tab.link and Tabs._getDisplayNameFromLink(tab.link)) or 'Таб без имени'
local name = tab.name or Tabs._getDisplayNameFromLink(tab.link --[[@as string]])
        local text = tab.link and Page.makeInternalLink({}, name, tab.link) or tab.name or 'Таб без ссылки'
local text = tab.link and Page.makeInternalLink({}, name, tab.link) or tab.name
        tabs:tag('li'):addClass(tab.this and 'active' or nil):wikitext(text)
tabs:tag('li'):addClass(tab.this and 'active' or nil):wikitext(text)
        subTabs:node(tab.this and tab.tabs or nil)
subTabs:node(tab.this and tab.tabs or nil)
    end
end)


return mw.html.create()
    -- Возвращаем HTML-код
:tag('div')
    return mw.html.create()
:addClass('tabs-static')
        :tag('div')
:attr('data-nosnippet', '')
            :addClass('tabs-static')
:node(tabs)
            :attr('data-nosnippet', '')
:done()
            :node(tabs)
:node(subTabs)
            :done()
        :node(subTabs)
end
end


---Creates dynamic tabs.
---Creates dynamic tabs.
Строка 57: Строка 63:
---@return Html|string?
---@return Html|string?
function Tabs.dynamic(args)
function Tabs.dynamic(args)
args = args or {}
    args = args or {}
 
    local tabArgs = Tabs._readArguments(args, {removeEmptyTabs = Logic.readBool(args.removeEmptyTabs)})
    local tabCount = #tabArgs
    if tabCount == 0 then return '' end -- Возвращаем пустую строку, если табов нет


local tabArgs = Tabs._readArguments(args, {removeEmptyTabs = Logic.readBool(args.removeEmptyTabs)})
    local hasContent = Array.all(tabArgs, function(tab) return Logic.isNotEmpty(tab.content) end)
local tabCount = #tabArgs
    local allEmpty = Array.all(tabArgs, function(tab) return Logic.isEmpty(tab.content) end)
if tabCount == 0 then return end
    assert(hasContent or allEmpty, 'Some of the tabs have contents while others do not')


local hasContent = Array.all(tabArgs, function(tab)
    local isSingular = tabCount == 1 and hasContent
return Logic.isNotEmpty(tab.content) end)
    if isSingular and not Logic.readBool(args.showSingularAsTab) then
local allEmpty = Array.all(tabArgs, function(tab)
        return Tabs._single(tabArgs[1], not Logic.readBool(args.suppressHeader))
return Logic.isEmpty(tab.content) end)
    end
assert(hasContent or allEmpty, 'Some of the tabs have contents while others do not')


local isSingular = tabCount == 1 and hasContent
    local tabs = mw.html.create('ul')
if isSingular and not Logic.readBool(args.showSingularAsTab) then
        :addClass('nav nav-tabs tabs tabs' .. tabCount)
return Tabs._single(tabArgs[1], not Logic.readBool(args.suppressHeader))
end


local tabs = mw.html.create('ul')
    if not Array.any(tabArgs, Operator.property('this')) then
:addClass('nav nav-tabs tabs tabs' .. tabCount)
        tabArgs[1].this = true
    end


if not Array.any(tabArgs, Operator.property('this')) then
    local build = function(obj, elementType, content, class, isActive)
tabArgs[1].this = true
        if not obj or not elementType then return end -- Проверка аргументов
end
        local element = mw.html.create(elementType)
            :addClass(class)
            :addClass(isActive and 'active' or nil)
            :newline()
            :node(content)


---@param obj Html
        obj:newline():node(element)
---@param elementType string
    end
---@param content string|Html|?
---@param class string
---@param isActive boolean
local build = function(obj, elementType, content, class, isActive)
local element = mw.html.create(elementType)
:addClass(class)
:addClass(isActive and 'active' or nil)
:newline()
:node(content)


obj:newline():node(element)
for tabIndex, tabData in ipairs(tabArgs) do
    build(tabs, 'li', tabData.name, 'tab' .. tabIndex, tabData.this)
end
for tabIndex, tabData in ipairs(tabArgs) do
    build(contents, 'div', tabData.content, 'content' .. tabIndex, tabData.this)
end
end


Array.forEach(tabArgs, function(tabData, tabIndex)
build(tabs, 'li', tabData.name, 'tab' .. tabIndex, tabData.this)
end)


if not Logic.nilOr(Logic.readBoolOrNil(args['hide-showall']), isSingular) then
    if not Logic.nilOr(Logic.readBoolOrNil(args['hide-showall']), isSingular) then
tabs:tag('li')
        tabs:tag('li')
:addClass('show-all')
            :addClass('show-all')
:wikitext('Show All')
            :wikitext('Show All')
end
    end


tabs:newline()
    tabs:newline()


local contents = Tabs._buildContentDiv(
    local contents = Tabs._buildContentDiv(
hasContent,
        hasContent,
Logic.readBool(args['hybrid-tabs']),
        Logic.readBool(args['hybrid-tabs']),
Logic.readBool(args['no-padding'])
        Logic.readBool(args['no-padding'])
)
    )


if not hasContent then
    if not hasContent then
return '<div class="tabs-dynamic navigation-not-searchable" data-nosnippet>\n'
        return '<div class="tabs-dynamic navigation-not-searchable" data-nosnippet>\n'
.. tostring(tabs) .. contents
            .. tostring(tabs) .. contents
end
    end
---@cast contents -string


Array.forEach(tabArgs, function(tabData, tabIndex)
    Array.forEach(tabArgs, function(tabData, tabIndex)
build(contents, 'div', tabData.content, 'content' .. tabIndex, tabData.this)
        build(contents, 'div', tabData.content, 'content' .. tabIndex, tabData.this)
end)
    end)


return mw.html.create('div')
    return mw.html.create('div')
:addClass('tabs-dynamic navigation-not-searchable')
        :addClass('tabs-dynamic navigation-not-searchable')
:attr('data-nosnippet')
        :attr('data-nosnippet')
:node(tabs)
        :node(tabs)
:newline()
        :newline()
:node(contents)
        :node(contents)
end
end


Строка 136: Строка 140:
---@return {name: string?, link: string?, content: string|Html?, tabs: string|Html?, this: boolean}[]
---@return {name: string?, link: string?, content: string|Html?, tabs: string|Html?, this: boolean}[]
function Tabs._readArguments(args, options)
function Tabs._readArguments(args, options)
local tabArgs = {}
    local tabArgs = {}
local tabIndex = 1
    local tabIndex = 1
local this = tonumber(args.This)
    local thisTab = tonumber(args.This)
local this2 = tonumber(args.This2)
    local this2Tab = tonumber(args.This2)


while args['name' .. tabIndex] or args['link' .. tabIndex] do
    while args['name' .. tabIndex] or args['link' .. tabIndex] do
if args['content' .. tabIndex] or not options.removeEmptyTabs then
        if args['content' .. tabIndex] or not options.removeEmptyTabs then
table.insert(tabArgs, {
            table.insert(tabArgs, {
name = Table.extract(args, 'name' .. tabIndex),
                name = Table.extract(args, 'name' .. tabIndex),
link = Table.extract(args, 'link' .. tabIndex),
                link = Table.extract(args, 'link' .. tabIndex),
content = Table.extract(args, 'content' .. tabIndex),
                content = Table.extract(args, 'content' .. tabIndex),
tabs = Table.extract(args, 'tabs' .. tabIndex),
                tabs = Table.extract(args, 'tabs' .. tabIndex),
this = this == tabIndex or (options.allowThis2 and this2 == tabIndex),
                this = thisTab == tabIndex or (options.allowThis2 and this2Tab == tabIndex),
})
            })
end
        end
tabIndex = tabIndex + 1
        tabIndex = tabIndex + 1
end
    end


if Logic.readBool(args.returnIfEmpty) then
    if Logic.readBool(args.returnIfEmpty) then
return tabArgs
        return tabArgs
end
    end


assert(Logic.isNotEmpty(tabArgs), 'You are trying to add a "Tabs" template without arguments for names nor links')
    assert(Logic.isNotEmpty(tabArgs), 'You are trying to add a "Tabs" template without arguments for names nor links')


return tabArgs
    return tabArgs
end
end


---@param tabArgs {name: string?, link: string?, content: string|Html?, tabs: string|Html?, this: boolean}[]
---@param tabArgs {name: string?, link: string?, content: string|Html?, tabs: string|Html?, this: boolean}[]
function Tabs._setThis(tabArgs)
function Tabs._setThis(tabArgs)
if Array.any(tabArgs, Operator.property('this')) then return end
    if Array.any(tabArgs, Operator.property('this')) then return end
 
    local fullPageName = mw.title.getCurrentTitle().prefixedText
    local this


local fullPageName = mw.title.getCurrentTitle().prefixedText
    local maxLinkLength = -1
local this


-- Finds the link that is a prefix of the current page. If there are more than one, choose the longest, then first.
for tabIndex, tab in ipairs(tabArgs) do
-- For example, if the current page is ab/cd/e3, then among
    local link = tab.link
--  ab/cd/e1
    if not link then return end
--  ab/cd/e2
    link = link:gsub('_', ' ')
--  ab/cd/e
    local linkLength = string.len(link)
--  ab/cd
    local charAfter = string.sub(fullPageName, linkLength + 1, linkLength + 1)
--  ab/cg
    local pagePartial = string.sub(fullPageName, 1, linkLength)
--  ab
    if pagePartial == link and (charAfter == '/' or charAfter == '') and linkLength > maxLinkLength then
-- it will pick ab/cd.
        maxLinkLength = linkLength
local maxLinkLength = -1
        this = tabIndex
    end
end


Array.forEach(tabArgs, function (tab, tabIndex)
local link = tab.link
if not link then return end
link = link:gsub('_', ' ')
local linkLength = string.len(link)
local charAfter = string.sub(fullPageName, linkLength + 1, linkLength + 1)
local pagePartial = string.sub(fullPageName, 1, linkLength)
if pagePartial == link and (charAfter == '/' or charAfter == '') and linkLength > maxLinkLength then
maxLinkLength = linkLength
this = tabIndex
end
end)


if not this then return end
    if not this then return end


tabArgs[this].this = true
    tabArgs[this].this = true
end
end


Строка 204: Строка 200:
---@return Html|string
---@return Html|string
function Tabs._buildContentDiv(hasContent, hybridTabs, noPadding)
function Tabs._buildContentDiv(hasContent, hybridTabs, noPadding)
if hasContent then
    if hasContent then
local contentDiv = mw.html.create('div')
        local contentDiv = mw.html.create('div')
:addClass('tabs-content')
            :addClass('tabs-content')
if hybridTabs then
        if hybridTabs then
contentDiv
            contentDiv
:css('border-style', 'none !important')
                :css('border-style', 'none !important')
:css('padding', '0 !important')
                :css('padding', '0 !important')
elseif noPadding then
        elseif noPadding then
contentDiv
            contentDiv
:css('padding', '0 !important')
                :css('padding', '0 !important')
end
        end
return contentDiv
        return contentDiv
end
    end


local style = ''
    local style = ''
if hybridTabs then
    if hybridTabs then
style = 'border-style:none !important; padding:0 !important;'
        style = 'border-style:none !important; padding:0 !important;'
elseif noPadding then
    elseif noPadding then
style = 'padding:0 !important;'
        style = 'padding:0 !important;'
end
    end
return '\n<div class="tabs-content" style="' .. style .. '">'
    return '\n<div class="tabs-content" style="' .. style .. '">'
end
end


Строка 231: Строка 227:
---@return Html
---@return Html
function Tabs._single(tab, showHeader)
function Tabs._single(tab, showHeader)
local header
    local header
if showHeader then
    if showHeader then
header = mw.html.create()
        header = mw.html.create()
:tag('h6'):wikitext(tab.name):done()
            :tag('h6'):wikitext(tab.name):done()
:newline()
            :newline()
end
    end
return mw.html.create()
    return mw.html.create()
:node(header)
        :node(header)
:node(tab.content)
        :node(tab.content)
end
end


Строка 245: Строка 241:
---@return string
---@return string
function Tabs._getDisplayNameFromLink(link)
function Tabs._getDisplayNameFromLink(link)
local linkParts = mw.text.split(link, '/', true)
    local linkParts = mw.text.split(link, '/', true)
return linkParts[#linkParts]
    return linkParts[#linkParts]
end
end


return Class.export(Tabs)
return Class.export(Tabs)

Текущая версия от 23:56, 10 января 2025

Модуль Module:Tabs используется для создания вкладок на страницах iCCup, предоставляя как статические, так и динамические табы для организации и структурирования информации.

API[править код]

Программное имя: Tabs


static (args: table?) → Html?

Создает статические вкладки.




dynamic (args: table) → Html

Создает динамические вкладки.




_readArguments (args: table, options: table) → table

Обрабатывает аргументы, переданные в шаблон, и определяет, какие из них использовать для создания вкладок.




_setThis (tabArgs: table) → nil

Устанавливает текущую вкладку на основе текущего URL или других условий.




_buildContentDiv (hasContent: boolean, hybridTabs: boolean, noPadding: boolean) → Html

Создает контейнер для содержания вкладок.




_single (tab: table, showHeader: boolean) → Html

Создает одиночную вкладку, если передан только один аргумент.




_getDisplayNameFromLink (link: string) → string

Получает имя для отображения из ссылки.





Посмотрите всю нашу документацию iCCup здесь.



Пример использования[править код]

Пример 1: Статические вкладки[править код]

{{#invoke:Tabs|static|name1=Вкладка 1|link1=Page1|name2=Вкладка 2|link2=Page2}}

Этот пример создаст две статические вкладки: "Вкладка 1" и "Вкладка 2", каждая из которых будет ссылаться на Page1 и Page2 соответственно.

Пример 2: Динамические вкладки[править код]

<pre> {{#invoke:Tabs|dynamic|name1=Вкладка 1|content1=Содержание 1|name2=Вкладка 2|content2=Содержание 2}} </pre>

Этот пример создаст две динамические вкладки, переключаясь между "Содержание 1" и "Содержание 2" при нажатии на "Вкладка 1" и "Вкладка 2".

Параметры[править код]

  • name<sub>n</sub>: Название вкладки, где n — номер вкладки.
  • link<sub>n</sub>: Ссылка для вкладки, если вкладка должна быть статической.
  • content<sub>n</sub>: Содержимое вкладки, если вкладка динамическая.
  • tabs<sub>n</sub>: Дополнительные вкладки, которые могут быть вложены в текущую.
  • This: Указывает, какая вкладка должна быть активной.
  • This2: Дополнительный параметр для указания активной вкладки.



---
-- @iCCup
-- wiki=commons
-- page=Module:Tabs
--
-- This module is adapted for iCCup.
--

local Array = require('Module:Array')
local Class = require('Module:Class')
local Logic = require('Module:Logic')
local Operator = require('Module:Operator')
local Page = require('Module:Page')
local Table = require('Module:Table')

local Tabs = {}

---Creates static tabs.
---Entry point of Template:Tabs static
---@param args table?
---@return Html|string?
function Tabs.static(args)
    args = args or {}

    -- Читаем аргументы и проверяем их корректность
    local tabArgs = Tabs._readArguments(args, {allowThis2 = true})
    local tabCount = #tabArgs
    if tabCount == 0 then
        return '<div class="error">Ошибка: Не переданы табы.</div>'
    end

    -- Устанавливаем активные табы
    Tabs._setThis(tabArgs)

    -- Создаём список табов
    local tabs = mw.html.create('ul')
        :addClass('nav nav-tabs navigation-not-searchable tabs tabs' .. tabCount)
        :attr('data-nosnippet', '')

    local subTabs = mw.html.create()

    for _, tab in ipairs(tabArgs) do
        local name = tab.name or (tab.link and Tabs._getDisplayNameFromLink(tab.link)) or 'Таб без имени'
        local text = tab.link and Page.makeInternalLink({}, name, tab.link) or tab.name or 'Таб без ссылки'
        tabs:tag('li'):addClass(tab.this and 'active' or nil):wikitext(text)
        subTabs:node(tab.this and tab.tabs or nil)
    end

    -- Возвращаем HTML-код
    return mw.html.create()
        :tag('div')
            :addClass('tabs-static')
            :attr('data-nosnippet', '')
            :node(tabs)
            :done()
        :node(subTabs)
end


---Creates dynamic tabs.
---Entry point of Template:Tabs dynamic
---@param args table
---@return Html|string?
function Tabs.dynamic(args)
    args = args or {}

    local tabArgs = Tabs._readArguments(args, {removeEmptyTabs = Logic.readBool(args.removeEmptyTabs)})
    local tabCount = #tabArgs
    if tabCount == 0 then return '' end -- Возвращаем пустую строку, если табов нет

    local hasContent = Array.all(tabArgs, function(tab) return Logic.isNotEmpty(tab.content) end)
    local allEmpty = Array.all(tabArgs, function(tab) return Logic.isEmpty(tab.content) end)
    assert(hasContent or allEmpty, 'Some of the tabs have contents while others do not')

    local isSingular = tabCount == 1 and hasContent
    if isSingular and not Logic.readBool(args.showSingularAsTab) then
        return Tabs._single(tabArgs[1], not Logic.readBool(args.suppressHeader))
    end

    local tabs = mw.html.create('ul')
        :addClass('nav nav-tabs tabs tabs' .. tabCount)

    if not Array.any(tabArgs, Operator.property('this')) then
        tabArgs[1].this = true
    end

    local build = function(obj, elementType, content, class, isActive)
        if not obj or not elementType then return end -- Проверка аргументов
        local element = mw.html.create(elementType)
            :addClass(class)
            :addClass(isActive and 'active' or nil)
            :newline()
            :node(content)

        obj:newline():node(element)
    end

	for tabIndex, tabData in ipairs(tabArgs) do
	    build(tabs, 'li', tabData.name, 'tab' .. tabIndex, tabData.this)
	end
	
	for tabIndex, tabData in ipairs(tabArgs) do
	    build(contents, 'div', tabData.content, 'content' .. tabIndex, tabData.this)
	end


    if not Logic.nilOr(Logic.readBoolOrNil(args['hide-showall']), isSingular) then
        tabs:tag('li')
            :addClass('show-all')
            :wikitext('Show All')
    end

    tabs:newline()

    local contents = Tabs._buildContentDiv(
        hasContent,
        Logic.readBool(args['hybrid-tabs']),
        Logic.readBool(args['no-padding'])
    )

    if not hasContent then
        return '<div class="tabs-dynamic navigation-not-searchable" data-nosnippet>\n'
            .. tostring(tabs) .. contents
    end

    Array.forEach(tabArgs, function(tabData, tabIndex)
        build(contents, 'div', tabData.content, 'content' .. tabIndex, tabData.this)
    end)

    return mw.html.create('div')
        :addClass('tabs-dynamic navigation-not-searchable')
        :attr('data-nosnippet')
        :node(tabs)
        :newline()
        :node(contents)
end

---@param args table
---@param options {allowThis2: boolean?, removeEmptyTabs: boolean?}
---@return {name: string?, link: string?, content: string|Html?, tabs: string|Html?, this: boolean}[]
function Tabs._readArguments(args, options)
    local tabArgs = {}
    local tabIndex = 1
    local thisTab = tonumber(args.This)
    local this2Tab = tonumber(args.This2)

    while args['name' .. tabIndex] or args['link' .. tabIndex] do
        if args['content' .. tabIndex] or not options.removeEmptyTabs then
            table.insert(tabArgs, {
                name = Table.extract(args, 'name' .. tabIndex),
                link = Table.extract(args, 'link' .. tabIndex),
                content = Table.extract(args, 'content' .. tabIndex),
                tabs = Table.extract(args, 'tabs' .. tabIndex),
                this = thisTab == tabIndex or (options.allowThis2 and this2Tab == tabIndex),
            })
        end
        tabIndex = tabIndex + 1
    end

    if Logic.readBool(args.returnIfEmpty) then
        return tabArgs
    end

    assert(Logic.isNotEmpty(tabArgs), 'You are trying to add a "Tabs" template without arguments for names nor links')

    return tabArgs
end

---@param tabArgs {name: string?, link: string?, content: string|Html?, tabs: string|Html?, this: boolean}[]
function Tabs._setThis(tabArgs)
    if Array.any(tabArgs, Operator.property('this')) then return end

    local fullPageName = mw.title.getCurrentTitle().prefixedText
    local this

    local maxLinkLength = -1

	for tabIndex, tab in ipairs(tabArgs) do
	    local link = tab.link
	    if not link then return end
	    link = link:gsub('_', ' ')
	    local linkLength = string.len(link)
	    local charAfter = string.sub(fullPageName, linkLength + 1, linkLength + 1)
	    local pagePartial = string.sub(fullPageName, 1, linkLength)
	    if pagePartial == link and (charAfter == '/' or charAfter == '') and linkLength > maxLinkLength then
	        maxLinkLength = linkLength
	        this = tabIndex
	    end
	end


    if not this then return end

    tabArgs[this].this = true
end

---@param hasContent boolean
---@param hybridTabs boolean
---@param noPadding boolean
---@return Html|string
function Tabs._buildContentDiv(hasContent, hybridTabs, noPadding)
    if hasContent then
        local contentDiv = mw.html.create('div')
            :addClass('tabs-content')
        if hybridTabs then
            contentDiv
                :css('border-style', 'none !important')
                :css('padding', '0 !important')
        elseif noPadding then
            contentDiv
                :css('padding', '0 !important')
        end
        return contentDiv
    end

    local style = ''
    if hybridTabs then
        style = 'border-style:none !important; padding:0 !important;'
    elseif noPadding then
        style = 'padding:0 !important;'
    end
    return '\n<div class="tabs-content" style="' .. style .. '">'
end

---@param tab {name: string?, link: string?, content: string|Html?, tabs: string|Html?, this: boolean}
---@param showHeader boolean
---@return Html
function Tabs._single(tab, showHeader)
    local header
    if showHeader then
        header = mw.html.create()
            :tag('h6'):wikitext(tab.name):done()
            :newline()
    end
    return mw.html.create()
        :node(header)
        :node(tab.content)
end

---@param link string
---@return string
function Tabs._getDisplayNameFromLink(link)
    local linkParts = mw.text.split(link, '/', true)
    return linkParts[#linkParts]
end

return Class.export(Tabs)