From 4de3589a127a0845edf84162304f37cf89fd4eb8 Mon Sep 17 00:00:00 2001 From: Eetu Rantanen Date: Mon, 15 Jun 2026 14:56:56 +0300 Subject: [PATCH 01/35] order points column before qualifies --- lua/spec/prize_pool_spec.lua | 2 +- lua/wikis/commons/PrizePool/Base.lua | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/lua/spec/prize_pool_spec.lua b/lua/spec/prize_pool_spec.lua index a6b871fa86b..a479ad4535b 100644 --- a/lua/spec/prize_pool_spec.lua +++ b/lua/spec/prize_pool_spec.lua @@ -67,8 +67,8 @@ describe('prize pool', function() currency = 'SEK', } }, - {id = 'QUALIFIES1', type = 'QUALIFIES', index = 1, data = {title = 'A Display', link = 'A_Tournament'}}, {id = 'POINTS1', type = 'POINTS', index = 1, data = {title = 'Points', link = 'A Page'}}, + {id = 'QUALIFIES1', type = 'QUALIFIES', index = 1, data = {title = 'A Display', link = 'A_Tournament'}}, {id = 'FREETEXT1', type = 'FREETEXT', index = 1, data = {title = 'A title'}}, }, ppt.prizes diff --git a/lua/wikis/commons/PrizePool/Base.lua b/lua/wikis/commons/PrizePool/Base.lua index 680786f4314..8f655a2da7b 100644 --- a/lua/wikis/commons/PrizePool/Base.lua +++ b/lua/wikis/commons/PrizePool/Base.lua @@ -252,7 +252,7 @@ BasePrizePool.prizeTypes = { end, }, [PRIZE_TYPE_QUALIFIES] = { - sortOrder = 40, + sortOrder = 50, header = 'qualifies', headerParse = function (prizePool, input, context, index) @@ -305,7 +305,7 @@ BasePrizePool.prizeTypes = { mergeDisplayColumns = true, }, [PRIZE_TYPE_POINTS] = { - sortOrder = 50, + sortOrder = 40, header = 'points', headerParse = function (prizePool, input, context, index) From bf0e623cb2d3736dd8a865754d8ed723e407020e Mon Sep 17 00:00:00 2001 From: Eetu Rantanen Date: Mon, 15 Jun 2026 14:59:49 +0300 Subject: [PATCH 02/35] display placements as plain numbers and ranges --- lua/wikis/commons/PrizePool/Placement.lua | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/lua/wikis/commons/PrizePool/Placement.lua b/lua/wikis/commons/PrizePool/Placement.lua index 3e11a0935b8..cc4376bc141 100644 --- a/lua/wikis/commons/PrizePool/Placement.lua +++ b/lua/wikis/commons/PrizePool/Placement.lua @@ -12,7 +12,6 @@ local Array = Lua.import('Module:Array') local Class = Lua.import('Module:Class') local Logic = Lua.import('Module:Logic') local Medals = Lua.import('Module:Medals') -local Ordinal = Lua.import('Module:Ordinal') local PlacementInfo = Lua.import('Module:Placement') local String = Lua.import('Module:StringUtils') local Table = Lua.import('Module:Table') @@ -315,12 +314,11 @@ function Placement:_displayPlace() end end - local start = Ordinal.toOrdinal(self.placeStart) if self.placeEnd > self.placeStart then - return start .. DASH .. Ordinal.toOrdinal(self.placeEnd) + return self.placeStart .. DASH .. self.placeEnd end - return start + return tostring(self.placeStart) end ---@return string? From 974a7e7d7608075a62e9a6dc9867bf1261221d4d Mon Sep 17 00:00:00 2001 From: Eetu Rantanen Date: Mon, 15 Jun 2026 15:02:17 +0300 Subject: [PATCH 03/35] colored number badge for top-3 placements --- lua/wikis/commons/Placement.lua | 1 - lua/wikis/commons/PrizePool.lua | 18 ++++++++++-------- lua/wikis/commons/PrizePool/Placement.lua | 11 +++++++++++ 3 files changed, 21 insertions(+), 9 deletions(-) diff --git a/lua/wikis/commons/Placement.lua b/lua/wikis/commons/Placement.lua index af4636a1386..dd73c54ccab 100644 --- a/lua/wikis/commons/Placement.lua +++ b/lua/wikis/commons/Placement.lua @@ -88,7 +88,6 @@ local prizepoolClasses = { 'background-color-first-place', 'background-color-second-place', 'background-color-third-place', - 'background-color-fourth-place', w = 'bg-win', q = 'bg-win', l = 'bg-lose', diff --git a/lua/wikis/commons/PrizePool.lua b/lua/wikis/commons/PrizePool.lua index 9c3e78a31b5..32a9405bbb4 100644 --- a/lua/wikis/commons/PrizePool.lua +++ b/lua/wikis/commons/PrizePool.lua @@ -22,6 +22,7 @@ local Opponent = Lua.import('Module:Opponent/Custom') local Widgets = Lua.import('Module:Widget/All') local Div = Widgets.Div local IconFa = Lua.import('Module:Widget/Image/Icon/Fontawesome') +local Span = Widgets.Span local TableRow = Widgets.TableRow local TableCell = Widgets.TableCell @@ -30,8 +31,6 @@ local TableCell = Widgets.TableCell ---@field placements PrizePoolPlacement[] local PrizePool = Class.new(BasePrizePool) -local NON_BREAKING_SPACE = ' ' - ---@param args table function PrizePool:readPlacements(args) local currentPlace = 0 @@ -54,14 +53,17 @@ end ---@param placement PrizePoolPlacement ---@return WidgetTableCell function PrizePool:placeOrAwardCell(placement) - local placeCell = TableCell{ - children = {placement:getMedal() or '', NON_BREAKING_SPACE, placement:_displayPlace()}, - css = {['font-weight'] = 'bolder'}, + local badgeClass = placement:getBadgeClass() + local placeDisplay = placement:_displayPlace() + local content = badgeClass + and Span{classes = {'prizepooltable-badge', badgeClass}, children = {placeDisplay}} + or placeDisplay + + return TableCell{ + children = {content}, classes = {'prizepooltable-place'}, + rowspan = #placement.opponents, } - placeCell.rowSpan = #placement.opponents - - return placeCell end ---@param placement PrizePoolPlacement diff --git a/lua/wikis/commons/PrizePool/Placement.lua b/lua/wikis/commons/PrizePool/Placement.lua index cc4376bc141..2c14d36971f 100644 --- a/lua/wikis/commons/PrizePool/Placement.lua +++ b/lua/wikis/commons/PrizePool/Placement.lua @@ -332,6 +332,17 @@ function Placement:getBackground() return PlacementInfo.getBgClass{placement = self.placeStart} end +---Returns the placement-badge color class for top-3 placements, else nil. +---Colored by the top of the range (placeStart). +---@return string? +function Placement:getBadgeClass() + if self:hasSpecialStatus() then + return + end + local badges = {[1] = 'placement-1', [2] = 'placement-2', [3] = 'placement-3'} + return badges[self.placeStart] +end + ---@return string? function Placement:getMedal() if self:hasSpecialStatus() then From 572e2ad8fa811dc435c996e60163fbaa574c41b5 Mon Sep 17 00:00:00 2001 From: Eetu Rantanen Date: Mon, 15 Jun 2026 15:12:07 +0300 Subject: [PATCH 04/35] emit rowspan and colspan without column definitions --- lua/wikis/commons/Widget/Table2/Cell.lua | 2 +- lua/wikis/commons/Widget/Table2/CellHeader.lua | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/lua/wikis/commons/Widget/Table2/Cell.lua b/lua/wikis/commons/Widget/Table2/Cell.lua index 7ca1fec3633..51748499df1 100644 --- a/lua/wikis/commons/Widget/Table2/Cell.lua +++ b/lua/wikis/commons/Widget/Table2/Cell.lua @@ -43,7 +43,7 @@ local function Table2Cell(props, context) props.align, props.nowrap, props.shrink, - props.attributes + ColumnUtil.buildAttributes(props) ), classes = props.classes, children = children, diff --git a/lua/wikis/commons/Widget/Table2/CellHeader.lua b/lua/wikis/commons/Widget/Table2/CellHeader.lua index 4230ed2f10d..6493d57ed88 100644 --- a/lua/wikis/commons/Widget/Table2/CellHeader.lua +++ b/lua/wikis/commons/Widget/Table2/CellHeader.lua @@ -52,7 +52,7 @@ local function Table2CellHeader(props, context) -- Skip context lookups and property merging if there are no column definitions if #columns == 0 then local align = props.align - local attributes = props.attributes or {} + local attributes = ColumnUtil.buildAttributes(props) if align == 'right' or align == 'center' then attributes['data-align'] = align end From 9a182b4aaddbe64289330ab7d150566a176bff4c Mon Sep 17 00:00:00 2001 From: Eetu Rantanen Date: Mon, 15 Jun 2026 15:12:32 +0300 Subject: [PATCH 05/35] render via Table2 with reordered columns --- lua/wikis/commons/PrizePool.lua | 76 ++-------- lua/wikis/commons/PrizePool/Base.lua | 200 ++++++++++++++------------- 2 files changed, 119 insertions(+), 157 deletions(-) diff --git a/lua/wikis/commons/PrizePool.lua b/lua/wikis/commons/PrizePool.lua index 32a9405bbb4..1446d874f8c 100644 --- a/lua/wikis/commons/PrizePool.lua +++ b/lua/wikis/commons/PrizePool.lua @@ -10,7 +10,6 @@ local Lua = require('Module:Lua') local Array = Lua.import('Module:Array') local Class = Lua.import('Module:Class') local Json = Lua.import('Module:Json') -local Operator = Lua.import('Module:Operator') local String = Lua.import('Module:StringUtils') local Import = Lua.import('Module:PrizePool/Import') @@ -20,11 +19,8 @@ local Placement = Lua.import('Module:PrizePool/Placement') local Opponent = Lua.import('Module:Opponent/Custom') local Widgets = Lua.import('Module:Widget/All') -local Div = Widgets.Div -local IconFa = Lua.import('Module:Widget/Image/Icon/Fontawesome') local Span = Widgets.Span -local TableRow = Widgets.TableRow -local TableCell = Widgets.TableCell +local TableCell = Lua.import('Module:Widget/Table2/All').Cell ---@class PrizePool: BasePrizePool ---@operator call(...): PrizePool @@ -72,64 +68,22 @@ function PrizePool:applyHideAfter(placement) return placement.placeStart > self.options.hideafter end ----@param placement PrizePoolPlacement ----@return boolean -function PrizePool:applyCutAfter(placement) - if placement.placeStart > self.options.cutafter then - return true +---@return integer? +function PrizePool:_cutafterRows() + if self.options.cutafter == math.huge then + return nil end - return false -end - ----@param placement PrizePoolPlacement? ----@param nextPlacement PrizePoolPlacement ----@param rows WidgetTableRow[] -function PrizePool:applyToggleExpand(placement, nextPlacement, rows) - if placement ~= nil - and placement.placeStart <= self.options.cutafter - and placement.placeEnd >= self.options.cutafter - and placement ~= self.placements[#self.placements] - and nextPlacement.placeStart ~= placement.placeStart - and nextPlacement.placeEnd ~= placement.placeEnd then - - table.insert(rows, self:_toggleExpand(placement.placeEnd + 1)) - end -end - ----@param placeStart number ----@return WidgetTableRow -function PrizePool:_toggleExpand(placeStart) - local placeEnd = self.placements[#self.placements].placeEnd - - if self.options.hideafter < math.huge then - local lastCut = Array.max( - Array.filter(self.placements, function (placement) - return placement.placeEnd <= self.options.hideafter - end), - Operator.property('placeEnd') - ) - placeEnd = lastCut.placeEnd + local count = 0 + for _, placement in ipairs(self.placements) do + if placement.placeStart > self.options.cutafter then + break + end + if placement.placeStart > self.options.hideafter then + break + end + count = count + math.max(#placement.opponents, 1) end - - local text = 'place ' .. placeStart .. ' to ' .. placeEnd - local expandButton = TableCell{ - children = Div{children = { - text, - ' ', - IconFa{iconName = 'expand'}, - }}, - classes = {'general-collapsible-expand-button'}, - } - local collapseButton = TableCell{ - children = Div{children = { - text, - ' ', - IconFa{iconName = 'collapse'}, - }}, - classes = {'general-collapsible-collapse-button'}, - } - - return TableRow{classes = {'ppt-toggle-expand'}, children = {expandButton, collapseButton}} + return count > 0 and count or nil end -- get the lpdbObjectName depending on opponenttype diff --git a/lua/wikis/commons/PrizePool/Base.lua b/lua/wikis/commons/PrizePool/Base.lua index 8f655a2da7b..08e71d3ff8f 100644 --- a/lua/wikis/commons/PrizePool/Base.lua +++ b/lua/wikis/commons/PrizePool/Base.lua @@ -28,11 +28,10 @@ local LpdbInjector = Lua.import('Module:Lpdb/Injector') local Opponent = Lua.import('Module:Opponent/Custom') local OpponentDisplay = Lua.import('Module:OpponentDisplay/Custom') -local Widgets = Lua.import('Module:Widget/All') local HtmlWidgets = Lua.import('Module:Widget/Html/All') -local WidgetTable = Widgets.TableOld -local TableRow = Widgets.TableRow -local TableCell = Widgets.TableCell +local TableWidgets = Lua.import('Module:Widget/Table2/All') +local TableCell = TableWidgets.Cell +local TableRow = TableWidgets.Row local Div = HtmlWidgets.Div local Span = HtmlWidgets.Span local WidgetUtil = Lua.import('Module:Widget/Util') @@ -587,35 +586,36 @@ end ---@param isAward boolean? ---@return Widget function BasePrizePool:_buildTable(isAward) - local headerRow = self:_buildHeader(isAward) - - return Div{ - css = {['overflow-x'] = 'auto'}, - children = {WidgetTable{ - classes = { - 'collapsed', - 'general-collapsible', - 'prizepooltable', - 'prizepooltable-' .. (isAward and 'award' or 'placement') - }, - css = {width = 'max-content'}, - columns = headerRow:getCellCount(), - children = WidgetUtil.collect(headerRow, unpack(self:_buildRows())) - }}, + local bodyRows = self:_buildRows() + local cutafterRows = self:_cutafterRows() + + return TableWidgets.Table{ + classes = {'prizepool-table-wrapper'}, + striped = true, + tableClasses = WidgetUtil.collect( + 'prizepooltable', + 'prizepooltable-' .. (isAward and 'award' or 'placement'), + cutafterRows and 'collapsed' or nil + ), + tableAttributes = cutafterRows and {['data-cutafter'] = cutafterRows} or nil, + children = { + TableWidgets.TableHeader{children = {self:_buildHeader(isAward)}}, + TableWidgets.TableBody{children = bodyRows}, + }, } end ---@param isAward boolean? ---@return WidgetTableRow function BasePrizePool:_buildHeader(isAward) - local children = {} - - table.insert(children, TableCell{children = {isAward and 'Award' or 'Place'}, css = {['min-width'] = '80px'}}) + local children = { + TableWidgets.CellHeader{children = {isAward and 'Award' or 'Place'}, align = 'center'}, + TableWidgets.CellHeader{children = {'Participant'}, classes = {'prizepooltable-col-team'}, align = 'left'}, + } local previousOfType = {} for _, prize in ipairs(self.prizes) do local prizeTypeData = self.prizeTypes[prize.type] - if not prizeTypeData.mergeDisplayColumns or not previousOfType[prize.type] then local cell = prizeTypeData.headerDisplay(prize.data) table.insert(children, cell) @@ -623,90 +623,106 @@ function BasePrizePool:_buildHeader(isAward) end end - table.insert(children, TableCell{children = {'Participant'}, classes = {'prizepooltable-col-team'}}) - - return TableRow{classes = {'prizepooltable-header'}, css = {['font-weight'] = 'bold'}, children = children} + return TableWidgets.Row{classes = {'prizepooltable-header'}, children = children} end ---@return WidgetTableRow[] function BasePrizePool:_buildRows() local rows = {} - local previousPlacement = nil for _, placement in ipairs(self.placements) do - local previousOpponent = {} - if self:applyHideAfter(placement) then break end - self:applyToggleExpand(previousPlacement, placement, rows) - - local cells = {} - table.insert(cells, self:placeOrAwardCell(placement)) - - for _, opponent in ipairs(placement.opponents) do - local previousOfPrizeType = {} - local prizeCells = Array.map(self.prizes, function (prize) - local prizeTypeData = self.prizeTypes[prize.type] - local reward = opponent.prizeRewards[prize.id] or placement.prizeRewards[prize.id] - - local cell = reward and prizeTypeData.rowDisplay(prize.data, reward) or TableCell{} - - -- Update the previous column of this type in the same row - local lastCellOfType = previousOfPrizeType[prize.type] - if lastCellOfType and prizeTypeData.mergeDisplayColumns then + local opponents = placement.opponents + local opponentCount = math.max(#opponents, 1) + local placeCell = self:placeOrAwardCell(placement) + local backgroundClass = placement:getBackground() - if Table.isNotEmpty(lastCellOfType.props.children) and Table.isNotEmpty(cell.props.children) then - table.insert(lastCellOfType.props.children, tostring(mw.html.create('hr'):css('width', '100%'))) - end - - Array.extendWith(lastCellOfType.props.children, cell.props.children) - lastCellOfType.css['flex-direction'] = 'column' + -- Build the prize-cell matrix: prizeMatrix[opponentIndex] = {cell, …} in display-column order. + local prizeMatrix = Array.map(opponents, function(opponent) + return self:_opponentPrizeCells(placement, opponent) + end) - return nil + -- Vertically merge consecutive-equal prize cells per column (declare span on the run's first row). + local numCols = prizeMatrix[1] and #prizeMatrix[1] or 0 + local omitted = {} -- omitted[opponentIndex][col] = true + for col = 1, numCols do + local opponentIndex = 1 + while opponentIndex <= opponentCount do + local runLength = 1 + while opponentIndex + runLength <= opponentCount + and Table.deepEquals( + prizeMatrix[opponentIndex][col].props.children, + prizeMatrix[opponentIndex + runLength][col].props.children + ) do + omitted[opponentIndex + runLength] = omitted[opponentIndex + runLength] or {} + omitted[opponentIndex + runLength][col] = true + runLength = runLength + 1 + end + if runLength > 1 then + prizeMatrix[opponentIndex][col].props.rowspan = runLength end + opponentIndex = opponentIndex + runLength + end + end - previousOfPrizeType[prize.type] = cell - return cell - end) + for opponentIndex, opponent in ipairs(opponents) do + local cells = {} + if opponentIndex == 1 then + table.insert(cells, placeCell) + end - Array.forEach(prizeCells, function (prizeCell, columnIndex) - local lastInColumn = previousOpponent[columnIndex] + table.insert(cells, TableCell{ + children = {tostring(OpponentDisplay.BlockOpponent{ + opponent = opponent.opponentData, + showPlayerTeam = true, + })}, + classes = {'prizepooltable-col-team'}, + align = 'left', + nowrap = false, + }) - ---@cast prizeCell -nil - if Table.isEmpty(prizeCell.props.children) then - prizeCell = BasePrizePool._emptyCell() + for col = 1, numCols do + if not (omitted[opponentIndex] and omitted[opponentIndex][col]) then + table.insert(cells, prizeMatrix[opponentIndex][col]) end + end - if lastInColumn and Table.deepEquals(lastInColumn.props.children, prizeCell.props.children) then - lastInColumn.rowSpan = (lastInColumn.rowSpan or 1) + 1 - else - previousOpponent[columnIndex] = prizeCell - table.insert(cells, prizeCell) - end - end) + table.insert(rows, TableRow{children = cells, classes = {backgroundClass}}) + end + end - local opponentDisplay = tostring(OpponentDisplay.BlockOpponent{ - opponent = opponent.opponentData, - showPlayerTeam = true, - }) - local opponentCss = {['justify-content'] = 'start'} + return rows +end - table.insert(cells, TableCell{children = {opponentDisplay}, css = opponentCss}) - end - local classes = {placement:getBackground()} - if self:applyCutAfter(placement) then - table.insert(classes, 'ppt-hide-on-collapse') - end - local row = TableRow{children = cells, classes = classes} +---@param placement BasePlacement +---@param opponent table +---@return WidgetTableCell[] +function BasePrizePool:_opponentPrizeCells(placement, opponent) + local previousOfPrizeType = {} + local prizeCells = Array.map(self.prizes, function(prize) + local prizeTypeData = self.prizeTypes[prize.type] + local reward = opponent.prizeRewards[prize.id] or placement.prizeRewards[prize.id] + local cell = reward and prizeTypeData.rowDisplay(prize.data, reward) or TableCell{} - table.insert(rows, row) + local lastCellOfType = previousOfPrizeType[prize.type] + if lastCellOfType and prizeTypeData.mergeDisplayColumns then + if Table.isNotEmpty(lastCellOfType.props.children) and Table.isNotEmpty(cell.props.children) then + table.insert(lastCellOfType.props.children, tostring(mw.html.create('hr'):css('width', '100%'))) + end + Array.extendWith(lastCellOfType.props.children, cell.props.children) + return nil + end - previousPlacement = placement - end + previousOfPrizeType[prize.type] = cell + return cell + end) - return rows + return Array.map(prizeCells, function(cell) + return Table.isEmpty(cell.props.children) and BasePrizePool._emptyCell() or cell + end) end ---@protected @@ -722,19 +738,11 @@ function BasePrizePool:applyHideAfter(placement) return false end ----@protected ----@param placement BasePlacement ----@return boolean -function BasePrizePool:applyCutAfter(placement) - error('Function applyCutAfter needs to be implemented by a child class of "Module:PrizePool/Base"') -end - ----@protected ----@param placement BasePlacement? ----@param nextPlacement BasePlacement ----@param row WidgetTableRow -function BasePrizePool:applyToggleExpand(placement, nextPlacement, row) - error('Function applyToggleExpand needs to be implemented by a child class of "Module:PrizePool/Base"') +--- Number of body rows to show before collapse; nil disables collapse. +--- Child classes override this to drive the JS `data-cutafter` behaviour. +---@return integer? +function BasePrizePool:_cutafterRows() + return nil end ---@return string From d8983e43d0be35b26a3b2bc06ea9ced32eab3a25 Mon Sep 17 00:00:00 2001 From: Eetu Rantanen Date: Mon, 15 Jun 2026 15:23:55 +0300 Subject: [PATCH 06/35] restyle for Table2 redesign --- stylesheets/commons/Prizepooltable.scss | 402 ++++++++++-------------- 1 file changed, 168 insertions(+), 234 deletions(-) diff --git a/stylesheets/commons/Prizepooltable.scss b/stylesheets/commons/Prizepooltable.scss index 7aebf9e98e4..41bb5565b09 100644 --- a/stylesheets/commons/Prizepooltable.scss +++ b/stylesheets/commons/Prizepooltable.scss @@ -1,279 +1,187 @@ -/******************************************************************************* -Template(s): Prize pool tables -Author(s): FO-nTTaX -*******************************************************************************/ +$prizepool-badge-size: 1.5rem; +$prizepool-accent-width: 0.25rem; :root { - --prize-pool-background-color: transparent; -} + // Placement badge / left-accent colours (light defaults) + --prizepool-place-1-accent: #f0b419; + --prizepool-place-2-accent: #58b5e3; + --prizepool-place-3-accent: #de7d1d; -.theme--light { - --prize-pool-variant-background-color: #f2f2f2; - --prize-pool-header-background-color: #eaecf0; - --prize-pool-alt-background-color: #f5f5f5; - --prize-pool-gold: var( --clr-semantic-gold-40 ); - --prize-pool-silver: var( --clr-semantic-silver-40 ); - --prize-pool-bronze: var( --clr-semantic-bronze-40 ); - --prize-pool-copper: var( --clr-semantic-copper-40 ); + // Subtle full-row tints (light defaults) + --prizepool-place-1-tint: #fff9eb; + --prizepool-place-2-tint: #f0fcff; + --prizepool-place-3-tint: #fff3ec; } .theme--dark { - --prize-pool-variant-background-color: var( --clr-moon-20 ); - --prize-pool-header-background-color: var( --table-header-variant-background-color ); - --prize-pool-alt-background-color: var( --clr-secondary-9 ); - --prize-pool-gold: var( --clr-semantic-gold-30 ); - --prize-pool-silver: var( --clr-secondary-39 ); - --prize-pool-bronze: var( --clr-semantic-bronze-20 ); - --prize-pool-copper: var( --clr-semantic-copper-20 ); -} - -table.prizepooltable:not( .collapsed ) .prizepooltableshow, -table.prizepooltable.collapsed .prizepooltablehide { - display: none; -} - -.prizepooltabletoggle { - cursor: pointer; -} + --prizepool-place-1-accent: #fbcb50; + --prizepool-place-2-accent: #91d7f9; + --prizepool-place-3-accent: #ffb267; -.prizepooltable-col-player, -.prizepooltable-col-team { - min-width: 150px; + --prizepool-place-1-tint: #353932; + --prizepool-place-2-tint: #243b4d; + --prizepool-place-3-tint: #363535; } -.prizepooltable-col-two-players { - min-width: 300px; -} +/******************************************************************************* +Table2-based placement prize pool table +*******************************************************************************/ -@media ( max-width: 600px ) { - .prizepooltable-col-team { - min-width: 65px; +.prizepool-table-wrapper { + .table2__container { + overflow-x: auto; + scrollbar-width: none; - a:nth-child( 2 ) { + &::-webkit-scrollbar { display: none; } } } -@for $i from 0 through 32 { - html.client-js .prizepooltable.collapsed[ data-cutafter="#{$i}" ] tr:nth-child( n + #{ $i + 3 } ) { - display: none; +.prizepooltable { + // Header row: light fill, muted bold text, bottom divider (mostly inherited + // from Table2's head-row styling; only the divider weight is reinforced here). + .prizepooltable-header { + th, + td { + font-weight: bold; + } } -} -.prizepooltable-header { - background-color: var( --prize-pool-header-background-color, inherit ); -} + // Placement number badge: small rounded-square chip, centered, white text. + .prizepooltable-badge { + display: inline-flex; + align-items: center; + justify-content: center; + min-width: $prizepool-badge-size; + height: $prizepool-badge-size; + padding: 0 0.25rem; + border-radius: 0.25rem; + font-weight: bold; + line-height: 1; + color: #ffffff; + + &.placement-1 { + background-color: var( --prizepool-place-1-accent ); + } -.csstable-widget.prizepooltable { - border: 0.125rem solid var( --prize-pool-header-background-color ); + &.placement-2 { + background-color: var( --prizepool-place-2-accent ); + } - .csstable-widget-row, - .csstable-widget-cell { - border: 0; + &.placement-3 { + background-color: var( --prizepool-place-3-accent ); + } } - .csstable-widget-row:nth-child( odd ):not( .prizepooltable-header ):not( .ppt-toggle-expand ) { - background-color: var( --prize-pool-alt-background-color ); + // Min widths so opponent columns do not collapse on wide tables. + .prizepooltable-col-player, + .prizepooltable-col-team { + min-width: 150px; } -} - -/* Background colours */ - -tr.bg-first, -div.bg-first, -tr.bg-p1, -div.bg-p1, -tr.background-color-first-place, -div.background-color-first-place { - background-color: var( --prize-pool-background-color, #ffe982 ); -} - -td.bg-first, -td.bg-p1, -td.background-color-first-place { - background-color: var( --prize-pool-gold, #ffe982 ); -} - -tr.bg-first, -div.bg-first, -tr.bg-p1, -div.bg-p1, -tr.background-color-first-place, -div.background-color-first-place, -div.csstable-widget-row.background-color-first-place > .csstable-widget-cell { - border-bottom: 0.125rem solid var( --prize-pool-gold, transparent ); -} - -tr.bg-first > td.prizepooltable-place, -div.bg-first > td.prizepooltable-place, -tr.bg-p1 > td.prizepooltable-place, -div.bg-p1 > td.prizepooltable-place, -tr.background-color-first-place > td.prizepooltable-place, -div.background-color-first-place > *.prizepooltable-place { - background-color: var( --prize-pool-gold, inherit ); - color: #ffffff; -} - -tr.bg-second, -div.bg-second, -tr.bg-p2, -div.bg-p2, -tr.background-color-second-place, -div.background-color-second-place { - background-color: var( --prize-pool-background-color, #eeeeee ); -} - -td.bg-second, -td.bg-p2, -td.background-color-second-place { - background-color: var( --prize-pool-silver, #eeeeee ); -} - -tr.bg-second, -div.bg-second, -tr.bg-p2, -div.bg-p2, -tr.background-color-second-place, -div.background-color-second-place, -div.csstable-widget-row.background-color-second-place > .csstable-widget-cell { - border-bottom: 0.125rem solid var( --prize-pool-silver, transparent ); -} - -tr.bg-second > td.prizepooltable-place, -div.bg-second > td.prizepooltable-place, -tr.bg-p2 > td.prizepooltable-place, -div.bg-p2 > td.prizepooltable-place, -tr.background-color-second-place > td.prizepooltable-place, -div.background-color-second-place > *.prizepooltable-place { - background-color: var( --prize-pool-silver, inherit ); - color: #ffffff; -} - -tr.bg-third, -div.bg-third, -tr.bg-p3, -div.bg-p3, -tr.background-color-third-place, -div.background-color-third-place:not( :first-child ) { - background-color: var( --prize-pool-background-color, #f9e8c7 ); -} - -td.bg-third, -td.bg-p3, -td.background-color-third-place { - background-color: var( --prize-pool-bronze, #f9e8c7 ); -} - -tr.bg-third, -div.bg-third, -tr.bg-p3, -div.bg-p3, -tr.background-color-third-place, -div.background-color-third-place, -div.csstable-widget-row.background-color-third-place > .csstable-widget-cell { - border-bottom: 0.125rem solid var( --prize-pool-bronze, transparent ); -} -tr.bg-third > td.prizepooltable-place, -div.bg-third > td.prizepooltable-place, -tr.bg-p3 > td.prizepooltable-place, -div.bg-p3 > td.prizepooltable-place, -tr.background-color-third-place > td.prizepooltable-place, -div.background-color-third-place > *.prizepooltable-place { - background-color: var( --prize-pool-bronze, inherit ); - color: #ffffff; -} + .prizepooltable-col-two-players { + min-width: 300px; + } -tr.bg-fourth, -div.bg-fourth, -tr.bg-p4, -div.bg-p4, -tr.background-color-fourth-place, -div.background-color-fourth-place { - background-color: var( --prize-pool-background-color, #f9dec7 ); -} + // Top-3 placement rows: subtle full-row tint + coloured left accent bar. + // The extra `.table2__row--body` class plus `!important`-free higher + // specificity makes these win over the Table2 zebra (`--odd`/`--even`) + // rule, which itself sits under a `.theme--*` wrapper. + tr.table2__row--body.background-color-first-place, + tr.table2__row--body.background-color-second-place, + tr.table2__row--body.background-color-third-place { + .prizepooltable-place { + position: relative; + + &::before { + content: ""; + position: absolute; + inset: 0 auto 0 0; + width: $prizepool-accent-width; + } + } + } -td.bg-fourth, -td.bg-p4, -td.background-color-fourth-place { - background-color: var( --prize-pool-copper, #f9dec7 ); -} + // Theme wrappers + the `.table2__table` qualifier raise specificity to + // (0,5,2) so the tint outranks the Table2 zebra rule (0,4,2) regardless of + // stylesheet load order, and the `:hover` variant keeps it from washing out. + .theme--light &.table2__table, + .theme--dark &.table2__table { + tr.table2__row--body.background-color-first-place { + &, + &:hover { + background-color: var( --prizepool-place-1-tint ); + } + + .prizepooltable-place::before { + background-color: var( --prizepool-place-1-accent ); + } + } -tr.bg-fourth, -div.bg-fourth, -tr.bg-p4, -div.bg-p4, -tr.background-color-fourth-place, -div.background-color-fourth-place, -div.csstable-widget-row.background-color-fourth-place > .csstable-widget-cell { - border-bottom: 0.125rem solid var( --prize-pool-copper, transparent ); -} + tr.table2__row--body.background-color-second-place { + &, + &:hover { + background-color: var( --prizepool-place-2-tint ); + } -tr.bg-fourth > td.prizepooltable-place, -div.bg-fourth > td.prizepooltable-place, -tr.bg-p4 > td.prizepooltable-place, -div.bg-p4 > td.prizepooltable-place, -tr.background-color-fourth-place > td.prizepooltable-place, -div.background-color-fourth-place > *.prizepooltable-place { - background-color: var( --prize-pool-copper, inherit ); - color: #ffffff; -} + .prizepooltable-place::before { + background-color: var( --prizepool-place-2-accent ); + } + } -div.csstable-widget-row.bg-win { - div.prizepooltable > & { - /* need the `!important` due to bg-win ( sadly ) having an important in one of its definitions ... */ - background-color: inherit !important; - } + tr.table2__row--body.background-color-third-place { + &, + &:hover { + background-color: var( --prizepool-place-3-tint ); + } - > .csstable-widget-cell { - border-bottom: 0.125rem solid var( --clr-forest-background-color, transparent ); + .prizepooltable-place::before { + background-color: var( --prizepool-place-3-accent ); + } + } } -} -div.bg-win > *.prizepooltable-place { - background-color: var( --clr-forest-background-color, inherit ); -} - -div.csstable-widget-row.bg-lose { - background-color: inherit; + // Footer toggle: full-width, centered, brand-blue link with chevron. + .prizepooltabletoggle { + cursor: pointer; + font-weight: bold; + text-align: center; + color: var( --clr-wiki-theme-30, #0a558f ); - > .csstable-widget-cell { - border-bottom: 0.125rem solid var( --clr-cinnabar-background-color, transparent ); + .theme--dark & { + color: var( --clr-wiki-theme-80, #81a0bf ); + } } } -div.bg-lose > *.prizepooltable-place { - background-color: var( --clr-cinnabar-background-color, inherit ); -} - -tr.background-color-disqualified, -div.background-color-disqualified { - background-color: #dee3ef; -} - -tr.background-color-win, -div.background-color-win { - background-color: #ddf4dd; +// Show/hide the correct toggle label depending on collapse state. +table.prizepooltable:not( .collapsed ) .prizepooltableshow, +table.prizepooltable.collapsed .prizepooltablehide { + display: none; } -tr.background-color-lose, -div.background-color-lose { - background-color: #fbdfdf; +// Hide rows past the cut-off while collapsed. The toggle row is injected by +// Prizepooltable.js before the (cutAfter + 2)-th row, so rows from +// (cutAfter + 3) onward are hidden when collapsed. +@for $i from 0 through 32 { + html.client-js .prizepooltable.collapsed[ data-cutafter="#{$i}" ] tbody tr:nth-child( n + #{ $i + 3 } ) { + display: none; + } } -tr.background-color-highlight, -div.background-color-highlight { - background-color: #cce5ea; -} +@media ( max-width: 600px ) { + .prizepooltable-col-team { + min-width: 65px; -.prizepooltable-background-variant { - background-color: var( --prize-pool-variant-background-color, #f2f2f2 ); + a:nth-child( 2 ) { + display: none; + } + } } /******************************************************************************* -Template: ( Standardized ) prize pool tables -Author: Rathoz +Legacy css-grid prize pool tables (still used by Module:PrizePool/Award) *******************************************************************************/ .collapsed > .ppt-hide-on-collapse { @@ -315,9 +223,35 @@ Author: Rathoz } } +div.csstable-widget-row.bg-win { + div.prizepooltable > & { + /* need the `!important` due to bg-win ( sadly ) having an important in one of its definitions ... */ + background-color: inherit !important; + } + + > .csstable-widget-cell { + border-bottom: 0.125rem solid var( --clr-forest-background-color, transparent ); + } +} + +div.bg-win > *.prizepooltable-place { + background-color: var( --clr-forest-background-color, inherit ); +} + +div.csstable-widget-row.bg-lose { + background-color: inherit; + + > .csstable-widget-cell { + border-bottom: 0.125rem solid var( --clr-cinnabar-background-color, transparent ); + } +} + +div.bg-lose > *.prizepooltable-place { + background-color: var( --clr-cinnabar-background-color, inherit ); +} + /******************************************************************************* -Template: ( Standardized ) prize pool section -Author: iMarbot +( Standardized ) prize pool section *******************************************************************************/ .prizepool-section-wrapper { From 44a551d7420a67a2849504ec8f38a3ee53072019 Mon Sep 17 00:00:00 2001 From: Eetu Rantanen Date: Mon, 15 Jun 2026 15:31:50 +0300 Subject: [PATCH 07/35] migrate award variant to Table2 and drop dead collapse code --- lua/wikis/commons/PrizePool/Award.lua | 62 ++++++------------------ stylesheets/commons/Prizepooltable.scss | 64 +++++++------------------ 2 files changed, 31 insertions(+), 95 deletions(-) diff --git a/lua/wikis/commons/PrizePool/Award.lua b/lua/wikis/commons/PrizePool/Award.lua index 6eb7b073a65..b9a263539e9 100644 --- a/lua/wikis/commons/PrizePool/Award.lua +++ b/lua/wikis/commons/PrizePool/Award.lua @@ -17,11 +17,7 @@ local Placement = Lua.import('Module:PrizePool/Award/Placement') local Opponent = Lua.import('Module:Opponent/Custom') -local Widgets = Lua.import('Module:Widget/All') -local Div = Widgets.Div -local IconFa = Lua.import('Module:Widget/Image/Icon/Fontawesome') -local TableRow = Widgets.TableRow -local TableCell = Widgets.TableCell +local TableCell = Lua.import('Module:Widget/Table2/All').Cell --- @class AwardPrizePool: BasePrizePool --- @operator call(...): AwardPrizePool @@ -52,56 +48,26 @@ end ---@param placement AwardPlacement ---@return WidgetTableCell function AwardPrizePool:placeOrAwardCell(placement) - local awardCell = TableCell{ + return TableCell{ children = {placement.award}, - css = {['font-weight'] = 'bolder'}, classes = {'prizepooltable-place'}, + rowspan = #placement.opponents, } - awardCell.rowSpan = #placement.opponents - - return awardCell end ----@param placement AwardPlacement ----@return boolean -function AwardPrizePool:applyCutAfter(placement) - if (placement.previousTotalNumberOfParticipants + 1) > self.options.cutafter then - return true +---@return integer? +function AwardPrizePool:_cutafterRows() + if self.options.cutafter == math.huge then + return nil end - return false -end - ----@param placement AwardPlacement? ----@param nextPlacement AwardPlacement ----@param rows WidgetTableRow[] -function AwardPrizePool:applyToggleExpand(placement, nextPlacement, rows) - if placement ~= nil - and (placement.previousTotalNumberOfParticipants + 1) <= self.options.cutafter - and placement.currentTotalNumberOfParticipants >= self.options.cutafter - and placement ~= self.placements[#self.placements] then - - table.insert(rows, self:_toggleExpand()) + local count = 0 + for _, placement in ipairs(self.placements) do + if (placement.previousTotalNumberOfParticipants + 1) > self.options.cutafter then + break + end + count = count + math.max(#placement.opponents, 1) end -end - ----@return WidgetTableRow -function AwardPrizePool:_toggleExpand() - local expandButton = TableCell{ - children = Div{children = { - 'Show more Awards ', - IconFa{iconName = 'expand'}, - }}, - classes = {'general-collapsible-expand-button'}, - } - local collapseButton = TableCell{ - children = Div{children = { - 'Show less Awards ', - IconFa{iconName = 'collapse'}, - }}, - classes = {'general-collapsible-collapse-button'}, - } - - return TableRow{classes = {'ppt-toggle-expand'}, children = {expandButton, collapseButton}} + return count > 0 and count or nil end -- Get the lpdbObjectName depending on opponenttype diff --git a/stylesheets/commons/Prizepooltable.scss b/stylesheets/commons/Prizepooltable.scss index 41bb5565b09..e9983e9f7f4 100644 --- a/stylesheets/commons/Prizepooltable.scss +++ b/stylesheets/commons/Prizepooltable.scss @@ -142,6 +142,20 @@ Table2-based placement prize pool table } } + // Status rows (win / lose / disqualified): tint the place cell, preserving + // the pre-Table2 treatment. + tr.table2__row--body.bg-win .prizepooltable-place { + background-color: var( --clr-forest-background-color, inherit ); + } + + tr.table2__row--body.bg-lose .prizepooltable-place { + background-color: var( --clr-cinnabar-background-color, inherit ); + } + + tr.table2__row--body.bg-dq .prizepooltable-place { + background-color: var( --clr-sapphire-background-color, inherit ); + } + // Footer toggle: full-width, centered, brand-blue link with chevron. .prizepooltabletoggle { cursor: pointer; @@ -181,24 +195,16 @@ table.prizepooltable.collapsed .prizepooltablehide { } /******************************************************************************* -Legacy css-grid prize pool tables (still used by Module:PrizePool/Award) +Legacy css-grid table widget (Module:Widget/Table/Old), still used by +non-prizepool modules (e.g. infoboxes). Prizepool no longer renders via this. *******************************************************************************/ -.collapsed > .ppt-hide-on-collapse { - display: none !important; -} - -.collapsed .ppt-toggle-expand .general-collapsible-collapse-button, -.general-collapsible:not( .collapsed ) .ppt-toggle-expand .general-collapsible-expand-button { - display: none !important; -} - .csstable-widget { border-right: 1px solid var( --table-border-color, #bbbbbb ); border-bottom: 1px solid var( --table-border-color, #bbbbbb ); display: inline-grid; - &-row:not( .ppt-toggle-expand ) { + &-row { display: contents; } @@ -214,42 +220,6 @@ Legacy css-grid prize pool tables (still used by Module:PrizePool/Award) } } -.ppt-toggle-expand { - grid-column: 1 / -1; - - > * { - color: unset !important; - text-decoration: none !important; - } -} - -div.csstable-widget-row.bg-win { - div.prizepooltable > & { - /* need the `!important` due to bg-win ( sadly ) having an important in one of its definitions ... */ - background-color: inherit !important; - } - - > .csstable-widget-cell { - border-bottom: 0.125rem solid var( --clr-forest-background-color, transparent ); - } -} - -div.bg-win > *.prizepooltable-place { - background-color: var( --clr-forest-background-color, inherit ); -} - -div.csstable-widget-row.bg-lose { - background-color: inherit; - - > .csstable-widget-cell { - border-bottom: 0.125rem solid var( --clr-cinnabar-background-color, transparent ); - } -} - -div.bg-lose > *.prizepooltable-place { - background-color: var( --clr-cinnabar-background-color, inherit ); -} - /******************************************************************************* ( Standardized ) prize pool section *******************************************************************************/ From e240501661ed24b7e4e555a721a51f4f4b37a79c Mon Sep 17 00:00:00 2001 From: Eetu Rantanen Date: Mon, 15 Jun 2026 15:46:47 +0300 Subject: [PATCH 08/35] correct collapse row offset for thead/tbody and fix stale annotations --- javascript/commons/Prizepooltable.js | 4 ++-- lua/wikis/commons/PrizePool.lua | 2 +- lua/wikis/commons/PrizePool/Award.lua | 2 +- lua/wikis/commons/PrizePool/Base.lua | 8 ++++---- stylesheets/commons/Prizepooltable.scss | 6 +++--- 5 files changed, 11 insertions(+), 11 deletions(-) diff --git a/javascript/commons/Prizepooltable.js b/javascript/commons/Prizepooltable.js index f7a4a32765b..6bad30991a8 100644 --- a/javascript/commons/Prizepooltable.js +++ b/javascript/commons/Prizepooltable.js @@ -23,8 +23,8 @@ liquipedia.prizepooltable = { closeText = prizepooltable.dataset.closetext; } closeText += ' '; - if ( numRows > cutAfter ) { - const row = prizepooltable.querySelector( 'tr:nth-child(' + ( cutAfter + 2 ) + ')' ); + if ( numRows - 1 > cutAfter ) { + const row = prizepooltable.querySelector( 'tbody tr:nth-child(' + ( cutAfter + 1 ) + ')' ); if ( row !== null ) { const rowNode = document.createElement( 'tr' ); rowNode.innerHTML = '' + openText + '' + closeText + ''; diff --git a/lua/wikis/commons/PrizePool.lua b/lua/wikis/commons/PrizePool.lua index 1446d874f8c..b9ae28e22ca 100644 --- a/lua/wikis/commons/PrizePool.lua +++ b/lua/wikis/commons/PrizePool.lua @@ -47,7 +47,7 @@ function PrizePool:readPlacements(args) end ---@param placement PrizePoolPlacement ----@return WidgetTableCell +---@return Renderable function PrizePool:placeOrAwardCell(placement) local badgeClass = placement:getBadgeClass() local placeDisplay = placement:_displayPlace() diff --git a/lua/wikis/commons/PrizePool/Award.lua b/lua/wikis/commons/PrizePool/Award.lua index b9a263539e9..cdaf68cc7c8 100644 --- a/lua/wikis/commons/PrizePool/Award.lua +++ b/lua/wikis/commons/PrizePool/Award.lua @@ -46,7 +46,7 @@ function AwardPrizePool:readPlacements(args) end ---@param placement AwardPlacement ----@return WidgetTableCell +---@return Renderable function AwardPrizePool:placeOrAwardCell(placement) return TableCell{ children = {placement.award}, diff --git a/lua/wikis/commons/PrizePool/Base.lua b/lua/wikis/commons/PrizePool/Base.lua index 08e71d3ff8f..47b1b0028e2 100644 --- a/lua/wikis/commons/PrizePool/Base.lua +++ b/lua/wikis/commons/PrizePool/Base.lua @@ -606,7 +606,7 @@ function BasePrizePool:_buildTable(isAward) end ---@param isAward boolean? ----@return WidgetTableRow +---@return Renderable function BasePrizePool:_buildHeader(isAward) local children = { TableWidgets.CellHeader{children = {isAward and 'Award' or 'Place'}, align = 'center'}, @@ -626,7 +626,7 @@ function BasePrizePool:_buildHeader(isAward) return TableWidgets.Row{classes = {'prizepooltable-header'}, children = children} end ----@return WidgetTableRow[] +---@return Renderable[] function BasePrizePool:_buildRows() local rows = {} @@ -699,7 +699,7 @@ end ---@param placement BasePlacement ---@param opponent table ----@return WidgetTableCell[] +---@return Renderable[] function BasePrizePool:_opponentPrizeCells(placement, opponent) local previousOfPrizeType = {} local prizeCells = Array.map(self.prizes, function(prize) @@ -823,7 +823,7 @@ function BasePrizePool:_hasBaseCurrency() end --- Creates an empty table cell ----@return WidgetTableCell +---@return Renderable function BasePrizePool._emptyCell() return TableCell{children = {DASH}} end diff --git a/stylesheets/commons/Prizepooltable.scss b/stylesheets/commons/Prizepooltable.scss index e9983e9f7f4..e37ad76d9f0 100644 --- a/stylesheets/commons/Prizepooltable.scss +++ b/stylesheets/commons/Prizepooltable.scss @@ -176,10 +176,10 @@ table.prizepooltable.collapsed .prizepooltablehide { } // Hide rows past the cut-off while collapsed. The toggle row is injected by -// Prizepooltable.js before the (cutAfter + 2)-th row, so rows from -// (cutAfter + 3) onward are hidden when collapsed. +// Prizepooltable.js before the (cutAfter + 1)-th tbody row, so rows from +// (cutAfter + 2) onward are hidden when collapsed. @for $i from 0 through 32 { - html.client-js .prizepooltable.collapsed[ data-cutafter="#{$i}" ] tbody tr:nth-child( n + #{ $i + 3 } ) { + html.client-js .prizepooltable.collapsed[ data-cutafter="#{$i}" ] tbody tr:nth-child( n + #{ $i + 2 } ) { display: none; } } From cd8812188581b3328985c77f392369b36e5f1fcf Mon Sep 17 00:00:00 2001 From: Eetu Rantanen Date: Mon, 15 Jun 2026 16:11:28 +0300 Subject: [PATCH 09/35] idempotent collapse toggle and placement-based toggle label --- javascript/commons/Prizepooltable.js | 3 +++ lua/wikis/commons/PrizePool.lua | 19 +++++++++++++++++++ lua/wikis/commons/PrizePool/Award.lua | 5 +++++ lua/wikis/commons/PrizePool/Base.lua | 13 ++++++++++++- 4 files changed, 39 insertions(+), 1 deletion(-) diff --git a/javascript/commons/Prizepooltable.js b/javascript/commons/Prizepooltable.js index 6bad30991a8..c8d27297298 100644 --- a/javascript/commons/Prizepooltable.js +++ b/javascript/commons/Prizepooltable.js @@ -5,6 +5,9 @@ liquipedia.prizepooltable = { init: function() { document.querySelectorAll( '.prizepooltable' ).forEach( ( prizepooltable ) => { + if ( prizepooltable.querySelector( '.prizepooltabletoggle' ) !== null ) { + return; + } let cutAfter; if ( typeof prizepooltable.dataset.cutafter !== 'undefined' ) { cutAfter = parseInt( prizepooltable.dataset.cutafter ); diff --git a/lua/wikis/commons/PrizePool.lua b/lua/wikis/commons/PrizePool.lua index b9ae28e22ca..2c7ab0ee2d7 100644 --- a/lua/wikis/commons/PrizePool.lua +++ b/lua/wikis/commons/PrizePool.lua @@ -86,6 +86,25 @@ function PrizePool:_cutafterRows() return count > 0 and count or nil end +---@return {opentext: string, closetext: string}? +function PrizePool:_collapseText() + local firstHidden, lastPlace + for _, placement in ipairs(self.placements) do + if placement.placeStart > self.options.hideafter then + break + end + lastPlace = placement.placeEnd + if not firstHidden and placement.placeStart > self.options.cutafter then + firstHidden = placement.placeStart + end + end + if not firstHidden or not lastPlace then + return nil + end + local text = 'place ' .. firstHidden .. ' to ' .. lastPlace + return {opentext = text, closetext = text} +end + -- get the lpdbObjectName depending on opponenttype ---@param lpdbEntry placement ---@param prizePoolIndex integer|string diff --git a/lua/wikis/commons/PrizePool/Award.lua b/lua/wikis/commons/PrizePool/Award.lua index cdaf68cc7c8..7f172f1b851 100644 --- a/lua/wikis/commons/PrizePool/Award.lua +++ b/lua/wikis/commons/PrizePool/Award.lua @@ -70,6 +70,11 @@ function AwardPrizePool:_cutafterRows() return count > 0 and count or nil end +---@return {opentext: string, closetext: string}? +function AwardPrizePool:_collapseText() + return {opentext = 'Show more Awards', closetext = 'Show less Awards'} +end + -- Get the lpdbObjectName depending on opponenttype ---@param lpdbEntry placement ---@param prizePoolIndex integer|string diff --git a/lua/wikis/commons/PrizePool/Base.lua b/lua/wikis/commons/PrizePool/Base.lua index 47b1b0028e2..74e9a96b441 100644 --- a/lua/wikis/commons/PrizePool/Base.lua +++ b/lua/wikis/commons/PrizePool/Base.lua @@ -588,6 +588,7 @@ end function BasePrizePool:_buildTable(isAward) local bodyRows = self:_buildRows() local cutafterRows = self:_cutafterRows() + local collapseText = cutafterRows and self:_collapseText() or nil return TableWidgets.Table{ classes = {'prizepool-table-wrapper'}, @@ -597,7 +598,11 @@ function BasePrizePool:_buildTable(isAward) 'prizepooltable-' .. (isAward and 'award' or 'placement'), cutafterRows and 'collapsed' or nil ), - tableAttributes = cutafterRows and {['data-cutafter'] = cutafterRows} or nil, + tableAttributes = cutafterRows and { + ['data-cutafter'] = cutafterRows, + ['data-opentext'] = collapseText and collapseText.opentext or nil, + ['data-closetext'] = collapseText and collapseText.closetext or nil, + } or nil, children = { TableWidgets.TableHeader{children = {self:_buildHeader(isAward)}}, TableWidgets.TableBody{children = bodyRows}, @@ -745,6 +750,12 @@ function BasePrizePool:_cutafterRows() return nil end +---Open/close labels for the collapse toggle. Child classes override. +---@return {opentext: string, closetext: string}? +function BasePrizePool:_collapseText() + return nil +end + ---@return string function BasePrizePool:_getPrizeSummaryText() local tba = Abbreviation.make{text = 'TBA', title = 'To Be Announced'} From f1a5e9976015a8d40dbace18a325d77d6d527d82 Mon Sep 17 00:00:00 2001 From: Eetu Rantanen Date: Tue, 16 Jun 2026 10:18:05 +0300 Subject: [PATCH 10/35] class-based collapse, restore legacy data-cutafter path --- javascript/commons/Prizepooltable.js | 25 ++++++++++++++++++-- lua/wikis/commons/PrizePool.lua | 20 ++++------------ lua/wikis/commons/PrizePool/Award.lua | 17 ++++---------- lua/wikis/commons/PrizePool/Base.lua | 31 ++++++++++++++----------- stylesheets/commons/Prizepooltable.scss | 15 ++++++++---- 5 files changed, 60 insertions(+), 48 deletions(-) diff --git a/javascript/commons/Prizepooltable.js b/javascript/commons/Prizepooltable.js index c8d27297298..6b381b393dd 100644 --- a/javascript/commons/Prizepooltable.js +++ b/javascript/commons/Prizepooltable.js @@ -8,6 +8,27 @@ liquipedia.prizepooltable = { if ( prizepooltable.querySelector( '.prizepooltabletoggle' ) !== null ) { return; } + // Class-based collapse (prize pool): Lua marks the cut rows with + // `ppt-hide-on-collapse`; the toggle goes directly before the first one. + const firstHiddenRow = prizepooltable.querySelector( '.ppt-hide-on-collapse' ); + if ( firstHiddenRow !== null ) { + let openLabel = 'show more'; + if ( typeof prizepooltable.dataset.opentext !== 'undefined' ) { + openLabel = prizepooltable.dataset.opentext; + } + openLabel += ' '; + let closeLabel = 'show less'; + if ( typeof prizepooltable.dataset.closetext !== 'undefined' ) { + closeLabel = prizepooltable.dataset.closetext; + } + closeLabel += ' '; + const colspan = prizepooltable.querySelectorAll( 'tr:nth-child(1) th, tr:nth-child(1) td' ).length; + const rowNode = document.createElement( 'tr' ); + rowNode.innerHTML = '' + openLabel + '' + closeLabel + ''; + firstHiddenRow.parentNode.insertBefore( rowNode, firstHiddenRow ); + return; + } + // Legacy data-cutafter collapse (mvp / medal / winnings tables). let cutAfter; if ( typeof prizepooltable.dataset.cutafter !== 'undefined' ) { cutAfter = parseInt( prizepooltable.dataset.cutafter ); @@ -26,8 +47,8 @@ liquipedia.prizepooltable = { closeText = prizepooltable.dataset.closetext; } closeText += ' '; - if ( numRows - 1 > cutAfter ) { - const row = prizepooltable.querySelector( 'tbody tr:nth-child(' + ( cutAfter + 1 ) + ')' ); + if ( numRows > cutAfter ) { + const row = prizepooltable.querySelector( 'tr:nth-child(' + ( cutAfter + 2 ) + ')' ); if ( row !== null ) { const rowNode = document.createElement( 'tr' ); rowNode.innerHTML = '' + openText + '' + closeText + ''; diff --git a/lua/wikis/commons/PrizePool.lua b/lua/wikis/commons/PrizePool.lua index 2c7ab0ee2d7..f425b64749b 100644 --- a/lua/wikis/commons/PrizePool.lua +++ b/lua/wikis/commons/PrizePool.lua @@ -68,22 +68,10 @@ function PrizePool:applyHideAfter(placement) return placement.placeStart > self.options.hideafter end ----@return integer? -function PrizePool:_cutafterRows() - if self.options.cutafter == math.huge then - return nil - end - local count = 0 - for _, placement in ipairs(self.placements) do - if placement.placeStart > self.options.cutafter then - break - end - if placement.placeStart > self.options.hideafter then - break - end - count = count + math.max(#placement.opponents, 1) - end - return count > 0 and count or nil +---@param placement PrizePoolPlacement +---@return boolean +function PrizePool:applyCutAfter(placement) + return placement.placeStart > self.options.cutafter end ---@return {opentext: string, closetext: string}? diff --git a/lua/wikis/commons/PrizePool/Award.lua b/lua/wikis/commons/PrizePool/Award.lua index 7f172f1b851..c3fe42ddb4a 100644 --- a/lua/wikis/commons/PrizePool/Award.lua +++ b/lua/wikis/commons/PrizePool/Award.lua @@ -55,19 +55,10 @@ function AwardPrizePool:placeOrAwardCell(placement) } end ----@return integer? -function AwardPrizePool:_cutafterRows() - if self.options.cutafter == math.huge then - return nil - end - local count = 0 - for _, placement in ipairs(self.placements) do - if (placement.previousTotalNumberOfParticipants + 1) > self.options.cutafter then - break - end - count = count + math.max(#placement.opponents, 1) - end - return count > 0 and count or nil +---@param placement AwardPlacement +---@return boolean +function AwardPrizePool:applyCutAfter(placement) + return (placement.previousTotalNumberOfParticipants + 1) > self.options.cutafter end ---@return {opentext: string, closetext: string}? diff --git a/lua/wikis/commons/PrizePool/Base.lua b/lua/wikis/commons/PrizePool/Base.lua index 74e9a96b441..c50283e23fb 100644 --- a/lua/wikis/commons/PrizePool/Base.lua +++ b/lua/wikis/commons/PrizePool/Base.lua @@ -587,8 +587,10 @@ end ---@return Widget function BasePrizePool:_buildTable(isAward) local bodyRows = self:_buildRows() - local cutafterRows = self:_cutafterRows() - local collapseText = cutafterRows and self:_collapseText() or nil + local hasCutRows = Array.any(self.placements, function(placement) + return not self:applyHideAfter(placement) and self:applyCutAfter(placement) + end) + local collapseText = hasCutRows and self:_collapseText() or nil return TableWidgets.Table{ classes = {'prizepool-table-wrapper'}, @@ -596,12 +598,11 @@ function BasePrizePool:_buildTable(isAward) tableClasses = WidgetUtil.collect( 'prizepooltable', 'prizepooltable-' .. (isAward and 'award' or 'placement'), - cutafterRows and 'collapsed' or nil + hasCutRows and 'collapsed' or nil ), - tableAttributes = cutafterRows and { - ['data-cutafter'] = cutafterRows, - ['data-opentext'] = collapseText and collapseText.opentext or nil, - ['data-closetext'] = collapseText and collapseText.closetext or nil, + tableAttributes = collapseText and { + ['data-opentext'] = collapseText.opentext, + ['data-closetext'] = collapseText.closetext, } or nil, children = { TableWidgets.TableHeader{children = {self:_buildHeader(isAward)}}, @@ -695,7 +696,10 @@ function BasePrizePool:_buildRows() end end - table.insert(rows, TableRow{children = cells, classes = {backgroundClass}}) + table.insert(rows, TableRow{children = cells, classes = WidgetUtil.collect( + backgroundClass, + self:applyCutAfter(placement) and 'ppt-hide-on-collapse' or nil + )}) end end @@ -743,11 +747,12 @@ function BasePrizePool:applyHideAfter(placement) return false end ---- Number of body rows to show before collapse; nil disables collapse. ---- Child classes override this to drive the JS `data-cutafter` behaviour. ----@return integer? -function BasePrizePool:_cutafterRows() - return nil +--- Whether a placement's rows are hidden behind the collapse toggle. +--- Child classes override this; the default keeps every row visible. +---@param placement BasePlacement +---@return boolean +function BasePrizePool:applyCutAfter(placement) + return false end ---Open/close labels for the collapse toggle. Child classes override. diff --git a/stylesheets/commons/Prizepooltable.scss b/stylesheets/commons/Prizepooltable.scss index e37ad76d9f0..91bfb9773a2 100644 --- a/stylesheets/commons/Prizepooltable.scss +++ b/stylesheets/commons/Prizepooltable.scss @@ -175,11 +175,18 @@ table.prizepooltable.collapsed .prizepooltablehide { display: none; } -// Hide rows past the cut-off while collapsed. The toggle row is injected by -// Prizepooltable.js before the (cutAfter + 1)-th tbody row, so rows from -// (cutAfter + 2) onward are hidden when collapsed. +// Class-based collapse (prize pool): Lua marks every row past the cut-off with +// `ppt-hide-on-collapse`, and Prizepooltable.js injects the toggle directly +// before the first such row. Gated on `client-js` so non-JS readers see all rows. +html.client-js .prizepooltable.collapsed .ppt-hide-on-collapse { + display: none; +} + +// Legacy data-cutafter collapse (mvp / medal / winnings tables, which share the +// `prizepooltable` class and Prizepooltable.js). The JS injects the toggle before +// the (cutAfter + 2)-th row, so rows from (cutAfter + 3) onward are hidden. @for $i from 0 through 32 { - html.client-js .prizepooltable.collapsed[ data-cutafter="#{$i}" ] tbody tr:nth-child( n + #{ $i + 2 } ) { + html.client-js .prizepooltable.collapsed[ data-cutafter="#{$i}" ] tr:nth-child( n + #{ $i + 3 } ) { display: none; } } From 17d3e5f3b3964c016adb2d37b124aace19c8ea74 Mon Sep 17 00:00:00 2001 From: Eetu Rantanen Date: Tue, 16 Jun 2026 11:28:15 +0300 Subject: [PATCH 11/35] prevent table card stretching to taller flex sibling --- stylesheets/commons/Prizepooltable.scss | 1 + 1 file changed, 1 insertion(+) diff --git a/stylesheets/commons/Prizepooltable.scss b/stylesheets/commons/Prizepooltable.scss index 91bfb9773a2..1ec3c348307 100644 --- a/stylesheets/commons/Prizepooltable.scss +++ b/stylesheets/commons/Prizepooltable.scss @@ -240,6 +240,7 @@ non-prizepool modules (e.g. infoboxes). Prizepool no longer renders via this. .prizepool-section-tables { display: flex; flex-flow: row wrap; + align-items: flex-start; gap: 1em; @media ( max-width: 600px ) { From 52ff53cb07880f3628b9e48b6b01d2b6c3cd7759 Mon Sep 17 00:00:00 2001 From: Eetu Rantanen Date: Tue, 16 Jun 2026 13:33:23 +0300 Subject: [PATCH 12/35] fix toggle --- stylesheets/commons/Prizepooltable.scss | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/stylesheets/commons/Prizepooltable.scss b/stylesheets/commons/Prizepooltable.scss index 1ec3c348307..9908c25ce12 100644 --- a/stylesheets/commons/Prizepooltable.scss +++ b/stylesheets/commons/Prizepooltable.scss @@ -156,15 +156,18 @@ Table2-based placement prize pool table background-color: var( --clr-sapphire-background-color, inherit ); } - // Footer toggle: full-width, centered, brand-blue link with chevron. .prizepooltabletoggle { cursor: pointer; font-weight: bold; text-align: center; - color: var( --clr-wiki-theme-30, #0a558f ); + color: var( --clr-brand-30, #0a558f ); .theme--dark & { - color: var( --clr-wiki-theme-80, #81a0bf ); + color: var( --clr-brand-80, #a7cdf1 ); + } + + > small { + font-weight: unset; } } } From 26b4ded8ef86f31ff0624ef54c6eee8d00e1b0e3 Mon Sep 17 00:00:00 2001 From: Eetu Rantanen Date: Tue, 16 Jun 2026 15:11:44 +0300 Subject: [PATCH 13/35] style fixes --- lua/wikis/commons/PrizePool/Base.lua | 12 ++++++------ stylesheets/commons/Prizepooltable.scss | 16 ++++++++++++++++ 2 files changed, 22 insertions(+), 6 deletions(-) diff --git a/lua/wikis/commons/PrizePool/Base.lua b/lua/wikis/commons/PrizePool/Base.lua index c50283e23fb..55a4c57ded6 100644 --- a/lua/wikis/commons/PrizePool/Base.lua +++ b/lua/wikis/commons/PrizePool/Base.lua @@ -155,7 +155,7 @@ BasePrizePool.prizeTypes = { headerDisplay = function (data) local currencyText = Currency.display(BASE_CURRENCY) - return TableCell{children = {currencyText}} + return TableCell{children = {currencyText}, align = 'right'} end, row = BASE_CURRENCY:lower() .. 'prize', @@ -167,7 +167,7 @@ BasePrizePool.prizeTypes = { return TableCell{children = { Currency.display(BASE_CURRENCY, data, {formatValue = true, formatPrecision = headerData.roundPrecision, displayCurrencyCode = false}) - }} + }, align = 'right'} end end, }, @@ -194,7 +194,7 @@ BasePrizePool.prizeTypes = { } end, headerDisplay = function (data) - return TableCell{children = {Currency.display(data.currency)}} + return TableCell{children = {Currency.display(data.currency)}, align = 'right'} end, row = 'localprize', @@ -206,7 +206,7 @@ BasePrizePool.prizeTypes = { return TableCell{children = { Currency.display(headerData.currency, data, {formatValue = true, formatPrecision = headerData.roundPrecision, displayCurrencyCode = false}) - }} + }, align = 'right'} end end, @@ -364,7 +364,7 @@ BasePrizePool.prizeTypes = { return {title = input} end, headerDisplay = function (data) - return TableCell{children = {data.title}} + return TableCell{children = {data.title}, align = 'right'} end, row = 'freetext', @@ -373,7 +373,7 @@ BasePrizePool.prizeTypes = { end, rowDisplay = function (headerData, data) if String.isNotEmpty(data) then - return TableCell{children = {data}} + return TableCell{children = {data}, align = 'right'} end end, } diff --git a/stylesheets/commons/Prizepooltable.scss b/stylesheets/commons/Prizepooltable.scss index 9908c25ce12..34776a5e3f2 100644 --- a/stylesheets/commons/Prizepooltable.scss +++ b/stylesheets/commons/Prizepooltable.scss @@ -59,8 +59,14 @@ Table2-based placement prize pool table border-radius: 0.25rem; font-weight: bold; line-height: 1; + font-size: 0.75rem; + text-shadow: 0 1px 1px rgba( 0, 0, 0, 0.5 ); color: #ffffff; + .theme--dark & { + color: var( --clr-secondary-16 ); + } + &.placement-1 { background-color: var( --prizepool-place-1-accent ); } @@ -74,6 +80,16 @@ Table2-based placement prize pool table } } + &.prizepooltable-placement .prizepooltable-place { + text-align: center; + font-weight: bold; + color: var( --clr-secondary-25 ); + + .theme--dark & { + color: var( --clr-secondary-100 ); + } + } + // Min widths so opponent columns do not collapse on wide tables. .prizepooltable-col-player, .prizepooltable-col-team { From 5d1f5f4aeb1c9d2686557d5dc367fa2edda3dd8c Mon Sep 17 00:00:00 2001 From: Eetu Rantanen Date: Tue, 16 Jun 2026 15:26:01 +0300 Subject: [PATCH 14/35] freetext align left --- lua/wikis/commons/PrizePool/Base.lua | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/lua/wikis/commons/PrizePool/Base.lua b/lua/wikis/commons/PrizePool/Base.lua index 55a4c57ded6..3de82e89499 100644 --- a/lua/wikis/commons/PrizePool/Base.lua +++ b/lua/wikis/commons/PrizePool/Base.lua @@ -364,7 +364,7 @@ BasePrizePool.prizeTypes = { return {title = input} end, headerDisplay = function (data) - return TableCell{children = {data.title}, align = 'right'} + return TableCell{children = {data.title}} end, row = 'freetext', @@ -373,7 +373,7 @@ BasePrizePool.prizeTypes = { end, rowDisplay = function (headerData, data) if String.isNotEmpty(data) then - return TableCell{children = {data}, align = 'right'} + return TableCell{children = {data}} end end, } From 42693faa37bdee8a8a85aa77b6bc44c955dd5460 Mon Sep 17 00:00:00 2001 From: Eetu Rantanen Date: Thu, 18 Jun 2026 09:54:01 +0300 Subject: [PATCH 15/35] use standard gold/silver/bronze placement colors for top-3 badges --- stylesheets/commons/Prizepooltable.scss | 16 +++++++++------- 1 file changed, 9 insertions(+), 7 deletions(-) diff --git a/stylesheets/commons/Prizepooltable.scss b/stylesheets/commons/Prizepooltable.scss index 34776a5e3f2..8bf1f441f81 100644 --- a/stylesheets/commons/Prizepooltable.scss +++ b/stylesheets/commons/Prizepooltable.scss @@ -2,10 +2,12 @@ $prizepool-badge-size: 1.5rem; $prizepool-accent-width: 0.25rem; :root { - // Placement badge / left-accent colours (light defaults) - --prizepool-place-1-accent: #f0b419; - --prizepool-place-2-accent: #58b5e3; - --prizepool-place-3-accent: #de7d1d; + // Placement badge / left-accent colours. Reuse the standard placement-box + // medal tokens (see Miscellaneous.scss `$placement-colors`) so the prize-pool + // top-3 badges match every other placement badge: gold / silver / bronze. + --prizepool-place-1-accent: var( --clr-semantic-gold-40 ); + --prizepool-place-2-accent: var( --clr-semantic-silver-40 ); + --prizepool-place-3-accent: var( --clr-semantic-bronze-40 ); // Subtle full-row tints (light defaults) --prizepool-place-1-tint: #fff9eb; @@ -14,9 +16,9 @@ $prizepool-accent-width: 0.25rem; } .theme--dark { - --prizepool-place-1-accent: #fbcb50; - --prizepool-place-2-accent: #91d7f9; - --prizepool-place-3-accent: #ffb267; + --prizepool-place-1-accent: var( --clr-semantic-gold-30 ); + --prizepool-place-2-accent: var( --clr-semantic-silver-40 ); + --prizepool-place-3-accent: var( --clr-semantic-bronze-30 ); --prizepool-place-1-tint: #353932; --prizepool-place-2-tint: #243b4d; From 785d03ad401bc9727e3eb35ab1612445fd20a5cb Mon Sep 17 00:00:00 2001 From: Eetu Rantanen Date: Thu, 18 Jun 2026 10:22:26 +0300 Subject: [PATCH 16/35] tint top-3 prize pool rows with pale gold/silver/bronze --- stylesheets/commons/Prizepooltable.scss | 15 +++++++-------- 1 file changed, 7 insertions(+), 8 deletions(-) diff --git a/stylesheets/commons/Prizepooltable.scss b/stylesheets/commons/Prizepooltable.scss index 8bf1f441f81..93a74708d48 100644 --- a/stylesheets/commons/Prizepooltable.scss +++ b/stylesheets/commons/Prizepooltable.scss @@ -9,20 +9,19 @@ $prizepool-accent-width: 0.25rem; --prizepool-place-2-accent: var( --clr-semantic-silver-40 ); --prizepool-place-3-accent: var( --clr-semantic-bronze-40 ); - // Subtle full-row tints (light defaults) - --prizepool-place-1-tint: #fff9eb; - --prizepool-place-2-tint: #f0fcff; - --prizepool-place-3-tint: #fff3ec; + // Subtle full-row tints: the placement accent blended into the page surface so + // each top-3 row reads as a faint gold / silver / bronze wash. Defined once via + // the accent var + surface, so both the light/dark accents above and the + // surface flip through the cascade — no separate dark overrides needed. + --prizepool-place-1-tint: color-mix( in srgb, var( --prizepool-place-1-accent ) 15%, var( --clr-background ) ); + --prizepool-place-2-tint: color-mix( in srgb, var( --prizepool-place-2-accent ) 15%, var( --clr-background ) ); + --prizepool-place-3-tint: color-mix( in srgb, var( --prizepool-place-3-accent ) 15%, var( --clr-background ) ); } .theme--dark { --prizepool-place-1-accent: var( --clr-semantic-gold-30 ); --prizepool-place-2-accent: var( --clr-semantic-silver-40 ); --prizepool-place-3-accent: var( --clr-semantic-bronze-30 ); - - --prizepool-place-1-tint: #353932; - --prizepool-place-2-tint: #243b4d; - --prizepool-place-3-tint: #363535; } /******************************************************************************* From 12f7daeeb89ab8754469ed48187e36fe30296764 Mon Sep 17 00:00:00 2001 From: Eetu Rantanen Date: Thu, 18 Jun 2026 10:54:08 +0300 Subject: [PATCH 17/35] white text for both --- stylesheets/commons/Prizepooltable.scss | 4 ---- 1 file changed, 4 deletions(-) diff --git a/stylesheets/commons/Prizepooltable.scss b/stylesheets/commons/Prizepooltable.scss index 93a74708d48..a0d0f68ae2b 100644 --- a/stylesheets/commons/Prizepooltable.scss +++ b/stylesheets/commons/Prizepooltable.scss @@ -64,10 +64,6 @@ Table2-based placement prize pool table text-shadow: 0 1px 1px rgba( 0, 0, 0, 0.5 ); color: #ffffff; - .theme--dark & { - color: var( --clr-secondary-16 ); - } - &.placement-1 { background-color: var( --prizepool-place-1-accent ); } From ae34ac1ebd51c59d16b2d5b99363993c2a3851ec Mon Sep 17 00:00:00 2001 From: Eetu Rantanen Date: Tue, 23 Jun 2026 11:11:41 +0300 Subject: [PATCH 18/35] render the show places in footer & use collapsible logic --- javascript/commons/Prizepooltable.js | 26 +++----------- lua/wikis/commons/PrizePool/Base.lua | 47 ++++++++++++++++++++----- stylesheets/commons/Prizepooltable.scss | 34 +++++++++++++++--- 3 files changed, 72 insertions(+), 35 deletions(-) diff --git a/javascript/commons/Prizepooltable.js b/javascript/commons/Prizepooltable.js index 6b381b393dd..f84bd25c5e7 100644 --- a/javascript/commons/Prizepooltable.js +++ b/javascript/commons/Prizepooltable.js @@ -5,30 +5,12 @@ liquipedia.prizepooltable = { init: function() { document.querySelectorAll( '.prizepooltable' ).forEach( ( prizepooltable ) => { - if ( prizepooltable.querySelector( '.prizepooltabletoggle' ) !== null ) { + // The redesigned (Table2) prize pool wraps its table in + // `.prizepool-table-wrapper` and collapses via general-collapsible, so it + // supplies its own toggle. Skip it here to avoid a duplicate legacy toggle. + if ( prizepooltable.closest( '.prizepool-table-wrapper' ) !== null ) { return; } - // Class-based collapse (prize pool): Lua marks the cut rows with - // `ppt-hide-on-collapse`; the toggle goes directly before the first one. - const firstHiddenRow = prizepooltable.querySelector( '.ppt-hide-on-collapse' ); - if ( firstHiddenRow !== null ) { - let openLabel = 'show more'; - if ( typeof prizepooltable.dataset.opentext !== 'undefined' ) { - openLabel = prizepooltable.dataset.opentext; - } - openLabel += ' '; - let closeLabel = 'show less'; - if ( typeof prizepooltable.dataset.closetext !== 'undefined' ) { - closeLabel = prizepooltable.dataset.closetext; - } - closeLabel += ' '; - const colspan = prizepooltable.querySelectorAll( 'tr:nth-child(1) th, tr:nth-child(1) td' ).length; - const rowNode = document.createElement( 'tr' ); - rowNode.innerHTML = '' + openLabel + '' + closeLabel + ''; - firstHiddenRow.parentNode.insertBefore( rowNode, firstHiddenRow ); - return; - } - // Legacy data-cutafter collapse (mvp / medal / winnings tables). let cutAfter; if ( typeof prizepooltable.dataset.cutafter !== 'undefined' ) { cutAfter = parseInt( prizepooltable.dataset.cutafter ); diff --git a/lua/wikis/commons/PrizePool/Base.lua b/lua/wikis/commons/PrizePool/Base.lua index 3de82e89499..cb78487b349 100644 --- a/lua/wikis/commons/PrizePool/Base.lua +++ b/lua/wikis/commons/PrizePool/Base.lua @@ -34,6 +34,7 @@ local TableCell = TableWidgets.Cell local TableRow = TableWidgets.Row local Div = HtmlWidgets.Div local Span = HtmlWidgets.Span +local IconFa = Lua.import('Module:Widget/Image/Icon/Fontawesome') local WidgetUtil = Lua.import('Module:Widget/Util') local pageVars = PageVariableNamespace('PrizePool') @@ -590,20 +591,24 @@ function BasePrizePool:_buildTable(isAward) local hasCutRows = Array.any(self.placements, function(placement) return not self:applyHideAfter(placement) and self:applyCutAfter(placement) end) - local collapseText = hasCutRows and self:_collapseText() or nil + local toggle = hasCutRows and self:_collapseToggle() or nil + -- The collapse runs through `general-collapsible` (Collapse.js): the wrapper + -- carries the state class and the footer holds the toggle anchors, so no + -- prize-pool-specific JS is needed. The cut rows stay in `` in document + -- order (after the visible rows) and are merely CSS-hidden while collapsed. return TableWidgets.Table{ - classes = {'prizepool-table-wrapper'}, + classes = WidgetUtil.collect( + 'prizepool-table-wrapper', + toggle and 'general-collapsible' or nil, + toggle and 'collapsed' or nil + ), striped = true, - tableClasses = WidgetUtil.collect( + tableClasses = { 'prizepooltable', 'prizepooltable-' .. (isAward and 'award' or 'placement'), - hasCutRows and 'collapsed' or nil - ), - tableAttributes = collapseText and { - ['data-opentext'] = collapseText.opentext, - ['data-closetext'] = collapseText.closetext, - } or nil, + }, + footer = toggle, children = { TableWidgets.TableHeader{children = {self:_buildHeader(isAward)}}, TableWidgets.TableBody{children = bodyRows}, @@ -611,6 +616,30 @@ function BasePrizePool:_buildTable(isAward) } end +--- Footer toggle for the collapse. The two buttons carry the `general-collapsible-*` +--- classes that Collapse.js turns into anchors; the global stylesheet shows exactly +--- one of them per collapse state. +---@return Renderable? +function BasePrizePool:_collapseToggle() + local collapseText = self:_collapseText() + if not collapseText then + return nil + end + return Div{ + classes = {'prizepooltable-toggle'}, + children = { + Span{ + classes = {'general-collapsible-expand-button'}, + children = {collapseText.opentext, ' ', IconFa{iconName = 'expand'}}, + }, + Span{ + classes = {'general-collapsible-collapse-button'}, + children = {collapseText.closetext, ' ', IconFa{iconName = 'collapse'}}, + }, + }, + } +end + ---@param isAward boolean? ---@return Renderable function BasePrizePool:_buildHeader(isAward) diff --git a/stylesheets/commons/Prizepooltable.scss b/stylesheets/commons/Prizepooltable.scss index a0d0f68ae2b..db516a6477c 100644 --- a/stylesheets/commons/Prizepooltable.scss +++ b/stylesheets/commons/Prizepooltable.scss @@ -191,10 +191,36 @@ table.prizepooltable.collapsed .prizepooltablehide { display: none; } -// Class-based collapse (prize pool): Lua marks every row past the cut-off with -// `ppt-hide-on-collapse`, and Prizepooltable.js injects the toggle directly -// before the first such row. Gated on `client-js` so non-JS readers see all rows. -html.client-js .prizepooltable.collapsed .ppt-hide-on-collapse { +// Class-based collapse (prize pool): the table is wrapped in a `general-collapsible` +// whose footer holds the toggle, and Lua marks every row past the cut-off with +// `ppt-hide-on-collapse`. Collapse.js wires the toggle; CSS hides the cut rows while +// collapsed. Gated on `client-js` so non-JS readers see all rows and no dead toggle. +html.client-js .prizepool-table-wrapper.collapsed .ppt-hide-on-collapse { + display: none; +} + +.prizepooltable-toggle { + text-align: center; + font-weight: bold; + cursor: pointer; + color: var( --clr-brand-30, #0a558f ); + + .theme--dark & { + color: var( --clr-brand-80, #a7cdf1 ); + } + + a { + color: inherit; + } +} + +// Show exactly one button per collapse state (the wrapper carries `collapsed`). +.general-collapsible.collapsed .prizepooltable-toggle .general-collapsible-collapse-button, +.general-collapsible:not( .collapsed ) .prizepooltable-toggle .general-collapsible-expand-button { + display: none; +} + +html:not( .client-js ) .prizepooltable-toggle { display: none; } From 620808fad3a68e1c90d5477c66d00b644fd4d418 Mon Sep 17 00:00:00 2001 From: Eetu Rantanen Date: Tue, 23 Jun 2026 11:33:22 +0300 Subject: [PATCH 19/35] wrap toggle text & icon --- lua/wikis/commons/PrizePool/Base.lua | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) diff --git a/lua/wikis/commons/PrizePool/Base.lua b/lua/wikis/commons/PrizePool/Base.lua index cb78487b349..28b832641a5 100644 --- a/lua/wikis/commons/PrizePool/Base.lua +++ b/lua/wikis/commons/PrizePool/Base.lua @@ -616,9 +616,6 @@ function BasePrizePool:_buildTable(isAward) } end ---- Footer toggle for the collapse. The two buttons carry the `general-collapsible-*` ---- classes that Collapse.js turns into anchors; the global stylesheet shows exactly ---- one of them per collapse state. ---@return Renderable? function BasePrizePool:_collapseToggle() local collapseText = self:_collapseText() @@ -630,11 +627,11 @@ function BasePrizePool:_collapseToggle() children = { Span{ classes = {'general-collapsible-expand-button'}, - children = {collapseText.opentext, ' ', IconFa{iconName = 'expand'}}, + children = {Span{children = {collapseText.opentext, ' ', IconFa{iconName = 'expand'}}}}, }, Span{ classes = {'general-collapsible-collapse-button'}, - children = {collapseText.closetext, ' ', IconFa{iconName = 'collapse'}}, + children = {Span{children = {collapseText.closetext, ' ', IconFa{iconName = 'collapse'}}}}, }, }, } From 17f9d69a5ec5c3c99e9b979ff19dfbc3b5bb7a57 Mon Sep 17 00:00:00 2001 From: Eetu Rantanen Date: Tue, 23 Jun 2026 12:05:25 +0300 Subject: [PATCH 20/35] refactoring --- lua/wikis/commons/PrizePool.lua | 21 ++++---- lua/wikis/commons/PrizePool/Base.lua | 74 ++++++++++++---------------- 2 files changed, 40 insertions(+), 55 deletions(-) diff --git a/lua/wikis/commons/PrizePool.lua b/lua/wikis/commons/PrizePool.lua index f425b64749b..2b7d963a4f9 100644 --- a/lua/wikis/commons/PrizePool.lua +++ b/lua/wikis/commons/PrizePool.lua @@ -76,20 +76,17 @@ end ---@return {opentext: string, closetext: string}? function PrizePool:_collapseText() - local firstHidden, lastPlace - for _, placement in ipairs(self.placements) do - if placement.placeStart > self.options.hideafter then - break - end - lastPlace = placement.placeEnd - if not firstHidden and placement.placeStart > self.options.cutafter then - firstHidden = placement.placeStart - end - end - if not firstHidden or not lastPlace then + local visible = Array.filter(self.placements, function(placement) + return placement.placeStart <= self.options.hideafter + end) + local lastVisible = visible[#visible] + local firstHidden = Array.find(visible, function(placement) + return placement.placeStart > self.options.cutafter + end) + if not firstHidden or not lastVisible then return nil end - local text = 'place ' .. firstHidden .. ' to ' .. lastPlace + local text = 'place ' .. firstHidden.placeStart .. ' to ' .. lastVisible.placeEnd return {opentext = text, closetext = text} end diff --git a/lua/wikis/commons/PrizePool/Base.lua b/lua/wikis/commons/PrizePool/Base.lua index 28b832641a5..3dd6ea7f8a9 100644 --- a/lua/wikis/commons/PrizePool/Base.lua +++ b/lua/wikis/commons/PrizePool/Base.lua @@ -593,17 +593,12 @@ function BasePrizePool:_buildTable(isAward) end) local toggle = hasCutRows and self:_collapseToggle() or nil - -- The collapse runs through `general-collapsible` (Collapse.js): the wrapper - -- carries the state class and the footer holds the toggle anchors, so no - -- prize-pool-specific JS is needed. The cut rows stay in `` in document - -- order (after the visible rows) and are merely CSS-hidden while collapsed. return TableWidgets.Table{ classes = WidgetUtil.collect( 'prizepool-table-wrapper', toggle and 'general-collapsible' or nil, toggle and 'collapsed' or nil ), - striped = true, tableClasses = { 'prizepooltable', 'prizepooltable-' .. (isAward and 'award' or 'placement'), @@ -668,7 +663,6 @@ function BasePrizePool:_buildRows() end local opponents = placement.opponents - local opponentCount = math.max(#opponents, 1) local placeCell = self:placeOrAwardCell(placement) local backgroundClass = placement:getBackground() @@ -677,36 +671,29 @@ function BasePrizePool:_buildRows() return self:_opponentPrizeCells(placement, opponent) end) - -- Vertically merge consecutive-equal prize cells per column (declare span on the run's first row). + -- Vertically merge consecutive-equal prize cells per column: the first cell of + -- each run spans the run, the rest are dropped (tracked by cell identity). local numCols = prizeMatrix[1] and #prizeMatrix[1] or 0 - local omitted = {} -- omitted[opponentIndex][col] = true + local omittedCells = {} for col = 1, numCols do - local opponentIndex = 1 - while opponentIndex <= opponentCount do - local runLength = 1 - while opponentIndex + runLength <= opponentCount - and Table.deepEquals( - prizeMatrix[opponentIndex][col].props.children, - prizeMatrix[opponentIndex + runLength][col].props.children - ) do - omitted[opponentIndex + runLength] = omitted[opponentIndex + runLength] or {} - omitted[opponentIndex + runLength][col] = true - runLength = runLength + 1 + local columnCells = Array.map(prizeMatrix, function(row) return row[col] end) + local runs = Array.groupAdjacentBy(columnCells, function(cell) + return cell.props.children + end, Table.deepEquals) + Array.forEach(runs, function(run) + if #run <= 1 then + return end - if runLength > 1 then - prizeMatrix[opponentIndex][col].props.rowspan = runLength - end - opponentIndex = opponentIndex + runLength - end + run[1].props.rowspan = #run + Array.forEach(Array.sub(run, 2), function(cell) + omittedCells[cell] = true + end) + end) end - for opponentIndex, opponent in ipairs(opponents) do - local cells = {} - if opponentIndex == 1 then - table.insert(cells, placeCell) - end - - table.insert(cells, TableCell{ + local isCut = self:applyCutAfter(placement) + Array.forEach(opponents, function(opponent, opponentIndex) + local opponentCell = TableCell{ children = {tostring(OpponentDisplay.BlockOpponent{ opponent = opponent.opponentData, showPlayerTeam = true, @@ -714,19 +701,20 @@ function BasePrizePool:_buildRows() classes = {'prizepooltable-col-team'}, align = 'left', nowrap = false, - }) - - for col = 1, numCols do - if not (omitted[opponentIndex] and omitted[opponentIndex][col]) then - table.insert(cells, prizeMatrix[opponentIndex][col]) - end - end + } + local prizeCells = Array.filter(prizeMatrix[opponentIndex], function(cell) + return not omittedCells[cell] + end) - table.insert(rows, TableRow{children = cells, classes = WidgetUtil.collect( - backgroundClass, - self:applyCutAfter(placement) and 'ppt-hide-on-collapse' or nil - )}) - end + table.insert(rows, TableRow{ + children = WidgetUtil.collect( + opponentIndex == 1 and placeCell or nil, + opponentCell, + prizeCells + ), + classes = WidgetUtil.collect(backgroundClass, isCut and 'ppt-hide-on-collapse' or nil), + }) + end) end return rows From 6c5e3946ebb427d1359568575fd763386ecd365e Mon Sep 17 00:00:00 2001 From: Eetu Rantanen Date: Tue, 23 Jun 2026 12:25:14 +0300 Subject: [PATCH 21/35] cleanup comments --- stylesheets/commons/Prizepooltable.scss | 33 ++++++------------------- 1 file changed, 7 insertions(+), 26 deletions(-) diff --git a/stylesheets/commons/Prizepooltable.scss b/stylesheets/commons/Prizepooltable.scss index db516a6477c..458731efd77 100644 --- a/stylesheets/commons/Prizepooltable.scss +++ b/stylesheets/commons/Prizepooltable.scss @@ -2,17 +2,13 @@ $prizepool-badge-size: 1.5rem; $prizepool-accent-width: 0.25rem; :root { - // Placement badge / left-accent colours. Reuse the standard placement-box - // medal tokens (see Miscellaneous.scss `$placement-colors`) so the prize-pool - // top-3 badges match every other placement badge: gold / silver / bronze. + // Reuse the standard gold/silver/bronze placement tokens so top-3 badges match. --prizepool-place-1-accent: var( --clr-semantic-gold-40 ); --prizepool-place-2-accent: var( --clr-semantic-silver-40 ); --prizepool-place-3-accent: var( --clr-semantic-bronze-40 ); - // Subtle full-row tints: the placement accent blended into the page surface so - // each top-3 row reads as a faint gold / silver / bronze wash. Defined once via - // the accent var + surface, so both the light/dark accents above and the - // surface flip through the cascade — no separate dark overrides needed. + // Faint full-row tint per place, built from the accent var so the dark-theme + // accents and surface flip through the cascade — no separate dark overrides. --prizepool-place-1-tint: color-mix( in srgb, var( --prizepool-place-1-accent ) 15%, var( --clr-background ) ); --prizepool-place-2-tint: color-mix( in srgb, var( --prizepool-place-2-accent ) 15%, var( --clr-background ) ); --prizepool-place-3-tint: color-mix( in srgb, var( --prizepool-place-3-accent ) 15%, var( --clr-background ) ); @@ -40,8 +36,6 @@ Table2-based placement prize pool table } .prizepooltable { - // Header row: light fill, muted bold text, bottom divider (mostly inherited - // from Table2's head-row styling; only the divider weight is reinforced here). .prizepooltable-header { th, td { @@ -49,7 +43,6 @@ Table2-based placement prize pool table } } - // Placement number badge: small rounded-square chip, centered, white text. .prizepooltable-badge { display: inline-flex; align-items: center; @@ -87,7 +80,6 @@ Table2-based placement prize pool table } } - // Min widths so opponent columns do not collapse on wide tables. .prizepooltable-col-player, .prizepooltable-col-team { min-width: 150px; @@ -97,10 +89,7 @@ Table2-based placement prize pool table min-width: 300px; } - // Top-3 placement rows: subtle full-row tint + coloured left accent bar. - // The extra `.table2__row--body` class plus `!important`-free higher - // specificity makes these win over the Table2 zebra (`--odd`/`--even`) - // rule, which itself sits under a `.theme--*` wrapper. + // Top-3 rows: left accent bar (coloured, with the row tint, in the theme block below). tr.table2__row--body.background-color-first-place, tr.table2__row--body.background-color-second-place, tr.table2__row--body.background-color-third-place { @@ -116,9 +105,8 @@ Table2-based placement prize pool table } } - // Theme wrappers + the `.table2__table` qualifier raise specificity to - // (0,5,2) so the tint outranks the Table2 zebra rule (0,4,2) regardless of - // stylesheet load order, and the `:hover` variant keeps it from washing out. + // The theme wrapper + `.table2__table` qualifier raise specificity above the + // Table2 zebra rule so the tint wins regardless of load order; `:hover` keeps it. .theme--light &.table2__table, .theme--dark &.table2__table { tr.table2__row--body.background-color-first-place { @@ -155,8 +143,6 @@ Table2-based placement prize pool table } } - // Status rows (win / lose / disqualified): tint the place cell, preserving - // the pre-Table2 treatment. tr.table2__row--body.bg-win .prizepooltable-place { background-color: var( --clr-forest-background-color, inherit ); } @@ -185,16 +171,12 @@ Table2-based placement prize pool table } } -// Show/hide the correct toggle label depending on collapse state. table.prizepooltable:not( .collapsed ) .prizepooltableshow, table.prizepooltable.collapsed .prizepooltablehide { display: none; } -// Class-based collapse (prize pool): the table is wrapped in a `general-collapsible` -// whose footer holds the toggle, and Lua marks every row past the cut-off with -// `ppt-hide-on-collapse`. Collapse.js wires the toggle; CSS hides the cut rows while -// collapsed. Gated on `client-js` so non-JS readers see all rows and no dead toggle. +// Gated on `client-js` so non-JS readers see all rows (the toggle can't expand them). html.client-js .prizepool-table-wrapper.collapsed .ppt-hide-on-collapse { display: none; } @@ -214,7 +196,6 @@ html.client-js .prizepool-table-wrapper.collapsed .ppt-hide-on-collapse { } } -// Show exactly one button per collapse state (the wrapper carries `collapsed`). .general-collapsible.collapsed .prizepooltable-toggle .general-collapsible-collapse-button, .general-collapsible:not( .collapsed ) .prizepooltable-toggle .general-collapsible-expand-button { display: none; From 12cc412f4477aa62710a702989c66f752a41baf9 Mon Sep 17 00:00:00 2001 From: Eetwalt <75437856+Eetwalt@users.noreply.github.com> Date: Tue, 23 Jun 2026 09:33:32 +0000 Subject: [PATCH 22/35] chore: update visual snapshots --- lua/spec/snapshots/prize_pool.png | Bin 28531 -> 28604 bytes 1 file changed, 0 insertions(+), 0 deletions(-) diff --git a/lua/spec/snapshots/prize_pool.png b/lua/spec/snapshots/prize_pool.png index 74dcd66d32dd42e9f5eab1948342cfa9f34a2e1f..2eb65a2bcb228d72a80351dbdcd1b2d29d106904 100644 GIT binary patch literal 28604 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 E29(kj&;S4c literal 28531 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 Date: Tue, 23 Jun 2026 12:45:39 +0300 Subject: [PATCH 23/35] Cell to CellHeader --- lua/wikis/commons/PrizePool/Base.lua | 19 ++++++++++--------- 1 file changed, 10 insertions(+), 9 deletions(-) diff --git a/lua/wikis/commons/PrizePool/Base.lua b/lua/wikis/commons/PrizePool/Base.lua index 3dd6ea7f8a9..1cab2415057 100644 --- a/lua/wikis/commons/PrizePool/Base.lua +++ b/lua/wikis/commons/PrizePool/Base.lua @@ -31,6 +31,7 @@ local OpponentDisplay = Lua.import('Module:OpponentDisplay/Custom') local HtmlWidgets = Lua.import('Module:Widget/Html/All') local TableWidgets = Lua.import('Module:Widget/Table2/All') local TableCell = TableWidgets.Cell +local TableCellHeader = TableWidgets.CellHeader local TableRow = TableWidgets.Row local Div = HtmlWidgets.Div local Span = HtmlWidgets.Span @@ -156,7 +157,7 @@ BasePrizePool.prizeTypes = { headerDisplay = function (data) local currencyText = Currency.display(BASE_CURRENCY) - return TableCell{children = {currencyText}, align = 'right'} + return TableCellHeader{children = {currencyText}, align = 'right'} end, row = BASE_CURRENCY:lower() .. 'prize', @@ -195,7 +196,7 @@ BasePrizePool.prizeTypes = { } end, headerDisplay = function (data) - return TableCell{children = {Currency.display(data.currency)}, align = 'right'} + return TableCellHeader{children = {Currency.display(data.currency)}, align = 'right'} end, row = 'localprize', @@ -233,7 +234,7 @@ BasePrizePool.prizeTypes = { return {title = 'Percentage'} end, headerDisplay = function (data) - return TableCell{children = {data.title}} + return TableCellHeader{children = {data.title}} end, row = 'percentage', @@ -271,7 +272,7 @@ BasePrizePool.prizeTypes = { } end, headerDisplay = function (data) - return TableCell{children = {'Qualifies To'}} + return TableCellHeader{children = {'Qualifies To'}} end, row = 'qualified', @@ -344,7 +345,7 @@ BasePrizePool.prizeTypes = { table.insert(headerDisplay, text) end - return TableCell{children = {table.concat(headerDisplay)}} + return TableCellHeader{children = {table.concat(headerDisplay)}} end, row = 'points', @@ -365,7 +366,7 @@ BasePrizePool.prizeTypes = { return {title = input} end, headerDisplay = function (data) - return TableCell{children = {data.title}} + return TableCellHeader{children = {data.title}} end, row = 'freetext', @@ -636,8 +637,8 @@ end ---@return Renderable function BasePrizePool:_buildHeader(isAward) local children = { - TableWidgets.CellHeader{children = {isAward and 'Award' or 'Place'}, align = 'center'}, - TableWidgets.CellHeader{children = {'Participant'}, classes = {'prizepooltable-col-team'}, align = 'left'}, + TableCellHeader{children = {isAward and 'Award' or 'Place'}, align = 'center'}, + TableCellHeader{children = {'Participant'}, classes = {'prizepooltable-col-team'}, align = 'left'}, } local previousOfType = {} @@ -650,7 +651,7 @@ function BasePrizePool:_buildHeader(isAward) end end - return TableWidgets.Row{classes = {'prizepooltable-header'}, children = children} + return TableRow{classes = {'prizepooltable-header'}, children = children} end ---@return Renderable[] From 9ef6e85384e3a9a88a7c6d5c14acba0a330f48a3 Mon Sep 17 00:00:00 2001 From: Eetu Rantanen Date: Tue, 23 Jun 2026 16:04:24 +0300 Subject: [PATCH 24/35] right align points and percentages --- lua/wikis/commons/PrizePool/Base.lua | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/lua/wikis/commons/PrizePool/Base.lua b/lua/wikis/commons/PrizePool/Base.lua index 1cab2415057..b3e2e63f114 100644 --- a/lua/wikis/commons/PrizePool/Base.lua +++ b/lua/wikis/commons/PrizePool/Base.lua @@ -234,7 +234,7 @@ BasePrizePool.prizeTypes = { return {title = 'Percentage'} end, headerDisplay = function (data) - return TableCellHeader{children = {data.title}} + return TableCellHeader{children = {data.title}, align = 'right'} end, row = 'percentage', @@ -248,7 +248,7 @@ BasePrizePool.prizeTypes = { end, rowDisplay = function (headerData, data) if String.isNotEmpty(data) then - return TableCell{children = {data .. '%'}} + return TableCell{children = {data .. '%'}, align = 'right'} end end, }, @@ -345,7 +345,7 @@ BasePrizePool.prizeTypes = { table.insert(headerDisplay, text) end - return TableCellHeader{children = {table.concat(headerDisplay)}} + return TableCellHeader{children = {table.concat(headerDisplay)}, align = 'right'} end, row = 'points', @@ -354,7 +354,7 @@ BasePrizePool.prizeTypes = { end, rowDisplay = function (headerData, data) if data > 0 then - return TableCell{children = {LANG:formatNum(data)}} + return TableCell{children = {LANG:formatNum(data)}, align = 'right'} end end, }, From 58d244cc214c140a3060cd1674e8c6b66ae36701 Mon Sep 17 00:00:00 2001 From: Eetu Rantanen Date: Tue, 23 Jun 2026 16:48:34 +0300 Subject: [PATCH 25/35] don't kick old styles --- stylesheets/commons/Prizepooltable.scss | 466 +++++++++++++++++++----- 1 file changed, 367 insertions(+), 99 deletions(-) diff --git a/stylesheets/commons/Prizepooltable.scss b/stylesheets/commons/Prizepooltable.scss index 458731efd77..8117614688f 100644 --- a/stylesheets/commons/Prizepooltable.scss +++ b/stylesheets/commons/Prizepooltable.scss @@ -1,7 +1,18 @@ $prizepool-badge-size: 1.5rem; $prizepool-accent-width: 0.25rem; +/******************************************************************************* +Shared prize-pool custom properties + +`--prize-pool-*` are consumed by legacy tables here AND by Colours.scss +(`.background-color-first-place` / `.bg-first` / `.bg-p1` resolve `--prize-pool-gold`), +so they must stay defined even though the redesigned table no longer uses them. +`--prizepool-place-*` are the redesign tokens for the Table2 placement table. +*******************************************************************************/ + :root { + --prize-pool-background-color: transparent; + // Reuse the standard gold/silver/bronze placement tokens so top-3 badges match. --prizepool-place-1-accent: var( --clr-semantic-gold-40 ); --prizepool-place-2-accent: var( --clr-semantic-silver-40 ); @@ -14,14 +25,338 @@ $prizepool-accent-width: 0.25rem; --prizepool-place-3-tint: color-mix( in srgb, var( --prizepool-place-3-accent ) 15%, var( --clr-background ) ); } +.theme--light { + --prize-pool-variant-background-color: #f2f2f2; + --prize-pool-header-background-color: #eaecf0; + --prize-pool-alt-background-color: #f5f5f5; + --prize-pool-gold: var( --clr-semantic-gold-40 ); + --prize-pool-silver: var( --clr-semantic-silver-40 ); + --prize-pool-bronze: var( --clr-semantic-bronze-40 ); + --prize-pool-copper: var( --clr-semantic-copper-40 ); +} + .theme--dark { + --prize-pool-variant-background-color: var( --clr-moon-20 ); + --prize-pool-header-background-color: var( --table-header-variant-background-color ); + --prize-pool-alt-background-color: var( --clr-secondary-9 ); + --prize-pool-gold: var( --clr-semantic-gold-30 ); + --prize-pool-silver: var( --clr-secondary-39 ); + --prize-pool-bronze: var( --clr-semantic-bronze-20 ); + --prize-pool-copper: var( --clr-semantic-copper-20 ); + --prizepool-place-1-accent: var( --clr-semantic-gold-30 ); --prizepool-place-2-accent: var( --clr-semantic-silver-40 ); --prizepool-place-3-accent: var( --clr-semantic-bronze-30 ); } /******************************************************************************* -Table2-based placement prize pool table +Legacy / shared prize pool table styles +Template(s): Prize pool tables — Author(s): FO-nTTaX + +Still relied on by non-redesigned consumers that share the `prizepooltable` +class and the global `bg-*` / `background-color-*-place` placement classes +(MvpTable, SeriesMedalStats, StageWinnings, TournamentPlayerInformation, +TournamentsListing, css-grid tables). Do not remove without auditing those. +*******************************************************************************/ + +table.prizepooltable:not( .collapsed ) .prizepooltableshow, +table.prizepooltable.collapsed .prizepooltablehide { + display: none; +} + +.prizepooltable-col-player, +.prizepooltable-col-team { + min-width: 150px; +} + +.prizepooltable-col-two-players { + min-width: 300px; +} + +@media ( max-width: 600px ) { + .prizepooltable-col-team { + min-width: 65px; + + a:nth-child( 2 ) { + display: none; + } + } +} + +// Legacy data-cutafter collapse (mvp / medal / winnings tables, which share the +// `prizepooltable` class and Prizepooltable.js). The JS injects the toggle before +// the (cutAfter + 2)-th row, so rows from (cutAfter + 3) onward are hidden. +@for $i from 0 through 32 { + html.client-js .prizepooltable.collapsed[ data-cutafter="#{$i}" ] tr:nth-child( n + #{ $i + 3 } ) { + display: none; + } +} + +.prizepooltable-header { + background-color: var( --prize-pool-header-background-color, inherit ); +} + +.csstable-widget.prizepooltable { + border: 0.125rem solid var( --prize-pool-header-background-color ); + + .csstable-widget-row, + .csstable-widget-cell { + border: 0; + } + + .csstable-widget-row:nth-child( odd ):not( .prizepooltable-header ):not( .ppt-toggle-expand ) { + background-color: var( --prize-pool-alt-background-color ); + } +} + +/* Background colours */ + +tr.bg-first, +div.bg-first, +tr.bg-p1, +div.bg-p1, +tr.background-color-first-place, +div.background-color-first-place { + background-color: var( --prize-pool-background-color, #ffe982 ); +} + +td.bg-first, +td.bg-p1, +td.background-color-first-place { + background-color: var( --prize-pool-gold, #ffe982 ); +} + +tr.bg-first, +div.bg-first, +tr.bg-p1, +div.bg-p1, +tr.background-color-first-place, +div.background-color-first-place, +div.csstable-widget-row.background-color-first-place > .csstable-widget-cell { + border-bottom: 0.125rem solid var( --prize-pool-gold, transparent ); +} + +tr.bg-first > td.prizepooltable-place, +div.bg-first > td.prizepooltable-place, +tr.bg-p1 > td.prizepooltable-place, +div.bg-p1 > td.prizepooltable-place, +tr.background-color-first-place > td.prizepooltable-place, +div.background-color-first-place > *.prizepooltable-place { + background-color: var( --prize-pool-gold, inherit ); + color: #ffffff; +} + +tr.bg-second, +div.bg-second, +tr.bg-p2, +div.bg-p2, +tr.background-color-second-place, +div.background-color-second-place { + background-color: var( --prize-pool-background-color, #eeeeee ); +} + +td.bg-second, +td.bg-p2, +td.background-color-second-place { + background-color: var( --prize-pool-silver, #eeeeee ); +} + +tr.bg-second, +div.bg-second, +tr.bg-p2, +div.bg-p2, +tr.background-color-second-place, +div.background-color-second-place, +div.csstable-widget-row.background-color-second-place > .csstable-widget-cell { + border-bottom: 0.125rem solid var( --prize-pool-silver, transparent ); +} + +tr.bg-second > td.prizepooltable-place, +div.bg-second > td.prizepooltable-place, +tr.bg-p2 > td.prizepooltable-place, +div.bg-p2 > td.prizepooltable-place, +tr.background-color-second-place > td.prizepooltable-place, +div.background-color-second-place > *.prizepooltable-place { + background-color: var( --prize-pool-silver, inherit ); + color: #ffffff; +} + +tr.bg-third, +div.bg-third, +tr.bg-p3, +div.bg-p3, +tr.background-color-third-place, +div.background-color-third-place:not( :first-child ) { + background-color: var( --prize-pool-background-color, #f9e8c7 ); +} + +td.bg-third, +td.bg-p3, +td.background-color-third-place { + background-color: var( --prize-pool-bronze, #f9e8c7 ); +} + +tr.bg-third, +div.bg-third, +tr.bg-p3, +div.bg-p3, +tr.background-color-third-place, +div.background-color-third-place, +div.csstable-widget-row.background-color-third-place > .csstable-widget-cell { + border-bottom: 0.125rem solid var( --prize-pool-bronze, transparent ); +} + +tr.bg-third > td.prizepooltable-place, +div.bg-third > td.prizepooltable-place, +tr.bg-p3 > td.prizepooltable-place, +div.bg-p3 > td.prizepooltable-place, +tr.background-color-third-place > td.prizepooltable-place, +div.background-color-third-place > *.prizepooltable-place { + background-color: var( --prize-pool-bronze, inherit ); + color: #ffffff; +} + +tr.bg-fourth, +div.bg-fourth, +tr.bg-p4, +div.bg-p4, +tr.background-color-fourth-place, +div.background-color-fourth-place { + background-color: var( --prize-pool-background-color, #f9dec7 ); +} + +td.bg-fourth, +td.bg-p4, +td.background-color-fourth-place { + background-color: var( --prize-pool-copper, #f9dec7 ); +} + +tr.bg-fourth, +div.bg-fourth, +tr.bg-p4, +div.bg-p4, +tr.background-color-fourth-place, +div.background-color-fourth-place, +div.csstable-widget-row.background-color-fourth-place > .csstable-widget-cell { + border-bottom: 0.125rem solid var( --prize-pool-copper, transparent ); +} + +tr.bg-fourth > td.prizepooltable-place, +div.bg-fourth > td.prizepooltable-place, +tr.bg-p4 > td.prizepooltable-place, +div.bg-p4 > td.prizepooltable-place, +tr.background-color-fourth-place > td.prizepooltable-place, +div.background-color-fourth-place > *.prizepooltable-place { + background-color: var( --prize-pool-copper, inherit ); + color: #ffffff; +} + +div.csstable-widget-row.bg-win { + div.prizepooltable > & { + /* need the `!important` due to bg-win ( sadly ) having an important in one of its definitions ... */ + background-color: inherit !important; + } + + > .csstable-widget-cell { + border-bottom: 0.125rem solid var( --clr-forest-background-color, transparent ); + } +} + +div.bg-win > *.prizepooltable-place { + background-color: var( --clr-forest-background-color, inherit ); +} + +div.csstable-widget-row.bg-lose { + background-color: inherit; + + > .csstable-widget-cell { + border-bottom: 0.125rem solid var( --clr-cinnabar-background-color, transparent ); + } +} + +div.bg-lose > *.prizepooltable-place { + background-color: var( --clr-cinnabar-background-color, inherit ); +} + +tr.background-color-disqualified, +div.background-color-disqualified { + background-color: #dee3ef; +} + +tr.background-color-win, +div.background-color-win { + background-color: #ddf4dd; +} + +tr.background-color-lose, +div.background-color-lose { + background-color: #fbdfdf; +} + +tr.background-color-highlight, +div.background-color-highlight { + background-color: #cce5ea; +} + +.prizepooltable-background-variant { + background-color: var( --prize-pool-variant-background-color, #f2f2f2 ); +} + +/******************************************************************************* +Legacy ( standardized ) prize pool tables — Author: Rathoz +*******************************************************************************/ + +.collapsed > .ppt-hide-on-collapse { + display: none !important; +} + +.collapsed .ppt-toggle-expand .general-collapsible-collapse-button, +.general-collapsible:not( .collapsed ) .ppt-toggle-expand .general-collapsible-expand-button { + display: none !important; +} + +.prizepooltabletoggle { + cursor: pointer; +} + +// Legacy css-grid table widget (Module:Widget/Table/Old), still used by +// non-prizepool modules (e.g. infoboxes). Prizepool no longer renders via this. +.csstable-widget { + border-right: 1px solid var( --table-border-color, #bbbbbb ); + border-bottom: 1px solid var( --table-border-color, #bbbbbb ); + display: inline-grid; + + &-row:not( .ppt-toggle-expand ) { + display: contents; + } + + &-cell { + border-left: 1px solid var( --table-border-color, #bbbbbb ); + border-top: 1px solid var( --table-border-color, #bbbbbb ); + background: inherit; + padding: 4px; + display: flex; + align-items: center; + justify-content: center; + flex-wrap: wrap; + } +} + +.ppt-toggle-expand { + grid-column: 1 / -1; + + > * { + color: unset !important; + text-decoration: none !important; + } +} + +/******************************************************************************* +Table2-based placement / award prize pool table ( redesign ) + +Scoped to `.prizepooltable-placement` / `.prizepool-table-wrapper` so it owns the +redesigned table only and does not affect the other Table2 tables that merely +share the `prizepooltable` class. *******************************************************************************/ .prizepool-table-wrapper { @@ -36,13 +371,6 @@ Table2-based placement prize pool table } .prizepooltable { - .prizepooltable-header { - th, - td { - font-weight: bold; - } - } - .prizepooltable-badge { display: inline-flex; align-items: center; @@ -75,41 +403,41 @@ Table2-based placement prize pool table font-weight: bold; color: var( --clr-secondary-25 ); + // Override the legacy `… > td.prizepooltable-place` metal fill so the row + // tint + accent bar show through on the redesigned table instead. + background-color: transparent; + .theme--dark & { color: var( --clr-secondary-100 ); } } - .prizepooltable-col-player, - .prizepooltable-col-team { - min-width: 150px; - } - - .prizepooltable-col-two-players { - min-width: 300px; - } - // Top-3 rows: left accent bar (coloured, with the row tint, in the theme block below). - tr.table2__row--body.background-color-first-place, - tr.table2__row--body.background-color-second-place, - tr.table2__row--body.background-color-third-place { - .prizepooltable-place { - position: relative; - - &::before { - content: ""; - position: absolute; - inset: 0 auto 0 0; - width: $prizepool-accent-width; + &.prizepooltable-placement { + tr.table2__row--body.background-color-first-place, + tr.table2__row--body.background-color-second-place, + tr.table2__row--body.background-color-third-place { + .prizepooltable-place { + position: relative; + + &::before { + content: ""; + position: absolute; + inset: 0 auto 0 0; + width: $prizepool-accent-width; + } } } } // The theme wrapper + `.table2__table` qualifier raise specificity above the // Table2 zebra rule so the tint wins regardless of load order; `:hover` keeps it. - .theme--light &.table2__table, - .theme--dark &.table2__table { + // `border-bottom: 0` drops the legacy per-place coloured row border. + .theme--light &.prizepooltable-placement.table2__table, + .theme--dark &.prizepooltable-placement.table2__table { tr.table2__row--body.background-color-first-place { + border-bottom: 0; + &, &:hover { background-color: var( --prizepool-place-1-tint ); @@ -121,6 +449,8 @@ Table2-based placement prize pool table } tr.table2__row--body.background-color-second-place { + border-bottom: 0; + &, &:hover { background-color: var( --prizepool-place-2-tint ); @@ -132,6 +462,8 @@ Table2-based placement prize pool table } tr.table2__row--body.background-color-third-place { + border-bottom: 0; + &, &:hover { background-color: var( --prizepool-place-3-tint ); @@ -154,33 +486,9 @@ Table2-based placement prize pool table tr.table2__row--body.bg-dq .prizepooltable-place { background-color: var( --clr-sapphire-background-color, inherit ); } - - .prizepooltabletoggle { - cursor: pointer; - font-weight: bold; - text-align: center; - color: var( --clr-brand-30, #0a558f ); - - .theme--dark & { - color: var( --clr-brand-80, #a7cdf1 ); - } - - > small { - font-weight: unset; - } - } -} - -table.prizepooltable:not( .collapsed ) .prizepooltableshow, -table.prizepooltable.collapsed .prizepooltablehide { - display: none; -} - -// Gated on `client-js` so non-JS readers see all rows (the toggle can't expand them). -html.client-js .prizepool-table-wrapper.collapsed .ppt-hide-on-collapse { - display: none; } +// Footer toggle for the redesigned (general-collapsible) table. .prizepooltable-toggle { text-align: center; font-weight: bold; @@ -205,53 +513,13 @@ html:not( .client-js ) .prizepooltable-toggle { display: none; } -// Legacy data-cutafter collapse (mvp / medal / winnings tables, which share the -// `prizepooltable` class and Prizepooltable.js). The JS injects the toggle before -// the (cutAfter + 2)-th row, so rows from (cutAfter + 3) onward are hidden. -@for $i from 0 through 32 { - html.client-js .prizepooltable.collapsed[ data-cutafter="#{$i}" ] tr:nth-child( n + #{ $i + 3 } ) { - display: none; - } -} - -@media ( max-width: 600px ) { - .prizepooltable-col-team { - min-width: 65px; - - a:nth-child( 2 ) { - display: none; - } - } -} - -/******************************************************************************* -Legacy css-grid table widget (Module:Widget/Table/Old), still used by -non-prizepool modules (e.g. infoboxes). Prizepool no longer renders via this. -*******************************************************************************/ - -.csstable-widget { - border-right: 1px solid var( --table-border-color, #bbbbbb ); - border-bottom: 1px solid var( --table-border-color, #bbbbbb ); - display: inline-grid; - - &-row { - display: contents; - } - - &-cell { - border-left: 1px solid var( --table-border-color, #bbbbbb ); - border-top: 1px solid var( --table-border-color, #bbbbbb ); - background: inherit; - padding: 4px; - display: flex; - align-items: center; - justify-content: center; - flex-wrap: wrap; - } +// Gated on `client-js` so non-JS readers see all rows (the toggle can't expand them). +html.client-js .prizepool-table-wrapper.collapsed .ppt-hide-on-collapse { + display: none; } /******************************************************************************* -( Standardized ) prize pool section +( Standardized ) prize pool section — Author: iMarbot *******************************************************************************/ .prizepool-section-wrapper { From 0f636d41afd6e0a9c9e143e146815c3db8d22879 Mon Sep 17 00:00:00 2001 From: Eetu Rantanen Date: Thu, 25 Jun 2026 10:01:39 +0300 Subject: [PATCH 26/35] keep @protected --- lua/wikis/commons/PrizePool/Base.lua | 2 ++ 1 file changed, 2 insertions(+) diff --git a/lua/wikis/commons/PrizePool/Base.lua b/lua/wikis/commons/PrizePool/Base.lua index b3e2e63f114..c0512e18a1d 100644 --- a/lua/wikis/commons/PrizePool/Base.lua +++ b/lua/wikis/commons/PrizePool/Base.lua @@ -764,6 +764,7 @@ end --- Whether a placement's rows are hidden behind the collapse toggle. --- Child classes override this; the default keeps every row visible. +---@protected ---@param placement BasePlacement ---@return boolean function BasePrizePool:applyCutAfter(placement) @@ -771,6 +772,7 @@ function BasePrizePool:applyCutAfter(placement) end ---Open/close labels for the collapse toggle. Child classes override. +---@protected ---@return {opentext: string, closetext: string}? function BasePrizePool:_collapseText() return nil From 7417e9abcb16ceb8ecb48455f05124bba6b4ab5e Mon Sep 17 00:00:00 2001 From: Eetu Rantanen Date: Thu, 25 Jun 2026 10:02:42 +0300 Subject: [PATCH 27/35] use Module:Placement --- lua/wikis/commons/PrizePool/Placement.lua | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/lua/wikis/commons/PrizePool/Placement.lua b/lua/wikis/commons/PrizePool/Placement.lua index 2c14d36971f..e46d1099cae 100644 --- a/lua/wikis/commons/PrizePool/Placement.lua +++ b/lua/wikis/commons/PrizePool/Placement.lua @@ -336,11 +336,10 @@ end ---Colored by the top of the range (placeStart). ---@return string? function Placement:getBadgeClass() - if self:hasSpecialStatus() then + if self:hasSpecialStatus() or self.placeStart > 3 then return end - local badges = {[1] = 'placement-1', [2] = 'placement-2', [3] = 'placement-3'} - return badges[self.placeStart] + return PlacementInfo.raw(self.placeStart).backgroundClass end ---@return string? From 9966888b7b9e2f4fdc0de0980e380be5d44f4b2a Mon Sep 17 00:00:00 2001 From: Eetu Rantanen Date: Thu, 25 Jun 2026 10:33:46 +0300 Subject: [PATCH 28/35] gate webkit scrollbar hiding behind @supports not (scrollbar-width: none) --- stylesheets/commons/Prizepooltable.scss | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/stylesheets/commons/Prizepooltable.scss b/stylesheets/commons/Prizepooltable.scss index 8117614688f..1766688c10f 100644 --- a/stylesheets/commons/Prizepooltable.scss +++ b/stylesheets/commons/Prizepooltable.scss @@ -364,8 +364,10 @@ share the `prizepooltable` class. overflow-x: auto; scrollbar-width: none; - &::-webkit-scrollbar { - display: none; + @supports not ( scrollbar-width: none ) { + &::-webkit-scrollbar { + display: none; + } } } } From a97d5f8b00b2675500029a8762cbd1c80c0b6609 Mon Sep 17 00:00:00 2001 From: Eetu Rantanen Date: Thu, 25 Jun 2026 10:35:07 +0300 Subject: [PATCH 29/35] lower table2 zebra specificity with :where() and drop prizepool selector qualifiers --- stylesheets/commons/Prizepooltable.scss | 10 +++++----- stylesheets/commons/Table2.scss | 4 +++- 2 files changed, 8 insertions(+), 6 deletions(-) diff --git a/stylesheets/commons/Prizepooltable.scss b/stylesheets/commons/Prizepooltable.scss index 1766688c10f..c66a01a4fdd 100644 --- a/stylesheets/commons/Prizepooltable.scss +++ b/stylesheets/commons/Prizepooltable.scss @@ -432,11 +432,11 @@ share the `prizepooltable` class. } } - // The theme wrapper + `.table2__table` qualifier raise specificity above the - // Table2 zebra rule so the tint wins regardless of load order; `:hover` keeps it. - // `border-bottom: 0` drops the legacy per-place coloured row border. - .theme--light &.prizepooltable-placement.table2__table, - .theme--dark &.prizepooltable-placement.table2__table { + // Top-3 row tint + accent bar. The Table2 zebra rule is `:where()`-wrapped, so + // these plain selectors out-specify it without a theme/`.table2__table` qualifier; + // `:hover` keeps the tint on hover. `border-bottom: 0` drops the legacy per-place + // coloured row border. + &.prizepooltable-placement { tr.table2__row--body.background-color-first-place { border-bottom: 0; diff --git a/stylesheets/commons/Table2.scss b/stylesheets/commons/Table2.scss index 596710bdb1a..dd78fcca872 100644 --- a/stylesheets/commons/Table2.scss +++ b/stylesheets/commons/Table2.scss @@ -109,7 +109,9 @@ $table2-cell-padding-x: 0.75rem; } tr.table2__row--body { - &.table2__row--even { + // `:where()` keeps the zebra tint at low specificity so consumers (e.g. the + // prize-pool placement tints) can override it without a specificity arms race. + &:where( .table2__row--even ) { background-color: var( --clr-on-surface-light-primary-4 ); .theme--dark & { From 7720c6441f516296dc55fe2384781a6595f2dde4 Mon Sep 17 00:00:00 2001 From: Eetu Rantanen Date: Thu, 25 Jun 2026 10:38:44 +0300 Subject: [PATCH 30/35] drop redundant tostring --- lua/wikis/commons/PrizePool/Base.lua | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/lua/wikis/commons/PrizePool/Base.lua b/lua/wikis/commons/PrizePool/Base.lua index c0512e18a1d..b74b07f5eba 100644 --- a/lua/wikis/commons/PrizePool/Base.lua +++ b/lua/wikis/commons/PrizePool/Base.lua @@ -695,10 +695,10 @@ function BasePrizePool:_buildRows() local isCut = self:applyCutAfter(placement) Array.forEach(opponents, function(opponent, opponentIndex) local opponentCell = TableCell{ - children = {tostring(OpponentDisplay.BlockOpponent{ + children = {OpponentDisplay.BlockOpponent{ opponent = opponent.opponentData, showPlayerTeam = true, - })}, + }}, classes = {'prizepooltable-col-team'}, align = 'left', nowrap = false, From 6ace7d2f9a08e42252b4ebb7a1ebb3de29d68fc0 Mon Sep 17 00:00:00 2001 From: Eetu Rantanen Date: Thu, 25 Jun 2026 10:40:57 +0300 Subject: [PATCH 31/35] drop redundant table.concat --- lua/wikis/commons/PrizePool/Base.lua | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lua/wikis/commons/PrizePool/Base.lua b/lua/wikis/commons/PrizePool/Base.lua index b74b07f5eba..30b63fae88f 100644 --- a/lua/wikis/commons/PrizePool/Base.lua +++ b/lua/wikis/commons/PrizePool/Base.lua @@ -345,7 +345,7 @@ BasePrizePool.prizeTypes = { table.insert(headerDisplay, text) end - return TableCellHeader{children = {table.concat(headerDisplay)}, align = 'right'} + return TableCellHeader{children = headerDisplay, align = 'right'} end, row = 'points', From 09cfa119f68419acc05834b7d8fc072103badb3c Mon Sep 17 00:00:00 2001 From: Eetu Rantanen Date: Thu, 25 Jun 2026 11:09:31 +0300 Subject: [PATCH 32/35] annotate _opponentPrizeCells as private --- lua/wikis/commons/PrizePool/Base.lua | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/lua/wikis/commons/PrizePool/Base.lua b/lua/wikis/commons/PrizePool/Base.lua index 30b63fae88f..bdd46b93b34 100644 --- a/lua/wikis/commons/PrizePool/Base.lua +++ b/lua/wikis/commons/PrizePool/Base.lua @@ -721,8 +721,9 @@ function BasePrizePool:_buildRows() return rows end +---@private ---@param placement BasePlacement ----@param opponent table +---@param opponent BasePlacementOpponent ---@return Renderable[] function BasePrizePool:_opponentPrizeCells(placement, opponent) local previousOfPrizeType = {} From 930ca661dc1016a6c29c590c9079a3b97e45071b Mon Sep 17 00:00:00 2001 From: Eetu Rantanen Date: Thu, 25 Jun 2026 11:10:27 +0300 Subject: [PATCH 33/35] extract column alignment into prize type definitions and column constants --- lua/wikis/commons/PrizePool/Base.lua | 34 +++++++++++++++++++--------- 1 file changed, 23 insertions(+), 11 deletions(-) diff --git a/lua/wikis/commons/PrizePool/Base.lua b/lua/wikis/commons/PrizePool/Base.lua index bdd46b93b34..9b398161fcb 100644 --- a/lua/wikis/commons/PrizePool/Base.lua +++ b/lua/wikis/commons/PrizePool/Base.lua @@ -65,6 +65,10 @@ local PRIZE_TYPE_POINTS = 'POINTS' local PRIZE_TYPE_PERCENTAGE = 'PERCENT' local PRIZE_TYPE_FREETEXT = 'FREETEXT' +-- Alignment for the fixed (non-prize) columns; prize columns carry `align` on their prize type. +local PLACE_COLUMN_ALIGN = 'center' +local OPPONENT_COLUMN_ALIGN = 'left' + BasePrizePool.config = { showBaseCurrency = { default = false @@ -154,10 +158,11 @@ BasePrizePool.config = { BasePrizePool.prizeTypes = { [PRIZE_TYPE_BASE_CURRENCY] = { sortOrder = 10, + align = 'right', headerDisplay = function (data) local currencyText = Currency.display(BASE_CURRENCY) - return TableCellHeader{children = {currencyText}, align = 'right'} + return TableCellHeader{children = {currencyText}} end, row = BASE_CURRENCY:lower() .. 'prize', @@ -169,12 +174,13 @@ BasePrizePool.prizeTypes = { return TableCell{children = { Currency.display(BASE_CURRENCY, data, {formatValue = true, formatPrecision = headerData.roundPrecision, displayCurrencyCode = false}) - }, align = 'right'} + }} end end, }, [PRIZE_TYPE_LOCAL_CURRENCY] = { sortOrder = 20, + align = 'right', header = 'localcurrency', headerParse = function (prizePool, input, context, index) @@ -196,7 +202,7 @@ BasePrizePool.prizeTypes = { } end, headerDisplay = function (data) - return TableCellHeader{children = {Currency.display(data.currency)}, align = 'right'} + return TableCellHeader{children = {Currency.display(data.currency)}} end, row = 'localprize', @@ -208,7 +214,7 @@ BasePrizePool.prizeTypes = { return TableCell{children = { Currency.display(headerData.currency, data, {formatValue = true, formatPrecision = headerData.roundPrecision, displayCurrencyCode = false}) - }, align = 'right'} + }} end end, @@ -227,6 +233,7 @@ BasePrizePool.prizeTypes = { }, [PRIZE_TYPE_PERCENTAGE] = { sortOrder = 30, + align = 'right', header = 'percentage', headerParse = function (prizePool, input, context, index) @@ -234,7 +241,7 @@ BasePrizePool.prizeTypes = { return {title = 'Percentage'} end, headerDisplay = function (data) - return TableCellHeader{children = {data.title}, align = 'right'} + return TableCellHeader{children = {data.title}} end, row = 'percentage', @@ -248,12 +255,13 @@ BasePrizePool.prizeTypes = { end, rowDisplay = function (headerData, data) if String.isNotEmpty(data) then - return TableCell{children = {data .. '%'}, align = 'right'} + return TableCell{children = {data .. '%'}} end end, }, [PRIZE_TYPE_QUALIFIES] = { sortOrder = 50, + align = 'left', header = 'qualifies', headerParse = function (prizePool, input, context, index) @@ -307,6 +315,7 @@ BasePrizePool.prizeTypes = { }, [PRIZE_TYPE_POINTS] = { sortOrder = 40, + align = 'right', header = 'points', headerParse = function (prizePool, input, context, index) @@ -345,7 +354,7 @@ BasePrizePool.prizeTypes = { table.insert(headerDisplay, text) end - return TableCellHeader{children = headerDisplay, align = 'right'} + return TableCellHeader{children = headerDisplay} end, row = 'points', @@ -354,12 +363,13 @@ BasePrizePool.prizeTypes = { end, rowDisplay = function (headerData, data) if data > 0 then - return TableCell{children = {LANG:formatNum(data)}, align = 'right'} + return TableCell{children = {LANG:formatNum(data)}} end end, }, [PRIZE_TYPE_FREETEXT] = { sortOrder = 60, + align = 'left', header = 'freetext', headerParse = function (prizePool, input, context, index) @@ -637,8 +647,8 @@ end ---@return Renderable function BasePrizePool:_buildHeader(isAward) local children = { - TableCellHeader{children = {isAward and 'Award' or 'Place'}, align = 'center'}, - TableCellHeader{children = {'Participant'}, classes = {'prizepooltable-col-team'}, align = 'left'}, + TableCellHeader{children = {isAward and 'Award' or 'Place'}, align = PLACE_COLUMN_ALIGN}, + TableCellHeader{children = {'Participant'}, classes = {'prizepooltable-col-team'}, align = OPPONENT_COLUMN_ALIGN}, } local previousOfType = {} @@ -646,6 +656,7 @@ function BasePrizePool:_buildHeader(isAward) local prizeTypeData = self.prizeTypes[prize.type] if not prizeTypeData.mergeDisplayColumns or not previousOfType[prize.type] then local cell = prizeTypeData.headerDisplay(prize.data) + cell.props.align = cell.props.align or prizeTypeData.align table.insert(children, cell) previousOfType[prize.type] = cell end @@ -700,7 +711,7 @@ function BasePrizePool:_buildRows() showPlayerTeam = true, }}, classes = {'prizepooltable-col-team'}, - align = 'left', + align = OPPONENT_COLUMN_ALIGN, nowrap = false, } local prizeCells = Array.filter(prizeMatrix[opponentIndex], function(cell) @@ -731,6 +742,7 @@ function BasePrizePool:_opponentPrizeCells(placement, opponent) local prizeTypeData = self.prizeTypes[prize.type] local reward = opponent.prizeRewards[prize.id] or placement.prizeRewards[prize.id] local cell = reward and prizeTypeData.rowDisplay(prize.data, reward) or TableCell{} + cell.props.align = cell.props.align or prizeTypeData.align local lastCellOfType = previousOfPrizeType[prize.type] if lastCellOfType and prizeTypeData.mergeDisplayColumns then From 427fa0f5c905cd416c1cf95eceb27aaee9500310 Mon Sep 17 00:00:00 2001 From: Eetu Rantanen Date: Thu, 25 Jun 2026 11:37:14 +0300 Subject: [PATCH 34/35] extend and use ChevronToggle --- lua/wikis/commons/PrizePool/Base.lua | 13 +----- .../GeneralCollapsible/ChevronToggle.lua | 41 +++++++++++-------- 2 files changed, 25 insertions(+), 29 deletions(-) diff --git a/lua/wikis/commons/PrizePool/Base.lua b/lua/wikis/commons/PrizePool/Base.lua index 9b398161fcb..2f0c738c8a5 100644 --- a/lua/wikis/commons/PrizePool/Base.lua +++ b/lua/wikis/commons/PrizePool/Base.lua @@ -35,7 +35,7 @@ local TableCellHeader = TableWidgets.CellHeader local TableRow = TableWidgets.Row local Div = HtmlWidgets.Div local Span = HtmlWidgets.Span -local IconFa = Lua.import('Module:Widget/Image/Icon/Fontawesome') +local ChevronToggle = Lua.import('Module:Widget/GeneralCollapsible/ChevronToggle') local WidgetUtil = Lua.import('Module:Widget/Util') local pageVars = PageVariableNamespace('PrizePool') @@ -630,16 +630,7 @@ function BasePrizePool:_collapseToggle() end return Div{ classes = {'prizepooltable-toggle'}, - children = { - Span{ - classes = {'general-collapsible-expand-button'}, - children = {Span{children = {collapseText.opentext, ' ', IconFa{iconName = 'expand'}}}}, - }, - Span{ - classes = {'general-collapsible-collapse-button'}, - children = {Span{children = {collapseText.closetext, ' ', IconFa{iconName = 'collapse'}}}}, - }, - }, + children = {ChevronToggle{expandText = collapseText.opentext, collapseText = collapseText.closetext}}, } end diff --git a/lua/wikis/commons/Widget/GeneralCollapsible/ChevronToggle.lua b/lua/wikis/commons/Widget/GeneralCollapsible/ChevronToggle.lua index 062c0981c51..a86294527aa 100644 --- a/lua/wikis/commons/Widget/GeneralCollapsible/ChevronToggle.lua +++ b/lua/wikis/commons/Widget/GeneralCollapsible/ChevronToggle.lua @@ -11,30 +11,35 @@ local Component = Lua.import('Module:Widget/Component') local Html = Lua.import('Module:Widget/Html') local Button = Lua.import('Module:Widget/Basic/Button') local Icon = Lua.import('Module:Widget/Image/Icon/Fontawesome') +local WidgetUtil = Lua.import('Module:Widget/Util') local Span = Html.Span ----@return HtmlNode -local function ChevronToggle() - local expandButton = Button{ - classes = {'general-collapsible-expand-button'}, - children = Span{ - children = { - Icon{iconName = 'expand'}, - }, - }, - size = 'xs', - variant = 'icon', - } - local collapseButton = Button{ - classes = {'general-collapsible-collapse-button'}, +---@param class string +---@param text (string|Widget)? +---@param iconName string +---@return Widget +local function chevronButton(class, text, iconName) + return Button{ + classes = {class}, children = Span{ - children = { - Icon{iconName = 'collapse'}, - }, + children = WidgetUtil.collect(text, text and ' ' or nil, Icon{iconName = iconName}), }, size = 'xs', - variant = 'icon', + -- Ghost (borderless, text-friendly) when a label is shown; icon-only otherwise. + variant = text and 'ghost' or 'icon', } +end + +---@class ChevronToggleProps +---@field expandText (string|Widget)? optional label shown next to the expand chevron +---@field collapseText (string|Widget)? optional label shown next to the collapse chevron + +---@param props ChevronToggleProps? +---@return HtmlNode +local function ChevronToggle(props) + props = props or {} + local expandButton = chevronButton('general-collapsible-expand-button', props.expandText, 'expand') + local collapseButton = chevronButton('general-collapsible-collapse-button', props.collapseText, 'collapse') return Span{ classes = {'general-collapsible-default-toggle'}, From 64278681d967e898e7a0e91ac729947fda9fb872 Mon Sep 17 00:00:00 2001 From: Eetu Rantanen Date: Thu, 25 Jun 2026 11:52:16 +0300 Subject: [PATCH 35/35] allow size change --- lua/wikis/commons/PrizePool/Base.lua | 6 +++++- .../Widget/GeneralCollapsible/ChevronToggle.lua | 11 +++++++---- 2 files changed, 12 insertions(+), 5 deletions(-) diff --git a/lua/wikis/commons/PrizePool/Base.lua b/lua/wikis/commons/PrizePool/Base.lua index 2f0c738c8a5..81d40140de5 100644 --- a/lua/wikis/commons/PrizePool/Base.lua +++ b/lua/wikis/commons/PrizePool/Base.lua @@ -630,7 +630,11 @@ function BasePrizePool:_collapseToggle() end return Div{ classes = {'prizepooltable-toggle'}, - children = {ChevronToggle{expandText = collapseText.opentext, collapseText = collapseText.closetext}}, + children = {ChevronToggle{ + expandText = collapseText.opentext, + collapseText = collapseText.closetext, + size = 'sm', + }}, } end diff --git a/lua/wikis/commons/Widget/GeneralCollapsible/ChevronToggle.lua b/lua/wikis/commons/Widget/GeneralCollapsible/ChevronToggle.lua index a86294527aa..b2e909f0395 100644 --- a/lua/wikis/commons/Widget/GeneralCollapsible/ChevronToggle.lua +++ b/lua/wikis/commons/Widget/GeneralCollapsible/ChevronToggle.lua @@ -17,14 +17,15 @@ local Span = Html.Span ---@param class string ---@param text (string|Widget)? ---@param iconName string +---@param size 'xs'|'sm'|'md'|'lg' ---@return Widget -local function chevronButton(class, text, iconName) +local function chevronButton(class, text, iconName, size) return Button{ classes = {class}, children = Span{ children = WidgetUtil.collect(text, text and ' ' or nil, Icon{iconName = iconName}), }, - size = 'xs', + size = size, -- Ghost (borderless, text-friendly) when a label is shown; icon-only otherwise. variant = text and 'ghost' or 'icon', } @@ -33,13 +34,15 @@ end ---@class ChevronToggleProps ---@field expandText (string|Widget)? optional label shown next to the expand chevron ---@field collapseText (string|Widget)? optional label shown next to the collapse chevron +---@field size ('xs'|'sm'|'md'|'lg')? button size, defaults to 'xs' ---@param props ChevronToggleProps? ---@return HtmlNode local function ChevronToggle(props) props = props or {} - local expandButton = chevronButton('general-collapsible-expand-button', props.expandText, 'expand') - local collapseButton = chevronButton('general-collapsible-collapse-button', props.collapseText, 'collapse') + local size = props.size or 'xs' + local expandButton = chevronButton('general-collapsible-expand-button', props.expandText, 'expand', size) + local collapseButton = chevronButton('general-collapsible-collapse-button', props.collapseText, 'collapse', size) return Span{ classes = {'general-collapsible-default-toggle'},