Jump to content

Module:Creo/Components/Stats

From Creopedia

This documentation is transcluded from Module:Creo/Components/Stats/doc.

Output a stat chart with the data from Module:Creo.

Stats mapping are located in Module:Creo/Components/Stats/Map.json.

Implements Template:Creo/Stats.

It is powered by Chart.js.


require( 'strict' )

local STATS_MAP = mw.loadJsonData( 'Module:Creo/Components/Stats/Map.json' )
local chartJs = require( 'Module:ChartJs' )
local gameTabs = require( 'Module:GameTabs' )

local p = {}

local overlayData = {}

--- Get the chart options for Chart.js
---
--- @return table
local function getChartOptions()
	return {
		plugins = {
			legend = {
				display = false,
			}
		},
		scales = {
			r = {
				beginAtZero = true,
				min = 0,
				max = 50,
				ticks = {
					display = false,
					maxTicksLimit = 1
				},
				pointLabels = {
					display = false
				}
			}
		}
	}
end

--- Get the dataset for Chart.js
---
--- @return table
local function getDataset()
	-- TODO: Update styles
	return {
		label = 'Stats',
		data = {},
		backgroundColor = 'rgba(54, 162, 235, 0.2)',
		borderColor = 'rgb(54, 162, 235)',
		pointBackgroundColor = 'rgb(54, 162, 235)',
		pointBorderColor = '#fff',
		pointHoverBackgroundColor = '#fff',
		pointHoverBorderColor = 'rgb(54, 162, 235)'
	}
end

--- Build the stats data for Chart.js
---
--- @param data table
--- @param gameId string
--- @return table|nil
local function getChartData( data, gameId )
	-- Reset the overlay data
	overlayData = {}

	local chartData = {
		labels = {},
		datasets = { getDataset() }
	}

	if not STATS_MAP[gameId] then
		return nil
	end

	for _, stat in ipairs( STATS_MAP[gameId] ) do
		local statValue = data[stat.id]

		if statValue then
			table.insert( chartData.labels, stat.label )
			table.insert( chartData.datasets[1].data, statValue )
			table.insert( overlayData, {
				id = stat.id,
				label = stat.label,
				value = statValue,
				icon = stat.icon
			} )
		end
	end

	return chartData
end

--- Get the wikitext for the stat label
---
--- @param stat table
--- @return string
local function getStatLabelWikitext( stat )
	local wikitext = stat.label

	if type( stat.icon ) == 'string' then
		wikitext = string.format( '[[File:%s|20px|link=|class=metadata]] %s', stat.icon, stat.label )
	end

	return wikitext
end

--- Get the HTML for the overlay
---
--- @param labels table
--- @return mw.html
local function getOverlayHtml( labels )
	local html = mw.html.create( 'div' ):addClass( 't-creo-stats-overlay' )

	local numStats = #labels
	if numStats == 0 then
		return html
	end

	html:css( '--t-creo-stats-num-stats', numStats )

	for i, stat in ipairs( overlayData ) do
		html:tag( 'div' )
			:addClass( 't-creo-stats-overlay-stat' )
			:addClass( 't-creo-stats-overlay-stat--' .. stat.id )
			:css( '--t-creo-stats-stat-index', i - 1 )
			:tag( 'div' )
				:addClass( 't-creo-stats-overlay-stat-label' )
				:attr( 'title', stat.label )
				:wikitext( getStatLabelWikitext( stat ) )
			:done()
			:tag( 'div' )
				:addClass( 't-creo-stats-overlay-stat-value' )
				:wikitext( stat.value )
			:done()
		:done()
	end

	return html
end

--- Get the HTML for the stats
---
--- @param chartData table
--- @return mw.html
local function getStatsHtml( chartData )
	local html = mw.html.create( 'div' ):addClass( 't-creo-stats' )

	html:tag( 'div' ):addClass( 't-creo-stats-inner' )
		:wikitext( chartJs.render( {
			type = 'radar',
			data = chartData,
			options = getChartOptions()
		} ) )
		:node( getOverlayHtml( chartData.labels ) )

	return html
end

--- Get the data for the game tabs
---
--- @param context ComponentContext
--- @return table
local function getGameTabsData( context )
	local gameTabsData = gameTabs.mapGameArgs( context.data, function( data, game )
		local chartData = getChartData( data, game.id )
		if not chartData then
			return string.format( 'No stats available for %s (%s)', game.name, game.id )
		end
		return tostring( getStatsHtml( chartData ) )
	end )

	gameTabsData.noPadding = true

	return gameTabsData
end

--- Return the wikitext for the stats
---
--- @param context ComponentContext
--- @return string
function p.render( context )
	local root = mw.html.create( 'div' ):addClass( 't-creo-stats-container' )
		:wikitext( gameTabs._main( getGameTabsData( context ) ) )

	return mw.getCurrentFrame():extensionTag {
		name = 'templatestyles', args = { src = 'Module:Creo/Components/Stats/styles.css' }
	} .. tostring( root )
end

return p