Module:NavboxMobile: Difference between revisions
EnWikiAdmin (talk | contribs) No edit summary Tag: Reverted |
EnWikiAdmin (talk | contribs) No edit summary Tag: Reverted |
||
Line 80: | Line 80: | ||
end | end | ||
-- Function to check if the given HTML class is a list class | |||
local function has_list_class(htmlclass) | |||
local patterns = { | |||
'^' .. htmlclass .. '$', | |||
'%s' .. htmlclass .. '$', | |||
'^' .. htmlclass .. '%s', | |||
'%s' .. htmlclass .. '%s' | |||
} | |||
for arg, _ in pairs(args) do | |||
if type(arg) == 'string' and mw.ustring.find(arg, cfg.pattern.class) then | |||
for _, pattern in ipairs(patterns) do | |||
if mw.ustring.find(args[arg] or '', pattern) then | |||
return true | |||
end | |||
end | |||
end | |||
end | |||
return false | |||
end | |||
Revision as of 14:05, 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 = {}
-- Helper Functions
local function trim(s)
return (mw.ustring.gsub(s, "^%s*(.-)%s*$", "%1"))
end
local function addNewline(s)
if s:match('^[*:;#]') or s:match('^{|') then
return '\n' .. s .. '\n'
else
return s
end
end
local function processItem(item, nowrapitems)
if not item then
return '' -- Prevent nil errors
end
if item:sub(1, 2) == '{|' then
-- Applying nowrap to lines in a table does not make sense.
-- Add newlines to compensate for trim of x in |parm=x in a template.
return '\n' .. item .. '\n'
end
if nowrapitems == cfg.keyword.nowrapitems_yes then
local lines = {}
for line in (item .. '\n'):gmatch('([^\n]*)\n') do
local prefix, content = line:match('^([*:;#]+)%s*(.*)')
if prefix and not content:match(cfg.pattern.nowrap) then
line = format(cfg.nowrap_item, prefix, content)
end
table.insert(lines, line)
end
item = table.concat(lines, '\n')
end
if item:match('^[*:;#]') then
return '\n' .. item .. '\n'
end
return item
end
-- Function to add a new table row with optional gutter
local function addTableRow(tbl)
-- Check if the spacer rows are necessary based on the listclass or bodyclass
local suppressSpacer = false
if args.listclass then
suppressSpacer = suppressSpacer or args.listclass:find("hlist")
end
if args.bodyclass then
suppressSpacer = suppressSpacer or args.bodyclass:find("hlist")
end
-- Only add spacer rows if they are not explicitly suppressed
if tableRowAdded and not suppressSpacer then
tbl:tag('tr')
:css('height', '0.25em') -- Adjust spacer height as needed
:tag('td')
:attr('colspan', 2)
:css('background-color', 'transparent') -- Ensure spacer is invisible
:wikitext(' ')
end
tableRowAdded = true
return tbl:tag('tr')
end
-- Function to check if the given HTML class is a list class
local function has_list_class(htmlclass)
local patterns = {
'^' .. htmlclass .. '$',
'%s' .. htmlclass .. '$',
'^' .. htmlclass .. '%s',
'%s' .. htmlclass .. '%s'
}
for arg, _ in pairs(args) do
if type(arg) == 'string' and mw.ustring.find(arg, cfg.pattern.class) then
for _, pattern in ipairs(patterns) do
if mw.ustring.find(args[arg] or '', pattern) then
return true
end
end
end
end
return false
end
-- Function to check if the navbar is enabled
local function has_navbar()
return args[cfg.arg.navbar] ~= cfg.keyword.navbar_off
and args[cfg.arg.navbar] ~= cfg.keyword.navbar_plain
and (
args[cfg.arg.name]
or mw.getCurrentFrame():getParent():getTitle():gsub(cfg.pattern.sandbox, '')
~= cfg.pattern.navbox
)
end
-- Function to render the navigation bar
local function renderNavBar(titleCell)
if has_navbar() then
titleCell:tag('div')
:addClass('navbar-container') -- New container for navbar
:node(navbar{
args.name,
mini = 1,
fontstyle = (args.basestyle or '') .. ';' .. (args.titlestyle or '') .. ';background:none transparent;border:none;'
})
end
end
-- Function to render group rows
local function renderGroupRow(tbl, groupnum, level)
local groupRow = addTableRow(tbl)
local groupCell = groupRow:tag('th')
:attr('scope', 'row')
:addClass('navboxMobile-group-content')
:addClass('navboxMobile-group-level' .. level)
:cssText(args.basestyle)
:cssText(args['group' .. groupnum .. 'style'])
:wikitext(processItem(args['group' .. groupnum]))
-- Center align group content
groupCell:css('text-align', 'center')
-- Add spacer below group row for consistent spacing
tbl:tag('tr')
:tag('td')
:attr('colspan', 2)
:css('height', '0.25em') -- Spacer height
:wikitext(' ')
end
-- Function to render 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-content')
: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
if args.titlegroup then titleColspan = titleColspan - 1 end
titleCell
:addClass('navboxMobile-title')
:attr('colspan', titleColspan)
:cssText(args.basestyle)
:cssText(args.titlestyle)
:addClass('navboxMobile-list') -- Consistent list styling
:addClass(args.titleclass)
renderNavBar(titleCell)
titleCell
:tag('div')
:css('font-size', '114%')
:css('margin', '0') -- Remove default margins
:wikitext(addNewline(args.title))
end
-- Function to render above row
local function renderAboveRow(tbl)
if not args.above then return end
addTableRow(tbl)
:tag('td')
:addClass('navboxMobile-abovebelow-content')
:addClass(args.aboveclass)
:cssText(args.basestyle)
:cssText(args.abovestyle)
:css('padding', args.abovepadding or '0.5em') -- Optional padding
:attr('colspan', 2)
:tag('div')
:wikitext(addNewline(args.above))
end
-- Function to render below row
local function renderBelowRow(tbl)
if not args.below then return end
addTableRow(tbl)
:tag('td')
:addClass('navboxMobile-abovebelow-content')
:addClass(args.belowclass)
:cssText(args.basestyle)
:cssText(args.belowstyle)
:css('padding', args.belowpadding or '0.5em') -- Optional padding
:attr('colspan', 2)
:tag('div')
:wikitext(addNewline(args.below))
end
-- Function to render list rows
local function renderListRow(tbl, listnum)
-- Determine level (2 for main groups, 3 for subgroups)
local level = 2
if border == 'subgroup' then
level = 3
end
-- Render group row
renderGroupRow(tbl, listnum, level)
-- Continue with rendering the list
local listRow = addTableRow(tbl)
local listCell = listRow:tag('td')
:addClass('navboxMobile-list-content')
:addClass('navboxMobile-' .. ((listnum % 2 == 1) and 'odd' or 'even'))
:attr('colspan', 2)
:css('padding', '0 0.25em')
:css('width', '100%')
:cssText(args.liststyle)
:cssText(args['list' .. listnum .. 'style'])
if args['list' .. listnum] == 'child' then
-- Render nested subgroup as a separate table
for i = 1, 10 do
local nestedGroup = args['child' .. listnum .. '_group' .. i]
local nestedList = args['child' .. listnum .. '_list' .. i]
if not nestedGroup and not nestedList then break end
renderGroupRow(tbl, i, 3) -- Level 3 for subgroups
local nestedListRow = addTableRow(tbl)
local nestedListCell = nestedListRow:tag('td')
:addClass('navboxMobile-list-content')
:addClass('navboxMobile-' .. ((i % 2 == 1) and 'odd' or 'even'))
:attr('colspan', 2)
:css('padding', '0 0.25em')
:cssText(args['child' .. listnum .. '_list' .. i .. 'style'])
:wikitext(processItem(nestedList))
end
else
-- Render flat list
listCell:tag('div')
:css('padding', args.listpadding or '0 0.25em')
:wikitext(processItem(args['list' .. listnum], args.nowrapitems))
end
end
-- Function to add list styles
local function add_list_styles()
local frame = mw.getCurrentFrame()
local function add_list_templatestyles(htmlclass, templatestyles)
if has_list_class(htmlclass) then
return frame:extensionTag{
name = 'templatestyles', args = { src = templatestyles }
}
else
return ''
end
end
local hlist_styles = add_list_templatestyles('hlist', cfg.hlist_templatestyles)
local plainlist_styles = add_list_templatestyles('plainlist', cfg.plainlist_templatestyles)
-- Workaround for [[phab:T303378]]
if has_navbar() and hlist_styles == '' then
hlist_styles = frame:extensionTag{
name = 'templatestyles', args = { src = cfg.hlist_templatestyles }
}
end
-- hlist -> plainlist is best-effort to preserve old Common.css ordering.
return hlist_styles .. plainlist_styles
end
-- Function to add styles
local function add_navbox_mobile_styles(hiding_templatestyles)
local frame = mw.getCurrentFrame()
-- Function to add user-defined templatestyles
local function add_user_styles(templatestyles)
if templatestyles and templatestyles ~= '' then
return frame:extensionTag{
name = 'templatestyles',
args = { src = templatestyles }
}
end
return ''
end
-- Combine list styles with base and user styles
local list_styles = add_list_styles()
local base_templatestyles = cfg.templatestyles
local templatestyles = add_user_styles(args[cfg.arg.templatestyles])
local child_templatestyles = add_user_styles(args[cfg.arg.child_templatestyles])
-- Combine all styles into a single <div>
return mw.html.create('div')
:addClass('navboxMobile-styles')
:wikitext(
list_styles ..
base_templatestyles ..
templatestyles ..
child_templatestyles ..
table.concat(hiding_templatestyles)
)
:done()
end
-- Function to move and collect hiding templatestyles
local function move_hiding_templatestyles(args)
local gfind = string.gmatch
local gsub = string.gsub
local templatestyles_markers = {}
local strip_marker_pattern = '(\127[^\127]*UNIQ%-%-templatestyles%-%x+%-QINU[^\127]*\127)'
for k, arg in pairs(args) do
for marker in gfind(arg, strip_marker_pattern) do
table.insert(templatestyles_markers, marker)
end
args[k] = gsub(arg, strip_marker_pattern, '')
end
return templatestyles_markers
end
-- Function to render 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, cfg.category.horizontal_lists) end
if hasBackgroundColors() then table.insert(cats, cfg.category.background_colors) end
if isIllegible() then table.insert(cats, cfg.category.illegible) end
if border == 'subgroup' then table.insert(cats, cfg.category.subgroup) end
if args.tracking == 'no' then table.insert(cats, cfg.category.no_tracking) 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
-- Function to render main table
local function renderMainTable()
local tbl = mw.html.create('table')
:addClass('nowraplinks')
:addClass('navboxMobile-list')
:addClass(args.bodyclass)
:cssText(args.bodystyle)
:cssText(args.style)
:css('margin-top', '0')
:css('margin-bottom', '0')
:css('border-spacing', '0')
if border == 'subgroup' or border == 'child' or border == 'none' then
tbl
:addClass('navboxMobile-subgroup')
:css('width', '100%')
else
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 = {}
-- Move and collect hiding templatestyles
local hiding_templatestyles = move_hiding_templatestyles(args)
-- Collect list numbers
for k, v in pairs(args) do
if type(k) == 'string' then
local listnum = k:match('^list(%d+)$')
if listnum then table.insert(listnums, tonumber(listnum)) end
end
end
table.sort(listnums)
border = trim(args.border or args[1] or '')
if border == cfg.keyword.border_child then
border = cfg.keyword.border_subgroup
end
-- Render the main body of the navboxMobile
local tbl = renderMainTable()
-- Create the final HTML with styles
local res = mw.html.create()
-- Inject styles analogous to Module:Navbox
res:node(add_navbox_mobile_styles(hiding_templatestyles))
-- 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')
:addClass('navboxMobile')
if args.title or args.above or (args.group1 and not args.group2) then
navWrapper:attr(
'aria-labelledby',
mw.uri.anchorEncode(args.title or args.above or args.group1)
)
else
navWrapper:attr('aria-label', cfg.aria_label)
end
navWrapper:node(tbl)
res:node(navWrapper)
elseif border == 'subgroup' or border == 'child' then
-- Assume this navboxMobile is inside a parent navboxMobile's list cell
-- Insert closing and opening divs to manage padding
local subgroupWrapper = mw.html.create('div')
:addClass('navboxMobile-container') -- Consistent wrapping container
:node(tbl) -- Add the table as a child
res:node(subgroupWrapper)
else
-- Wrap the table within a navigation div with additional classes and styles
local navWrapper = mw.html.create('div')
:addClass('navboxMobile') -- Add consistent container
:attr('role', 'navigation')
:cssText(args.bodystyle)
:cssText(args.style)
:css('padding', '2px') -- Adjust padding as needed
if args.title or args.above or (args.group1 and not args.group2) then
navWrapper:attr(
'aria-labelledby',
mw.uri.anchorEncode(args.title or args.above or args.group1)
)
else
navWrapper:attr('aria-label', cfg.aria_label)
end
navWrapper:node(tbl)
res:node(navWrapper)
end
-- Render tracking categories
renderTrackingCategories(res)
return tostring(res)
end
-- Public function to invoke navboxMobile
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.
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