Jump to content
Toggle menu
  • 1 articles
  • 7 files
  • 3 users
  • 102 edits
Shroudforge
Toggle preferences menu
Toggle personal menu
Not logged in
Your IP address will be publicly visible if you make any edits.

Module:Icon: Difference between revisions

From Shroudforge
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)
 
m 1 revision imported: Import of Star-Citizen-Modules
 
(No difference)

Latest revision as of 21:58, 4 November 2026

Module documentation
<details class="t-mbox t-mbox--notice navigation-not-searchable noexcerpt t-dependencies" open="no"><summary class="t-mbox__header">Technical detailswiki-tools · uses 3</summary>
Source
Synced with wiki-tools on GitHub
Used by
Unavailable: looking up the pages that use it failed on this render. Purge the page to retry.
Uses
</details>
{{#seo:

|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-image fetch is constrained by the wiki's enforced CSP default-src, not by CORS: the file URL must resolve to a host that policy allows, regardless of cross-origin headers.
  • The nowiki filepath parser function HTML-entity-encodes : as :; a browser decodes that inside an HTML style attribute (the server-rendered mask) but not when a script assigns it to a CSS custom property, which is why p.src decodes it back to plain text for client-side callers.
  • Thumbnail mode always prepends class=metadata to the [[File:...]] markup, ahead of t-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 `&#58;`, 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