Jump to content

Module:ElementHubNavigation: Difference between revisions

// via Wikitext Extension for VSCode
// via Wikitext Extension for VSCode
 
(4 intermediate revisions by the same user not shown)
Line 1: Line 1:
--[[
--[[
* Name: ElementHubNavigation
* Name: ElementHubNavigation
* Version: 2.1
* Author: Mark W. Datysgeld
* Author: Mark W. Datysgeld
* Description: Hub navigation module for Main Page - MINIMAL TEXT-ONLY VERSION
* Description: Hub navigation module for Main Page with responsive design
* Notes: Phase 1 - Text links with layout only, no icons yet
* Notes: v2.1 fixes MediaWiki <pre> tag wrapping by removing leading whitespace
* Pattern: MediaWiki table syntax + string concatenation (like ElementNavigation)
* Pattern: MediaWiki table syntax + string concatenation (like ElementNavigation)
* Icon Data: CSS backgrounds defined in Hubs.css - see :root custom properties
]]
]]


local p = {}
local p = {}


-- Hub configuration with icon test methods
-- Hub configuration with CSS background icon identifiers and submenu items
-- Icon backgrounds defined in Hubs.css :root custom properties (--icon-*)
-- Submenu URLs cross-reference: Special:Ask queries and Category pages
local hubs = {
local hubs = {
     { label = 'Nations', url = 'Hub:Nations', method = 'inline_svg' },
     {
     { label = 'People', url = 'Hub:People', method = 'css_bg' },
        id = 'nations',
     { label = 'Organizations', url = 'Hub:Organizations', method = 'mw_file' },
        label = 'Nations',
     { label = 'Events', url = 'Hub:Events', method = 'direct_url' },
        url = 'Hub:Nations',
     { label = 'Topics', url = 'Hub:Topics', method = 'text_only' }
        icon = 'globe',
        submenu = {
            { label = 'All Nations', url = 'Special:Ask/-5B-5BCategory:Countries-5D-5D' },
            { label = 'ccNSO Members', url = 'Category:ccNSO_Members' },
            { label = 'GAC Representatives', url = 'Category:GAC' },
            { label = 'Regional Groups', url = 'Category:Regional_Internet_Registries' }
        }
    },
     {
        id = 'people',
        label = 'People',
        url = 'Hub:People',
        icon = 'user',
        submenu = {
            { label = 'All People', url = 'Special:Ask/-5B-5BCategory:People-5D-5D' },
            { label = 'ICANN Board', url = 'Category:ICANN_Board' },
            { label = 'Executives', url = 'Category:Executives' },
            { label = 'Authors', url = 'Category:Authors' }
        }
    },
     {
        id = 'organizations',
        label = 'Organizations',
        url = 'Hub:Organizations',
        icon = 'building',
        submenu = {
            { label = 'All Organizations', url = 'Special:Ask/-5B-5BCategory:Organizations-5D-5D' },
            { label = 'Supporting Organizations', url = 'Category:Supporting_Organizations' },
            { label = 'Advisory Committees', url = 'Category:Advisory_Committees' },
            { label = 'Registries', url = 'Category:Registries' }
        }
    },
     {
        id = 'events',
        label = 'Events',
        url = 'Hub:Events',
        icon = 'calendar',
        submenu = {
            { label = 'All Events', url = 'Special:Ask/-5B-5BCategory:Events-5D-5D' },
            { label = 'ICANN Meetings', url = 'Category:ICANN_Meetings' },
            { label = 'Regional Events', url = 'Category:Regional_Events' },
            { label = 'Upcoming', url = 'Special:Ask/-5B-5BHas-20start-20date::-3E{{CURRENTTIMESTAMP}}-5D-5D' }
        }
    },
     {
        id = 'topics',
        label = 'Topics',
        url = 'Hub:Topics',
        icon = 'layers',
        submenu = {
            { label = 'All Topics', url = 'Special:Ask/-5B-5BCategory:Topics-5D-5D' },
            { label = 'DNS', url = 'Category:DNS' },
            { label = 'Policy', url = 'Category:Policy' },
            { label = 'Technical', url = 'Category:Technical' }
        }
    }
}
}


-- Inline SVG code for Icon-User.svg
-- Generate submenu HTML from submenu items
local inlineSVG = '<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.75" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true" class="hub-icon"><path d="M20 21a8 8 0 0 0-16 0"/><circle cx="12" cy="7" r="4"/></svg>'
local function generateSubmenu(submenu)
    if not submenu or #submenu == 0 then
        return ''
    end
   
    local items = {}
    for _, item in ipairs(submenu) do
        -- Generate wikilink: [[url|label]] (no leading whitespace to prevent <pre> wrapping)
        table.insert(items, string.format(
            '<div class="hub-submenu__item">[[%s|%s]]</div>',
            item.url,
            item.label
        ))
    end
   
    return string.format(
        '<div class="hub-item__submenu" hidden>%s</div>',
        table.concat(items, '')
    )
