Module:Icon: Difference between revisions
More actions
starcitizen>Alistar Bot Icon.src: decode the filepath URL so client-side mask consumers get real chars (not :) (via update-page on MediaWiki MCP Server) |
Bonsaibauer (talk | contribs) m 1 revision imported: Import of Star-Citizen-Modules |
(No difference)
| |
Latest revision as of 21:58, 4 November 2026
- getIconSrcL32
- p.srcL43
- iconClassL53
- renderMaskL67
- renderThumbL85
- p.renderL101
- p.mainL122
|type = website |description = Module:Icon is a template page used on the Star Citizen Wiki. Templates are pages that are embedded (transcluded) into other pages to allow for the repetition of information. |site_name = Star Citizen Wiki |locale = en-gb }}
Renders a single inline icon, either as a rasterised [[File:]] thumbnail or as a CSS mask filled with currentColor (so it recolours with the surrounding text). Optionally linked.
Required by Module:BadgeLua, Module:Boolean, Module:IconText, Module:IconLink, and Module:AGGridColumns' Kind/BadgeList and Kind/Boolean; not invoked from templates.
For module editors
API
p.render(props) returns markup only, no <templatestyles> (see Styles below).
| Field | Type | Required | Default | Description |
|---|---|---|---|---|
icon
|
string
|
Yes | File name (without the File: prefix).
| |
size
|
string
|
No | 20px
|
Mode-dependent: with mask a CSS length for width and height (1em works); otherwise a MediaWiki image size passed into [[File:…]] (x16px works, 1em does not).
|
mask
|
boolean
|
No | false
|
Render as a currentColor mask instead of a thumbnail.
|
link
|
string
|
No | Link target; empty/nil leaves the icon unlinked. | |
title
|
string
|
No | Tooltip / caption. | |
class
|
string
|
No | Extra class(es), appended after the base t-icon (+ t-icon--mask in mask mode).
|
p.src(icon) resolves an icon file name to its plain (entity-decoded) URL, for a caller that paints its own mask client-side, e.g. an AG Grid badge cell whose iconSrc is read into a CSS custom property by JavaScript rather than an HTML style attribute.
p.main(frame) is the #invoke entry point: reads named arguments (and positional 1 as icon; title also accepts iconTitle), emits Module:Icon/styles.css, and forwards to render. mask accepts any Module:Yesno truthy value.
Gotchas
- Mask mode's
mask-imagefetch is constrained by the wiki's enforced CSPdefault-src, not by CORS: the file URL must resolve to a host that policy allows, regardless of cross-origin headers. - The
nowikifilepathparser function HTML-entity-encodes:as:; a browser decodes that inside an HTMLstyleattribute (the server-rendered mask) but not when a script assigns it to a CSS custom property, which is whyp.srcdecodes it back to plain text for client-side callers. - Thumbnail mode always prepends
class=metadatato the[[File:...]]markup, ahead oft-icon.
Styles
render returns markup only, no <templatestyles>: callers must load Module:Icon/styles.css themselves. This keeps the style tag out of any wikilink label when a caller wraps the icon in a link of its own (e.g. BadgeLua's whole-pill link). p.main (the wikitext entry point) does emit the stylesheet, since it returns a standalone value.
require('strict')
--- @module Icon
--- Renders a single inline icon, either as a rasterised [[File:]] thumbnail or as
--- a CSS mask filled with currentColor (so it recolours with the surrounding
--- text). Optionally linked. The shared icon primitive behind Module:IconText,
--- Module:IconLink, and Module:BadgeLua.
---
--- `render` returns markup ONLY — no <templatestyles>. Callers load
--- Module:Icon/styles.css themselves, so the style tag never ends up inside a
--- wikilink label when a caller wraps the icon in a link of its own (e.g.
--- BadgeLua's whole-pill link). The wikitext entry point `main` does emit the
--- stylesheet, since it returns a standalone value.
local yesno = require('Module:Yesno')
local p = {}
local DEFAULT_SIZE = '20px'
--- @class IconProps
--- @field icon string File name (without the File: prefix)
--- @field size? string CSS length; default '20px'
--- @field mask? boolean Render as a currentColor mask instead of a thumbnail
--- @field link? string Link target; nil/empty = unlinked
--- @field title? string Tooltip / caption
--- @field class? string Extra class(es) on the icon element
--- Resolve the icon's file URL for use as a CSS mask image.
--- @param icon string
--- @return string
local function getIconSrc(icon)
return mw.getCurrentFrame():callParserFunction('filepath', { icon, 'nowiki' })
end
--- Public: resolve an icon file name to its URL (for callers that paint their own
--- mask client-side, e.g. an AG Grid badge cell). Decoded to a plain URL — the
--- `nowiki` filepath HTML-encodes `:` as `:`, which a browser decodes inside an
--- HTML style attribute (the server-rendered mask) but NOT when a script assigns it
--- to a CSS custom property, so client-side callers need the real characters.
--- @param icon string
--- @return string
function p.src(icon)
return mw.text.decode(getIconSrc(icon), true)
end
--- Space-joined class list for the icon element: the base `t-icon`, the `--mask`
--- modifier when masked, then any caller class (its existing slot class, e.g.
--- `t-icon-text__icon`, so consumer selectors keep working).
--- @param mask boolean
--- @param extra string|nil
--- @return string
local function iconClass(mask, extra)
local classes = { 't-icon' }
if mask then
classes[#classes + 1] = 't-icon--mask'
end
if extra and extra ~= '' then
classes[#classes + 1] = extra
end
return table.concat(classes, ' ')
end
--- Render as a recolourable CSS mask span, optionally wrapped in a link.
--- @param props IconProps
--- @return string
local function renderMask(props)
local span = mw.html.create('span'):addClass(iconClass(true, props.class)):cssText(
string.format('--t-icon-url: "%s"; width: %s; height: %s;', getIconSrc(props.icon), props.size, props.size)
)
if props.title then
span:attr('title', props.title)
end
local markup = tostring(span)
if props.link and props.link ~= '' then
return string.format('[[%s|%s]]', props.link, markup)
end
return markup
end
--- Render as a [[File:]] thumbnail. `link=` (empty) suppresses the file link
--- unless a target is given; `metadata` keeps it out of the MultimediaViewer.
--- @param props IconProps
--- @return string
local function renderThumb(props)
local parts = {
'File:' .. props.icon,
props.size,
'link=' .. (props.link or ''),
'class=metadata ' .. iconClass(false, props.class),
}
if props.title then
parts[#parts + 1] = props.title
end
return '[[' .. table.concat(parts, '|') .. ']]'
end
--- Render an icon. Returns markup only (no templatestyles).
--- @param props IconProps
--- @return string
function p.render(props)
if not props.icon or props.icon == '' then
error('Module:Icon: no icon provided')
end
local resolved = {
icon = props.icon,
size = props.size or DEFAULT_SIZE,
mask = props.mask and true or false,
link = props.link,
title = props.title,
class = props.class,
}
if resolved.mask then
return renderMask(resolved)
end
return renderThumb(resolved)
end
--- Wikitext entry point. Unlike `render`, this emits the stylesheet.
--- @param frame mw.frame
--- @return string
function p.main(frame)
local getArgs = require('Module:Arguments').getArgs
local args = getArgs(frame)
local styles = mw.getCurrentFrame():extensionTag({
name = 'templatestyles',
args = { src = 'Module:Icon/styles.css' },
})
return styles
.. p.render({
icon = args.icon or args[1],
size = args.size,
mask = yesno(args.mask),
link = args.link,
title = args.title or args.iconTitle,
class = args.class,
})
end
return p