Jump to content
Menú alternativo
Alternar el menú de preferencias
Menú alternativo personal
No has accedido
Tu dirección IP será visible si haces alguna edición

La documentación para este módulo puede ser creada en Módulo:Mainpage/Hero/doc

require('strict')

--- @module Mainpage/Hero
--- The full-bleed hero band: patch status, wiki statline, lede, search trigger
--- and the chip strip.
---
--- NOTHING here fetches the artwork. The band carries the image URL in a
--- `data-gadget-mainpage-hero-src` attribute and the `mainpage` gadget
--- ([[MediaWiki:Gadget-mainpage.js]]) loads it after `window.load`, fading it in
--- by adding `home-hero--loaded`. That is what keeps a 1920px photograph off the
--- critical path and lets the band render — legibly, over its own ground colour
--- — for a reader on Save-Data or with scripts off.
---
--- The gadget hooks are all `data-gadget-mainpage-*`, per the house convention:
--- prefixing by gadget name means `grep gadget-mainpage-` finds every emitter
--- and the one gadget that consumes them.
---
--- Everything variable here comes from [[Module:Mainpage/settings.json]].

local cfg = require('Module:Mainpage/Config')
local nav = require('Module:Mainpage/Nav')

--- Width of the thumbnail handed to the gadget. Large enough for a 2x 960px
--- band; the gadget requests it lazily so the size costs nothing up front.
local HERO_WIDTH = '1920'

local p = {}

--- The build chips, one per entry in `patches`. The live build takes the
--- filled marker and the live colour, every other channel the hollow one —
--- and WHICH entry is live is Config's answer, not one computed here, so the
--- chip and the patch card below can never disagree.
---
--- An entry with no name is skipped, and with none left the strip is not
--- emitted at all rather than rendered empty.
---
--- @param root mw.html
local function renderStatus(root)
	local status = mw.html.create('div'):addClass('home-hero__status')
	local live = cfg.livePatch()
	local any = false

	for _, patch in ipairs(cfg.list('patches')) do
		if patch.name then
			local isLive = patch == live
			local label = patch.channel and (patch.channel .. ' ' .. patch.name) or patch.name

			status:tag('span'):addClass(isLive and 'home-hero__status-live' or 'home-hero__status-ptu'):wikitext(
				(isLive and '●' or '◌')
					.. ' '
					.. (patch.page and string.format('[[%s|%s]]', patch.page, label) or label)
			)
			any = true
		end
	end

	if any then
		root:node(status)
	end
end

--- The three site figures. `mw.site.stats` rather than the magic words so the
--- numbers arrive as numbers and can be formatted once, in the content
--- language, the way {{NUMBEROFARTICLES}} would have done.
---
--- Each figure is marked `data-gadget-mainpage-stat`, which is the gadget's
--- hook for rolling the digits on load — it needs to know which figure it is
--- animating so it can count from a sensible starting point.
local STATS = {
	{ key = 'articles', label = 'Articles', icon = 'article' },
	{ key = 'edits', label = 'Edits', icon = 'edit' },
	{ key = 'users', label = 'Registered users', icon = 'userAvatar' },
}

--- @param content mw.html
local function renderStatline(content)
	local lang = mw.language.getContentLanguage()

	-- A description list, because that is the shape of the data: three labelled
	-- figures. The label is an icon plus visually-hidden text, so the meaning
	-- survives with images off or under a screen reader.
	local list = content:tag('dl'):addClass('home-hero__statline')

	for _, stat in ipairs(STATS) do
		local group = list:tag('div'):addClass('home-hero__stat')

		local label = group:tag('dt'):addClass('home-hero__stat-label')
		label
			:tag('span')
			:addClass('citizen-ui-icon')
			:addClass('mw-ui-icon-' .. stat.icon)
			:addClass('mw-ui-icon-wikimedia-' .. stat.icon)
			:attr('aria-hidden', 'true')
		label:tag('span'):addClass('home-hero__sr'):wikitext(stat.label)

		group
			:tag('dd')
			:addClass('home-hero__stat-value')
			:attr('data-gadget-mainpage-stat', stat.key)
			:wikitext(lang:formatNum(mw.site.stats[stat.key]))
	end
end

--- @param content mw.html
--- @param frame mw.frame
local function renderSearch(content, frame)
	local tails = cfg.section('hero').searchTails or {}

	-- `role="button"` and not a real <button>: the element opens Citizen's own
	-- search overlay, which the skin binds by the `citizen-search-trigger`
	-- class. A <button> here would be a second, competing control.
	local search = content
		:tag('div')
		:addClass('home-hero__search')
		:addClass('citizen-search-trigger')
		:attr('role', 'button')
		:wikitext('[[File:Codex_icon_search_color-placeholder_(v2.6).svg|16px|link=|alt=]]')

	local label = search:tag('span'):addClass('home-hero__search-label')
	label:wikitext('Busca&nbsp;')

	local tail = label:tag('span'):addClass('home-hero__search-tail')
	if tails[1] ~= nil then
		-- The tail the gadget rolls through. The FIRST value is also what is
		-- rendered here, so the server-side text is one of the real phrases
		-- rather than a placeholder that flashes and is replaced.
		tail:attr('data-gadget-mainpage-search-tails', table.concat(tails, '|'))
		tail:wikitext(tails[1])
	else
		tail:wikitext('en esta wiki')
	end
end

--- @param content mw.html
local function renderChips(content)
	local strip = content:tag('div'):addClass('home-hero__tabs')
	for _, chip in ipairs(nav.chips()) do
		local wikitext = nav.renderLink(chip)
		if wikitext then
			strip:wikitext(wikitext)
		end
	end
end

--- @class HeroProps
--- @field lazyArt? boolean  Emit the gadget's artwork hook. False renders the
---        band exactly as a reader with no JavaScript, or with Save-Data on,
---        receives it: no artwork is ever fetched.

--- @param props HeroProps
--- @return string
function p.render(props)
	props = props or {}
	local frame = mw.getCurrentFrame()

	local root = mw.html.create('div'):addClass('home-band'):addClass('home-hero')

	if props.lazyArt ~= false then
		local image = cfg.section('hero').image
		if image then
			root:attr('data-gadget-mainpage-hero-src', frame:callParserFunction('filepath', { image, HERO_WIDTH }))
		end
	end

	-- Both are empty and both are painted entirely in CSS: the media box is
	-- where the gadget puts the photograph, the scrim is the gradient that
	-- keeps the text legible over whatever the photograph turns out to be.
	root:tag('div'):addClass('home-hero__media')
	root:tag('div'):addClass('home-hero__scrim')

	renderStatus(root)

	-- home-band__inner is the measured column every band shares, so the hero's
	-- text lines up with the cards below it.
	local content = root:tag('div'):addClass('home-band__inner'):addClass('home-hero__content')

	renderStatline(content)

	-- The trailing space is load-bearing: the two halves are one sentence set in
	-- two tones, and mw.html concatenates its children with nothing between
	-- them, so without it the dim half would butt against the bright one.
	local hero = cfg.section('hero')
	content
		:tag('div')
		:addClass('home-hero__title')
		:wikitext((hero.lede or 'Espacio de trabajo de') .. ' ')
		:tag('span')
		:addClass('home-hero__title-dim')
		:wikitext(hero.ledeDetail or 'Wikimedia Small Projects')

	renderSearch(content, frame)
	renderChips(content)

	return tostring(root)
end

return p