end


-- Generate a single hub link item with specified icon method
-- Generate a single hub item with expandable structure
local function generateHubLink(hub)
local function generateHubItem(hub)
     local iconHtml = ''
     -- Icon as CSS background via data-icon attribute
     local wrapperClass = 'cdx-hub__link'
    -- CSS: .hub-icon[data-icon="globe"] { background-image: var(--icon-globe); }
     local iconHtml = string.format('<span class="hub-icon" data-icon="%s" aria-hidden="true"></span>', hub.icon)
      
      
     if hub.method == 'inline_svg' then
     -- Generate submenu markup
        -- Method 1: Inline SVG (baked in HTML)
     local submenuHtml = generateSubmenu(hub.submenu)
        iconHtml = inlineSVG
       
     elseif hub.method == 'css_bg' then
        -- Method 2: CSS background-image (add class for CSS to target)
        wrapperClass = 'cdx-hub__link hub-icon-css'
       
    elseif hub.method == 'mw_file' then
        -- Method 3: MediaWiki File reference
        iconHtml = '[[File:Icon-User.svg|24px|link=|class=hub-icon]]'
       
    elseif hub.method == 'direct_url' then
        -- Method 4: Direct image URL
        iconHtml = '<img src="https://icannwiki.org/images/9/9c/Icon-User.svg" width="24" height="24" class="hub-icon" alt="">'
       
    -- else: text_only - no icon
    end
      
      
    -- Hub item with expandable structure (no leading whitespace to prevent <pre> wrapping)
    -- data-hub: identifier for JS targeting
    -- data-state: collapsed/expanded state for CSS animations
     return string.format(
     return string.format(
         '<li class="cdx-hub__item"><span class="%s">%s[[%s|%s]]</span></li>',
         '<div class="hub-item" data-hub="%s" data-state="collapsed">' ..
         wrapperClass,
        '<div class="hub-item__trigger">' ..
        '%s' ..
        '<span class="hub-label">[[%s|%s]]</span>' ..
        '</div>%s' ..
        '</div>',
         hub.id,
         iconHtml,
         iconHtml,
         hub.url,
         hub.url,
         hub.label
         hub.label,
        submenuHtml
     )
     )
