From a526763e00b4353af738cbeb403106fe9a64ee3c Mon Sep 17 00:00:00 2001 From: ElectricalBoy <15651807+ElectricalBoy@users.noreply.github.com> Date: Fri, 19 Jun 2026 17:20:37 +0900 Subject: [PATCH 01/17] type annotation --- lua/wikis/commons/PrizePool/Award.lua | 1 + lua/wikis/commons/PrizePool/Base.lua | 1 + 2 files changed, 2 insertions(+) diff --git a/lua/wikis/commons/PrizePool/Award.lua b/lua/wikis/commons/PrizePool/Award.lua index 6eb7b073a65..dd32c8f8cc3 100644 --- a/lua/wikis/commons/PrizePool/Award.lua +++ b/lua/wikis/commons/PrizePool/Award.lua @@ -25,6 +25,7 @@ local TableCell = Widgets.TableCell --- @class AwardPrizePool: BasePrizePool --- @operator call(...): AwardPrizePool +--- @field placements AwardPlacement[] local AwardPrizePool = Class.new(BasePrizePool) ---@param args table diff --git a/lua/wikis/commons/PrizePool/Base.lua b/lua/wikis/commons/PrizePool/Base.lua index 680786f4314..8e82465a00e 100644 --- a/lua/wikis/commons/PrizePool/Base.lua +++ b/lua/wikis/commons/PrizePool/Base.lua @@ -43,6 +43,7 @@ local pageVars = PageVariableNamespace('PrizePool') --- @operator call(...): BasePrizePool --- @field options table --- @field _lpdbInjector LpdbInjector? +--- @field placements BasePlacement[] local BasePrizePool = Class.new(function(self, ...) self:init(...) end) ---@class BasePrizePoolPrize From 8586c92b066fa7c31d3fc04e9c90951230e61e4d Mon Sep 17 00:00:00 2001 From: ElectricalBoy <15651807+ElectricalBoy@users.noreply.github.com> Date: Fri, 19 Jun 2026 17:55:59 +0900 Subject: [PATCH 02/17] get basics working --- lua/wikis/commons/PrizePool.lua | 16 +- lua/wikis/commons/PrizePool/Base.lua | 156 +++++++++---------- lua/wikis/commons/Widget/PrizePool/Cell.lua | 31 ++++ lua/wikis/commons/Widget/PrizePool/Row.lua | 30 ++++ lua/wikis/commons/Widget/PrizePool/Table.lua | 29 ++++ stylesheets/commons/Prizepooltable.scss | 37 +++++ 6 files changed, 207 insertions(+), 92 deletions(-) create mode 100644 lua/wikis/commons/Widget/PrizePool/Cell.lua create mode 100644 lua/wikis/commons/Widget/PrizePool/Row.lua create mode 100644 lua/wikis/commons/Widget/PrizePool/Table.lua diff --git a/lua/wikis/commons/PrizePool.lua b/lua/wikis/commons/PrizePool.lua index 9c3e78a31b5..861bc245e20 100644 --- a/lua/wikis/commons/PrizePool.lua +++ b/lua/wikis/commons/PrizePool.lua @@ -22,8 +22,8 @@ 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 PrizePoolRow = Lua.import('Module:Widget/PrizePool/Row') +local PrizePoolCell = Lua.import('Module:Widget/PrizePool/Cell') ---@class PrizePool: BasePrizePool ---@operator call(...): PrizePool @@ -54,12 +54,10 @@ end ---@param placement PrizePoolPlacement ---@return WidgetTableCell function PrizePool:placeOrAwardCell(placement) - local placeCell = TableCell{ + local placeCell = PrizePoolCell{ children = {placement:getMedal() or '', NON_BREAKING_SPACE, placement:_displayPlace()}, - css = {['font-weight'] = 'bolder'}, - classes = {'prizepooltable-place'}, + fullHeight = true, } - placeCell.rowSpan = #placement.opponents return placeCell end @@ -110,7 +108,7 @@ function PrizePool:_toggleExpand(placeStart) end local text = 'place ' .. placeStart .. ' to ' .. placeEnd - local expandButton = TableCell{ + local expandButton = PrizePoolCell{ children = Div{children = { text, ' ', @@ -118,7 +116,7 @@ function PrizePool:_toggleExpand(placeStart) }}, classes = {'general-collapsible-expand-button'}, } - local collapseButton = TableCell{ + local collapseButton = PrizePoolCell{ children = Div{children = { text, ' ', @@ -127,7 +125,7 @@ function PrizePool:_toggleExpand(placeStart) classes = {'general-collapsible-collapse-button'}, } - return TableRow{classes = {'ppt-toggle-expand'}, children = {expandButton, collapseButton}} + return PrizePoolRow{classes = {'ppt-toggle-expand'}, children = {expandButton, collapseButton}} end -- get the lpdbObjectName depending on opponenttype diff --git a/lua/wikis/commons/PrizePool/Base.lua b/lua/wikis/commons/PrizePool/Base.lua index 8e82465a00e..5b4941030aa 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 PrizePoolTable = Lua.import('Module:Widget/PrizePool/Table') +local PrizePoolRow = Lua.import('Module:Widget/PrizePool/Row') +local PrizePoolCell = Lua.import('Module:Widget/PrizePool/Cell') local Div = HtmlWidgets.Div local Span = HtmlWidgets.Span local WidgetUtil = Lua.import('Module:Widget/Util') @@ -157,7 +156,7 @@ BasePrizePool.prizeTypes = { headerDisplay = function (data) local currencyText = Currency.display(BASE_CURRENCY) - return TableCell{children = {currencyText}} + return PrizePoolCell{children = {currencyText}} end, row = BASE_CURRENCY:lower() .. 'prize', @@ -166,10 +165,9 @@ BasePrizePool.prizeTypes = { end, rowDisplay = function (headerData, data) if data > 0 then - return TableCell{children = { - Currency.display(BASE_CURRENCY, data, - {formatValue = true, formatPrecision = headerData.roundPrecision, displayCurrencyCode = false}) - }} + return Currency.display(BASE_CURRENCY, data, { + formatValue = true, formatPrecision = headerData.roundPrecision, displayCurrencyCode = false + }) end end, }, @@ -196,7 +194,7 @@ BasePrizePool.prizeTypes = { } end, headerDisplay = function (data) - return TableCell{children = {Currency.display(data.currency)}} + return PrizePoolCell{children = {Currency.display(data.currency)}} end, row = 'localprize', @@ -205,10 +203,9 @@ BasePrizePool.prizeTypes = { end, rowDisplay = function (headerData, data) if data > 0 then - return TableCell{children = { - Currency.display(headerData.currency, data, - {formatValue = true, formatPrecision = headerData.roundPrecision, displayCurrencyCode = false}) - }} + return Currency.display(headerData.currency, data, { + formatValue = true, formatPrecision = headerData.roundPrecision, displayCurrencyCode = false + }) end end, @@ -234,7 +231,7 @@ BasePrizePool.prizeTypes = { return {title = 'Percentage'} end, headerDisplay = function (data) - return TableCell{children = {data.title}} + return PrizePoolCell{children = {data.title}} end, row = 'percentage', @@ -248,7 +245,7 @@ BasePrizePool.prizeTypes = { end, rowDisplay = function (headerData, data) if String.isNotEmpty(data) then - return TableCell{children = {data .. '%'}} + return data .. '%' end end, }, @@ -272,7 +269,7 @@ BasePrizePool.prizeTypes = { } end, headerDisplay = function (data) - return TableCell{children = {'Qualifies To'}} + return PrizePoolCell{children = {'Qualifies To'}} end, row = 'qualified', @@ -300,7 +297,7 @@ BasePrizePool.prizeTypes = { table.insert(content, '[[' .. headerData.link .. ']]') end - return TableCell{children = {Div{children = content}}} + return table.concat(content) end, mergeDisplayColumns = true, @@ -345,7 +342,7 @@ BasePrizePool.prizeTypes = { table.insert(headerDisplay, text) end - return TableCell{children = {table.concat(headerDisplay)}} + return PrizePoolCell{children = {headerDisplay}} end, row = 'points', @@ -354,7 +351,7 @@ BasePrizePool.prizeTypes = { end, rowDisplay = function (headerData, data) if data > 0 then - return TableCell{children = {LANG:formatNum(data)}} + return LANG:formatNum(data) end end, }, @@ -366,7 +363,7 @@ BasePrizePool.prizeTypes = { return {title = input} end, headerDisplay = function (data) - return TableCell{children = {data.title}} + return PrizePoolCell{children = {data.title}} end, row = 'freetext', @@ -375,7 +372,7 @@ BasePrizePool.prizeTypes = { end, rowDisplay = function (headerData, data) if String.isNotEmpty(data) then - return TableCell{children = {data}} + return data end end, } @@ -592,17 +589,9 @@ function BasePrizePool:_buildTable(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 = PrizePoolTable{ children = WidgetUtil.collect(headerRow, unpack(self:_buildRows())) - }}, + }, } end @@ -611,7 +600,8 @@ end function BasePrizePool:_buildHeader(isAward) local children = {} - table.insert(children, TableCell{children = {isAward and 'Award' or 'Place'}, css = {['min-width'] = '80px'}}) + table.insert(children, PrizePoolCell{children = {isAward and 'Award' or 'Place'}, css = {['min-width'] = '80px'}}) + table.insert(children, PrizePoolCell{children = {'Participant'}, classes = {'prizepooltable-col-team'}}) local previousOfType = {} for _, prize in ipairs(self.prizes) do @@ -624,9 +614,7 @@ 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 PrizePoolRow{classes = {'prizepooltable-header'}, css = {['font-weight'] = 'bold'}, children = children} end ---@return WidgetTableRow[] @@ -643,64 +631,66 @@ function BasePrizePool:_buildRows() 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 - - 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 + local cells = { + self:placeOrAwardCell(placement), + PrizePoolCell{ + classes = {'opponent-cell'}, + children = Array.map(placement.opponents, function (opponent) + return OpponentDisplay.BlockOpponent{ + opponent = opponent.opponentData, + showPlayerTeam = true, + } + end) + } + } - Array.extendWith(lastCellOfType.props.children, cell.props.children) - lastCellOfType.css['flex-direction'] = 'column' + local _, prizes = Array.groupBy(self.prizes, function (prize) return prize.type end) - return nil - end - - previousOfPrizeType[prize.type] = cell - return cell + for prizeType, v in Table.iter.spairs( + prizes, + function (_, a, b) + return self.prizeTypes[a].sortOrder < self.prizeTypes[b].sortOrder + end + ) do + ---@cast v BasePrizePoolPrize[] + local prizeTypeData = self.prizeTypes[prizeType] + local prizeDisplay = Array.map(placement.opponents, function (opponent) + ---@type string[] + local rewards = Array.map(v, function (prize) + local reward = opponent.prizeRewards[prize.id] or placement.prizeRewards[prize.id] + return reward and prizeTypeData.rowDisplay(prize.data, reward) or nil + end) + return rewards end) - Array.forEach(prizeCells, function (prizeCell, columnIndex) - local lastInColumn = previousOpponent[columnIndex] - - ---@cast prizeCell -nil - if Table.isEmpty(prizeCell.props.children) then - prizeCell = BasePrizePool._emptyCell() - end + local children = { + PrizePoolCell{ + height = 1, + children = Array.interleave(prizeDisplay[1], HtmlWidgets.Hr{}) + } + } - if lastInColumn and Table.deepEquals(lastInColumn.props.children, prizeCell.props.children) then - lastInColumn.rowSpan = (lastInColumn.rowSpan or 1) + 1 + for i = 2, #prizeDisplay do + if Table.deepEquals(prizeDisplay[i - 1], prizeDisplay[i]) then + children[#children].props.height = children[#children].props.height + 1 else - previousOpponent[columnIndex] = prizeCell - table.insert(cells, prizeCell) + table.insert(children, PrizePoolCell{ + height = 1, + children = Array.interleave(prizeDisplay[i], HtmlWidgets.Hr{}) + }) end - end) - - local opponentDisplay = tostring(OpponentDisplay.BlockOpponent{ - opponent = opponent.opponentData, - showPlayerTeam = true, + end + table.insert(cells, PrizePoolCell{ + classes = {'prize-cell'}, + children = children }) - local opponentCss = {['justify-content'] = 'start'} - - 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} + local row = PrizePoolRow{height = #placement.opponents, children = cells, classes = classes} table.insert(rows, row) @@ -816,9 +806,9 @@ function BasePrizePool:_hasBaseCurrency() end --- Creates an empty table cell ----@return WidgetTableCell +---@return WidgetPrizePoolCell function BasePrizePool._emptyCell() - return TableCell{children = {DASH}} + return PrizePoolCell{children = {DASH}} end --- Remove all non-numeric characters from an input and changes it to a number. diff --git a/lua/wikis/commons/Widget/PrizePool/Cell.lua b/lua/wikis/commons/Widget/PrizePool/Cell.lua new file mode 100644 index 00000000000..8326d946e0f --- /dev/null +++ b/lua/wikis/commons/Widget/PrizePool/Cell.lua @@ -0,0 +1,31 @@ +--- +-- @Liquipedia +-- page=Module:Widget/PrizePool/Cell +-- +-- Please see https://github.com/Liquipedia/Lua-Modules to contribute +-- + +local Lua = require('Module:Lua') + +local Array = Lua.import('Module:Array') + +local Component = Lua.import('Module:Widget/Component') +local Html = Lua.import('Module:Widget/Html') + +---@param props {classes: string[]?, children: Renderable|Renderable[]?, fullHeight: boolean?, height: integer?} +---@return VNode +local function PrizePoolCell(props) + return Html.Div{ + classes = Array.extend( + 'prize-pool-table-cell', + props.classes, + props.fullHeight and 'full-height' or nil + ), + css = { + ['--prize-pool-cell-height'] = props.height + }, + children = props.children, + } +end + +return Component.component(PrizePoolCell) diff --git a/lua/wikis/commons/Widget/PrizePool/Row.lua b/lua/wikis/commons/Widget/PrizePool/Row.lua new file mode 100644 index 00000000000..e376ad1db9b --- /dev/null +++ b/lua/wikis/commons/Widget/PrizePool/Row.lua @@ -0,0 +1,30 @@ +--- +-- @Liquipedia +-- page=Module:Widget/PrizePool/Table +-- +-- Please see https://github.com/Liquipedia/Lua-Modules to contribute +-- + +local Lua = require('Module:Lua') + +local Array = Lua.import('Module:Array') + +local Component = Lua.import('Module:Widget/Component') +local Html = Lua.import('Module:Widget/Html') + +---@param props {classes: string[]?, children: Renderable|Renderable[]?, height: integer} +---@return VNode +local function PrizePoolRow(props) + return Html.Div{ + classes = Array.extend( + 'prize-pool-table-row', + props.classes + ), + css = { + ['--prize-pool-row-height'] = props.height + }, + children = props.children + } +end + +return Component.component(PrizePoolRow) diff --git a/lua/wikis/commons/Widget/PrizePool/Table.lua b/lua/wikis/commons/Widget/PrizePool/Table.lua new file mode 100644 index 00000000000..63d3d3a4981 --- /dev/null +++ b/lua/wikis/commons/Widget/PrizePool/Table.lua @@ -0,0 +1,29 @@ +--- +-- @Liquipedia +-- page=Module:Widget/PrizePool/Table +-- +-- Please see https://github.com/Liquipedia/Lua-Modules to contribute +-- + +local Lua = require('Module:Lua') + +local Component = Lua.import('Module:Widget/Component') +local Html = Lua.import('Module:Widget/Html') + +---@param props {children: Renderable[], columns: integer} +---@return VNode +local function PrizePoolTable(props) + return Html.Div{ + classes = { + 'collapsed', + 'general-collapsible', + 'prize-pool-table', + }, + css = { + ['--prize-pool-columns'] = #props.children + }, + children = props.children + } +end + +return Component.component(PrizePoolTable) diff --git a/stylesheets/commons/Prizepooltable.scss b/stylesheets/commons/Prizepooltable.scss index 7aebf9e98e4..ec9bb18f31f 100644 --- a/stylesheets/commons/Prizepooltable.scss +++ b/stylesheets/commons/Prizepooltable.scss @@ -337,3 +337,40 @@ Author: iMarbot } } } + +/******************************************************************************* +Template: Prize Pool Table +*******************************************************************************/ + +.prize-pool-table { + display: grid; + grid-template-columns: repeat( var( --prize-pool-columns ), auto ); + overflow-x: auto; + + &-row { + display: grid; + grid-column: 1 / -1; + grid-template-columns: subgrid; + grid-template-rows: repeat( var( --prize-pool-row-height, 1 ), auto ); + + &.ppt-toggle-expand { + display: block; + } + } + + &-cell { + display: flex; + grid-row: span var( --prize-pool-cell-height, 1 ); + + &.full-height { + grid-row: span var( --prize-pool-row-height, 1 ); + } + + &.prize-cell, + &.opponent-cell:has( > :nth-child( 2 ) ) { + display: grid; + grid-row: span var( --prize-pool-row-height, 1 ); + grid-template-rows: subgrid; + } + } +} From 9f670108845db7e2af69306e563cd677163ba409 Mon Sep 17 00:00:00 2001 From: ElectricalBoy <15651807+ElectricalBoy@users.noreply.github.com> Date: Fri, 19 Jun 2026 18:24:00 +0900 Subject: [PATCH 03/17] avoid sorting twice --- lua/wikis/commons/PrizePool/Base.lua | 73 ++++++++++++---------------- 1 file changed, 30 insertions(+), 43 deletions(-) diff --git a/lua/wikis/commons/PrizePool/Base.lua b/lua/wikis/commons/PrizePool/Base.lua index 5b4941030aa..b11278e17ee 100644 --- a/lua/wikis/commons/PrizePool/Base.lua +++ b/lua/wikis/commons/PrizePool/Base.lua @@ -623,8 +623,6 @@ function BasePrizePool:_buildRows() local previousPlacement = nil for _, placement in ipairs(self.placements) do - local previousOpponent = {} - if self:applyHideAfter(placement) then break end @@ -644,47 +642,42 @@ function BasePrizePool:_buildRows() } } - local _, prizes = Array.groupBy(self.prizes, function (prize) return prize.type end) - - for prizeType, v in Table.iter.spairs( - prizes, - function (_, a, b) - return self.prizeTypes[a].sortOrder < self.prizeTypes[b].sortOrder - end - ) do - ---@cast v BasePrizePoolPrize[] - local prizeTypeData = self.prizeTypes[prizeType] - local prizeDisplay = Array.map(placement.opponents, function (opponent) - ---@type string[] - local rewards = Array.map(v, function (prize) - local reward = opponent.prizeRewards[prize.id] or placement.prizeRewards[prize.id] - return reward and prizeTypeData.rowDisplay(prize.data, reward) or nil + Array.forEach( + Array.groupAdjacentBy(self.prizes, function (prize) return prize.type end), + function (prizes) + local prizeTypeData = self.prizeTypes[prizes[1].type] + local prizeDisplay = Array.map(placement.opponents, function (opponent) + ---@type string[] + local rewards = Array.map(prizes, function (prize) + local reward = opponent.prizeRewards[prize.id] or placement.prizeRewards[prize.id] + return reward and prizeTypeData.rowDisplay(prize.data, reward) or nil + end) + return rewards end) - return rewards - end) - local children = { - PrizePoolCell{ - height = 1, - children = Array.interleave(prizeDisplay[1], HtmlWidgets.Hr{}) + local children = { + PrizePoolCell{ + height = 1, + children = Array.interleave(prizeDisplay[1], HtmlWidgets.Hr{}) + } } - } - for i = 2, #prizeDisplay do - if Table.deepEquals(prizeDisplay[i - 1], prizeDisplay[i]) then - children[#children].props.height = children[#children].props.height + 1 - else - table.insert(children, PrizePoolCell{ - height = 1, - children = Array.interleave(prizeDisplay[i], HtmlWidgets.Hr{}) - }) + for i = 2, #prizeDisplay do + if Array.equals(prizeDisplay[i - 1], prizeDisplay[i]) then + children[#children].props.height = children[#children].props.height + 1 + else + table.insert(children, PrizePoolCell{ + height = 1, + children = Array.interleave(prizeDisplay[i], HtmlWidgets.Hr{}) + }) + end end + table.insert(cells, PrizePoolCell{ + classes = {'prize-cell'}, + children = children + }) end - table.insert(cells, PrizePoolCell{ - classes = {'prize-cell'}, - children = children - }) - end + ) local classes = {placement:getBackground()} if self:applyCutAfter(placement) then @@ -805,12 +798,6 @@ function BasePrizePool:_hasBaseCurrency() end)) end ---- Creates an empty table cell ----@return WidgetPrizePoolCell -function BasePrizePool._emptyCell() - return PrizePoolCell{children = {DASH}} -end - --- Remove all non-numeric characters from an input and changes it to a number. -- Most commonly used on money inputs, as they often contain , or . ---@param input number|string From 5d88c93ff70e09b237e09dfc7b9fe9b2048415d7 Mon Sep 17 00:00:00 2001 From: ElectricalBoy <15651807+ElectricalBoy@users.noreply.github.com> Date: Sat, 20 Jun 2026 14:02:44 +0900 Subject: [PATCH 04/17] add BasePlacement:getBackground() --- lua/wikis/commons/PrizePool/Award/Placement.lua | 1 + lua/wikis/commons/PrizePool/Placement/Base.lua | 5 +++++ 2 files changed, 6 insertions(+) diff --git a/lua/wikis/commons/PrizePool/Award/Placement.lua b/lua/wikis/commons/PrizePool/Award/Placement.lua index b64c29a90e2..1402c4fda88 100644 --- a/lua/wikis/commons/PrizePool/Award/Placement.lua +++ b/lua/wikis/commons/PrizePool/Award/Placement.lua @@ -121,6 +121,7 @@ function AwardPlacement:_getLpdbData(...) end -- No BG for awards +---@return string? function AwardPlacement:getBackground() return end diff --git a/lua/wikis/commons/PrizePool/Placement/Base.lua b/lua/wikis/commons/PrizePool/Placement/Base.lua index 0d15ed82051..7cb80d9a8ea 100644 --- a/lua/wikis/commons/PrizePool/Placement/Base.lua +++ b/lua/wikis/commons/PrizePool/Placement/Base.lua @@ -242,4 +242,9 @@ function BasePlacement._isValidDateFormat(date) return date:match('%d%d%d%d%-%d%d%-%d%d') and true or false end +---@return string? +function BasePlacement:getBackground() + error('BasePlacement:getBackground() cannot be called directly and must be overridden.') +end + return BasePlacement From 29a0d265d57c24f2b67ac83d38ebb597e4ee10f2 Mon Sep 17 00:00:00 2001 From: ElectricalBoy <15651807+ElectricalBoy@users.noreply.github.com> Date: Sun, 21 Jun 2026 01:35:17 +0900 Subject: [PATCH 05/17] more style --- lua/wikis/commons/PrizePool.lua | 56 +++------ lua/wikis/commons/PrizePool/Award.lua | 45 +++----- lua/wikis/commons/PrizePool/Base.lua | 43 ++++--- lua/wikis/commons/PrizePool/Placement.lua | 12 +- .../commons/PrizePool/Placement/Base.lua | 5 + lua/wikis/commons/Widget/PrizePool/Row.lua | 17 ++- lua/wikis/commons/Widget/PrizePool/Table.lua | 41 +++++-- stylesheets/commons/Prizepooltable.scss | 108 +++++++++++++++++- 8 files changed, 221 insertions(+), 106 deletions(-) diff --git a/lua/wikis/commons/PrizePool.lua b/lua/wikis/commons/PrizePool.lua index 861bc245e20..b8437fc935f 100644 --- a/lua/wikis/commons/PrizePool.lua +++ b/lua/wikis/commons/PrizePool.lua @@ -19,10 +19,9 @@ 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 PrizePoolRow = Lua.import('Module:Widget/PrizePool/Row') +local ChevronToggle = Lua.import('Module:Widget/GeneralCollapsible/ChevronToggle') +local Html = Lua.import('Module:Widget/Html') +local Div = Html.Div local PrizePoolCell = Lua.import('Module:Widget/PrizePool/Cell') ---@class PrizePool: BasePrizePool @@ -77,24 +76,14 @@ function PrizePool:applyCutAfter(placement) 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)) +---@return VNode? +function PrizePool:getCollapsibleToggle() + local placeStart = Array.find(self.placements, function (placement) + return placement.placeStart > self.options.cutafter + end) + if not placeStart then + return 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 @@ -107,25 +96,16 @@ function PrizePool:_toggleExpand(placeStart) placeEnd = lastCut.placeEnd end - local text = 'place ' .. placeStart .. ' to ' .. placeEnd - local expandButton = PrizePoolCell{ - children = Div{children = { - text, - ' ', - IconFa{iconName = 'expand'}, - }}, - classes = {'general-collapsible-expand-button'}, - } - local collapseButton = PrizePoolCell{ - children = Div{children = { + local text = 'place ' .. placeStart.placeStart .. ' to ' .. placeEnd + + return Div{ + classes = {'prize-pool-toggle'}, + attributes = {['data-collapsible-click-region'] = 'true'}, + children = { text, - ' ', - IconFa{iconName = 'collapse'}, - }}, - classes = {'general-collapsible-collapse-button'}, + ChevronToggle{} + } } - - return PrizePoolRow{classes = {'ppt-toggle-expand'}, children = {expandButton, collapseButton}} end -- get the lpdbObjectName depending on opponenttype diff --git a/lua/wikis/commons/PrizePool/Award.lua b/lua/wikis/commons/PrizePool/Award.lua index dd32c8f8cc3..0b2bd6f57c1 100644 --- a/lua/wikis/commons/PrizePool/Award.lua +++ b/lua/wikis/commons/PrizePool/Award.lua @@ -20,8 +20,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 TableRow = Widgets.TableRow -local TableCell = Widgets.TableCell +local PrizePoolCell = Lua.import('Module:Widget/PrizePool/Cell') --- @class AwardPrizePool: BasePrizePool --- @operator call(...): AwardPrizePool @@ -53,12 +52,12 @@ end ---@param placement AwardPlacement ---@return WidgetTableCell function AwardPrizePool:placeOrAwardCell(placement) - local awardCell = TableCell{ + local awardCell = PrizePoolCell{ children = {placement.award}, css = {['font-weight'] = 'bolder'}, classes = {'prizepooltable-place'}, + height = #placement.opponents, } - awardCell.rowSpan = #placement.opponents return awardCell end @@ -72,37 +71,31 @@ function AwardPrizePool:applyCutAfter(placement) 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()) - end -end - ----@return WidgetTableRow -function AwardPrizePool:_toggleExpand() - local expandButton = TableCell{ - children = Div{children = { +---@return Renderable +function AwardPrizePool:getCollapsibleToggle() + local expandButton = Div{ + children = { 'Show more Awards ', IconFa{iconName = 'expand'}, - }}, + }, classes = {'general-collapsible-expand-button'}, } - local collapseButton = TableCell{ - children = Div{children = { + local collapseButton = Div{ + children = { 'Show less Awards ', IconFa{iconName = 'collapse'}, - }}, + }, classes = {'general-collapsible-collapse-button'}, } - return TableRow{classes = {'ppt-toggle-expand'}, children = {expandButton, collapseButton}} + return Div{ + classes = {'prize-pool-toggle'}, + attributes = {['data-collapsible-click-region'] = 'true'}, + children = Div{ + classes = {'general-collapsible-default-toggle'}, + children = {expandButton, collapseButton} + } + } end -- Get the lpdbObjectName depending on opponenttype diff --git a/lua/wikis/commons/PrizePool/Base.lua b/lua/wikis/commons/PrizePool/Base.lua index b11278e17ee..eaf7b53eb5c 100644 --- a/lua/wikis/commons/PrizePool/Base.lua +++ b/lua/wikis/commons/PrizePool/Base.lua @@ -587,16 +587,22 @@ end function BasePrizePool:_buildTable(isAward) local headerRow = self:_buildHeader(isAward) + local rows, collapsedRows = self:_buildRows() + return Div{ css = {['overflow-x'] = 'auto'}, children = PrizePoolTable{ - children = WidgetUtil.collect(headerRow, unpack(self:_buildRows())) + prizeTypes = #self.prizes, + header = headerRow, + toggle = self:getCollapsibleToggle(), + displayedRows = rows, + collapsedRows = collapsedRows, }, } end ---@param isAward boolean? ----@return WidgetTableRow +---@return VNode function BasePrizePool:_buildHeader(isAward) local children = {} @@ -617,18 +623,16 @@ function BasePrizePool:_buildHeader(isAward) return PrizePoolRow{classes = {'prizepooltable-header'}, css = {['font-weight'] = 'bold'}, children = children} end ----@return WidgetTableRow[] +---@return VNode[], VNode[] function BasePrizePool:_buildRows() local rows = {} - local previousPlacement = nil + local collapsedRows = {} for _, placement in ipairs(self.placements) do if self:applyHideAfter(placement) then break end - self:applyToggleExpand(previousPlacement, placement, rows) - local cells = { self:placeOrAwardCell(placement), PrizePoolCell{ @@ -679,22 +683,25 @@ function BasePrizePool:_buildRows() end ) - local classes = {placement:getBackground()} + local row = PrizePoolRow{ + height = #placement.opponents, + children = cells, + placement = placement:getPlacement() + } + if self:applyCutAfter(placement) then - table.insert(classes, 'ppt-hide-on-collapse') + table.insert(collapsedRows, row) + else + table.insert(rows, row) end - local row = PrizePoolRow{height = #placement.opponents, children = cells, classes = classes} - - table.insert(rows, row) - - previousPlacement = placement end - return rows + return rows, collapsedRows end ---@protected ---@param placement BasePlacement +---@return Renderable function BasePrizePool:placeOrAwardCell(placement) error('Function placeOrAwardCell needs to be implemented by a child class of "Module:PrizePool/Base"') end @@ -714,11 +721,9 @@ function BasePrizePool:applyCutAfter(placement) 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"') +---@return Renderable? +function BasePrizePool:getCollapsibleToggle() + error('BasePrizePool:getCollapsibleToggle() cannot be called directly and must be overridden.') end ---@return string diff --git a/lua/wikis/commons/PrizePool/Placement.lua b/lua/wikis/commons/PrizePool/Placement.lua index 3e11a0935b8..eb42b081b8d 100644 --- a/lua/wikis/commons/PrizePool/Placement.lua +++ b/lua/wikis/commons/PrizePool/Placement.lua @@ -323,15 +323,9 @@ function Placement:_displayPlace() return start end ----@return string? -function Placement:getBackground() - for statusName, status in pairs(Placement.specialStatuses) do - if status.active(self.args) then - return PlacementInfo.getBgClass{placement = statusName:lower()} - end - end - - return PlacementInfo.getBgClass{placement = self.placeStart} +---@return number? +function Placement:getPlacement() + return self.placeStart end ---@return string? diff --git a/lua/wikis/commons/PrizePool/Placement/Base.lua b/lua/wikis/commons/PrizePool/Placement/Base.lua index 7cb80d9a8ea..93a7a1ccdc9 100644 --- a/lua/wikis/commons/PrizePool/Placement/Base.lua +++ b/lua/wikis/commons/PrizePool/Placement/Base.lua @@ -247,4 +247,9 @@ function BasePlacement:getBackground() error('BasePlacement:getBackground() cannot be called directly and must be overridden.') end +---@return number? +function BasePlacement:getPlacement() + return +end + return BasePlacement diff --git a/lua/wikis/commons/Widget/PrizePool/Row.lua b/lua/wikis/commons/Widget/PrizePool/Row.lua index e376ad1db9b..bebbea8c54d 100644 --- a/lua/wikis/commons/Widget/PrizePool/Row.lua +++ b/lua/wikis/commons/Widget/PrizePool/Row.lua @@ -12,17 +12,26 @@ local Array = Lua.import('Module:Array') local Component = Lua.import('Module:Widget/Component') local Html = Lua.import('Module:Widget/Html') ----@param props {classes: string[]?, children: Renderable|Renderable[]?, height: integer} +---@class PrizePoolRowProps: HtmlNodeProps +---@field placement string|number? +---@field height integer? + +---@param props PrizePoolRowProps ---@return VNode local function PrizePoolRow(props) + local css = props.css or {} + css['--prize-pool-row-height'] = props.height + + local attributes = props.attributes or {} + attributes['data-placement'] = (props.placement or 0) <= 3 and props.placement or nil + return Html.Div{ classes = Array.extend( 'prize-pool-table-row', props.classes ), - css = { - ['--prize-pool-row-height'] = props.height - }, + attributes = attributes, + css = css, children = props.children } end diff --git a/lua/wikis/commons/Widget/PrizePool/Table.lua b/lua/wikis/commons/Widget/PrizePool/Table.lua index 63d3d3a4981..672feb54ac9 100644 --- a/lua/wikis/commons/Widget/PrizePool/Table.lua +++ b/lua/wikis/commons/Widget/PrizePool/Table.lua @@ -7,22 +7,47 @@ local Lua = require('Module:Lua') +local Table = Lua.import('Module:Table') + local Component = Lua.import('Module:Widget/Component') local Html = Lua.import('Module:Widget/Html') +local WidgetUtil = Lua.import('Module:Widget/Util') + +---@class PrizePoolTableProps +---@field header Renderable +---@field prizeTypes integer +---@field displayedRows Renderable[] +---@field toggle Renderable? +---@field collapsedRows Renderable[]? ----@param props {children: Renderable[], columns: integer} +---@param props PrizePoolTableProps ---@return VNode local function PrizePoolTable(props) return Html.Div{ - classes = { - 'collapsed', - 'general-collapsible', - 'prize-pool-table', - }, + classes = {'prize-pool-table'}, css = { - ['--prize-pool-columns'] = #props.children + ['--prize-pool-columns'] = props.prizeTypes + 2 }, - children = props.children + children = { + props.header, + Html.Div{ + classes = { + 'prize-pool-table-body', + 'collapsed', + 'general-collapsible', + }, + children = WidgetUtil.collect( + props.displayedRows, + Table.isNotEmpty(props.collapsedRows) and { + props.toggle, + Html.Div{ + classes = {'should-collapse'}, + children = props.collapsedRows + } + } or nil + ) + } + } } end diff --git a/stylesheets/commons/Prizepooltable.scss b/stylesheets/commons/Prizepooltable.scss index ec9bb18f31f..539ff3d34e6 100644 --- a/stylesheets/commons/Prizepooltable.scss +++ b/stylesheets/commons/Prizepooltable.scss @@ -346,21 +346,124 @@ Template: Prize Pool Table display: grid; grid-template-columns: repeat( var( --prize-pool-columns ), auto ); overflow-x: auto; + border-radius: 0.5rem; + border: 1px solid var( --clr-on-surface-light-primary-12 ); + background-color: var( --clr-background, #ffffff ); + color: var( --clr-secondary-16 ); + color-scheme: light; + + .theme--dark & { + background-color: var( --clr-background, #121212 ); + border-color: var( --clr-on-surface-dark-primary-12 ); + color: var( --clr-secondary-100 ); + color-scheme: dark; + } + + &-body { + display: grid; + grid-column: 1 / -1; + grid-template-columns: subgrid; + + > .prize-pool-toggle { + display: flex; + grid-column: 1 / -1; + padding: 0.5rem 0.75rem; + place-self: center; + width: 100%; + justify-content: center; + + .general-collapsible-default-toggle { + min-width: unset; + + .general-collapsible-expand-button, + .general-collapsible-expand-button:visited, + .general-collapsible-collapse-button, + .general-collapsible-collapse-button:visited { + outline: unset; + color: var( --clr-secondary-7 ); + + .theme--dark & { + color: var( --clr-secondary-90 ); + } + } + } + } + + &:not( .collapsed ) > .should-collapse { + display: contents; + } + } &-row { display: grid; grid-column: 1 / -1; grid-template-columns: subgrid; grid-template-rows: repeat( var( --prize-pool-row-height, 1 ), auto ); + background-color: var( --clr-on-surface-light-primary-4 ); + + .theme--dark & { + background-color: var( --clr-on-surface-dark-primary-4 ); + } + + &:nth-of-type( even ):not( [ data-placement ] ) { + background-color: var( --clr-on-surface-light-primary-8 ); + + .theme--dark & { + background-color: var( --clr-on-surface-dark-primary-8 ); + } + } - &.ppt-toggle-expand { - display: block; + &[ data-placement ] { + border-left: 0.25rem solid var( --prize-pool-placement-color, transparent ); + background-color: var( --prize-pool-placement-background-color ); + } + + &[ data-placement="1" ] { + --prize-pool-placement-color: #f0b419; + --prize-pool-placement-background-color: #fff9eb; + + .theme--dark & { + --prize-pool-placement-color: #fbcb50; + --prize-pool-placement-background-color: hsl( from var( --prize-pool-placement-color ) h s l / 0.16 ); + } + } + + &[ data-placement="2" ] { + --prize-pool-placement-color: #58b5e3; + --prize-pool-placement-background-color: #f0fcff; + + .theme--dark & { + --prize-pool-placement-color: #91d7f9; + --prize-pool-placement-background-color: hsl( from var( --prize-pool-placement-color ) h s l / 0.16 ); + } + } + + &[ data-placement="3" ] { + --prize-pool-placement-color: #de7d1d; + --prize-pool-placement-background-color: #fff3ec; + + .theme--dark & { + --prize-pool-placement-color: #ffb267; + --prize-pool-placement-background-color: hsl( from var( --prize-pool-placement-color ) h s l / 0.16 ); + } + } + } + + > &-row { + background-color: var( --clr-on-surface-light-primary-4 ); + color: var( --clr-secondary-25 ); + + .theme--dark & { + background-color: var( --clr-on-surface-dark-primary-4 ); + color: var( --clr-secondary-80 ); } } &-cell { display: flex; grid-row: span var( --prize-pool-cell-height, 1 ); + padding: 0.5rem 0.75rem; + align-items: center; &.full-height { grid-row: span var( --prize-pool-row-height, 1 ); @@ -370,6 +473,7 @@ Template: Prize Pool Table &.opponent-cell:has( > :nth-child( 2 ) ) { display: grid; grid-row: span var( --prize-pool-row-height, 1 ); + row-gap: 0.5rem; grid-template-rows: subgrid; } } From 2c664cf5d9e0a0ff1bbb3540e4ea0b8ba33b830d Mon Sep 17 00:00:00 2001 From: ElectricalBoy <15651807+ElectricalBoy@users.noreply.github.com> Date: Mon, 22 Jun 2026 10:19:25 +0900 Subject: [PATCH 06/17] suppress deprecation warning --- stylesheets/commons/Prizepooltable.scss | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/stylesheets/commons/Prizepooltable.scss b/stylesheets/commons/Prizepooltable.scss index 539ff3d34e6..7d2c70fcbe3 100644 --- a/stylesheets/commons/Prizepooltable.scss +++ b/stylesheets/commons/Prizepooltable.scss @@ -449,7 +449,7 @@ Template: Prize Pool Table } } - > &-row { + & > &-row { background-color: var( --clr-on-surface-light-primary-4 ); color: var( --clr-secondary-25 ); From c53595f002d7a75b9d964ddd563e2a783671004d Mon Sep 17 00:00:00 2001 From: ElectricalBoy <15651807+ElectricalBoy@users.noreply.github.com> Date: Mon, 22 Jun 2026 15:23:49 +0900 Subject: [PATCH 07/17] label and sticky toggle --- lua/wikis/commons/PrizePool.lua | 20 +++++----- lua/wikis/commons/PrizePool/Base.lua | 13 +++--- lua/wikis/commons/Widget/PrizePool/Row.lua | 2 +- stylesheets/commons/Prizepooltable.scss | 46 +++++++++++++--------- 4 files changed, 48 insertions(+), 33 deletions(-) diff --git a/lua/wikis/commons/PrizePool.lua b/lua/wikis/commons/PrizePool.lua index b8437fc935f..881d84ab858 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 ChevronToggle = Lua.import('Module:Widget/GeneralCollapsible/ChevronToggle') local Html = Lua.import('Module:Widget/Html') local Div = Html.Div +local Label = Lua.import('Module:Widget/Basic/Label') local PrizePoolCell = Lua.import('Module:Widget/PrizePool/Cell') ---@class PrizePool: BasePrizePool @@ -29,8 +30,6 @@ local PrizePoolCell = Lua.import('Module:Widget/PrizePool/Cell') ---@field placements PrizePoolPlacement[] local PrizePool = Class.new(BasePrizePool) -local NON_BREAKING_SPACE = ' ' - ---@param args table function PrizePool:readPlacements(args) local currentPlace = 0 @@ -51,14 +50,17 @@ function PrizePool:readPlacements(args) end ---@param placement PrizePoolPlacement ----@return WidgetTableCell +---@return VNode function PrizePool:placeOrAwardCell(placement) - local placeCell = PrizePoolCell{ - children = {placement:getMedal() or '', NON_BREAKING_SPACE, placement:_displayPlace()}, + local placementDisplay = placement:_lpdbValue() + + return PrizePoolCell{ + children = Label{ + labelScheme = 'prize-pool-placement', + children = placementDisplay + }, fullHeight = true, } - - return placeCell end ---@param placement PrizePoolPlacement @@ -101,10 +103,10 @@ function PrizePool:getCollapsibleToggle() return Div{ classes = {'prize-pool-toggle'}, attributes = {['data-collapsible-click-region'] = 'true'}, - children = { + children = Div{children = { text, ChevronToggle{} - } + }} } end diff --git a/lua/wikis/commons/PrizePool/Base.lua b/lua/wikis/commons/PrizePool/Base.lua index eaf7b53eb5c..4a22cf49aeb 100644 --- a/lua/wikis/commons/PrizePool/Base.lua +++ b/lua/wikis/commons/PrizePool/Base.lua @@ -604,10 +604,10 @@ end ---@param isAward boolean? ---@return VNode function BasePrizePool:_buildHeader(isAward) - local children = {} - - table.insert(children, PrizePoolCell{children = {isAward and 'Award' or 'Place'}, css = {['min-width'] = '80px'}}) - table.insert(children, PrizePoolCell{children = {'Participant'}, classes = {'prizepooltable-col-team'}}) + local children = { + PrizePoolCell{children = {isAward and 'Award' or '#'}}, + PrizePoolCell{children = {'Participant'}}, + } local previousOfType = {} for _, prize in ipairs(self.prizes) do @@ -620,7 +620,10 @@ function BasePrizePool:_buildHeader(isAward) end end - return PrizePoolRow{classes = {'prizepooltable-header'}, css = {['font-weight'] = 'bold'}, children = children} + return PrizePoolRow{ + classes = {'prize-pool-table-header-row'}, + children = children, + } end ---@return VNode[], VNode[] diff --git a/lua/wikis/commons/Widget/PrizePool/Row.lua b/lua/wikis/commons/Widget/PrizePool/Row.lua index bebbea8c54d..cfffa59c407 100644 --- a/lua/wikis/commons/Widget/PrizePool/Row.lua +++ b/lua/wikis/commons/Widget/PrizePool/Row.lua @@ -23,7 +23,7 @@ local function PrizePoolRow(props) css['--prize-pool-row-height'] = props.height local attributes = props.attributes or {} - attributes['data-placement'] = (props.placement or 0) <= 3 and props.placement or nil + attributes['data-placement'] = props.placement return Html.Div{ classes = Array.extend( diff --git a/stylesheets/commons/Prizepooltable.scss b/stylesheets/commons/Prizepooltable.scss index 7d2c70fcbe3..9de7d3bfabb 100644 --- a/stylesheets/commons/Prizepooltable.scss +++ b/stylesheets/commons/Prizepooltable.scss @@ -365,12 +365,16 @@ Template: Prize Pool Table grid-template-columns: subgrid; > .prize-pool-toggle { - display: flex; + display: inline-block; grid-column: 1 / -1; padding: 0.5rem 0.75rem; place-self: center; width: 100%; - justify-content: center; + + > div { + position: sticky; + left: 0.75rem; + } .general-collapsible-default-toggle { min-width: unset; @@ -399,23 +403,39 @@ Template: Prize Pool Table grid-column: 1 / -1; grid-template-columns: subgrid; grid-template-rows: repeat( var( --prize-pool-row-height, 1 ), auto ); - background-color: var( --clr-on-surface-light-primary-4 ); + background-color: var( --prize-pool-placement-background-color ); + + --prize-pool-placement-background-color: var( --clr-on-surface-light-primary-4 ); + + &.prize-pool-table-header-row { + background-color: var( --clr-on-surface-light-primary-4 ); + color: var( --clr-secondary-25 ); + + .theme--dark & { + background-color: var( --clr-on-surface-dark-primary-4 ); + color: var( --clr-secondary-80 ); + } + } .theme--dark & { - background-color: var( --clr-on-surface-dark-primary-4 ); + --prize-pool-placement-background-color: var( --clr-on-surface-dark-primary-4 ); } - &:nth-of-type( even ):not( [ data-placement ] ) { - background-color: var( --clr-on-surface-light-primary-8 ); + &:nth-of-type( even ) { + --prize-pool-placement-background-color: var( --clr-on-surface-light-primary-8 ); .theme--dark & { - background-color: var( --clr-on-surface-dark-primary-8 ); + --prize-pool-placement-background-color: var( --clr-on-surface-dark-primary-8 ); } } &[ data-placement ] { border-left: 0.25rem solid var( --prize-pool-placement-color, transparent ); - background-color: var( --prize-pool-placement-background-color ); + + .generic-label.label--prize-pool-placement { + background-color: var( --prize-pool-placement-color, transparent ); + box-shadow: 0 1px 1px 0 #000000; + } } &[ data-placement="1" ] { @@ -449,16 +469,6 @@ Template: Prize Pool Table } } - & > &-row { - background-color: var( --clr-on-surface-light-primary-4 ); - color: var( --clr-secondary-25 ); - - .theme--dark & { - background-color: var( --clr-on-surface-dark-primary-4 ); - color: var( --clr-secondary-80 ); - } - } - &-cell { display: flex; grid-row: span var( --prize-pool-cell-height, 1 ); From 7c7c0751d56b04a5816a0675b113598064e04e1a Mon Sep 17 00:00:00 2001 From: ElectricalBoy <15651807+ElectricalBoy@users.noreply.github.com> Date: Tue, 23 Jun 2026 12:45:33 +0900 Subject: [PATCH 08/17] slice content switch pill --- lua/wikis/commons/Widget/ContentSwitch.lua | 58 +------------- .../commons/Widget/ContentSwitch/Pill.lua | 77 +++++++++++++++++++ 2 files changed, 81 insertions(+), 54 deletions(-) create mode 100644 lua/wikis/commons/Widget/ContentSwitch/Pill.lua diff --git a/lua/wikis/commons/Widget/ContentSwitch.lua b/lua/wikis/commons/Widget/ContentSwitch.lua index ba93ca9dbd2..d21fbe9d103 100644 --- a/lua/wikis/commons/Widget/ContentSwitch.lua +++ b/lua/wikis/commons/Widget/ContentSwitch.lua @@ -8,10 +8,10 @@ local Lua = require('Module:Lua') local Array = Lua.import('Module:Array') -local Logic = Lua.import('Module:Logic') local Component = Lua.import('Module:Widget/Component') local Html = Lua.import('Module:Widget/Html') +local SwitchPill = Lua.import('Module:Widget/ContentSwitch/Pill') local Div = Html.Div ---@class ContentSwitchTab @@ -19,15 +19,8 @@ local Div = Html.Div ---@field value? string ---@field content? Renderable|Renderable[] ----@class ContentSwitchParameters ----@field tabs ContentSwitchTab[] ----@field variant? 'themed'|'generic' ----@field defaultActive? integer ----@field switchGroup string +---@class ContentSwitchProps: ContentSwitchPillProps ---@field classes? string[] ----@field size? 'extrasmall'|'small'|'medium' ----@field storeValue? boolean ----@field css? table local defaultProps = { variant = 'generic', @@ -36,35 +29,16 @@ local defaultProps = { storeValue = true, } ----@param props ContentSwitchParameters +---@param props ContentSwitchProps ---@return Renderable|Renderable[] local function ContentSwitch(props) local tabs = assert(props.tabs, 'ContentSwitch requires at least the tabs property to be set') - local variant = props.variant local defaultActive = props.defaultActive - local switchGroup = assert(Logic.nilIfEmpty(props.switchGroup), 'ContentSwitch: missing \'switchGroup\' property') if #tabs < 2 then return (tabs[1] or {}).content end - local tabOptions = Array.map(tabs, function(tab, index) - local isActive = index == defaultActive - local classes = {'switch-pill-option', 'toggle-area-button'} - if isActive then - table.insert(classes, 'switch-pill-option-active') - end - - return Div{ - classes = classes, - attributes = { - ['data-toggle-area-btn'] = tostring(index), - ['data-switch-value'] = tab.value or tostring(index), - }, - children = Logic.emptyOr(tab.label, tostring(index)), - } - end) - local contentAreas = Array.map(tabs, function(tab, index) local isActive = index == defaultActive return Div{ @@ -76,35 +50,11 @@ local function ContentSwitch(props) } end) - local switchPillClasses = {'switch-pill'} - if variant == 'generic' then - table.insert(switchPillClasses, 'switch-pill-generic') - end - if props.size == 'small' then - table.insert(switchPillClasses, 'switch-pill-small') - elseif props.size == 'extrasmall' then - table.insert(switchPillClasses, 'switch-pill-extrasmall') - end - - return Div{ classes = {'toggle-area', 'toggle-area-' .. tostring(defaultActive)}, attributes = {['data-toggle-area'] = tostring(defaultActive)}, children = { - Div{ - classes = {'switch-pill-container'}, - css = props.css, - children = { - Div{ - classes = switchPillClasses, - attributes = { - ['data-switch-group'] = switchGroup, - ['data-store-value'] = Logic.readBool(props.storeValue) and 'true' or nil, - }, - children = tabOptions, - }, - }, - }, + SwitchPill(props), Div{ classes = {'content-switch-content-container'}, children = contentAreas, diff --git a/lua/wikis/commons/Widget/ContentSwitch/Pill.lua b/lua/wikis/commons/Widget/ContentSwitch/Pill.lua new file mode 100644 index 00000000000..19a0ff100c4 --- /dev/null +++ b/lua/wikis/commons/Widget/ContentSwitch/Pill.lua @@ -0,0 +1,77 @@ +--- +-- @Liquipedia +-- page=Module:Widget/ContentSwitch/Pill +-- +-- Please see https://github.com/Liquipedia/Lua-Modules to contribute +-- + +local Lua = require('Module:Lua') + +local Array = Lua.import('Module:Array') +local Logic = Lua.import('Module:Logic') + +local Component = Lua.import('Module:Widget/Component') +local Html = Lua.import('Module:Widget/Html') +local Div = Html.Div + +---@class ContentSwitchPillProps +---@field tabs ContentSwitchTab[] +---@field variant? 'themed'|'generic' +---@field defaultActive? integer +---@field switchGroup string +---@field size? 'extrasmall'|'small'|'medium' +---@field storeValue? boolean +---@field css? table + +---@param props ContentSwitchPillProps +---@return Renderable|Renderable[] +local function ContentSwitchPill(props) + local tabs = assert(props.tabs, 'ContentSwitch requires at least the tabs property to be set') + local variant = props.variant + local defaultActive = props.defaultActive + local switchGroup = assert(Logic.nilIfEmpty(props.switchGroup), 'ContentSwitch: missing \'switchGroup\' property') + + local tabOptions = Array.map(tabs, function(tab, index) + local isActive = index == defaultActive + local classes = {'switch-pill-option', 'toggle-area-button'} + if isActive then + table.insert(classes, 'switch-pill-option-active') + end + + return Div{ + classes = classes, + attributes = { + ['data-toggle-area-btn'] = tostring(index), + ['data-switch-value'] = tab.value or tostring(index), + }, + children = Logic.emptyOr(tab.label, tostring(index)), + } + end) + + local switchPillClasses = {'switch-pill'} + if variant == 'generic' then + table.insert(switchPillClasses, 'switch-pill-generic') + end + if props.size == 'small' then + table.insert(switchPillClasses, 'switch-pill-small') + elseif props.size == 'extrasmall' then + table.insert(switchPillClasses, 'switch-pill-extrasmall') + end + + return Div{ + classes = {'switch-pill-container'}, + css = props.css, + children = { + Div{ + classes = switchPillClasses, + attributes = { + ['data-switch-group'] = switchGroup, + ['data-store-value'] = Logic.readBool(props.storeValue) and 'true' or nil, + }, + children = tabOptions, + }, + }, + } +end + +return Component.component(ContentSwitchPill) From 1404d8ece54203b0feefb80264113a18b04a3fcd Mon Sep 17 00:00:00 2001 From: ElectricalBoy <15651807+ElectricalBoy@users.noreply.github.com> Date: Tue, 23 Jun 2026 14:27:00 +0900 Subject: [PATCH 09/17] currency toggle --- lua/wikis/commons/PrizePool/Base.lua | 96 ++++++++++++++++---- lua/wikis/commons/Widget/PrizePool/Cell.lua | 7 +- lua/wikis/commons/Widget/PrizePool/Table.lua | 35 ++++++- stylesheets/commons/Prizepooltable.scss | 14 +++ 4 files changed, 134 insertions(+), 18 deletions(-) diff --git a/lua/wikis/commons/PrizePool/Base.lua b/lua/wikis/commons/PrizePool/Base.lua index 4a22cf49aeb..3f92213f79e 100644 --- a/lua/wikis/commons/PrizePool/Base.lua +++ b/lua/wikis/commons/PrizePool/Base.lua @@ -156,7 +156,12 @@ BasePrizePool.prizeTypes = { headerDisplay = function (data) local currencyText = Currency.display(BASE_CURRENCY) - return PrizePoolCell{children = {currencyText}} + return currencyText + end, + headerParse = function (prizePool, input, context, index) + return { + currency = BASE_CURRENCY, + } end, row = BASE_CURRENCY:lower() .. 'prize', @@ -194,7 +199,7 @@ BasePrizePool.prizeTypes = { } end, headerDisplay = function (data) - return PrizePoolCell{children = {Currency.display(data.currency)}} + return Currency.display(data.currency) end, row = 'localprize', @@ -269,7 +274,7 @@ BasePrizePool.prizeTypes = { } end, headerDisplay = function (data) - return PrizePoolCell{children = {'Qualifies To'}} + return 'Qualifies To' end, row = 'qualified', @@ -342,7 +347,7 @@ BasePrizePool.prizeTypes = { table.insert(headerDisplay, text) end - return PrizePoolCell{children = {headerDisplay}} + return headerDisplay end, row = 'points', @@ -363,7 +368,7 @@ BasePrizePool.prizeTypes = { return {title = input} end, headerDisplay = function (data) - return PrizePoolCell{children = {data.title}} + return data.title end, row = 'freetext', @@ -419,7 +424,10 @@ function BasePrizePool:create() if self:_hasBaseCurrency() then self:setConfig('showBaseCurrency', true) - self:addPrize(PRIZE_TYPE_BASE_CURRENCY, 1, {roundPrecision = self.options.currencyRoundPrecision}) + self:addPrize(PRIZE_TYPE_BASE_CURRENCY, 1, { + currency = BASE_CURRENCY, + roundPrecision = self.options.currencyRoundPrecision + }) if self.options.autoExchange then local canConvertCurrency = function(prize) @@ -589,15 +597,15 @@ function BasePrizePool:_buildTable(isAward) local rows, collapsedRows = self:_buildRows() - return Div{ - css = {['overflow-x'] = 'auto'}, - children = PrizePoolTable{ + return PrizePoolTable{ prizeTypes = #self.prizes, + currencies = Array.map(self.prizes, function (prize) + return prize.data.currency + end), header = headerRow, toggle = self:getCollapsibleToggle(), displayedRows = rows, collapsedRows = collapsedRows, - }, } end @@ -610,13 +618,31 @@ function BasePrizePool:_buildHeader(isAward) } local previousOfType = {} + + local currencyIndex = 1 + 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) - previousOfType[prize.type] = cell + local header = prizeTypeData.headerDisplay(prize.data) + if prize.data.currency then + table.insert(children, PrizePoolCell{ + classes = { + currencyIndex and ( + currencyIndex == 1 and 'toggle-area-content-active' or 'toggle-area-content-inactive' + ) or nil + }, + attributes = { + ['data-toggle-area-content'] = currencyIndex, + }, + children = header, + }) + currencyIndex = currencyIndex + 1 + elseif Logic.isNotEmpty(header) then + table.insert(children, PrizePoolCell{children = header}) + previousOfType[prize.type] = true + end end end @@ -649,10 +675,39 @@ function BasePrizePool:_buildRows() } } + ---@type BasePrizePoolPrize[][] + local groupedPrizes = {} + + local currentCurrencyIndex = 1 + local currencyIndices = {} + + Array.forEach(self.prizes, function (prize, index) + if index == 1 or prize.type ~= groupedPrizes[#groupedPrizes][1].type then + if prize.data.currency and currencyIndices[prize.data.currency] == nil then + currencyIndices[prize.data.currency:lower()] = currentCurrencyIndex + currentCurrencyIndex = currentCurrencyIndex + 1 + end + table.insert(groupedPrizes, {prize}) + return + end + local prizeType = self.prizeTypes[prize.type] + if prizeType.mergeDisplayColumns then + table.insert(groupedPrizes[#groupedPrizes], prize) + else + if prize.data.currency and currencyIndices[prize.data.currency] == nil then + currencyIndices[prize.data.currency:lower()] = currentCurrencyIndex + currentCurrencyIndex = currentCurrencyIndex + 1 + end + table.insert(groupedPrizes, {prize}) + end + end) + Array.forEach( - Array.groupAdjacentBy(self.prizes, function (prize) return prize.type end), + groupedPrizes, function (prizes) - local prizeTypeData = self.prizeTypes[prizes[1].type] + local prizeType = prizes[1].type + local prizeTypeData = self.prizeTypes[prizeType] + local currency = (prizes[1].data.currency or ''):lower() local prizeDisplay = Array.map(placement.opponents, function (opponent) ---@type string[] local rewards = Array.map(prizes, function (prize) @@ -679,8 +734,17 @@ function BasePrizePool:_buildRows() }) end end + local currencyIndex = currency and currencyIndices[currency] or nil table.insert(cells, PrizePoolCell{ - classes = {'prize-cell'}, + classes = { + 'prize-cell', + currencyIndex and ( + currencyIndex == 1 and 'toggle-area-content-active' or 'toggle-area-content-inactive' + ) or nil + }, + attributes = { + ['data-toggle-area-content'] = currencyIndex, + }, children = children }) end diff --git a/lua/wikis/commons/Widget/PrizePool/Cell.lua b/lua/wikis/commons/Widget/PrizePool/Cell.lua index 8326d946e0f..fbc1ef2636f 100644 --- a/lua/wikis/commons/Widget/PrizePool/Cell.lua +++ b/lua/wikis/commons/Widget/PrizePool/Cell.lua @@ -12,10 +12,15 @@ local Array = Lua.import('Module:Array') local Component = Lua.import('Module:Widget/Component') local Html = Lua.import('Module:Widget/Html') ----@param props {classes: string[]?, children: Renderable|Renderable[]?, fullHeight: boolean?, height: integer?} +---@class PrizePoolCellProps:HtmlNodeProps +---@field fullHeight boolean? +---@field height integer? + +---@param props PrizePoolCellProps ---@return VNode local function PrizePoolCell(props) return Html.Div{ + attributes = props.attributes, classes = Array.extend( 'prize-pool-table-cell', props.classes, diff --git a/lua/wikis/commons/Widget/PrizePool/Table.lua b/lua/wikis/commons/Widget/PrizePool/Table.lua index 672feb54ac9..6dbceda0672 100644 --- a/lua/wikis/commons/Widget/PrizePool/Table.lua +++ b/lua/wikis/commons/Widget/PrizePool/Table.lua @@ -7,10 +7,14 @@ local Lua = require('Module:Lua') +local Array = Lua.import('Module:Array') +local Currency = Lua.import('Module:Currency') +local Logic = Lua.import('Module:Logic') local Table = Lua.import('Module:Table') local Component = Lua.import('Module:Widget/Component') local Html = Lua.import('Module:Widget/Html') +local SwitchPill = Lua.import('Module:Widget/ContentSwitch/Pill') local WidgetUtil = Lua.import('Module:Widget/Util') ---@class PrizePoolTableProps @@ -19,11 +23,12 @@ local WidgetUtil = Lua.import('Module:Widget/Util') ---@field displayedRows Renderable[] ---@field toggle Renderable? ---@field collapsedRows Renderable[]? +---@field currencies string[]? ---@param props PrizePoolTableProps ---@return VNode local function PrizePoolTable(props) - return Html.Div{ + local prizePoolTable = Html.Div{ classes = {'prize-pool-table'}, css = { ['--prize-pool-columns'] = props.prizeTypes + 2 @@ -33,6 +38,7 @@ local function PrizePoolTable(props) Html.Div{ classes = { 'prize-pool-table-body', + 'content-switch-content-container', 'collapsed', 'general-collapsible', }, @@ -49,6 +55,33 @@ local function PrizePoolTable(props) } } } + + if Logic.isEmpty(props.currencies) then + return prizePoolTable + end + + return Html.Div{ + classes = { + 'prize-pool-table-container', + 'toggle-area', + 'toggle-area-1', + }, + attributes = {['data-toggle-area'] = 1}, + children = { + SwitchPill{ + switchGroup = 'prize-pool-currency', + storeValue = true, + defaultActive = 1, + tabs = Array.map(props.currencies, function (currency) + return { + label = Currency.display(currency), + value = currency:lower(), + } + end) + }, + prizePoolTable + } + } end return Component.component(PrizePoolTable) diff --git a/stylesheets/commons/Prizepooltable.scss b/stylesheets/commons/Prizepooltable.scss index 9de7d3bfabb..8b61a18ce02 100644 --- a/stylesheets/commons/Prizepooltable.scss +++ b/stylesheets/commons/Prizepooltable.scss @@ -359,6 +359,16 @@ Template: Prize Pool Table color-scheme: dark; } + &-container { + display: flex; + flex-direction: column; + gap: 0.5rem; + + > .switch-pill-container { + width: fit-content; + } + } + &-body { display: grid; grid-column: 1 / -1; @@ -479,6 +489,10 @@ Template: Prize Pool Table grid-row: span var( --prize-pool-row-height, 1 ); } + &:has( > & ) { + padding: unset; + } + &.prize-cell, &.opponent-cell:has( > :nth-child( 2 ) ) { display: grid; From 62037ee5d6f404200b78e484aa0dc2311689f221 Mon Sep 17 00:00:00 2001 From: ElectricalBoy <15651807+ElectricalBoy@users.noreply.github.com> Date: Tue, 23 Jun 2026 14:27:21 +0900 Subject: [PATCH 10/17] bold header --- stylesheets/commons/Prizepooltable.scss | 1 + 1 file changed, 1 insertion(+) diff --git a/stylesheets/commons/Prizepooltable.scss b/stylesheets/commons/Prizepooltable.scss index 8b61a18ce02..03a4f644be5 100644 --- a/stylesheets/commons/Prizepooltable.scss +++ b/stylesheets/commons/Prizepooltable.scss @@ -418,6 +418,7 @@ Template: Prize Pool Table --prize-pool-placement-background-color: var( --clr-on-surface-light-primary-4 ); &.prize-pool-table-header-row { + font-weight: bold; background-color: var( --clr-on-surface-light-primary-4 ); color: var( --clr-secondary-25 ); From 16c9406fc6126f2b925ad971caec156d619d91c4 Mon Sep 17 00:00:00 2001 From: ElectricalBoy <15651807+ElectricalBoy@users.noreply.github.com> Date: Tue, 23 Jun 2026 14:49:23 +0900 Subject: [PATCH 11/17] adjust colors --- stylesheets/commons/Prizepooltable.scss | 23 +++++++++++++++++------ 1 file changed, 17 insertions(+), 6 deletions(-) diff --git a/stylesheets/commons/Prizepooltable.scss b/stylesheets/commons/Prizepooltable.scss index 03a4f644be5..200c8fe2e0e 100644 --- a/stylesheets/commons/Prizepooltable.scss +++ b/stylesheets/commons/Prizepooltable.scss @@ -415,8 +415,6 @@ Template: Prize Pool Table grid-template-rows: repeat( var( --prize-pool-row-height, 1 ), auto ); background-color: var( --prize-pool-placement-background-color ); - --prize-pool-placement-background-color: var( --clr-on-surface-light-primary-4 ); - &.prize-pool-table-header-row { font-weight: bold; background-color: var( --clr-on-surface-light-primary-4 ); @@ -428,11 +426,17 @@ Template: Prize Pool Table } } - .theme--dark & { - --prize-pool-placement-background-color: var( --clr-on-surface-dark-primary-4 ); + &:nth-of-type( even ) { + --prize-pool-placement-background-color: var( --clr-on-surface-light-primary-4 ); + --prize-pool-placement-label-color: var( --clr-secondary-25 ); + + .theme--dark & { + --prize-pool-placement-background-color: var( --clr-on-surface-dark-primary-4 ); + --prize-pool-placement-label-color: var( --clr-secondary-100 ); + } } - &:nth-of-type( even ) { + &:hover { --prize-pool-placement-background-color: var( --clr-on-surface-light-primary-8 ); .theme--dark & { @@ -445,37 +449,44 @@ Template: Prize Pool Table .generic-label.label--prize-pool-placement { background-color: var( --prize-pool-placement-color, transparent ); - box-shadow: 0 1px 1px 0 #000000; + color: var( --prize-pool-placement-label-color ); + font-weight: bold; } } &[ data-placement="1" ] { --prize-pool-placement-color: #f0b419; --prize-pool-placement-background-color: #fff9eb; + --prize-pool-placement-label-color: var( --clr-secondary-100 ); .theme--dark & { --prize-pool-placement-color: #fbcb50; --prize-pool-placement-background-color: hsl( from var( --prize-pool-placement-color ) h s l / 0.16 ); + --prize-pool-placement-label-color: var( --clr-secondary-16 ); } } &[ data-placement="2" ] { --prize-pool-placement-color: #58b5e3; --prize-pool-placement-background-color: #f0fcff; + --prize-pool-placement-label-color: var( --clr-secondary-100 ); .theme--dark & { --prize-pool-placement-color: #91d7f9; --prize-pool-placement-background-color: hsl( from var( --prize-pool-placement-color ) h s l / 0.16 ); + --prize-pool-placement-label-color: var( --clr-secondary-16 ); } } &[ data-placement="3" ] { --prize-pool-placement-color: #de7d1d; --prize-pool-placement-background-color: #fff3ec; + --prize-pool-placement-label-color: var( --clr-secondary-100 ); .theme--dark & { --prize-pool-placement-color: #ffb267; --prize-pool-placement-background-color: hsl( from var( --prize-pool-placement-color ) h s l / 0.16 ); + --prize-pool-placement-label-color: var( --clr-secondary-16 ); } } } From fdd25f57444abe7fcc38bd15c79908c34f20c091 Mon Sep 17 00:00:00 2001 From: ElectricalBoy <15651807+ElectricalBoy@users.noreply.github.com> Date: Tue, 23 Jun 2026 14:57:21 +0900 Subject: [PATCH 12/17] adjust currency check --- lua/wikis/commons/Widget/PrizePool/Table.lua | 14 +++++++++----- 1 file changed, 9 insertions(+), 5 deletions(-) diff --git a/lua/wikis/commons/Widget/PrizePool/Table.lua b/lua/wikis/commons/Widget/PrizePool/Table.lua index 6dbceda0672..48a20ccb6cc 100644 --- a/lua/wikis/commons/Widget/PrizePool/Table.lua +++ b/lua/wikis/commons/Widget/PrizePool/Table.lua @@ -28,6 +28,8 @@ local WidgetUtil = Lua.import('Module:Widget/Util') ---@param props PrizePoolTableProps ---@return VNode local function PrizePoolTable(props) + local isCollapsed = Table.isNotEmpty(props.collapsedRows) + local prizePoolTable = Html.Div{ classes = {'prize-pool-table'}, css = { @@ -36,12 +38,14 @@ local function PrizePoolTable(props) children = { props.header, Html.Div{ - classes = { + classes = Array.extend( 'prize-pool-table-body', 'content-switch-content-container', - 'collapsed', - 'general-collapsible', - }, + isCollapsed and { + 'general-collapsible', + 'collapsed', + } or nil + ), children = WidgetUtil.collect( props.displayedRows, Table.isNotEmpty(props.collapsedRows) and { @@ -56,7 +60,7 @@ local function PrizePoolTable(props) } } - if Logic.isEmpty(props.currencies) then + if Logic.isEmpty(props.currencies) or #props.currencies < 2 then return prizePoolTable end From effb3a78d3ec963f3cc6482a68149c16b976e4cc Mon Sep 17 00:00:00 2001 From: ElectricalBoy <15651807+ElectricalBoy@users.noreply.github.com> Date: Tue, 23 Jun 2026 15:11:09 +0900 Subject: [PATCH 13/17] avoid redundant toggle area --- lua/wikis/commons/PrizePool/Base.lua | 21 ++++++++++++++++++++- 1 file changed, 20 insertions(+), 1 deletion(-) diff --git a/lua/wikis/commons/PrizePool/Base.lua b/lua/wikis/commons/PrizePool/Base.lua index 3f92213f79e..236255a822d 100644 --- a/lua/wikis/commons/PrizePool/Base.lua +++ b/lua/wikis/commons/PrizePool/Base.lua @@ -621,12 +621,26 @@ function BasePrizePool:_buildHeader(isAward) local currencyIndex = 1 + local nCurrencies = Array.reduce( + self.prizes, + ---@param currencyCount integer + ---@param prize BasePrizePoolPrize + ---@return integer + function (currencyCount, prize) + if Logic.isEmpty(prize.data.currency) then + return currencyCount + end + return currencyCount + 1 + end, + 0 + ) + for _, prize in ipairs(self.prizes) do local prizeTypeData = self.prizeTypes[prize.type] if not prizeTypeData.mergeDisplayColumns or not previousOfType[prize.type] then local header = prizeTypeData.headerDisplay(prize.data) - if prize.data.currency then + if nCurrencies > 1 and prize.data.currency then table.insert(children, PrizePoolCell{ classes = { currencyIndex and ( @@ -639,6 +653,7 @@ function BasePrizePool:_buildHeader(isAward) children = header, }) currencyIndex = currencyIndex + 1 + previousOfType[prize.type] = true elseif Logic.isNotEmpty(header) then table.insert(children, PrizePoolCell{children = header}) previousOfType[prize.type] = true @@ -702,6 +717,10 @@ function BasePrizePool:_buildRows() end end) + if Table.size(currencyIndices) == 1 then + currencyIndices = {} + end + Array.forEach( groupedPrizes, function (prizes) From 60c3f2d5552a17bb2aec1271f6cf79218986533f Mon Sep 17 00:00:00 2001 From: ElectricalBoy <15651807+ElectricalBoy@users.noreply.github.com> Date: Tue, 23 Jun 2026 15:11:18 +0900 Subject: [PATCH 14/17] style adjust --- stylesheets/commons/Prizepooltable.scss | 3 +++ 1 file changed, 3 insertions(+) diff --git a/stylesheets/commons/Prizepooltable.scss b/stylesheets/commons/Prizepooltable.scss index 200c8fe2e0e..cf00e47f474 100644 --- a/stylesheets/commons/Prizepooltable.scss +++ b/stylesheets/commons/Prizepooltable.scss @@ -346,6 +346,7 @@ Template: Prize Pool Table display: grid; grid-template-columns: repeat( var( --prize-pool-columns ), auto ); overflow-x: auto; + height: fit-content; border-radius: 0.5rem; border: 1px solid var( --clr-on-surface-light-primary-12 ); background-color: var( --clr-background, #ffffff ); @@ -380,6 +381,8 @@ Template: Prize Pool Table padding: 0.5rem 0.75rem; place-self: center; width: 100%; + cursor: pointer; + font-weight: bold; > div { position: sticky; From 68c876455388bb1e60c4970255ad453e6650ce0f Mon Sep 17 00:00:00 2001 From: ElectricalBoy <15651807+ElectricalBoy@users.noreply.github.com> Date: Tue, 23 Jun 2026 15:26:08 +0900 Subject: [PATCH 15/17] update test --- lua/spec/prize_pool_spec.lua | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/lua/spec/prize_pool_spec.lua b/lua/spec/prize_pool_spec.lua index a6b871fa86b..341e6e3bbc7 100644 --- a/lua/spec/prize_pool_spec.lua +++ b/lua/spec/prize_pool_spec.lua @@ -44,7 +44,15 @@ describe('prize pool', function() assert.are_same( { - {id = 'BASE_CURRENCY1', type = 'BASE_CURRENCY', index = 1, data = {roundPrecision = 3}}, + { + id = 'BASE_CURRENCY1', + type = 'BASE_CURRENCY', + index = 1, + data = { + roundPrecision = 3, + currency = 'USD', + } + }, { id = 'LOCAL_CURRENCY1', type = 'LOCAL_CURRENCY', From 0f34b1f20920e668e5cb43749bffdbe5e885eea9 Mon Sep 17 00:00:00 2001 From: ElectricalBoy <15651807+ElectricalBoy@users.noreply.github.com> Date: Tue, 23 Jun 2026 15:26:54 +0900 Subject: [PATCH 16/17] lint --- lua/wikis/commons/PrizePool/Base.lua | 1 - lua/wikis/commons/PrizePool/Placement.lua | 1 - 2 files changed, 2 deletions(-) diff --git a/lua/wikis/commons/PrizePool/Base.lua b/lua/wikis/commons/PrizePool/Base.lua index 236255a822d..a336712b313 100644 --- a/lua/wikis/commons/PrizePool/Base.lua +++ b/lua/wikis/commons/PrizePool/Base.lua @@ -52,7 +52,6 @@ local BasePrizePool = Class.new(function(self, ...) self:init(...) end) ---@field data table local LANG = mw.language.getContentLanguage() -local DASH = '-' local NON_BREAKING_SPACE = ' ' local BASE_CURRENCY = 'USD' local EXCHANGE_SUMMARY_PRECISION = 5 diff --git a/lua/wikis/commons/PrizePool/Placement.lua b/lua/wikis/commons/PrizePool/Placement.lua index eb42b081b8d..28e53835c59 100644 --- a/lua/wikis/commons/PrizePool/Placement.lua +++ b/lua/wikis/commons/PrizePool/Placement.lua @@ -13,7 +13,6 @@ 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') From ed386a7f194c8a4c04826a2ee71ffa0f9fcc9f55 Mon Sep 17 00:00:00 2001 From: ElectricalBoy <15651807+ElectricalBoy@users.noreply.github.com> Date: Tue, 23 Jun 2026 06:29:53 +0000 Subject: [PATCH 17/17] chore: update visual snapshots --- lua/spec/snapshots/prize_pool.png | Bin 28531 -> 28291 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..91ca9bb6abaebd4b6e2b6b4e3753745668605fef 100644 GIT binary patch literal 28291 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