From 25eeb7779b070ac4b7ab905ec6da3d94c730216a Mon Sep 17 00:00:00 2001 From: ElectricalBoy <15651807+ElectricalBoy@users.noreply.github.com> Date: Fri, 27 Mar 2026 16:39:36 +0900 Subject: [PATCH 01/23] create legend widget --- lua/wikis/commons/Widget/Legend.lua | 64 +++++++++++++++++++++++++++++ stylesheets/commons/Legend.scss | 63 ++++++++++++++++++++++++++++ 2 files changed, 127 insertions(+) create mode 100644 lua/wikis/commons/Widget/Legend.lua create mode 100644 stylesheets/commons/Legend.scss diff --git a/lua/wikis/commons/Widget/Legend.lua b/lua/wikis/commons/Widget/Legend.lua new file mode 100644 index 00000000000..bcc03ecae19 --- /dev/null +++ b/lua/wikis/commons/Widget/Legend.lua @@ -0,0 +1,64 @@ +--- +-- @Liquipedia +-- page=Module:Widget/Legend +-- +-- Please see https://github.com/Liquipedia/Lua-Modules to contribute +-- + +local Lua = require('Module:Lua') + +local Array = Lua.import('Module:Array') +local Class = Lua.import('Module:Class') +local Logic = Lua.import('Module:Logic') + +local Widget = Lua.import('Module:Widget') +local ChevronToggle = Lua.import('Module:Widget/Participants/Team/ChevronToggle') +local GeneralCollapsible = Lua.import('Module:Widget/GeneralCollapsible/Default') +local HtmlWidgets = Lua.import('Module:Widget/Html/All') +local Icon = Lua.import('Module:Widget/Image/Icon/Fontawesome') + +---@class LegendWidget: Widget +---@operator call(table): LegendWidget +local LegendWidget = Class.new(Widget) + +---@return Renderable|Renderable[]? +function LegendWidget:render() + return GeneralCollapsible{ + shouldCollapse = true, + collapseAreaClasses = {}, + classes = {'legend'}, + titleWidget = self:_createHeader(), + children = Array.mapIndexes(function (index) + local sectionContent = self.props['section' .. index] + if Logic.isEmpty(sectionContent) then + return + end + return HtmlWidgets.Div{ + classes = {'legend-section'}, + children = self.props['section' .. index] + } + end) + } +end + +---@private +---@return Widget +function LegendWidget:_createHeader() + return HtmlWidgets.Div{ + classes = {'legend-header'}, + attributes = {['data-collapsible-click-region'] = 'true'}, + children = { + HtmlWidgets.Div{ + classes = {}, + children = { + Icon{iconName = 'general-info'}, + ' Legend' + } + }, + ChevronToggle{} + } + } +end + +return LegendWidget + diff --git a/stylesheets/commons/Legend.scss b/stylesheets/commons/Legend.scss new file mode 100644 index 00000000000..33610b464fb --- /dev/null +++ b/stylesheets/commons/Legend.scss @@ -0,0 +1,63 @@ +.legend { + display: flex; + flex-direction: column; + gap: 0.5rem; + min-width: 200px; + width: fit-content; + max-width: 100%; + border: 0.25rem var( --clr-on-surface-light-primary-12 ); + border-radius: 0.5rem; + padding: 0.5rem; + background-color: var( --clr-primary-100 ); + font-size: 0.75rem; + + .theme--dark & { + border-color: var( --clr-on-surface-dark-primary-8 ); + background-color: var( --clr-on-surface-dark-primary-8 ); + } + + &-header { + display: grid; + grid-template-columns: repeat( 2, auto ); + align-items: center; + align-content: center; + gap: 0.25rem; + font-weight: bold; + color: var( --clr-secondary-16 ); + + .theme--dark & { + color: var( --clr-secondary-90 ); + } + + .general-collapsible-default-toggle { + min-width: unset; + } + + .general-collapsible-expand-button, + .general-collapsible-collapse-button { + outline: unset; + color: var( --clr-secondary-7 ); + + .theme--dark & { + color: var( --clr-secondary-90 ); + } + } + } + + &-section { + & + &::before { + display: block; + content: ""; + width: 100%; + border: 0; + border-top: 0.125rem solid var( --clr-on-surface-light-primary-8 ); + padding: 0; + position: relative; + top: -0.25rem; + + .theme--dark & { + border-color: var( --clr-on-surface-dark-primary-8 ); + } + } + } +} From 09f407b96a45e65dbb5f6880a3a81c6d91e276ce Mon Sep 17 00:00:00 2001 From: ElectricalBoy <15651807+ElectricalBoy@users.noreply.github.com> Date: Fri, 27 Mar 2026 18:01:29 +0900 Subject: [PATCH 02/23] use same flex container --- stylesheets/commons/Legend.scss | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/stylesheets/commons/Legend.scss b/stylesheets/commons/Legend.scss index 33610b464fb..0aefbe02792 100644 --- a/stylesheets/commons/Legend.scss +++ b/stylesheets/commons/Legend.scss @@ -60,4 +60,8 @@ } } } + + &:not( .collapsed ) .should-collapse { + display: contents; + } } From 5aa89275422b9565d5492945ff5bb8e308727d77 Mon Sep 17 00:00:00 2001 From: ElectricalBoy <15651807+ElectricalBoy@users.noreply.github.com> Date: Thu, 2 Apr 2026 15:43:40 +0900 Subject: [PATCH 03/23] add mobile breakpoint --- stylesheets/commons/Legend.scss | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/stylesheets/commons/Legend.scss b/stylesheets/commons/Legend.scss index 0aefbe02792..c5c54e855d0 100644 --- a/stylesheets/commons/Legend.scss +++ b/stylesheets/commons/Legend.scss @@ -16,6 +16,10 @@ background-color: var( --clr-on-surface-dark-primary-8 ); } + @media ( max-width: 435px ) { + width: 100% !important; + } + &-header { display: grid; grid-template-columns: repeat( 2, auto ); From 163c1f6cf7e82c00c19c4b24e26535f4bad1da24 Mon Sep 17 00:00:00 2001 From: ElectricalBoy <15651807+ElectricalBoy@users.noreply.github.com> Date: Fri, 8 May 2026 11:29:35 +0900 Subject: [PATCH 04/23] update min width --- stylesheets/commons/Legend.scss | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) diff --git a/stylesheets/commons/Legend.scss b/stylesheets/commons/Legend.scss index c5c54e855d0..dd39a0dfe52 100644 --- a/stylesheets/commons/Legend.scss +++ b/stylesheets/commons/Legend.scss @@ -2,7 +2,7 @@ display: flex; flex-direction: column; gap: 0.5rem; - min-width: 200px; + min-width: 16.5rem; width: fit-content; max-width: 100%; border: 0.25rem var( --clr-on-surface-light-primary-12 ); @@ -49,6 +49,10 @@ } &-section { + display: grid; + grid-template-columns: min-content auto; + column-gap: 0.25rem; + & + &::before { display: block; content: ""; @@ -65,6 +69,12 @@ } } + &-item { + display: grid; + grid-column: 1 / -1; + grid-template-columns: subgrid; + } + &:not( .collapsed ) .should-collapse { display: contents; } From 5a92f6f8cf4fe869e4ac2f84a3070178ac9454f8 Mon Sep 17 00:00:00 2001 From: ElectricalBoy <15651807+ElectricalBoy@users.noreply.github.com> Date: Fri, 8 May 2026 11:29:41 +0900 Subject: [PATCH 05/23] fix border --- stylesheets/commons/Legend.scss | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/stylesheets/commons/Legend.scss b/stylesheets/commons/Legend.scss index dd39a0dfe52..6343bcfc848 100644 --- a/stylesheets/commons/Legend.scss +++ b/stylesheets/commons/Legend.scss @@ -5,7 +5,7 @@ min-width: 16.5rem; width: fit-content; max-width: 100%; - border: 0.25rem var( --clr-on-surface-light-primary-12 ); + border: 1px solid var( --clr-on-surface-light-primary-12 ); border-radius: 0.5rem; padding: 0.5rem; background-color: var( --clr-primary-100 ); From c9aa520010d6497f67bb0fcfa6e42059d0c7bedf Mon Sep 17 00:00:00 2001 From: ElectricalBoy <15651807+ElectricalBoy@users.noreply.github.com> Date: Fri, 8 May 2026 11:31:24 +0900 Subject: [PATCH 06/23] add color section --- lua/wikis/commons/Widget/Legend.lua | 45 ++++++++++++++++++++++------- stylesheets/commons/Colours.scss | 28 ++++++++++++++++++ 2 files changed, 63 insertions(+), 10 deletions(-) diff --git a/lua/wikis/commons/Widget/Legend.lua b/lua/wikis/commons/Widget/Legend.lua index bcc03ecae19..59fb2e9dd86 100644 --- a/lua/wikis/commons/Widget/Legend.lua +++ b/lua/wikis/commons/Widget/Legend.lua @@ -9,6 +9,7 @@ local Lua = require('Module:Lua') local Array = Lua.import('Module:Array') local Class = Lua.import('Module:Class') +local Json = Lua.import('Module:Json') local Logic = Lua.import('Module:Logic') local Widget = Lua.import('Module:Widget') @@ -16,6 +17,9 @@ local ChevronToggle = Lua.import('Module:Widget/Participants/Team/ChevronToggle' local GeneralCollapsible = Lua.import('Module:Widget/GeneralCollapsible/Default') local HtmlWidgets = Lua.import('Module:Widget/Html/All') local Icon = Lua.import('Module:Widget/Image/Icon/Fontawesome') +local WidgetUtil = Lua.import('Module:Widget/Util') + +local LABEL_COLORS = {'byeup', 'seedup', 'up', 'stayup', 'stay', 'staydown', 'down'} ---@class LegendWidget: Widget ---@operator call(table): LegendWidget @@ -28,16 +32,9 @@ function LegendWidget:render() collapseAreaClasses = {}, classes = {'legend'}, titleWidget = self:_createHeader(), - children = Array.mapIndexes(function (index) - local sectionContent = self.props['section' .. index] - if Logic.isEmpty(sectionContent) then - return - end - return HtmlWidgets.Div{ - classes = {'legend-section'}, - children = self.props['section' .. index] - } - end) + children = WidgetUtil.collect( + self:_createColorSection() + ) } end @@ -60,5 +57,33 @@ function LegendWidget:_createHeader() } end +---@private +---@return Widget? +function LegendWidget:_createColorSection() + local sectionData = Json.parseIfString(self.props.color) + if Logic.isEmpty(sectionData) then + return + end + return HtmlWidgets.Div{ + classes = {'legend-section'}, + children = Array.map(LABEL_COLORS, function (labelColor) + local labelText = sectionData[labelColor] + if Logic.isEmpty(labelText) then + return + end + return HtmlWidgets.Div{ + classes = {'legend-item'}, + children = { + HtmlWidgets.Span{ + classes = {labelColor .. '-text'}, + children = '●' + }, + HtmlWidgets.Span{children = labelText} + } + } + end) + } +end + return LegendWidget diff --git a/stylesheets/commons/Colours.scss b/stylesheets/commons/Colours.scss index ff7329e0417..e4557b50923 100644 --- a/stylesheets/commons/Colours.scss +++ b/stylesheets/commons/Colours.scss @@ -775,3 +775,31 @@ ul.nav-tabs li.game-wiiu, --position-staydown-color: #f2a288; --position-down-color: #fc6868; } + +.byeup-text { + color: var( --position-byeup-color ); +} + +.seedup-text { + color: var( --position-seedup-color ); +} + +.up-text { + color: var( --position-up-color ); +} + +.stayup-text { + color: var( --position-stayup-color ); +} + +.stay-text { + color: var( --position-stay-color ); +} + +.staydown-text { + color: var( --position-staydown-color ); +} + +.down-text { + color: var( --position-down-color ); +} From cf971f307680b6a90aecd408ed36a1b9293fb82f Mon Sep 17 00:00:00 2001 From: ElectricalBoy <15651807+ElectricalBoy@users.noreply.github.com> Date: Fri, 8 May 2026 11:49:27 +0900 Subject: [PATCH 07/23] adjust title impl --- lua/wikis/commons/Widget/Legend.lua | 14 +++++++------- stylesheets/commons/Legend.scss | 26 ++++++++++++++++++-------- 2 files changed, 25 insertions(+), 15 deletions(-) diff --git a/lua/wikis/commons/Widget/Legend.lua b/lua/wikis/commons/Widget/Legend.lua index 59fb2e9dd86..0926d16392f 100644 --- a/lua/wikis/commons/Widget/Legend.lua +++ b/lua/wikis/commons/Widget/Legend.lua @@ -24,6 +24,9 @@ local LABEL_COLORS = {'byeup', 'seedup', 'up', 'stayup', 'stay', 'staydown', 'do ---@class LegendWidget: Widget ---@operator call(table): LegendWidget local LegendWidget = Class.new(Widget) +LegendWidget.defaultProps = { + title = 'Legend', +} ---@return Renderable|Renderable[]? function LegendWidget:render() @@ -45,13 +48,10 @@ function LegendWidget:_createHeader() classes = {'legend-header'}, attributes = {['data-collapsible-click-region'] = 'true'}, children = { - HtmlWidgets.Div{ - classes = {}, - children = { - Icon{iconName = 'general-info'}, - ' Legend' - } - }, + HtmlWidgets.Div{children = { + Icon{iconName = 'general-info'}, + HtmlWidgets.Span{children = self.props.title} + }}, ChevronToggle{} } } diff --git a/stylesheets/commons/Legend.scss b/stylesheets/commons/Legend.scss index 6343bcfc848..907309d0ac0 100644 --- a/stylesheets/commons/Legend.scss +++ b/stylesheets/commons/Legend.scss @@ -22,7 +22,7 @@ &-header { display: grid; - grid-template-columns: repeat( 2, auto ); + grid-template-columns: min-content auto min-content; align-items: center; align-content: center; gap: 0.25rem; @@ -33,17 +33,27 @@ color: var( --clr-secondary-90 ); } + > :first-child { + grid-column: 1 / -2; + display: grid; + grid-template-columns: subgrid; + margin-left: 0.5rem; + align-items: center; + } + .general-collapsible-default-toggle { min-width: unset; - } - .general-collapsible-expand-button, - .general-collapsible-collapse-button { - outline: unset; - color: var( --clr-secondary-7 ); + .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 ); + .theme--dark & { + color: var( --clr-secondary-90 ); + } } } } From 97e7b427456088a6dabddd7bc531692d5e79625f Mon Sep 17 00:00:00 2001 From: ElectricalBoy <15651807+ElectricalBoy@users.noreply.github.com> Date: Fri, 8 May 2026 11:57:51 +0900 Subject: [PATCH 08/23] fix section divider --- stylesheets/commons/Legend.scss | 1 + 1 file changed, 1 insertion(+) diff --git a/stylesheets/commons/Legend.scss b/stylesheets/commons/Legend.scss index 907309d0ac0..e6ac5236354 100644 --- a/stylesheets/commons/Legend.scss +++ b/stylesheets/commons/Legend.scss @@ -65,6 +65,7 @@ & + &::before { display: block; + grid-column: 1 / -1; content: ""; width: 100%; border: 0; From 4cef64acb867a85dabc78ad7ce4e28d1f4aa1b3a Mon Sep 17 00:00:00 2001 From: ElectricalBoy <15651807+ElectricalBoy@users.noreply.github.com> Date: Fri, 8 May 2026 11:58:44 +0900 Subject: [PATCH 09/23] add points section --- lua/wikis/commons/Widget/Legend.lua | 29 ++++++++++++++++++++++++++++- 1 file changed, 28 insertions(+), 1 deletion(-) diff --git a/lua/wikis/commons/Widget/Legend.lua b/lua/wikis/commons/Widget/Legend.lua index 0926d16392f..11f17c731b2 100644 --- a/lua/wikis/commons/Widget/Legend.lua +++ b/lua/wikis/commons/Widget/Legend.lua @@ -36,7 +36,8 @@ function LegendWidget:render() classes = {'legend'}, titleWidget = self:_createHeader(), children = WidgetUtil.collect( - self:_createColorSection() + self:_createColorSection(), + self:_createPointsSection() ) } end @@ -85,5 +86,31 @@ function LegendWidget:_createColorSection() } end +---@private +---@return Widget? +function LegendWidget:_createPointsSection() + local sectionData = Json.parseIfString(self.props.points) + if not sectionData then + return + end + local pointsText = sectionData[1] or sectionData.points + if Logic.isEmpty(pointsText) then + return + end + return HtmlWidgets.Div{ + classes = {'legend-section'}, + children = HtmlWidgets.Div{ + classes = {'legend-item'}, + children = { + HtmlWidgets.Span{ + css = {['font-weight'] = 'bold'}, + children = 'Pts' + }, + HtmlWidgets.Span{children = pointsText} + } + } + } +end + return LegendWidget From 7d17fb3ccaf93f3fe271b3aefb3607953c433237 Mon Sep 17 00:00:00 2001 From: ElectricalBoy <15651807+ElectricalBoy@users.noreply.github.com> Date: Fri, 8 May 2026 12:03:17 +0900 Subject: [PATCH 10/23] better color section handle --- lua/wikis/commons/Widget/Legend.lua | 38 +++++++++++++++++------------ 1 file changed, 22 insertions(+), 16 deletions(-) diff --git a/lua/wikis/commons/Widget/Legend.lua b/lua/wikis/commons/Widget/Legend.lua index 11f17c731b2..20d910c8b8f 100644 --- a/lua/wikis/commons/Widget/Legend.lua +++ b/lua/wikis/commons/Widget/Legend.lua @@ -65,24 +65,30 @@ function LegendWidget:_createColorSection() if Logic.isEmpty(sectionData) then return end + local labels = Array.map(LABEL_COLORS, function (labelColor) + local labelText = sectionData[labelColor] + if Logic.isEmpty(labelText) then + return + end + return HtmlWidgets.Div{ + classes = {'legend-item'}, + children = { + HtmlWidgets.Span{ + classes = {labelColor .. '-text'}, + children = '●' + }, + HtmlWidgets.Span{children = labelText} + } + } + end) + + if Logic.isEmpty(labels) then + return + end + return HtmlWidgets.Div{ classes = {'legend-section'}, - children = Array.map(LABEL_COLORS, function (labelColor) - local labelText = sectionData[labelColor] - if Logic.isEmpty(labelText) then - return - end - return HtmlWidgets.Div{ - classes = {'legend-item'}, - children = { - HtmlWidgets.Span{ - classes = {labelColor .. '-text'}, - children = '●' - }, - HtmlWidgets.Span{children = labelText} - } - } - end) + children = labels } end From 1c1027ab42bad82ed3e5faea8980bc617de5e627 Mon Sep 17 00:00:00 2001 From: ElectricalBoy <15651807+ElectricalBoy@users.noreply.github.com> Date: Fri, 8 May 2026 12:58:50 +0900 Subject: [PATCH 11/23] add number section --- lua/wikis/commons/Widget/Legend.lua | 60 ++++++++++++++++++++++++++++- stylesheets/commons/Label.scss | 40 ++++++++++++++++++- 2 files changed, 97 insertions(+), 3 deletions(-) diff --git a/lua/wikis/commons/Widget/Legend.lua b/lua/wikis/commons/Widget/Legend.lua index 20d910c8b8f..b892d20d0c9 100644 --- a/lua/wikis/commons/Widget/Legend.lua +++ b/lua/wikis/commons/Widget/Legend.lua @@ -17,6 +17,7 @@ local ChevronToggle = Lua.import('Module:Widget/Participants/Team/ChevronToggle' local GeneralCollapsible = Lua.import('Module:Widget/GeneralCollapsible/Default') local HtmlWidgets = Lua.import('Module:Widget/Html/All') local Icon = Lua.import('Module:Widget/Image/Icon/Fontawesome') +local LabelWidget = Lua.import('Module:Widget/Basic/Label') local WidgetUtil = Lua.import('Module:Widget/Util') local LABEL_COLORS = {'byeup', 'seedup', 'up', 'stayup', 'stay', 'staydown', 'down'} @@ -26,6 +27,8 @@ local LABEL_COLORS = {'byeup', 'seedup', 'up', 'stayup', 'stay', 'staydown', 'do local LegendWidget = Class.new(Widget) LegendWidget.defaultProps = { title = 'Legend', + showConfirmed = true, + showUndecided = true, } ---@return Renderable|Renderable[]? @@ -37,7 +40,8 @@ function LegendWidget:render() titleWidget = self:_createHeader(), children = WidgetUtil.collect( self:_createColorSection(), - self:_createPointsSection() + self:_createPointsSection(), + self:_createNumberSection() ) } end @@ -118,5 +122,59 @@ function LegendWidget:_createPointsSection() } end +---@private +---@return Widget? +function LegendWidget:_createNumberSection() + local props = self.props + if not Logic.readBool(props.showNumberSection) then + return + end + + local labels = WidgetUtil.collect( + Logic.readBool(props.showConfirmed) and HtmlWidgets.Div{ + classes = {'legend-item'}, + children = { + LabelWidget{ + labelScheme = 'placement', + labelType = 'legend-confirmed', + children = 1 + }, + HtmlWidgets.Span{children = 'Placement confirmed'} + } + } or nil, + Logic.readBool(props.showMinimum) and HtmlWidgets.Div{ + classes = {'legend-item'}, + children = { + LabelWidget{ + labelScheme = 'placement', + labelType = 'legend-minimum', + children = 1 + }, + HtmlWidgets.Span{children = 'Minimum placement reached'} + } + } or nil, + Logic.readBool(props.showUndecided) and HtmlWidgets.Div{ + classes = {'legend-item'}, + children = { + LabelWidget{ + labelScheme = 'placement', + labelType = 'legend-undecided', + children = 1 + }, + HtmlWidgets.Span{children = 'Placement undecided'} + } + } or nil + ) + + if Logic.isEmpty(labels) then + return + end + + return HtmlWidgets.Div{ + classes = {'legend-section'}, + children = labels + } +end + return LegendWidget diff --git a/stylesheets/commons/Label.scss b/stylesheets/commons/Label.scss index 7d20ee36f66..c3764f97f94 100644 --- a/stylesheets/commons/Label.scss +++ b/stylesheets/commons/Label.scss @@ -119,7 +119,7 @@ --placement-text-color: var( --clr-secondary-9 ); } - &:not( [ data-placement-type ] ) { + &:not( [ data-placement-type ] ):not( [ data-label-type|="legend" ] ) { --placement-solid-color: var( --clr-on-surface-light-primary-4 ); --placement-text-color: var( --clr-secondary-25 ); @@ -132,7 +132,43 @@ &[ data-label-type="placement-minimum" ] { color: var( --placement-solid-color ); background-color: hsl( from var( --placement-solid-color ) h s l / 0.08 ); - box-shadow: 0 0 0 calc( var( --label-scale ) * 0.0125rem ) var( --placement-solid-color ) inset; + box-shadow: 0 0 0 calc( var( --label-scale ) * 0.0625rem ) var( --placement-solid-color ) inset; + } + + &[ data-label-type|="legend" ] { + --label-scale: 0.75; + } + + &[ data-label-type="legend-confirmed" ] { + --placement-solid-color: var( --clr-secondary-25 ); + --placement-text-color: var( --clr-secondary-100 ); + + .theme--dark & { + --placement-solid-color: var( --clr-secondary-90 ); + --placement-text-color: var( --clr-secondary-25 ); + } + } + + &[ data-label-type="legend-minimum" ] { + color: var( --clr-secondary-25 ); + background-color: var( --clr-on-surface-light-primary-12 ); + box-shadow: 0 0 0 0.0625rem var( --clr-secondary-25 ) inset; + + .theme--dark & { + color: var( --clr-secondary-100 ); + background-color: var( --clr-on-surface-dark-primary-12 ); + box-shadow: 0 0 0 0.0625rem var( --clr-secondary-80 ) inset; + } + } + + &[ data-label-type="legend-undecided" ] { + --placement-solid-color: var( --clr-on-surface-light-primary-12 ); + --placement-text-color: var( --clr-secondary-25 ); + + .theme--dark & { + --placement-solid-color: var( --clr-on-surface-dark-primary-12 ); + --placement-text-color: var( --clr-secondary-100 ); + } } &[ data-placement-type="byeup" ] { From ace72dbf65f643cc63157aa905f2fd26294fa1f4 Mon Sep 17 00:00:00 2001 From: ElectricalBoy <15651807+ElectricalBoy@users.noreply.github.com> Date: Fri, 8 May 2026 13:04:35 +0900 Subject: [PATCH 12/23] add Legend.scss to main.scss --- stylesheets/Main.scss | 1 + 1 file changed, 1 insertion(+) diff --git a/stylesheets/Main.scss b/stylesheets/Main.scss index 6b492cf170f..8dda35a0607 100644 --- a/stylesheets/Main.scss +++ b/stylesheets/Main.scss @@ -29,6 +29,7 @@ @use "commons/Jquery"; @use "commons/Dialog"; @use "commons/Label"; +@use "commons/Legend"; @use "commons/Mainpage"; @use "commons/Matchseries"; @use "commons/MatchTable"; From 7f56e1de82536e04a3c9838f8a532270f567dc58 Mon Sep 17 00:00:00 2001 From: ElectricalBoy <15651807+ElectricalBoy@users.noreply.github.com> Date: Fri, 8 May 2026 13:09:22 +0900 Subject: [PATCH 13/23] switch internal components to widget3 --- lua/wikis/commons/Widget/Legend.lua | 50 ++++++++++++++--------------- 1 file changed, 25 insertions(+), 25 deletions(-) diff --git a/lua/wikis/commons/Widget/Legend.lua b/lua/wikis/commons/Widget/Legend.lua index b892d20d0c9..2e56a4a4646 100644 --- a/lua/wikis/commons/Widget/Legend.lua +++ b/lua/wikis/commons/Widget/Legend.lua @@ -15,7 +15,7 @@ local Logic = Lua.import('Module:Logic') local Widget = Lua.import('Module:Widget') local ChevronToggle = Lua.import('Module:Widget/Participants/Team/ChevronToggle') local GeneralCollapsible = Lua.import('Module:Widget/GeneralCollapsible/Default') -local HtmlWidgets = Lua.import('Module:Widget/Html/All') +local Html = Lua.import('Module:Widget/Html') local Icon = Lua.import('Module:Widget/Image/Icon/Fontawesome') local LabelWidget = Lua.import('Module:Widget/Basic/Label') local WidgetUtil = Lua.import('Module:Widget/Util') @@ -47,15 +47,15 @@ function LegendWidget:render() end ---@private ----@return Widget +---@return VNode function LegendWidget:_createHeader() - return HtmlWidgets.Div{ + return Html.Div{ classes = {'legend-header'}, attributes = {['data-collapsible-click-region'] = 'true'}, children = { - HtmlWidgets.Div{children = { + Html.Div{children = { Icon{iconName = 'general-info'}, - HtmlWidgets.Span{children = self.props.title} + Html.Span{children = self.props.title} }}, ChevronToggle{} } @@ -63,7 +63,7 @@ function LegendWidget:_createHeader() end ---@private ----@return Widget? +---@return VNode? function LegendWidget:_createColorSection() local sectionData = Json.parseIfString(self.props.color) if Logic.isEmpty(sectionData) then @@ -74,14 +74,14 @@ function LegendWidget:_createColorSection() if Logic.isEmpty(labelText) then return end - return HtmlWidgets.Div{ + return Html.Div{ classes = {'legend-item'}, children = { - HtmlWidgets.Span{ + Html.Span{ classes = {labelColor .. '-text'}, - children = '●' + children = {'●'} }, - HtmlWidgets.Span{children = labelText} + Html.Span{children = labelText} } } end) @@ -90,14 +90,14 @@ function LegendWidget:_createColorSection() return end - return HtmlWidgets.Div{ + return Html.Div{ classes = {'legend-section'}, children = labels } end ---@private ----@return Widget? +---@return VNode? function LegendWidget:_createPointsSection() local sectionData = Json.parseIfString(self.props.points) if not sectionData then @@ -107,23 +107,23 @@ function LegendWidget:_createPointsSection() if Logic.isEmpty(pointsText) then return end - return HtmlWidgets.Div{ + return Html.Div{ classes = {'legend-section'}, - children = HtmlWidgets.Div{ + children = Html.Div{ classes = {'legend-item'}, children = { - HtmlWidgets.Span{ + Html.Span{ css = {['font-weight'] = 'bold'}, - children = 'Pts' + children = {'Pts'} }, - HtmlWidgets.Span{children = pointsText} + Html.Span{children = pointsText} } } } end ---@private ----@return Widget? +---@return VNode? function LegendWidget:_createNumberSection() local props = self.props if not Logic.readBool(props.showNumberSection) then @@ -131,7 +131,7 @@ function LegendWidget:_createNumberSection() end local labels = WidgetUtil.collect( - Logic.readBool(props.showConfirmed) and HtmlWidgets.Div{ + Logic.readBool(props.showConfirmed) and Html.Div{ classes = {'legend-item'}, children = { LabelWidget{ @@ -139,10 +139,10 @@ function LegendWidget:_createNumberSection() labelType = 'legend-confirmed', children = 1 }, - HtmlWidgets.Span{children = 'Placement confirmed'} + Html.Span{children = {'Placement confirmed'}} } } or nil, - Logic.readBool(props.showMinimum) and HtmlWidgets.Div{ + Logic.readBool(props.showMinimum) and Html.Div{ classes = {'legend-item'}, children = { LabelWidget{ @@ -150,10 +150,10 @@ function LegendWidget:_createNumberSection() labelType = 'legend-minimum', children = 1 }, - HtmlWidgets.Span{children = 'Minimum placement reached'} + Html.Span{children = {'Minimum placement reached'}} } } or nil, - Logic.readBool(props.showUndecided) and HtmlWidgets.Div{ + Logic.readBool(props.showUndecided) and Html.Div{ classes = {'legend-item'}, children = { LabelWidget{ @@ -161,7 +161,7 @@ function LegendWidget:_createNumberSection() labelType = 'legend-undecided', children = 1 }, - HtmlWidgets.Span{children = 'Placement undecided'} + Html.Span{children = {'Placement undecided'}} } } or nil ) @@ -170,7 +170,7 @@ function LegendWidget:_createNumberSection() return end - return HtmlWidgets.Div{ + return Html.Div{ classes = {'legend-section'}, children = labels } From b519ba4d509dd35a590a84127274cb0691f3aae1 Mon Sep 17 00:00:00 2001 From: ElectricalBoy <15651807+ElectricalBoy@users.noreply.github.com> Date: Fri, 8 May 2026 13:56:16 +0900 Subject: [PATCH 14/23] move the rest to widget3 --- lua/wikis/commons/Widget/Legend.lua | 54 +++++++++++++++-------------- 1 file changed, 28 insertions(+), 26 deletions(-) diff --git a/lua/wikis/commons/Widget/Legend.lua b/lua/wikis/commons/Widget/Legend.lua index 2e56a4a4646..005771907c6 100644 --- a/lua/wikis/commons/Widget/Legend.lua +++ b/lua/wikis/commons/Widget/Legend.lua @@ -8,11 +8,10 @@ local Lua = require('Module:Lua') local Array = Lua.import('Module:Array') -local Class = Lua.import('Module:Class') local Json = Lua.import('Module:Json') local Logic = Lua.import('Module:Logic') -local Widget = Lua.import('Module:Widget') +local Component = Lua.import('Module:Widget/Component') local ChevronToggle = Lua.import('Module:Widget/Participants/Team/ChevronToggle') local GeneralCollapsible = Lua.import('Module:Widget/GeneralCollapsible/Default') local Html = Lua.import('Module:Widget/Html') @@ -22,40 +21,36 @@ local WidgetUtil = Lua.import('Module:Widget/Util') local LABEL_COLORS = {'byeup', 'seedup', 'up', 'stayup', 'stay', 'staydown', 'down'} ----@class LegendWidget: Widget ----@operator call(table): LegendWidget -local LegendWidget = Class.new(Widget) -LegendWidget.defaultProps = { - title = 'Legend', - showConfirmed = true, - showUndecided = true, -} - ----@return Renderable|Renderable[]? -function LegendWidget:render() +---@class LegendComponent +local LegendComponent = {} + +---@param props table +---@return Widget +function LegendComponent.render(props) + mw.logObject(props, props) return GeneralCollapsible{ shouldCollapse = true, collapseAreaClasses = {}, classes = {'legend'}, - titleWidget = self:_createHeader(), + titleWidget = LegendComponent._createHeader(props), children = WidgetUtil.collect( - self:_createColorSection(), - self:_createPointsSection(), - self:_createNumberSection() + LegendComponent._createColorSection(props), + LegendComponent._createPointsSection(props), + LegendComponent._createNumberSection(props) ) } end ---@private ---@return VNode -function LegendWidget:_createHeader() +function LegendComponent._createHeader(props) return Html.Div{ classes = {'legend-header'}, attributes = {['data-collapsible-click-region'] = 'true'}, children = { Html.Div{children = { Icon{iconName = 'general-info'}, - Html.Span{children = self.props.title} + Html.Span{children = props.title} }}, ChevronToggle{} } @@ -64,8 +59,9 @@ end ---@private ---@return VNode? -function LegendWidget:_createColorSection() - local sectionData = Json.parseIfString(self.props.color) +function LegendComponent._createColorSection(props) + mw.log('LegendComponent._createColorSection') + local sectionData = Json.parseIfString(props.color) if Logic.isEmpty(sectionData) then return end @@ -98,8 +94,8 @@ end ---@private ---@return VNode? -function LegendWidget:_createPointsSection() - local sectionData = Json.parseIfString(self.props.points) +function LegendComponent._createPointsSection(props) + local sectionData = Json.parseIfString(props.points) if not sectionData then return end @@ -124,8 +120,7 @@ end ---@private ---@return VNode? -function LegendWidget:_createNumberSection() - local props = self.props +function LegendComponent._createNumberSection(props) if not Logic.readBool(props.showNumberSection) then return end @@ -176,5 +171,12 @@ function LegendWidget:_createNumberSection() } end -return LegendWidget +return Component.component( + LegendComponent.render, + { + title = 'Legend', + showConfirmed = true, + showUndecided = true, + } +) From c357b877595429448f3ffe4c09920067ce78d839 Mon Sep 17 00:00:00 2001 From: ElectricalBoy <15651807+ElectricalBoy@users.noreply.github.com> Date: Fri, 8 May 2026 13:58:22 +0900 Subject: [PATCH 15/23] extract default props --- lua/wikis/commons/Widget/Legend.lua | 15 ++++++--------- 1 file changed, 6 insertions(+), 9 deletions(-) diff --git a/lua/wikis/commons/Widget/Legend.lua b/lua/wikis/commons/Widget/Legend.lua index 005771907c6..1a3145adb97 100644 --- a/lua/wikis/commons/Widget/Legend.lua +++ b/lua/wikis/commons/Widget/Legend.lua @@ -23,6 +23,11 @@ local LABEL_COLORS = {'byeup', 'seedup', 'up', 'stayup', 'stay', 'staydown', 'do ---@class LegendComponent local LegendComponent = {} +LegendComponent.defaultProps = { + title = 'Legend', + showConfirmed = true, + showUndecided = true, +} ---@param props table ---@return Widget @@ -171,12 +176,4 @@ function LegendComponent._createNumberSection(props) } end -return Component.component( - LegendComponent.render, - { - title = 'Legend', - showConfirmed = true, - showUndecided = true, - } -) - +return Component.component(LegendComponent.render, LegendComponent.defaultProps) From 4e5b31cbd3b2e799c9ce86edf92f2fbd44e09a98 Mon Sep 17 00:00:00 2001 From: ElectricalBoy <15651807+ElectricalBoy@users.noreply.github.com> Date: Fri, 8 May 2026 14:04:12 +0900 Subject: [PATCH 16/23] workaround --- lua/wikis/commons/Widget/Legend.lua | 18 ++++++++++++------ 1 file changed, 12 insertions(+), 6 deletions(-) diff --git a/lua/wikis/commons/Widget/Legend.lua b/lua/wikis/commons/Widget/Legend.lua index 1a3145adb97..aa6e43144d4 100644 --- a/lua/wikis/commons/Widget/Legend.lua +++ b/lua/wikis/commons/Widget/Legend.lua @@ -23,6 +23,8 @@ local LABEL_COLORS = {'byeup', 'seedup', 'up', 'stayup', 'stay', 'staydown', 'do ---@class LegendComponent local LegendComponent = {} + +--TODO: pass defaultProps directly to Component.component LegendComponent.defaultProps = { title = 'Legend', showConfirmed = true, @@ -32,7 +34,6 @@ LegendComponent.defaultProps = { ---@param props table ---@return Widget function LegendComponent.render(props) - mw.logObject(props, props) return GeneralCollapsible{ shouldCollapse = true, collapseAreaClasses = {}, @@ -55,7 +56,7 @@ function LegendComponent._createHeader(props) children = { Html.Div{children = { Icon{iconName = 'general-info'}, - Html.Span{children = props.title} + Html.Span{children = {Logic.emptyOr(props.title, LegendComponent.defaultProps.title)}} }}, ChevronToggle{} } @@ -65,7 +66,6 @@ end ---@private ---@return VNode? function LegendComponent._createColorSection(props) - mw.log('LegendComponent._createColorSection') local sectionData = Json.parseIfString(props.color) if Logic.isEmpty(sectionData) then return @@ -131,7 +131,10 @@ function LegendComponent._createNumberSection(props) end local labels = WidgetUtil.collect( - Logic.readBool(props.showConfirmed) and Html.Div{ + Logic.nilOr( + Logic.readBoolOrNil(props.showConfirmed), + LegendComponent.defaultProps.showConfirmed + ) and Html.Div{ classes = {'legend-item'}, children = { LabelWidget{ @@ -153,7 +156,10 @@ function LegendComponent._createNumberSection(props) Html.Span{children = {'Minimum placement reached'}} } } or nil, - Logic.readBool(props.showUndecided) and Html.Div{ + Logic.nilOr( + Logic.readBoolOrNil(props.showUndecided), + LegendComponent.defaultProps.showUndecided + ) and Html.Div{ classes = {'legend-item'}, children = { LabelWidget{ @@ -176,4 +182,4 @@ function LegendComponent._createNumberSection(props) } end -return Component.component(LegendComponent.render, LegendComponent.defaultProps) +return Component.component(LegendComponent.render) From f61baf90d3f8094d2d196ff528f69795a45564e3 Mon Sep 17 00:00:00 2001 From: ElectricalBoy <15651807+ElectricalBoy@users.noreply.github.com> Date: Fri, 8 May 2026 14:19:09 +0900 Subject: [PATCH 17/23] add issue reference --- lua/wikis/commons/Widget/Legend.lua | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lua/wikis/commons/Widget/Legend.lua b/lua/wikis/commons/Widget/Legend.lua index aa6e43144d4..6a7849ad146 100644 --- a/lua/wikis/commons/Widget/Legend.lua +++ b/lua/wikis/commons/Widget/Legend.lua @@ -24,7 +24,7 @@ local LABEL_COLORS = {'byeup', 'seedup', 'up', 'stayup', 'stay', 'staydown', 'do ---@class LegendComponent local LegendComponent = {} ---TODO: pass defaultProps directly to Component.component +--TODO: pass defaultProps directly to Component.component (see #7476) LegendComponent.defaultProps = { title = 'Legend', showConfirmed = true, From 88b52e6de23ab6712a1001abf6fc69f4a075a4bc Mon Sep 17 00:00:00 2001 From: ElectricalBoy <15651807+ElectricalBoy@users.noreply.github.com> Date: Fri, 8 May 2026 14:20:43 +0900 Subject: [PATCH 18/23] lint --- lua/wikis/commons/Widget/Legend.lua | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lua/wikis/commons/Widget/Legend.lua b/lua/wikis/commons/Widget/Legend.lua index 6a7849ad146..54930ba65b4 100644 --- a/lua/wikis/commons/Widget/Legend.lua +++ b/lua/wikis/commons/Widget/Legend.lua @@ -24,7 +24,7 @@ local LABEL_COLORS = {'byeup', 'seedup', 'up', 'stayup', 'stay', 'staydown', 'do ---@class LegendComponent local LegendComponent = {} ---TODO: pass defaultProps directly to Component.component (see #7476) +--TODO: pass defaultProps directly to Component.component (see #7476) LegendComponent.defaultProps = { title = 'Legend', showConfirmed = true, From 42f85d286e43127a7d6dc9673731cd8cd52a76ae Mon Sep 17 00:00:00 2001 From: ElectricalBoy <15651807+ElectricalBoy@users.noreply.github.com> Date: Fri, 8 May 2026 14:27:43 +0900 Subject: [PATCH 19/23] make collapsing configurable --- lua/wikis/commons/Widget/Legend.lua | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lua/wikis/commons/Widget/Legend.lua b/lua/wikis/commons/Widget/Legend.lua index 54930ba65b4..06021ebe3ed 100644 --- a/lua/wikis/commons/Widget/Legend.lua +++ b/lua/wikis/commons/Widget/Legend.lua @@ -35,7 +35,7 @@ LegendComponent.defaultProps = { ---@return Widget function LegendComponent.render(props) return GeneralCollapsible{ - shouldCollapse = true, + shouldCollapse = Logic.readBool(props.shouldCollapse), collapseAreaClasses = {}, classes = {'legend'}, titleWidget = LegendComponent._createHeader(props), From 5bcf33a339ad764379026ad7246cbc771a18829a Mon Sep 17 00:00:00 2001 From: ElectricalBoy <15651807+ElectricalBoy@users.noreply.github.com> Date: Fri, 8 May 2026 14:49:12 +0900 Subject: [PATCH 20/23] fix chevrontoggle import --- lua/wikis/commons/Widget/Legend.lua | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lua/wikis/commons/Widget/Legend.lua b/lua/wikis/commons/Widget/Legend.lua index 06021ebe3ed..1d31ecc54bd 100644 --- a/lua/wikis/commons/Widget/Legend.lua +++ b/lua/wikis/commons/Widget/Legend.lua @@ -12,7 +12,7 @@ local Json = Lua.import('Module:Json') local Logic = Lua.import('Module:Logic') local Component = Lua.import('Module:Widget/Component') -local ChevronToggle = Lua.import('Module:Widget/Participants/Team/ChevronToggle') +local ChevronToggle = Lua.import('Module:Widget/GeneralCollapsible/ChevronToggle') local GeneralCollapsible = Lua.import('Module:Widget/GeneralCollapsible/Default') local Html = Lua.import('Module:Widget/Html') local Icon = Lua.import('Module:Widget/Image/Icon/Fontawesome') From 83c148c8044e4cb2eb7c8273d6c4923175bf0a02 Mon Sep 17 00:00:00 2001 From: ElectricalBoy <15651807+ElectricalBoy@users.noreply.github.com> Date: Fri, 8 May 2026 17:44:59 +0900 Subject: [PATCH 21/23] add golden --- lua/spec/standings_legend_spec.lua | 20 ++++++++++++++++++++ 1 file changed, 20 insertions(+) create mode 100644 lua/spec/standings_legend_spec.lua diff --git a/lua/spec/standings_legend_spec.lua b/lua/spec/standings_legend_spec.lua new file mode 100644 index 00000000000..8d7ee31951e --- /dev/null +++ b/lua/spec/standings_legend_spec.lua @@ -0,0 +1,20 @@ +--- Triple Comment to Enable our LLS Plugin +insulate('Widget/Legend', function() + it('integration', function() + local LegendComponent = require('Module:Widget/Legend') + + GoldenTest('standings_legend', tostring(LegendComponent{ + color = { + byeup = 'Lorem ipsum', + seedup = 'Lorem ipsum', + up = 'Lorem ipsum', + stayup = 'Lorem ipsum', + stay = 'Lorem ipsum', + staydown = 'Lorem ipsum', + down = 'Lorem ipsum', + }, + points = {'Lorem ipsum'}, + showMinimum = true, + })) + end) +end) From 0661b78af683b07e828692820c078d9e1a68c7a9 Mon Sep 17 00:00:00 2001 From: ElectricalBoy <15651807+ElectricalBoy@users.noreply.github.com> Date: Fri, 8 May 2026 17:50:57 +0900 Subject: [PATCH 22/23] update golden props --- lua/spec/standings_legend_spec.lua | 1 + 1 file changed, 1 insertion(+) diff --git a/lua/spec/standings_legend_spec.lua b/lua/spec/standings_legend_spec.lua index 8d7ee31951e..402369c831e 100644 --- a/lua/spec/standings_legend_spec.lua +++ b/lua/spec/standings_legend_spec.lua @@ -15,6 +15,7 @@ insulate('Widget/Legend', function() }, points = {'Lorem ipsum'}, showMinimum = true, + showNumberSection = true, })) end) end) From 91331eb9452dcbb5eae10d8037a88b5b84d3a155 Mon Sep 17 00:00:00 2001 From: ElectricalBoy <15651807+ElectricalBoy@users.noreply.github.com> Date: Fri, 8 May 2026 08:53:51 +0000 Subject: [PATCH 23/23] chore: update visual snapshots --- lua/spec/snapshots/standings_legend.png | Bin 0 -> 21227 bytes 1 file changed, 0 insertions(+), 0 deletions(-) create mode 100644 lua/spec/snapshots/standings_legend.png diff --git a/lua/spec/snapshots/standings_legend.png b/lua/spec/snapshots/standings_legend.png new file mode 100644 index 0000000000000000000000000000000000000000..ec5ed640687d230e32d81377b6dadecbea6ac824 GIT binary patch literal 21227 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