diff --git a/lua/wikis/commons/MatchSummary/Base.lua b/lua/wikis/commons/MatchSummary/Base.lua index c06fd19c3ca..22debcc9fd6 100644 --- a/lua/wikis/commons/MatchSummary/Base.lua +++ b/lua/wikis/commons/MatchSummary/Base.lua @@ -29,6 +29,13 @@ local WidgetUtil = Lua.import('Module:Widget/Util') local MATCH_LINK_PRIORITY = Lua.import('Module:Links/MatchPriorityGroups', {loadData = true}) local TBD = Abbreviation.make{text = 'TBD', title = 'To Be Determined'} +---@class CustomMatchSummaryInterface +---@field createHeader? fun(match: MatchGroupUtilMatch, options: {teamStyle: teamStyle?}?): Widget +---@field createBody? fun(match: MatchGroupUtilMatch): Renderable|Renderable[] +---@field createGame? fun(date: string, game: table, gameIndex: integer): Renderable|Renderable[] +---@field addToFooter? fun(match: MatchGroupUtilMatch, footer: MatchSummaryFooter): MatchSummaryFooter +---@field createMatch? fun(matchData: MatchGroupUtilMatch): MatchSummaryMatch + ---@class MatchSummaryFooter ---@operator call: MatchSummaryFooter ---@field elements (Widget|Html|string|number)[] @@ -100,32 +107,32 @@ end ---@class MatchSummaryMatch ---@operator call: MatchSummaryMatch ---@field root Html ----@field headerElement Widget? ----@field bodyElement Widget[]? ----@field commentElement Widget? +---@field headerElement Renderable? +---@field bodyElement Renderable|Renderable[]? +---@field commentElement Renderable|Renderable[]? ---@field footerElement Widget? ----@field buttonElement Widget? +---@field buttonElement Renderable? local Match = Class.new( function(self) self.root = mw.html.create() end ) ----@param header Widget +---@param header Renderable ---@return MatchSummaryMatch function Match:header(header) self.headerElement = header return self end ----@param body Widget[] +---@param body Renderable|Renderable[] ---@return MatchSummaryMatch function Match:body(body) self.bodyElement = body return self end ----@param comment Widget +---@param comment Renderable ---@return MatchSummaryMatch function Match:comment(comment) self.commentElement = comment @@ -139,7 +146,7 @@ function Match:footer(footer) return self end ----@param button Widget +---@param button Renderable ---@return MatchSummaryMatch function Match:button(button) self.buttonElement = button @@ -165,7 +172,7 @@ local MatchSummary = { } ---Default header function ----@param match table +---@param match MatchGroupUtilMatch ---@param options {teamStyle: teamStyle?}? ---@return Widget function MatchSummary.createDefaultHeader(match, options) @@ -186,7 +193,7 @@ end -- Default body function ---@param match MatchGroupUtilMatch ----@param createGame fun(date: string, game: table, gameIndex: integer): Widget +---@param createGame fun(date: string, game: table, gameIndex: integer): Renderable|Renderable[] ---@return Widget[] function MatchSummary.createDefaultBody(match, createGame) return WidgetUtil.collect( @@ -230,7 +237,7 @@ end ---Default createMatch function for usage in Custom MatchSummary ---@param matchData MatchGroupUtilMatch? ----@param CustomMatchSummary table +---@param CustomMatchSummary CustomMatchSummaryInterface ---@param options {teamStyle: teamStyle?, noScore: boolean?}? ---@return MatchSummaryMatch? function MatchSummary.createMatch(matchData, CustomMatchSummary, options) @@ -270,7 +277,7 @@ function MatchSummary.createMatch(matchData, CustomMatchSummary, options) end ---Default getByMatchId function for usage in Custom MatchSummary ----@param CustomMatchSummary table +---@param CustomMatchSummary CustomMatchSummaryInterface ---@param args table ---@param options {teamStyle:teamStyle?, width: (fun(match: MatchGroupUtilMatch):string?)|string?, noScore:boolean?}? ---@return Widget diff --git a/lua/wikis/commons/Widget/Match/Summary/All.lua b/lua/wikis/commons/Widget/Match/Summary/All.lua index b2250d66eb9..3b46e3c5237 100644 --- a/lua/wikis/commons/Widget/Match/Summary/All.lua +++ b/lua/wikis/commons/Widget/Match/Summary/All.lua @@ -21,6 +21,8 @@ Widgets.DetailedScore = Lua.import('Module:Widget/Match/Summary/DetailedScore') Widgets.Footer = Lua.import('Module:Widget/Match/Summary/Footer') Widgets.GameCenter = Lua.import('Module:Widget/Match/Summary/GameCenter') Widgets.GameComment = Lua.import('Module:Widget/Match/Summary/GameComment') +Widgets.GamesContainer = Lua.import('Module:Widget/Match/Summary/GamesContainer') +Widgets.GameRow = Lua.import('Module:Widget/Match/Summary/GameRow') Widgets.GameTeamWrapper = Lua.import('Module:Widget/Match/Summary/GameTeamWrapper') Widgets.GameWinLossIndicator = Lua.import('Module:Widget/Match/Summary/GameWinLossIndicator') Widgets.MapVeto = Lua.import('Module:Widget/Match/Summary/MapVeto') diff --git a/lua/wikis/commons/Widget/Match/Summary/DetailedScore.lua b/lua/wikis/commons/Widget/Match/Summary/DetailedScore.lua index 31eeab1ff72..5a3c26f8f5f 100644 --- a/lua/wikis/commons/Widget/Match/Summary/DetailedScore.lua +++ b/lua/wikis/commons/Widget/Match/Summary/DetailedScore.lua @@ -23,40 +23,31 @@ function MatchSummaryDetailedScore:render() local partialScores = Array.map(self.props.partialScores or {}, function(partialScore) local children = {partialScore.score or '', partialScore.icon} - local styles = Array.append({}, - 'brkts-popup-body-match-sidewins', + local styles = Array.extend( + 'brkts-popup-body-detailed-score', partialScore.style, - partialScore.icon and 'brkts-popup-body-match-sidewins-icon' or nil + partialScore.icon and 'brkts-popup-body-detailed-score-icon' or nil ) - return HtmlWidgets.Td{ + return HtmlWidgets.Span{ classes = styles, - children = flipped and Array.reverse(children) or children, + children = children, } end) return HtmlWidgets.Div{ - css = {['text-align'] = 'center', direction = flipped and 'rtl' or 'ltr'}, - children = HtmlWidgets.Table{ - css = {['line-height'] = '28px', float = flipped and 'right' or 'left'}, - children = { - HtmlWidgets.Tr{ - children = { - HtmlWidgets.Td{ - attributes = {rowspan = 2}, - css = {['font-size'] = '16px', width = '24px'}, - children = self.props.score - }, - unpack(Array.filter(partialScores, function(_, i) - return i % 2 == 1 - end)) - } - }, - HtmlWidgets.Tr{ - children = Array.filter(partialScores, function(_, i) - return i % 2 == 0 - end) - } + classes = { + 'brkts-popup-body-detailed-scores-container', + flipped and 'flipped' or nil, + }, + children = { + HtmlWidgets.Div{ + classes = {'brkts-popup-body-detailed-scores-main-score'}, + children = self.props.score + }, + HtmlWidgets.Div{ + classes = {'brkts-popup-body-detailed-scores'}, + children = partialScores } } } diff --git a/lua/wikis/commons/Widget/Match/Summary/GameRow.lua b/lua/wikis/commons/Widget/Match/Summary/GameRow.lua new file mode 100644 index 00000000000..e9f827febb6 --- /dev/null +++ b/lua/wikis/commons/Widget/Match/Summary/GameRow.lua @@ -0,0 +1,107 @@ +--- +-- @Liquipedia +-- page=Module:Widget/Match/Summary/GameRow +-- +-- Please see https://github.com/Liquipedia/Lua-Modules to contribute +-- + +local Lua = require('Module:Lua') + +local Class = Lua.import('Module:Class') +local Logic = Lua.import('Module:Logic') + +local DisplayHelper = Lua.import('Module:MatchGroup/Display/Helper') + +local Widget = Lua.import('Module:Widget') +local HtmlWidgets = Lua.import('Module:Widget/Html/All') +local GameCenter = Lua.import('Module:Widget/Match/Summary/GameCenter') +local GameWinLossIndicator = Lua.import('Module:Widget/Match/Summary/GameWinLossIndicator') + +---@class MatchSummaryGameRowProps +---@field css table? +---@field game MatchGroupUtilGame +---@field gameIndex integer + +---@class MatchSummaryGameRow: Widget +---@operator call(MatchSummaryGameRowProps): MatchSummaryGameRow +---@field props MatchSummaryGameRowProps +local MatchSummaryGameRow = Class.new(Widget) + +---@return Widget? +function MatchSummaryGameRow:render() + local props = self.props + return HtmlWidgets.Div{ + classes = {'brkts-popup-body-grid-row'}, + css = props.css, + children = { + GameWinLossIndicator{ + opponentIndex = 1, + winner = props.game.winner, + }, + HtmlWidgets.Div{ + classes = {'brkts-popup-body-grid-row-detail'}, + children = { + GameCenter{children = self:createGameOpponentView(1)}, + GameCenter{children = self:createGameOverview()}, + GameCenter{children = self:createGameOpponentView(2)} + }, + }, + GameWinLossIndicator{ + opponentIndex = 2, + winner = props.game.winner, + }, + self:_renderGameComment() + }, + } +end + +---@protected +---@param opponentIndex integer +---@return Renderable|Renderable[] +function MatchSummaryGameRow:createGameOpponentView(opponentIndex) + error('MatchSummaryGameRow:createGameOpponentView() cannot be called directly and must be overridden.') +end + +---@protected +---@return Renderable|Renderable[] +function MatchSummaryGameRow:createGameOverview() + error('MatchSummaryGameRow:createGameOverview() cannot be called directly and must be overridden.') +end + +---@protected +---@return Renderable? +function MatchSummaryGameRow:lengthDisplay() + local game = self.props.game + return Logic.emptyOr(game.length, 'Game ' .. self.props.gameIndex) +end + +---@protected +---@param config {noLink: boolean?}? +---@return string +function MatchSummaryGameRow:mapDisplay(config) + local game = self.props.game + return DisplayHelper.Map(game, config) +end + +---@protected +---@param opponentIndex integer +---@return string +function MatchSummaryGameRow:scoreDisplay(opponentIndex) + local game = self.props.game + return DisplayHelper.MapScore(game.opponents[opponentIndex], game.status) +end + +---@private +---@return Widget? +function MatchSummaryGameRow:_renderGameComment() + local game = self.props.game + if Logic.isEmpty(game.comment) then + return + end + return HtmlWidgets.Div{ + classes = {'brkts-popup-comment'}, + children = game.comment, + } +end + +return MatchSummaryGameRow diff --git a/lua/wikis/commons/Widget/Match/Summary/GamesContainer.lua b/lua/wikis/commons/Widget/Match/Summary/GamesContainer.lua new file mode 100644 index 00000000000..bd1523ed339 --- /dev/null +++ b/lua/wikis/commons/Widget/Match/Summary/GamesContainer.lua @@ -0,0 +1,32 @@ +--- +-- @Liquipedia +-- page=Module:Widget/Match/Summary/GamesContainer +-- +-- Please see https://github.com/Liquipedia/Lua-Modules to contribute +-- + +local Lua = require('Module:Lua') + +local Class = Lua.import('Module:Class') +local Logic = Lua.import('Module:Logic') + +local Widget = Lua.import('Module:Widget') +local HtmlWidgets = Lua.import('Module:Widget/Html/All') + +---@class MatchSummaryGamesContainer: Widget +---@operator call(table): MatchSummaryGamesContainer +local MatchSummaryGamesContainer = Class.new(Widget) + +---@return Widget? +function MatchSummaryGamesContainer:render() + if Logic.isEmpty(self.props.children) then + return + end + return HtmlWidgets.Div{ + classes = {'brkts-popup-body-grid'}, + css = self.props.css, + children = self.props.children, + } +end + +return MatchSummaryGamesContainer diff --git a/lua/wikis/dota2/MatchSummary.lua b/lua/wikis/dota2/MatchSummary.lua index 26c10640a66..fd4296dcfa6 100644 --- a/lua/wikis/dota2/MatchSummary.lua +++ b/lua/wikis/dota2/MatchSummary.lua @@ -5,12 +5,10 @@ -- Please see https://github.com/Liquipedia/Lua-Modules to contribute -- -local CustomMatchSummary = {} - local Lua = require('Module:Lua') local Array = Lua.import('Module:Array') -local Logic = Lua.import('Module:Logic') +local Class = Lua.import('Module:Class') local MatchSummary = Lua.import('Module:MatchSummary/Base') local MatchSummaryWidgets = Lua.import('Module:Widget/Match/Summary/All') @@ -20,6 +18,13 @@ local MAX_NUM_BANS = 7 local NUM_HEROES_PICK = 5 local STATUS_NOT_PLAYED = 'notplayed' +---@class Dota2CustomMatchSummary: CustomMatchSummaryInterface +local CustomMatchSummary = {} + +---@class Dota2MatchSummaryGameRow: MatchSummaryGameRow +---@operator call(MatchSummaryGameRowProps): Dota2MatchSummaryGameRow +local Dota2MatchSummaryGameRow = Class.new(MatchSummaryWidgets.GameRow) + ---@param args table ---@return Widget function CustomMatchSummary.getByMatchId(args) @@ -32,46 +37,40 @@ function CustomMatchSummary.createBody(match) local characterBansData = MatchSummary.buildCharacterBanData(match.games, MAX_NUM_BANS) return WidgetUtil.collect( - Array.map(match.games, CustomMatchSummary._createGame), + MatchSummaryWidgets.GamesContainer{ + gridLayout = 'standard', + children = Array.map(match.games, function (game, gameIndex) + if game.status == STATUS_NOT_PLAYED then + return + end + return Dota2MatchSummaryGameRow{game = game, gameIndex = gameIndex} + end) + }, MatchSummaryWidgets.Mvp(match.extradata.mvp), MatchSummaryWidgets.CharacterBanTable{bans = characterBansData, date = match.date} ) end ----@param game MatchGroupUtilGame ----@param gameIndex integer ----@return MatchSummaryRow? -function CustomMatchSummary._createGame(game, gameIndex) - if game.status == STATUS_NOT_PLAYED then - return - end +---@param opponentIndex integer +---@return Widget +function Dota2MatchSummaryGameRow:createGameOpponentView(opponentIndex) + local props = self.props + local game = props.game local extradata = game.extradata or {} - -- TODO: Change to use participant data - local characterData = { - MatchSummary.buildCharacterList(extradata, 'team1hero', NUM_HEROES_PICK), - MatchSummary.buildCharacterList(extradata, 'team2hero', NUM_HEROES_PICK), + return MatchSummaryWidgets.Characters{ + flipped = opponentIndex == 2, + characters = MatchSummary.buildCharacterList( + extradata, 'team' .. opponentIndex .. 'hero', NUM_HEROES_PICK + ), + bg = 'brkts-popup-side-color brkts-popup-side-color--' .. (extradata['team' .. opponentIndex .. 'side'] or ''), + date = game.date, } +end - return MatchSummaryWidgets.Row{ - classes = {'brkts-popup-body-game'}, - children = WidgetUtil.collect( - MatchSummaryWidgets.Characters{ - flipped = false, - characters = characterData[1], - bg = 'brkts-popup-side-color brkts-popup-side-color--' .. (extradata.team1side or ''), - }, - MatchSummaryWidgets.GameWinLossIndicator{winner = game.winner, opponentIndex = 1}, - MatchSummaryWidgets.GameCenter{children = Logic.nilIfEmpty(game.length) or ('Game ' .. gameIndex)}, - MatchSummaryWidgets.GameWinLossIndicator{winner = game.winner, opponentIndex = 2}, - MatchSummaryWidgets.Characters{ - flipped = true, - characters = characterData[2], - bg = 'brkts-popup-side-color brkts-popup-side-color--' .. (extradata.team2side or ''), - }, - MatchSummaryWidgets.GameComment{children = game.comment} - ) - } +---@return Renderable? +function Dota2MatchSummaryGameRow:createGameOverview() + return self:lengthDisplay() end return CustomMatchSummary diff --git a/lua/wikis/leagueoflegends/MatchSummary.lua b/lua/wikis/leagueoflegends/MatchSummary.lua index 57db347bed1..a7d1dc07edc 100644 --- a/lua/wikis/leagueoflegends/MatchSummary.lua +++ b/lua/wikis/leagueoflegends/MatchSummary.lua @@ -5,13 +5,10 @@ -- Please see https://github.com/Liquipedia/Lua-Modules to contribute -- -local CustomMatchSummary = {} - local Lua = require('Module:Lua') local Array = Lua.import('Module:Array') -local FnUtil = Lua.import('Module:FnUtil') -local Logic = Lua.import('Module:Logic') +local Class = Lua.import('Module:Class') local MatchSummary = Lua.import('Module:MatchSummary/Base') local MatchSummaryWidgets = Lua.import('Module:Widget/Match/Summary/All') @@ -21,6 +18,13 @@ local MAX_NUM_BANS = 5 local NUM_HEROES_PICK = 5 local STATUS_NOT_PLAYED = 'notplayed' +---@class LoLCustomMatchSummary: CustomMatchSummaryInterface +local CustomMatchSummary = {} + +---@class LoLMatchSummaryGameRow: MatchSummaryGameRow +---@operator call(MatchSummaryGameRowProps): LoLMatchSummaryGameRow +local LoLMatchSummaryGameRow = Class.new(MatchSummaryWidgets.GameRow) + ---@param args table ---@return Widget function CustomMatchSummary.getByMatchId(args) @@ -33,63 +37,40 @@ function CustomMatchSummary.createBody(match) local characterBansData = MatchSummary.buildCharacterBanData(match.games, MAX_NUM_BANS) return WidgetUtil.collect( - Array.map(match.games, FnUtil.curry(CustomMatchSummary._createGame, match.date)), + MatchSummaryWidgets.GamesContainer{ + gridLayout = 'standard', + children = Array.map(match.games, function (game, gameIndex) + if game.status == STATUS_NOT_PLAYED then + return + end + return LoLMatchSummaryGameRow{game = game, gameIndex = gameIndex} + end) + }, MatchSummaryWidgets.Mvp(match.extradata.mvp), MatchSummaryWidgets.CharacterBanTable{bans = characterBansData, date = match.date} ) end ----@param date string ----@param game MatchGroupUtilGame ----@param gameIndex integer ----@return MatchSummaryRow? -function CustomMatchSummary._createGame(date, game, gameIndex) - if game.status == STATUS_NOT_PLAYED then - return - end +---@param opponentIndex integer +---@return Widget +function LoLMatchSummaryGameRow:createGameOpponentView(opponentIndex) + local props = self.props + local game = props.game local extradata = game.extradata or {} - -- TODO: Change to use participant data - local characterData = { - MatchSummary.buildCharacterList(extradata, 'team1champion', NUM_HEROES_PICK), - MatchSummary.buildCharacterList(extradata, 'team2champion', NUM_HEROES_PICK), + return MatchSummaryWidgets.Characters{ + flipped = opponentIndex == 2, + characters = MatchSummary.buildCharacterList( + extradata, 'team' .. opponentIndex .. 'champion', NUM_HEROES_PICK + ), + bg = 'brkts-popup-side-color brkts-popup-side-color--' .. (extradata['team' .. opponentIndex .. 'side'] or ''), + date = game.date, } +end - return MatchSummaryWidgets.Row{ - classes = {'brkts-popup-body-game'}, - children = WidgetUtil.collect( - MatchSummaryWidgets.Characters{ - css = { - flex = '1 1 33%', - }, - flipped = false, - characters = characterData[1], - bg = 'brkts-popup-side-color brkts-popup-side-color--' .. (extradata.team1side or ''), - date = date, - }, - MatchSummaryWidgets.GameCenter{ - css = { - flex = '1 1 fit-content', - gap = '0.5rem', - }, - children = { - MatchSummaryWidgets.GameWinLossIndicator{winner = game.winner, opponentIndex = 1}, - MatchSummaryWidgets.GameCenter{children = Logic.emptyOr(game.length, 'Game ' .. gameIndex)}, - MatchSummaryWidgets.GameWinLossIndicator{winner = game.winner, opponentIndex = 2}, - } - }, - MatchSummaryWidgets.Characters{ - css = { - flex = '1 1 33%', - }, - flipped = true, - characters = characterData[2], - bg = 'brkts-popup-side-color brkts-popup-side-color--' .. (extradata.team2side or ''), - date = date, - }, - MatchSummaryWidgets.GameComment{children = game.comment} - ) - } +---@return Renderable? +function LoLMatchSummaryGameRow:createGameOverview() + return self:lengthDisplay() end return CustomMatchSummary diff --git a/lua/wikis/rainbowsix/MatchSummary.lua b/lua/wikis/rainbowsix/MatchSummary.lua index 94d05bef2eb..a0487864fce 100644 --- a/lua/wikis/rainbowsix/MatchSummary.lua +++ b/lua/wikis/rainbowsix/MatchSummary.lua @@ -8,21 +8,42 @@ local Lua = require('Module:Lua') local Array = Lua.import('Module:Array') -local DisplayHelper = Lua.import('Module:MatchGroup/Display/Helper') -local FnUtil = Lua.import('Module:FnUtil') +local Class = Lua.import('Module:Class') +local Logic = Lua.import('Module:Logic') +local Table = Lua.import('Module:Table') + local MatchSummary = Lua.import('Module:MatchSummary/Base') + local MatchSummaryWidgets = Lua.import('Module:Widget/Match/Summary/All') +local IconImage = Lua.import('Module:Widget/Image/Icon/Image') local WidgetUtil = Lua.import('Module:Widget/Util') local ROUND_ICONS = { - atk = '[[File:R6S Para Bellum atk logo.png|14px|link=]]', - def = '[[File:R6S Para Bellum def logo.png|14px|link=]]', - otatk = '[[File:R6S Para Bellum atk logo ot rounds.png|11px|link=]]', - otdef = '[[File:R6S Para Bellum def logo ot rounds.png|11px|link=]]', + atk = IconImage{ + imageLight = 'R6S Para Bellum atk logo.png', + size = '14px', + }, + def = IconImage{ + imageLight = 'R6S Para Bellum def logo.png', + size = '14px', + }, + otatk = IconImage{ + imageLight = 'R6S Para Bellum atk logo ot rounds.png', + size = '11px', + }, + otdef = IconImage{ + imageLight = 'R6S Para Bellum def logo ot rounds.png', + size = '11px', + }, } +---@class RainbowsixMatchSummary: CustomMatchSummaryInterface local CustomMatchSummary = {} +---@class RainbowsixMatchSummaryGameRow: MatchSummaryGameRow +---@operator call(MatchSummaryGameRowProps): RainbowsixMatchSummaryGameRow +local RainbowsixMatchSummaryGameRow = Class.new(MatchSummaryWidgets.GameRow) + ---@param args table ---@return Widget function CustomMatchSummary.getByMatchId(args) @@ -42,81 +63,65 @@ function CustomMatchSummary.createBody(match) end) return WidgetUtil.collect( - Array.map(match.games, FnUtil.curry(CustomMatchSummary.createGame, match.date)), + MatchSummaryWidgets.GamesContainer{ + gridLayout = 'standard', + children = Array.map(match.games, function (game, gameIndex) + if Logic.isEmpty(game.map) then + return + end + return RainbowsixMatchSummaryGameRow{game = game, gameIndex = gameIndex} + end) + }, MatchSummaryWidgets.Mvp(match.extradata.mvp), MatchSummaryWidgets.MapVeto(MatchSummary.preProcessMapVeto(match.extradata.mapveto, {game = match.game})), MatchSummaryWidgets.CharacterBanTable{bans = characterBansData, date = match.date} ) end ----@param date string ----@param game MatchGroupUtilGame ----@param gameIndex integer ----@return Widget? -function CustomMatchSummary.createGame(date, game, gameIndex) - if not game.map then - return - end +---@private +---@param opponentIndex integer +---@return table[] +function RainbowsixMatchSummaryGameRow:_makePartialScores(opponentIndex) + local game = self.props.game local extradata = game.extradata or {} + local halves = extradata['t' .. opponentIndex .. 'halfs'] + + ---@type table + local firstSides = Table.mapValues( + extradata.t1firstside or {}, + function (side) + if opponentIndex == 1 then + return side + end + return CustomMatchSummary._getOppositeSide(side:lower()) + end + ) + local firstSide = (firstSides.rt or '') + local firstSideOt = (firstSides.ot or '') + local oppositeSide = CustomMatchSummary._getOppositeSide(firstSide) + local oppositeSideOt = CustomMatchSummary._getOppositeSide(firstSideOt) + return { + {score = halves[firstSide], icon = ROUND_ICONS[firstSide]}, + {score = halves[oppositeSide], icon = ROUND_ICONS[oppositeSide]}, + {score = halves['ot' .. firstSideOt], icon = ROUND_ICONS['ot' .. firstSideOt]}, + {score = halves['ot' .. oppositeSideOt], icon = ROUND_ICONS['ot' .. oppositeSideOt]}, + } +end - local function scoreDisplay(oppIdx) - return DisplayHelper.MapScore(game.opponents[oppIdx], game.status) - end - - local function makePartialScores(halves, firstSide, firstSideOt) - local oppositeSide = CustomMatchSummary._getOppositeSide(firstSide) - local oppositeSideOt = CustomMatchSummary._getOppositeSide(firstSideOt) - return { - {score = halves[firstSide], icon = ROUND_ICONS[firstSide]}, - {score = halves[oppositeSide], icon = ROUND_ICONS[oppositeSide]}, - {score = halves['ot' .. firstSideOt], icon = ROUND_ICONS['ot' .. firstSideOt]}, - {score = halves['ot' .. oppositeSideOt], icon = ROUND_ICONS['ot' .. oppositeSideOt]}, - } - end - - local firstSides = extradata.t1firstside or {} - local firstSide = (firstSides.rt or ''):lower() - local firstSideOt = (firstSides.ot or ''):lower() - - -- Winner/Loser backgrounds - local gameStatusBackground = 'brkts-popup-body-gradient-default' - if game.winner == 1 then - gameStatusBackground = 'brkts-popup-body-gradient-left' - elseif game.winner == 2 then - gameStatusBackground = 'brkts-popup-body-gradient-right' - elseif game.winner == 0 then - gameStatusBackground = 'brkts-popup-body-gradient-draw' - end - - return MatchSummaryWidgets.Row{ - classes = {'brkts-popup-body-game', gameStatusBackground}, - children = WidgetUtil.collect( - MatchSummaryWidgets.GameWinLossIndicator{winner = game.winner, opponentIndex = 1}, - MatchSummaryWidgets.DetailedScore{ - score = scoreDisplay(1), - flipped = false, - partialScores = makePartialScores( - extradata.t1halfs or {}, - firstSide, - firstSideOt - ) - }, - MatchSummaryWidgets.GameCenter{children = DisplayHelper.Map(game), css = {['flex-grow'] = '1'}}, - MatchSummaryWidgets.DetailedScore{ - score = scoreDisplay(2), - flipped = true, - partialScores = makePartialScores( - extradata.t2halfs or {}, - CustomMatchSummary._getOppositeSide(firstSide), - CustomMatchSummary._getOppositeSide(firstSideOt) - ) - }, - MatchSummaryWidgets.GameWinLossIndicator{winner = game.winner, opponentIndex = 2}, - MatchSummaryWidgets.GameComment{children = game.comment} - ) +---@param opponentIndex integer +---@return Widget +function RainbowsixMatchSummaryGameRow:createGameOpponentView(opponentIndex) + return MatchSummaryWidgets.DetailedScore{ + score = self:scoreDisplay(opponentIndex), + partialScores = self:_makePartialScores(opponentIndex) } end +---@return string +function RainbowsixMatchSummaryGameRow:createGameOverview() + return self:mapDisplay() +end + ---@param side string ---@return string function CustomMatchSummary._getOppositeSide(side) diff --git a/lua/wikis/valorant/MatchSummary.lua b/lua/wikis/valorant/MatchSummary.lua index c27e59acd9c..3ba3cfb2c96 100644 --- a/lua/wikis/valorant/MatchSummary.lua +++ b/lua/wikis/valorant/MatchSummary.lua @@ -8,16 +8,22 @@ local Lua = require('Module:Lua') local Array = Lua.import('Module:Array') -local FnUtil = Lua.import('Module:FnUtil') +local Class = Lua.import('Module:Class') +local Logic = Lua.import('Module:Logic') local Operator = Lua.import('Module:Operator') -local DisplayHelper = Lua.import('Module:MatchGroup/Display/Helper') local MatchSummary = Lua.import('Module:MatchSummary/Base') + local MatchSummaryWidgets = Lua.import('Module:Widget/Match/Summary/All') local WidgetUtil = Lua.import('Module:Widget/Util') +---@class ValorantMatchSummary: CustomMatchSummaryInterface local CustomMatchSummary = {} +---@class ValorantMatchSummaryGameRow: MatchSummaryGameRow +---@operator call(MatchSummaryGameRowProps): ValorantMatchSummaryGameRow +local ValorantMatchSummaryGameRow = Class.new(MatchSummaryWidgets.GameRow) + ---@param args table ---@return Widget function CustomMatchSummary.getByMatchId(args) @@ -27,73 +33,67 @@ end ---@param match MatchGroupUtilMatch ---@return Widget[] function CustomMatchSummary.createBody(match) - return WidgetUtil.collect( - Array.map(match.games, FnUtil.curry(CustomMatchSummary.createGame, match.date)), + MatchSummaryWidgets.GamesContainer{ + gridLayout = 'standard', + children = Array.map(match.games, function (game, gameIndex) + if Logic.isEmpty(game.map) then + return + end + return ValorantMatchSummaryGameRow{game = game, gameIndex = gameIndex} + end) + }, MatchSummaryWidgets.Mvp(match.extradata.mvp), MatchSummaryWidgets.MapVeto(MatchSummary.preProcessMapVeto(match.extradata.mapveto, {game = match.game})) ) end +---@return string +function ValorantMatchSummaryGameRow:createGameOverview() + return self:mapDisplay() +end - ----@param date string ----@param game MatchGroupUtilGame ----@param gameIndex integer ----@return Widget? -function CustomMatchSummary.createGame(date, game, gameIndex) - if not game.map then - return - end - - local function scoreDisplay(oppIdx) - return DisplayHelper.MapScore(game.opponents[oppIdx], game.status) - end - - local function makePartialScores(halves, firstSide) - local oppositeSide = CustomMatchSummary._getOppositeSide(firstSide) - return { - {style = 'brkts-valorant-score-color-' .. firstSide, score = halves[firstSide]}, - {style = 'brkts-valorant-score-color-' .. oppositeSide, score = halves[oppositeSide]}, - {style = 'brkts-valorant-score-color-' .. firstSide, score = halves['ot' .. firstSide]}, - {style = 'brkts-valorant-score-color-' .. oppositeSide, score = halves['ot' .. oppositeSide]}, - } - end - +---@private +---@param opponentIndex integer +---@return table[] +function ValorantMatchSummaryGameRow:_makePartialScores(opponentIndex) + local game = self.props.game local extradata = game.extradata or {} - local function makeTeamSection(opponentIndex) - local flipped = opponentIndex == 2 - local firstSide = flipped and CustomMatchSummary._getOppositeSide(extradata.t1firstside) or extradata.t1firstside - local characters = Array.map((game.opponents[opponentIndex] or {}).players or {}, Operator.property('agent')) - return { - MatchSummaryWidgets.GameWinLossIndicator{winner = game.winner, opponentIndex = opponentIndex}, - MatchSummaryWidgets.Characters{characters = characters, flipped = flipped, hideOnMobile = true}, - MatchSummaryWidgets.DetailedScore{ - score = scoreDisplay(opponentIndex), - flipped = flipped, - partialScores = makePartialScores( - extradata['t' .. opponentIndex .. 'halfs'] or {}, - firstSide or '' - ) - } - } + local firstSide = extradata.t1firstside or '' + local oppositeSide = CustomMatchSummary._getOppositeSide(firstSide) + local halves = extradata['t' .. opponentIndex .. 'halfs'] or {} + if opponentIndex == 2 then + firstSide, oppositeSide = oppositeSide, firstSide end + return { + {style = 'brkts-valorant-score-color-' .. firstSide, score = halves[firstSide]}, + {style = 'brkts-valorant-score-color-' .. oppositeSide, score = halves[oppositeSide]}, + {style = 'brkts-valorant-score-color-' .. firstSide, score = halves['ot' .. firstSide]}, + {style = 'brkts-valorant-score-color-' .. oppositeSide, score = halves['ot' .. oppositeSide]}, + } +end - return MatchSummaryWidgets.Row{ - classes = {'brkts-popup-body-game'}, - children = WidgetUtil.collect( - MatchSummaryWidgets.GameTeamWrapper{children = makeTeamSection(1)}, - MatchSummaryWidgets.GameCenter{children = DisplayHelper.Map(game)}, - MatchSummaryWidgets.GameTeamWrapper{children = makeTeamSection(2), flipped = true}, - MatchSummaryWidgets.GameComment{children = game.comment} - ) +---@param opponentIndex integer +---@return Widget[] +function ValorantMatchSummaryGameRow:createGameOpponentView(opponentIndex) + local game = self.props.game + local flipped = opponentIndex == 2 + local characters = Array.map((game.opponents[opponentIndex] or {}).players or {}, Operator.property('agent')) + return { + MatchSummaryWidgets.Characters{characters = characters, flipped = flipped, hideOnMobile = true}, + MatchSummaryWidgets.DetailedScore{ + score = self:scoreDisplay(opponentIndex), + partialScores = self:_makePartialScores(opponentIndex) + } } end ---@param side string? ---@return string function CustomMatchSummary._getOppositeSide(side) - if side == 'atk' then + if Logic.isEmpty(side) then + return '' + elseif side == 'atk' then return 'def' end return 'atk' diff --git a/stylesheets/commons/Brackets.scss b/stylesheets/commons/Brackets.scss index 3e399abfb92..4c845b6306a 100644 --- a/stylesheets/commons/Brackets.scss +++ b/stylesheets/commons/Brackets.scss @@ -638,6 +638,52 @@ div.brkts-popup-body-element-thumbs { } } +.brkts-popup-body-grid { + display: grid; + grid-template-columns: min-content 1fr auto 1fr min-content; + align-items: center; + margin: 0 -0.5rem; + + &-row { + display: grid; + grid-column: 1 / -1; + grid-template-columns: subgrid; + align-items: center; + gap: 0.5rem; + padding: 0.5rem; + + &:nth-of-type( even ) { + background-color: var( --clr-on-surface-light-primary-4 ); + + .theme--dark & { + background-color: var( --clr-on-surface-dark-primary-4 ); + } + } + + > .brkts-popup-comment { + padding: unset; + grid-column: 1 / -1; + } + + &-detail { + display: grid; + grid-column: 2 / -2; + grid-template-columns: subgrid; + white-space: nowrap; + + > .brkts-popup-spaced:nth-of-type( 1 ) { + flex-direction: row; + justify-content: flex-start; + } + + > .brkts-popup-spaced:nth-last-of-type( 1 ) { + flex-direction: row-reverse; + justify-content: flex-start; + } + } + } +} + .brkts-champion-icon { gap: 0.125rem; display: flex; @@ -909,42 +955,49 @@ div.brkts-popup-body-element-thumbs { font-weight: bold; } -.brkts-popup-body-match-sidewins { - padding: 0 0 3px 3px; - line-height: 11px; - width: 10px; - font-weight: 800; +.brkts-popup-body-detailed-scores { + display: grid; + grid-template-rows: repeat( 2, 1fr ); + grid-auto-flow: column; + gap: 0.125rem 0.1875rem; + align-items: center; - &-icon { - width: 24px; + &-container { + display: flex; + flex-direction: row; + gap: 0.25rem; + justify-content: center; + align-items: center; + text-align: center; - img { - [ data-darkreader-scheme="dark" ] &, - .theme--dark & { - filter: invert( 1 ); - } + &.flipped { + flex-direction: row-reverse; + } + + .brkts-popup-spaced > & { + display: contents; } } -} -.brkts-popup-body-gradient-left { - padding: 4px 0; - background: linear-gradient( to left, var( --clr-cinnabar-background-color, #fbdfdf ) 35%, var( --table-background-color, #ffffff ) 35%, var( --table-background-color, #ffffff ) 65%, var( --clr-forest-background-color, #ddf4dd ) 65% ) !important; -} + &-container.flipped > &, + .brkts-popup-body-grid-row-detail > .brkts-popup-spaced:nth-last-of-type( 1 ) &-container > & { + direction: rtl; + } -.brkts-popup-body-gradient-right { - padding: 4px 0; - background: linear-gradient( to right, var( --clr-cinnabar-background-color, #fbdfdf ) 35%, var( --table-background-color, #ffffff ) 35%, var( --table-background-color, #ffffff ) 65%, var( --clr-forest-background-color, #ddf4dd ) 65% ) !important; -} + &-main-score { + font-size: 1rem; + width: 1.5rem; + } -.brkts-popup-body-gradient-draw { - padding: 4px 0; - background: linear-gradient( to left, var( --clr-pear-background-color, #f9f9c7 ) 35%, var( --table-background-color, #ffffff ) 35%, var( --table-background-color, #ffffff ) 65%, var( --clr-pear-background-color, #f9f9c7 ) 65% ) !important; -} + .brkts-popup-body-detailed-score { + font-weight: bolder; + font-size: 0.6875rem; + line-height: 1; -.brkts-popup-body-gradient-default { - padding: 4px 0; - background: var( --table-background-color, #ffffff ) !important; + .theme--dark &-icon > img { + filter: invert( 1 ); + } + } } .brkts-popup-side-color {