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

Модуль:Tabs

Материал из wiki.iccup.org

Модуль 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)