end
end
Line 56: Line 128:
-- Main render function
-- Main render function
function p.render(frame)
function p.render(frame)
     -- Generate all hub links
     -- Generate all hub items
     local hubLinks = {}
     local hubItems = {}
     for i, hub in ipairs(hubs) do
     for i, hub in ipairs(hubs) do
         hubLinks[i] = generateHubLink(hub)
         hubItems[i] = generateHubItem(hub)
     end
     end
      
      
     -- Build complete output using MediaWiki table syntax + HTML
     -- Build complete output using MediaWiki table syntax + HTML
    -- data-active-hub: tracks which hub is expanded (for CSS sibling effects)
    -- hub-drawer-overlay: dimmed background for mobile drawer
     local output = {
     local output = {
         '{| class="hub-nav-table"',
         '{| class="hub-nav-table"',
         '|- class="hub-nav-row"',
         '|- class="hub-nav-row"',
         '| colspan="2" |',
         '| colspan="2" |',
         '<div id="hub" class="cdx-hub" role="navigation" aria-label="Hub Navigation">',
         '<div id="hub" class="hub-navigation" role="navigation" aria-label="Hub Navigation" data-active-hub="">',
         '  <ul class="cdx-hub__list">',
         '  <div class="hub-bar">',
         '    ' .. table.concat(hubLinks, '\n   '),
         table.concat(hubItems, '\n'),
         '  </ul>',
         '  </div>',
        '  <div class="hub-drawer-overlay" hidden></div>',
         '</div>',
         '</div>',
         '<div id="hub-spacer" aria-hidden="true"></div>',
         '<div id="hub-spacer" aria-hidden="true"></div>',

Latest revision as of 13:51, 29 October 2025

Documentation for this module may be created at Module:ElementHubNavigation/doc

--[[
* Name: ElementHubNavigation
* Version: 2.1
* Author: Mark W. Datysgeld
* Description: Hub navigation module for Main Page with responsive design
* Notes: v2.1 fixes MediaWiki <pre> tag wrapping by removing leading whitespace
* Pattern: MediaWiki table syntax + string concatenation (like ElementNavigation)
* Icon Data: CSS backgrounds defined in Hubs.css - see :root custom properties
]]

local p = {}

-- Hub configuration with CSS background icon identifiers and submenu items
-- Icon backgrounds defined in Hubs.css :root custom properties (--icon-*)
-- Submenu URLs cross-reference: Special:Ask queries and Category pages
local hubs = {
    {
        id = 'nations',
        label = 'Nations',
        url = 'Hub:Nations',
        icon = 'globe',
        submenu = {
            { label = 'All Nations', url = 'Special:Ask/-5B-5BCategory:Countries-5D-5D' },
            { label = 'ccNSO Members', url = 'Category:ccNSO_Members' },
            { label = 'GAC Representatives', url = 'Category:GAC' },
            { label = 'Regional Groups', url = 'Category:Regional_Internet_Registries' }
        }
    },
    {
        id = 'people',
        label = 'People',
        url = 'Hub:People',
        icon = 'user',
        submenu = {
            { label = 'All People', url = 'Special:Ask/-5B-5BCategory:People-5D-5D' },
            { label = 'ICANN Board', url = 'Category:ICANN_Board' },
            { label = 'Executives', url = 'Category:Executives' },
            { label = 'Authors', url = 'Category:Authors' }
        }
    },
    {
        id = 'organizations',
        label = 'Organizations',
        url = 'Hub:Organizations',
        icon = 'building',
        submenu = {
            { label = 'All Organizations', url = 'Special:Ask/-5B-5BCategory:Organizations-5D-5D' },
            { label = 'Supporting Organizations', url = 'Category:Supporting_Organizations' },
            { label = 'Advisory Committees', url = 'Category:Advisory_Committees' },
            { label = 'Registries', url = 'Category:Registries' }
        }
    },
    {
        id = 'events',
        label = 'Events',
        url = 'Hub:Events',
        icon = 'calendar',
        submenu = {
            { label = 'All Events', url = 'Special:Ask/-5B-5BCategory:Events-5D-5D' },
            { label = 'ICANN Meetings', url = 'Category:ICANN_Meetings' },
            { label = 'Regional Events', url = 'Category:Regional_Events' },
            { label = 'Upcoming', url = 'Special:Ask/-5B-5BHas-20start-20date::-3E{{CURRENTTIMESTAMP}}-5D-5D' }
        }
    },
    {
        id = 'topics',
        label = 'Topics',
        url = 'Hub:Topics',
        icon = 'layers',
        submenu = {
            { label = 'All Topics', url = 'Special:Ask/-5B-5BCategory:Topics-5D-5D' },
            { label = 'DNS', url = 'Category:DNS' },
            { label = 'Policy', url = 'Category:Policy' },
            { label = 'Technical', url = 'Category:Technical' }
        }
    }
}

-- Generate submenu HTML from submenu items
local function generateSubmenu(submenu)
    if not submenu or #submenu == 0 then
        return ''
    end
    
    local items = {}
    for _, item in ipairs(submenu) do
        -- Generate wikilink: [[url|label]] (no leading whitespace to prevent <pre> wrapping)
        table.insert(items, string.format(
            '<div class="hub-submenu__item">[[%s|%s]]</div>',
            item.url,
            item.label
        ))
    end
    
    return string.format(
        '<div class="hub-item__submenu" hidden>%s</div>',
        table.concat(items, '')
    )
end

-- Generate a single hub item with expandable structure
local function generateHubItem(hub)
    -- Icon as CSS background via data-icon attribute
    -- CSS: .hub-icon[data-icon="globe"] { background-image: var(--icon-globe); }
    local iconHtml = string.format('<span class="hub-icon" data-icon="%s" aria-hidden="true"></span>', hub.icon)
    
    -- Generate submenu markup
    local submenuHtml = generateSubmenu(hub.submenu)
    
    -- Hub item with expandable structure (no leading whitespace to prevent <pre> wrapping)
    -- data-hub: identifier for JS targeting
    -- data-state: collapsed/expanded state for CSS animations
    return string.format(
        '<div class="hub-item" data-hub="%s" data-state="collapsed">' ..
        '<div class="hub-item__trigger">' ..
        '%s' ..
        '<span class="hub-label">[[%s|%s]]</span>' ..
        '</div>%s' ..
        '</div>',
        hub.id,
        iconHtml,
        hub.url,
        hub.label,
        submenuHtml
    )
end

-- Main render function
function p.render(frame)
    -- Generate all hub items
    local hubItems = {}
    for i, hub in ipairs(hubs) do
        hubItems[i] = generateHubItem(hub)
    end
    
    -- Build complete output using MediaWiki table syntax + HTML
    -- data-active-hub: tracks which hub is expanded (for CSS sibling effects)
    -- hub-drawer-overlay: dimmed background for mobile drawer
    local output = {
        '{| class="hub-nav-table"',
        '|- class="hub-nav-row"',
        '| colspan="2" |',
        '<div id="hub" class="hub-navigation" role="navigation" aria-label="Hub Navigation" data-active-hub="">',
        '  <div class="hub-bar">',
        table.concat(hubItems, '\n'),
        '  </div>',
        '  <div class="hub-drawer-overlay" hidden></div>',
        '</div>',
        '<div id="hub-spacer" aria-hidden="true"></div>',
        '|}'
    }
    
    return table.concat(output, '\n')
end

return p