Module:NavboxMobile: Difference between revisions
EnWikiAdmin (talk | contribs) No edit summary |
EnWikiAdmin (talk | contribs) No edit summary |
||
Line 1: | Line 1: | ||
-- Module:NavboxMobile | |||
-- This module implements {{navbox}} for MobileFrontend | -- This module implements {{navbox}} for MobileFrontend | ||
Line 6: | Line 7: | ||
local cfg = mw.loadData('Module:NavboxMobile/configuration') | local cfg = mw.loadData('Module:NavboxMobile/configuration') | ||
local navbar = require('Module:Navbar')._navbar | local navbar = require('Module:Navbar')._navbar | ||
local getArgs -- | local getArgs -- Lazily initialized | ||
local inArray = require('Module:TableTools').inArray | local inArray = require('Module:TableTools').inArray | ||
local format = string.format | local format = string.format | ||
Line 15: | Line 16: | ||
local listnums = {} | local listnums = {} | ||
-- Function to inject styles from styles.css | |||
local function addNavboxMobileStyles() | local function addNavboxMobileStyles() | ||
return mw.html.create('style') | |||
:attr('data-mw-deduplicate', 'TemplateStyles-navboxmobile') -- Ensures styles are not duplicated | |||
:wikitext(mw.loadData('Module:NavboxMobile/styles.css')) | |||
:done() | |||
end | end | ||
-- Helper function to trim whitespace | |||
local function trim(s) | local function trim(s) | ||
return (mw.ustring.gsub(s, "^%s*(.-)%s*$", "%1")) | return (mw.ustring.gsub(s, "^%s*(.-)%s*$", "%1")) | ||
end | end | ||
-- Helper function to add newlines based on content | |||
local function addNewline(s) | local function addNewline(s) | ||
if s:match('^[*:;#]') or s:match('^{|') then | if s:match('^[*:;#]') or s:match('^{|') then | ||
Line 35: | Line 38: | ||
end | end | ||
-- Function to add a new table row with optional gutter | |||
local function addTableRow(tbl) | local function addTableRow(tbl) | ||
if tableRowAdded then | if tableRowAdded then | ||
tbl | tbl | ||
Line 42: | Line 45: | ||
:css('height', '2px') | :css('height', '2px') | ||
:tag('td') | :tag('td') | ||
:attr('colspan',2) | :attr('colspan', 2) | ||
-- Note: You can add additional gutter styling here if needed | |||
end | end | ||
tableRowAdded = true | tableRowAdded = true | ||
return tbl:tag('tr') | return tbl:tag('tr') | ||
end | end | ||
-- Function to render the navigation bar | |||
local function renderNavBar(titleCell) | local function renderNavBar(titleCell) | ||
local spacerSide = nil | local spacerSide = nil | ||
if args.navbar == 'off' then | if args.navbar == 'off' then | ||
if args.state == 'plain' then spacerSide = 'right' end | if args.state == 'plain' then spacerSide = 'right' end | ||
elseif args.navbar == 'plain' or (not args.name and mw.getCurrentFrame():getParent():getTitle():gsub('/sandbox$', '') == 'Template:navbox') then | elseif args.navbar == 'plain' or (not args.name and mw.getCurrentFrame():getParent():getTitle():gsub('/sandbox$', '') == 'Template:navbox') then | ||
if args.state ~= 'plain' then spacerSide = 'left' end | if args.state ~= 'plain' then spacerSide = 'left' end | ||
else | else | ||
if args.state == 'plain' then spacerSide = 'right' end | if args.state == 'plain' then spacerSide = 'right' end | ||
Line 74: | Line 70: | ||
end | end | ||
if spacerSide then | if spacerSide then | ||
titleCell | titleCell | ||
Line 113: | Line 108: | ||
local titleColspan = 2 | local titleColspan = 2 | ||
-- | -- Adjust colspan based on image presence if needed | ||
if args.titlegroup then titleColspan = titleColspan - 1 end | if args.titlegroup then titleColspan = titleColspan - 1 end | ||
Line 137: | Line 131: | ||
local function getAboveBelowColspan() | local function getAboveBelowColspan() | ||
return 2 | |||
end | end | ||
Line 196: | Line 187: | ||
listCell:cssText(args.liststyle) | listCell:cssText(args.liststyle) | ||
listCell:addClass('navboxMobile-list') | listCell:addClass('navboxMobile-list') | ||
-- Check for nested lists (e.g., child1_groupX) | -- Check for nested lists (e.g., child1_groupX) | ||
local nestedGroup = args['child' .. listnum .. '_group1'] | local nestedGroup = args['child' .. listnum .. '_group1'] | ||
Line 235: | Line 226: | ||
end | end | ||
end | end | ||
-- | -- | ||
Line 245: | Line 235: | ||
local listClasses = {'plainlist', 'hlist', 'hlist hnum', 'hlist hwrap', 'hlist vcard', 'vcard hlist', 'hlist vevent'} | local listClasses = {'plainlist', 'hlist', 'hlist hnum', 'hlist hwrap', 'hlist vcard', 'vcard hlist', 'hlist vevent'} | ||
for | for _, cls in ipairs(listClasses) do | ||
if args.listclass == cls or args.bodyclass == cls then | if args.listclass == cls or args.bodyclass == cls then | ||
return false | return false | ||
Line 255: | Line 245: | ||
local function hasBackgroundColors() | local function hasBackgroundColors() | ||
return mw.ustring.match(args.titlestyle or '','background') or mw.ustring.match(args.groupstyle or '','background') or mw.ustring.match(args.basestyle or '','background') | return mw.ustring.match(args.titlestyle or '', 'background') | ||
or mw.ustring.match(args.groupstyle or '', 'background') | |||
or mw.ustring.match(args.basestyle or '', 'background') | |||
end | end | ||
Line 281: | Line 273: | ||
local function renderTrackingCategories(builder) | local function renderTrackingCategories(builder) | ||
local title = mw.title.getCurrentTitle() | local title = mw.title.getCurrentTitle() | ||
if title.namespace ~= 10 then return end -- | if title.namespace ~= 10 then return end -- Not in template space | ||
local subpage = title.subpageText | local subpage = title.subpageText | ||
if subpage == 'doc' or subpage == 'sandbox' or subpage == 'testcases' then return end | if subpage == 'doc' or subpage == 'sandbox' or subpage == 'testcases' then return end | ||
for | for _, cat in ipairs(getTrackingCategories()) do | ||
builder:wikitext('[[Category:' .. cat .. ']]') | builder:wikitext('[[Category:' .. cat .. ']]') | ||
end | end | ||
Line 298: | Line 290: | ||
:addClass(args.bodyclass) | :addClass(args.bodyclass) | ||
-- Uncomment and implement collapsibility if needed | |||
--[[if args.title and (args.state ~= 'plain' and args.state ~= 'off') then | --[[if args.title and (args.state ~= 'plain' and args.state ~= 'off') then | ||
tbl | tbl | ||
Line 310: | Line 303: | ||
:cssText(args.bodystyle) | :cssText(args.bodystyle) | ||
:cssText(args.style) | :cssText(args.style) | ||
else -- | else -- Regular navbox - bodystyle and style will be applied to the wrapper table | ||
tbl | tbl | ||
:addClass('navboxMobile-inner') | :addClass('navboxMobile-inner') | ||
Line 320: | Line 313: | ||
renderTitleRow(tbl) | renderTitleRow(tbl) | ||
renderAboveRow(tbl) | renderAboveRow(tbl) | ||
for | for _, listnum in ipairs(listnums) do | ||
renderListRow(tbl, listnum) | renderListRow(tbl, listnum) | ||
end | end | ||
Line 328: | Line 321: | ||
end | end | ||
-- | |||
-- Main function to render NavboxMobile | |||
-- | |||
function p._navboxMobile(navboxMobileArgs) | function p._navboxMobile(navboxMobileArgs) | ||
args = navboxMobileArgs | args = navboxMobileArgs | ||
Line 343: | Line 339: | ||
border = trim(args.border or args[1] or '') | border = trim(args.border or args[1] or '') | ||
-- | -- Render the main body of the navboxMobile | ||
local tbl = renderMainTable() | local tbl = renderMainTable() | ||
Line 357: | Line 353: | ||
local navWrapper = mw.html.create('div') | local navWrapper = mw.html.create('div') | ||
:attr('role', 'navigation') | :attr('role', 'navigation') | ||
:attr('aria-label', cfg.aria_label) | :attr('aria-label', cfg.aria_label) -- Ensure cfg.aria_label is defined appropriately | ||
:node(tbl) | :node(tbl) | ||
res:node(navWrapper) | res:node(navWrapper) | ||
elseif border == 'subgroup' or border == 'child' then | elseif border == 'subgroup' or border == 'child' then | ||
-- Assume this navboxMobile is | -- Assume this navboxMobile is being rendered in a list cell of a parent navboxMobile | ||
-- Insert closing and opening divs to manage padding | -- Insert closing and opening divs to manage padding | ||
res | res | ||
Line 375: | Line 371: | ||
:cssText(args.bodystyle) | :cssText(args.bodystyle) | ||
:cssText(args.style) | :cssText(args.style) | ||
:node(tbl) | |||
res:node(navWrapper) | res:node(navWrapper) | ||
end | end | ||
Line 388: | Line 381: | ||
end | end | ||
-- | |||
-- Entry point function for the template | |||
-- | |||
function p.navboxMobile(frame) | function p.navboxMobile(frame) | ||
if not getArgs then | if not getArgs then | ||
Line 395: | Line 391: | ||
-- Read the arguments in the order they'll be output in, to make references number in the right order. | -- Read the arguments in the order they'll be output in, to make references number in the right order. | ||
-- This ensures that list numbers are processed sequentially. | |||
local _ | local _ | ||
_ = args.title | _ = args.title |
Revision as of 01:34, 24 November 2024
This module implements the {{Navbox}} template for mobile devices when Extension:MobileFrontend is installed. This mobile version differs from the desktop version as follows:
- The table does not collapse.
- The list cells, e.g. group1, list1, each display across the width of the screen
- Images are not displayed, as it increases the complexity of the layout while reducing the area needed for content.
Original from wikipedia::Module:User:Lady G2016/NavboxMobile/doc
Overview
This approach works by utilizing CSS class rules. See: Hiding content on desktop devices
An onlymobile
class in MediaWiki:Common.css will not display classes intended for mobile devices.
A nomobile
class in MediaWiki:Mobile.css will not display classes intended for desktop devices.
Use these classes together inside the same template. The template will be rendered twice, but only display once depending on the view (desktop or mobile).
Module description
The module was copied from Module:Navbox and modified as follows:
- All instances of
navbox-
were replaced withnavboxMobile-
to provide unique CSS identifiers that are independent ofnavbox
. - All statements which processed images were removed. The statements remain as comments to show what was changed.
In function renderListRow()
, the cell pair of (groupCell, listCell) appearing as a single row is broken into separate rows by adding local row = addTableRow(tbl)
just before listCell.
Adding this row and removing the images are the only functional changes made to the existing navbox
template. Except for removal of the images, the parameters used in the current navbox
are supported.
Implementation
MediaWiki:Common.css
In MediaWiki:Common.css, add:
/* Only for mobile devices */ .onlymobile { display:none; }
MediaWiki:Mobile.css
In MediaWiki:Mobile.css, add:
/* Only for desktop devices */ .nomobile { display:none; }
MobileFrontend processes MediaWiki:Mobile.css. To provide an independent class that is not affected by the navbox rules, copy all of the navbox
and hlist
CSS styles from MediaWiki:Common.css to MediaWiki:Mobile.css.
Replace all navbox-
with navboxMobile-
. For example, navbox-title
becomes navboxMobile-title
. This will match the CSS properties in Module:NavboxMobile.
An example page can be found at User:Lady G2016/MediaWiki:Mobile.css.
Modify Template:Navbox to call both the desktop and mobile Lua modules as follows:
<div class="nomobile">{{#invoke:Navbox|navbox}}</div><div class="onlymobile">{{#invoke:NavboxMobile|navboxMobile}}</div><noinclude> {{Documentation}} </noinclude>
This will render both the desktop (navbox) and mobile (navboxMobile) versions. When displaying from a desktop view, MediaWiki:Common.css will not display the mobile template. When displaying from a mobile, only the mobile template will be displayed.
Constraints and limitations
This template was developed with MediaWiki 1.27.4 (Long Term Support) and MobileFrontEnd 1.0.0 (29 June 2016). No testing was performed with other versions.
The navbox class is explicitly disabled in /extensions/MobileFrontend/resources/skins.minerva.content.styles/hacks.less. Attempts to override navbox using $wgMFRemovableClasses
do not display as intended when the navbox
class is enabled.
-- Module:NavboxMobile
-- This module implements {{navbox}} for MobileFrontend
local p = {}
require('strict')
local cfg = mw.loadData('Module:NavboxMobile/configuration')
local navbar = require('Module:Navbar')._navbar
local getArgs -- Lazily initialized
local inArray = require('Module:TableTools').inArray
local format = string.format
local args
local tableRowAdded = false
local border
local listnums = {}
-- Function to inject styles from styles.css
local function addNavboxMobileStyles()
return mw.html.create('style')
:attr('data-mw-deduplicate', 'TemplateStyles-navboxmobile') -- Ensures styles are not duplicated
:wikitext(mw.loadData('Module:NavboxMobile/styles.css'))
:done()
end
-- Helper function to trim whitespace
local function trim(s)
return (mw.ustring.gsub(s, "^%s*(.-)%s*$", "%1"))
end
-- Helper function to add newlines based on content
local function addNewline(s)
if s:match('^[*:;#]') or s:match('^{|') then
return '\n' .. s ..'\n'
else
return s
end
end
-- Function to add a new table row with optional gutter
local function addTableRow(tbl)
if tableRowAdded then
tbl
:tag('tr')
:css('height', '2px')
:tag('td')
:attr('colspan', 2)
-- Note: You can add additional gutter styling here if needed
end
tableRowAdded = true
return tbl:tag('tr')
end
-- Function to render the navigation bar
local function renderNavBar(titleCell)
local spacerSide = nil
if args.navbar == 'off' then
if args.state == 'plain' then spacerSide = 'right' end
elseif args.navbar == 'plain' or (not args.name and mw.getCurrentFrame():getParent():getTitle():gsub('/sandbox$', '') == 'Template:navbox') then
if args.state ~= 'plain' then spacerSide = 'left' end
else
if args.state == 'plain' then spacerSide = 'right' end
titleCell:wikitext(navbar{
args.name,
mini = 1,
fontstyle = (args.basestyle or '') .. ';' .. (args.titlestyle or '') .. ';background:none transparent;border:none;'
})
end
if spacerSide then
titleCell
:tag('span')
:css('float', spacerSide)
:css('width', '6em')
:wikitext(' ')
end
end
--
-- Title row
--
local function renderTitleRow(tbl)
if not args.title then return end
local titleRow = addTableRow(tbl)
if args.titlegroup then
titleRow
:tag('th')
:attr('scope', 'row')
:addClass('navboxMobile-group')
:addClass(args.titlegroupclass)
:cssText(args.basestyle)
:cssText(args.groupstyle)
:cssText(args.titlegroupstyle)
:wikitext(args.titlegroup)
end
local titleCell = titleRow:tag('th'):attr('scope', 'col')
if args.titlegroup then
titleCell
:css('border-left', '2px solid #fdfdfd')
:css('width', '100%')
end
local titleColspan = 2
-- Adjust colspan based on image presence if needed
if args.titlegroup then titleColspan = titleColspan - 1 end
titleCell
:cssText(args.basestyle)
:cssText(args.titlestyle)
:addClass('navboxMobile-title')
:attr('colspan', titleColspan)
renderNavBar(titleCell)
titleCell
:tag('div')
:addClass(args.titleclass)
:css('font-size', '114%')
:wikitext(addNewline(args.title))
end
--
-- Above/Below rows
--
local function getAboveBelowColspan()
return 2
end
local function renderAboveRow(tbl)
if not args.above then return end
addTableRow(tbl)
:tag('td')
:addClass('navboxMobile-abovebelow')
:addClass(args.aboveclass)
:cssText(args.basestyle)
:cssText(args.abovestyle)
:attr('colspan', getAboveBelowColspan())
:tag('div')
:wikitext(addNewline(args.above))
end
local function renderBelowRow(tbl)
if not args.below then return end
addTableRow(tbl)
:tag('td')
:addClass('navboxMobile-abovebelow')
:addClass(args.belowclass)
:cssText(args.basestyle)
:cssText(args.belowstyle)
:attr('colspan', getAboveBelowColspan())
:tag('div')
:wikitext(addNewline(args.below))
end
--
-- List rows
--
local function renderListRow(tbl, listnum)
local row = addTableRow(tbl)
-- Process group
if args['group' .. listnum] then
local groupCell = row:tag('th')
groupCell
:attr('scope', 'row')
:addClass('navboxMobile-group')
:cssText(args.basestyle)
:cssText(args.groupstyle)
:cssText(args['group' .. listnum .. 'style'])
:wikitext(args['group' .. listnum])
end
-- Add table row for the list
row = addTableRow(tbl)
local listCell = row:tag('td')
listCell:attr('colspan', 2)
listCell:css('width', '100%')
listCell:cssText(args.liststyle)
listCell:addClass('navboxMobile-list')
-- Check for nested lists (e.g., child1_groupX)
local nestedGroup = args['child' .. listnum .. '_group1']
local nestedList = args['child' .. listnum .. '_list1']
if nestedGroup or nestedList then
local nestedTbl = mw.html.create('table')
:addClass('navboxMobile-subgroup')
:css('width', '100%')
for i = 1, 10 do -- Assume a max of 10 nested groups/lists
local nestedGroup = args['child' .. listnum .. '_group' .. i]
local nestedList = args['child' .. listnum .. '_list' .. i]
if not nestedGroup and not nestedList then break end
local nestedRow = addTableRow(nestedTbl)
if nestedGroup then
nestedRow:tag('th')
:addClass('navboxMobile-group')
:cssText(args.basestyle)
:cssText(args['child' .. listnum .. '_group' .. i .. 'style'])
:wikitext(nestedGroup)
end
if nestedList then
nestedRow:tag('td')
:addClass('navboxMobile-list')
:cssText(args['child' .. listnum .. '_list' .. i .. 'style'])
:wikitext(addNewline(nestedList))
end
end
listCell:node(nestedTbl)
else
listCell:wikitext(addNewline(args['list' .. listnum]))
end
end
--
-- Tracking categories
--
local function needsHorizontalLists()
if border == 'child' or border == 'subgroup' or args.tracking == 'no' then return false end
local listClasses = {'plainlist', 'hlist', 'hlist hnum', 'hlist hwrap', 'hlist vcard', 'vcard hlist', 'hlist vevent'}
for _, cls in ipairs(listClasses) do
if args.listclass == cls or args.bodyclass == cls then
return false
end
end
return true
end
local function hasBackgroundColors()
return mw.ustring.match(args.titlestyle or '', 'background')
or mw.ustring.match(args.groupstyle or '', 'background')
or mw.ustring.match(args.basestyle or '', 'background')
end
local function isIllegible()
local styleratio = require('Module:Color contrast')._styleratio
for key, style in pairs(args) do
if tostring(key):match("style$") then
if styleratio{mw.text.unstripNoWiki(style)} < 4.5 then
return true
end
end
end
return false
end
local function getTrackingCategories()
local cats = {}
if needsHorizontalLists() then table.insert(cats, 'Navigational boxes without horizontal lists') end
if hasBackgroundColors() then table.insert(cats, 'navboxMobiles using background colours') end
if isIllegible() then table.insert(cats, 'Potentially illegible navboxMobiles') end
return cats
end
local function renderTrackingCategories(builder)
local title = mw.title.getCurrentTitle()
if title.namespace ~= 10 then return end -- Not in template space
local subpage = title.subpageText
if subpage == 'doc' or subpage == 'sandbox' or subpage == 'testcases' then return end
for _, cat in ipairs(getTrackingCategories()) do
builder:wikitext('[[Category:' .. cat .. ']]')
end
end
--
-- Main navboxMobile tables
--
local function renderMainTable()
local tbl = mw.html.create('table')
:addClass('nowraplinks')
:addClass(args.bodyclass)
-- Uncomment and implement collapsibility if needed
--[[if args.title and (args.state ~= 'plain' and args.state ~= 'off') then
tbl
:addClass('collapsible')
:addClass(args.state or 'autocollapse')
end]]
tbl:css('border-spacing', 0)
if border == 'subgroup' or border == 'child' or border == 'none' then
tbl
:addClass('navboxMobile-subgroup')
:cssText(args.bodystyle)
:cssText(args.style)
else -- Regular navbox - bodystyle and style will be applied to the wrapper table
tbl
:addClass('navboxMobile-inner')
:css('background', 'transparent')
:css('color', 'inherit')
end
tbl:cssText(args.innerstyle)
renderTitleRow(tbl)
renderAboveRow(tbl)
for _, listnum in ipairs(listnums) do
renderListRow(tbl, listnum)
end
renderBelowRow(tbl)
return tbl
end
--
-- Main function to render NavboxMobile
--
function p._navboxMobile(navboxMobileArgs)
args = navboxMobileArgs
-- Reset state variables to prevent state leakage between invocations
tableRowAdded = false
listnums = {}
for k, v in pairs(args) do
local listnum = ('' .. k):match('^list(%d+)$')
if listnum then table.insert(listnums, tonumber(listnum)) end
end
table.sort(listnums)
border = trim(args.border or args[1] or '')
-- Render the main body of the navboxMobile
local tbl = renderMainTable()
-- Create the final HTML with styles
local res = mw.html.create()
-- Inject styles
res:node(addNavboxMobileStyles())
-- Handle wrapping based on border parameter
if border == 'none' then
-- Wrap the table within a navigation div with ARIA attributes
local navWrapper = mw.html.create('div')
:attr('role', 'navigation')
:attr('aria-label', cfg.aria_label) -- Ensure cfg.aria_label is defined appropriately
:node(tbl)
res:node(navWrapper)
elseif border == 'subgroup' or border == 'child' then
-- Assume this navboxMobile is being rendered in a list cell of a parent navboxMobile
-- Insert closing and opening divs to manage padding
res
:wikitext('</div>') -- Close parent div
:node(tbl)
:wikitext('<div>') -- Reopen parent div
else
-- Wrap the table within a navigation div with additional classes and styles
local navWrapper = mw.html.create('div')
:attr('role', 'navigation')
:addClass('navboxMobile') -- Add appropriate classes
:css('border-spacing', 0)
:cssText(args.bodystyle)
:cssText(args.style)
:node(tbl)
res:node(navWrapper)
end
-- Render tracking categories
renderTrackingCategories(res)
return tostring(res)
end
--
-- Entry point function for the template
--
function p.navboxMobile(frame)
if not getArgs then
getArgs = require('Module:Arguments').getArgs
end
args = getArgs(frame, {wrappers = 'Template:Navbox'})
-- Read the arguments in the order they'll be output in, to make references number in the right order.
-- This ensures that list numbers are processed sequentially.
local _
_ = args.title
_ = args.above
for i = 1, 20 do
_ = args["group" .. tostring(i)]
_ = args["list" .. tostring(i)]
end
_ = args.below
return p._navboxMobile(args)
end
return p