diff --git a/lua/spec/league_icon_spec.lua b/lua/spec/league_icon_spec.lua index 455700d019a..c82b72b9e6b 100644 --- a/lua/spec/league_icon_spec.lua +++ b/lua/spec/league_icon_spec.lua @@ -1,7 +1,7 @@ --- Triple Comment to Enable our LLS Plugin local LeagueIcon = require('Module:LeagueIcon') -local FILLER_EXPECT = '[[File:Logo filler event.png|link=]]' +local FILLER_EXPECT = '' local ICON_DARK_EXPECT = '[[File:DarkIcon.png|link=||50x50px]]' local ICON_BOTH_EXPECT = diff --git a/lua/wikis/apexlegends/MainPageLayout/data.lua b/lua/wikis/apexlegends/MainPageLayout/data.lua index cb2a4aa8502..882f99fdfd0 100644 --- a/lua/wikis/apexlegends/MainPageLayout/data.lua +++ b/lua/wikis/apexlegends/MainPageLayout/data.lua @@ -10,7 +10,7 @@ local Lua = require('Module:Lua') local MainPageLayoutUtil = Lua.import('Module:MainPageLayout/Util') local FilterButtonsWidget = Lua.import('Module:Widget/FilterButtons') -local TournamentsTicker = Lua.import('Module:Widget/Tournaments/Ticker') +local TournamentsTicker = Lua.import('Module:Widget/Tournaments/Ticker/List') local HtmlWidgets = Lua.import('Module:Widget/Html/All') local Div = HtmlWidgets.Div @@ -76,9 +76,10 @@ local CONTENT = { heading = 'Tournaments', body = TournamentsTicker{ upcomingDays = 30, - completedDays = 30 + completedDays = 30, + tierColorScheme = 'top3', }, - padding = true, + padding = false, boxid = MainPageLayoutUtil.BoxId.TOURNAMENTS_TICKER, }, headlines = { diff --git a/lua/wikis/commons/LeagueIcon.lua b/lua/wikis/commons/LeagueIcon.lua index c577c240791..08aca3291a9 100644 --- a/lua/wikis/commons/LeagueIcon.lua +++ b/lua/wikis/commons/LeagueIcon.lua @@ -9,11 +9,10 @@ local Lua = require('Module:Lua') local LeagueIcon = {} local Class = Lua.import('Module:Class') +local Icon = Lua.import('Module:Icon') local Template = Lua.import('Module:Template') local Logic = Lua.import('Module:Logic') local String = Lua.import('Module:StringUtils') - -local FILLER = '[[File:Logo filler event.png|link=]]' local NO_ICON_BUT_ICONDARK_TRACKING_CATEGORY = '[[Category:Pages with only icondark]]' ---@class LeagueIconDisplayArgs @@ -56,7 +55,9 @@ function LeagueIcon.display(args) --if icon and iconDark are not given and can not be retrieved return filler icon if String.isEmpty(icon) and String.isEmpty(iconDark) then - return FILLER + return tostring(mw.html.create('span') + :addClass('league-icon-small-image') + :node(Icon.makeIcon{iconName = 'firstplace'})) end if String.isEmpty(icon) then diff --git a/lua/wikis/commons/TournamentsTicker/Data.lua b/lua/wikis/commons/TournamentsTicker/Data.lua new file mode 100644 index 00000000000..589e855ea6d --- /dev/null +++ b/lua/wikis/commons/TournamentsTicker/Data.lua @@ -0,0 +1,149 @@ +--- +-- @Liquipedia +-- page=Module:TournamentsTicker/Data +-- +-- Please see https://github.com/Liquipedia/Lua-Modules to contribute +-- + +local Lua = require('Module:Lua') + +local Array = Lua.import('Module:Array') +local Condition = Lua.import('Module:Condition') +local DateExt = Lua.import('Module:Date/Ext') +local Operator = Lua.import('Module:Operator') + +local Tournament = Lua.import('Module:Tournament') + +local TournamentsTickerData = {} + +---@class TournamentsTickerDataProps +---@field upcomingDays number +---@field completedDays number +---@field modifierTier1 number? +---@field modifierTier2 number? +---@field modifierTier3 number? +---@field modifierTier4 number? +---@field modifierTier5 number? +---@field modifierTierMisc number? +---@field modifierTypeQualifier number? + +---@class TournamentsTickerDataResult +---@field upcoming StandardTournament[] +---@field ongoing StandardTournament[] +---@field completed StandardTournament[] + +---@param props TournamentsTickerDataProps +---@return TournamentsTickerDataResult +function TournamentsTickerData.get(props) + local upcomingDays = props.upcomingDays or 5 + local completedDays = props.completedDays or 5 + + local tierThresholdModifiers = { + [1] = props.modifierTier1, + [2] = props.modifierTier2, + [3] = props.modifierTier3, + [4] = props.modifierTier4, + [5] = props.modifierTier5, + [-1] = props.modifierTierMisc, + } + + --- The Tier Type thresholds only affect completed tournaments. + local tierTypeThresholdModifiers = { + ['qualifier'] = props.modifierTypeQualifier, + } + + local currentTimestamp = DateExt.getCurrentTimestamp() + + ---@param tournament StandardTournament + ---@return boolean + local function isWithinDateRange(tournament) + local modifiedThreshold = tierThresholdModifiers[tournament.liquipediaTier] or 0 + local modifiedCompletedThreshold = tierTypeThresholdModifiers[tournament.liquipediaTierType] or modifiedThreshold + + if not tournament.startDate then + return false + end + + local startDateThreshold = currentTimestamp + DateExt.daysToSeconds(upcomingDays + modifiedThreshold) + local endDateThreshold = currentTimestamp - DateExt.daysToSeconds(completedDays + modifiedCompletedThreshold) + + if tournament.phase == 'ONGOING' then + return true + elseif tournament.phase == 'UPCOMING' then + return tournament.startDate.timestamp < startDateThreshold + elseif tournament.phase == 'FINISHED' then + assert(tournament.endDate, 'Tournament without end date: ' .. tournament.pageName) + return tournament.endDate.timestamp > endDateThreshold + end + return false + end + + local lpdbFilter = Condition.Tree(Condition.BooleanOperator.all):add{ + Condition.Util.anyOf(Condition.ColumnName('status'), {'', 'finished'}), + Condition.Node(Condition.ColumnName('liquipediatiertype'), Condition.Comparator.neq, 'Points') + } + + local allTournaments = Tournament.getAllTournaments(lpdbFilter, function(tournament) + return isWithinDateRange(tournament) + end) + + ---@param a StandardTournament + ---@param b StandardTournament + ---@param dateProperty 'endDate'|'startDate' + ---@param operator fun(a: integer, b: integer): boolean + ---@return boolean? + local function sortByDateProperty(a, b, dateProperty, operator) + if not a[dateProperty] and not b[dateProperty] then return nil end + if not a[dateProperty] then return true end + if not b[dateProperty] then return false end + if a[dateProperty].timestamp ~= b[dateProperty].timestamp then + return operator(a[dateProperty].timestamp, b[dateProperty].timestamp) + end + return nil + end + + ---@param a StandardTournament + ---@param b StandardTournament + ---@return boolean + local function sortByDate(a, b) + local endDateSort = sortByDateProperty(a, b, 'endDate', Operator.gt) + if endDateSort ~= nil then return endDateSort end + local startDateSort = sortByDateProperty(a, b, 'startDate', Operator.gt) + if startDateSort ~= nil then return startDateSort end + return a.pageName < b.pageName + end + + ---@param a StandardTournament + ---@param b StandardTournament + ---@return boolean + local function sortByDateUpcoming(a, b) + local startDateSort = sortByDateProperty(a, b, 'startDate', Operator.gt) + if startDateSort ~= nil then return startDateSort end + local endDateSort = sortByDateProperty(a, b, 'endDate', Operator.gt) + if endDateSort ~= nil then return endDateSort end + return a.pageName < b.pageName + end + + ---@param phase TournamentPhase + ---@return fun(tournament: StandardTournament): boolean + local function filterByPhase(phase) + return function(tournament) + return tournament.phase == phase + end + end + + local upcomingTournaments = Array.filter(allTournaments, filterByPhase('UPCOMING')) + local ongoingTournaments = Array.filter(allTournaments, filterByPhase('ONGOING')) + local completedTournaments = Array.filter(allTournaments, filterByPhase('FINISHED')) + table.sort(upcomingTournaments, sortByDateUpcoming) + table.sort(ongoingTournaments, sortByDate) + table.sort(completedTournaments, sortByDate) + + return { + upcoming = upcomingTournaments, + ongoing = ongoingTournaments, + completed = completedTournaments, + } +end + +return TournamentsTickerData diff --git a/lua/wikis/commons/Widget/Participants/Team/ChevronToggle.lua b/lua/wikis/commons/Widget/GeneralCollapsible/ChevronToggle.lua similarity index 95% rename from lua/wikis/commons/Widget/Participants/Team/ChevronToggle.lua rename to lua/wikis/commons/Widget/GeneralCollapsible/ChevronToggle.lua index a8866b13ad3..e3c98047382 100644 --- a/lua/wikis/commons/Widget/Participants/Team/ChevronToggle.lua +++ b/lua/wikis/commons/Widget/GeneralCollapsible/ChevronToggle.lua @@ -1,6 +1,6 @@ --- -- @Liquipedia --- page=Module:Widget/Participants/Team/ChevronToggle +-- page=Module:Widget/GeneralCollapsible/ChevronToggle -- -- Please see https://github.com/Liquipedia/Lua-Modules to contribute -- diff --git a/lua/wikis/commons/Widget/Participants/Team/Header.lua b/lua/wikis/commons/Widget/Participants/Team/Header.lua index 295b9e01e93..834f6516b22 100644 --- a/lua/wikis/commons/Widget/Participants/Team/Header.lua +++ b/lua/wikis/commons/Widget/Participants/Team/Header.lua @@ -14,7 +14,7 @@ local Opponent = Lua.import('Module:Opponent/Custom') local OpponentDisplay = Lua.import('Module:OpponentDisplay/Custom') local Widget = Lua.import('Module:Widget') -local ChevronToggle = Lua.import('Module:Widget/Participants/Team/ChevronToggle') +local ChevronToggle = Lua.import('Module:Widget/GeneralCollapsible/ChevronToggle') local WidgetUtil = Lua.import('Module:Widget/Util') local HtmlWidgets = Lua.import('Module:Widget/Html/All') local Div = HtmlWidgets.Div diff --git a/lua/wikis/commons/Widget/Tournament/TierPill.lua b/lua/wikis/commons/Widget/Tournament/TierPill.lua index fe7c3b56829..20eab8d907d 100644 --- a/lua/wikis/commons/Widget/Tournament/TierPill.lua +++ b/lua/wikis/commons/Widget/Tournament/TierPill.lua @@ -10,13 +10,25 @@ local Lua = require('Module:Lua') local Class = Lua.import('Module:Class') local Tier = Lua.import('Module:Tier/Utils') +local WidgetUtil = Lua.import('Module:Widget/Util') local Widget = Lua.import('Module:Widget') local HtmlWidgets = Lua.import('Module:Widget/Html/All') +---@class TournamentsTickerPillWidgetProps +---@field tournament StandardTournament +---@field variant 'solid'|'subtle'? +---@field colorScheme 'full'|'top3'? + ---@class TournamentsTickerPillWidget: Widget ----@operator call(table): TournamentsTickerPillWidget +---@operator call(TournamentsTickerPillWidgetProps): TournamentsTickerPillWidget +---@field props TournamentsTickerPillWidgetProps local TournamentsTickerPillWidget = Class.new(Widget) +TournamentsTickerPillWidget.defaultProps = { + variant = 'solid', + colorScheme = 'full', +} + local COLOR_CLASSES = { [1] = 'tier1', [2] = 'tier2', @@ -41,32 +53,40 @@ function TournamentsTickerPillWidget:render() return end - local tierShort, tierTypeShort = Tier.toShortName(tournament.liquipediaTier,tournament.liquipediaTierType) + local subtle = self.props.variant == 'subtle' + local tierShort, tierTypeShort = Tier.toShortName(tournament.liquipediaTier, tournament.liquipediaTierType) - local tierNode, tierTypeNode, colorClass - if tierTypeShort then + local colorClass + if tierTypeShort and not subtle then colorClass = COLOR_CLASSES[tournament.liquipediaTierType] - tierNode = HtmlWidgets.Div{ - classes = {'tournament-badge__chip', 'chip--' .. COLOR_CLASSES[tournament.liquipediaTier]}, - children = tierShort, - } - tierTypeNode = HtmlWidgets.Div{ - classes = {'tournament-badge__text'}, - children = tierTypeShort, - } else colorClass = COLOR_CLASSES[tournament.liquipediaTier] - tierNode = HtmlWidgets.Div{ - classes = {'tournament-badge__text'}, - children = Tier.toName(tournament.liquipediaTier), - } end - colorClass = colorClass or COLOR_CLASSES.default + local chipText = subtle and Tier.toName(tournament.liquipediaTier) or tierShort + local textContent = tierTypeShort and tierTypeShort or Tier.toName(tournament.liquipediaTier) + return HtmlWidgets.Div{ - classes = {'tournament-badge', 'badge--' .. colorClass}, - children = {tierNode, tierTypeNode}, + classes = WidgetUtil.collect( + 'tournament-badge', + 'badge--' .. colorClass, + subtle and 'tournament-badge--subtle' or nil, + self.props.colorScheme == 'top3' and 'tournament-badge--top3' or nil + ), + children = WidgetUtil.collect( + tierTypeShort and HtmlWidgets.Div{ + classes = WidgetUtil.collect( + 'tournament-badge__chip', + not subtle and 'chip--' .. COLOR_CLASSES[tournament.liquipediaTier] or nil + ), + children = chipText, + } or nil, + HtmlWidgets.Div{ + classes = {'tournament-badge__text'}, + children = textContent, + } + ), } end diff --git a/lua/wikis/commons/Widget/Tournaments/Ticker.lua b/lua/wikis/commons/Widget/Tournaments/Ticker.lua index 859a63f17ff..2e6f5a2dc00 100644 --- a/lua/wikis/commons/Widget/Tournaments/Ticker.lua +++ b/lua/wikis/commons/Widget/Tournaments/Ticker.lua @@ -7,19 +7,14 @@ local Lua = require('Module:Lua') -local Array = Lua.import('Module:Array') -local Condition = Lua.import('Module:Condition') local Class = Lua.import('Module:Class') -local DateExt = Lua.import('Module:Date/Ext') local I18n = Lua.import('Module:I18n') local Logic = Lua.import('Module:Logic') -local Operator = Lua.import('Module:Operator') local Widget = Lua.import('Module:Widget') local HtmlWidgets = Lua.import('Module:Widget/Html/All') local Sublist = Lua.import('Module:Widget/Tournaments/Ticker/Sublist') - -local Tournament = Lua.import('Module:Tournament') +local TickerData = Lua.import('Module:TournamentsTicker/Data') ---@class TournamentsTickerWidget: Widget ---@operator call(table): TournamentsTickerWidget @@ -31,123 +26,8 @@ TournamentsTickerWidget.defaultProps = { ---@return Widget function TournamentsTickerWidget:render() - local upcomingDays = self.props.upcomingDays - local completedDays = self.props.completedDays - - local tierThresholdModifiers = { - [1] = self.props.modifierTier1, - [2] = self.props.modifierTier2, - [3] = self.props.modifierTier3, - [4] = self.props.modifierTier4, - [5] = self.props.modifierTier5, - [-1] = self.props.modifierTierMisc, - } - - --- The Tier Type thresholds only affect completed tournaments. - local tierTypeThresholdModifiers = { - ['qualifier'] = self.props.modifierTypeQualifier, - } - - local currentTimestamp = DateExt.getCurrentTimestamp() - - ---@param tournament StandardTournament - ---@return boolean - local function isWithinDateRange(tournament) - local modifiedThreshold = tierThresholdModifiers[tournament.liquipediaTier] or 0 - local modifiedCompletedThreshold = tierTypeThresholdModifiers[tournament.liquipediaTierType] or modifiedThreshold - - if not tournament.startDate then - return false - end - - local startDateThreshold = currentTimestamp + (upcomingDays + modifiedThreshold) * 24 * 60 * 60 - local endDateThreshold = currentTimestamp - (completedDays + modifiedCompletedThreshold) * 24 * 60 * 60 - - if tournament.phase == 'ONGOING' then - return true - elseif tournament.phase == 'UPCOMING' then - return tournament.startDate.timestamp < startDateThreshold - elseif tournament.phase == 'FINISHED' then - assert(tournament.endDate, 'Tournament without end date: ' .. tournament.pageName) - return tournament.endDate.timestamp > endDateThreshold - end - return false - end - - local lpdbFilter = Condition.Tree(Condition.BooleanOperator.all):add{ - Condition.Util.anyOf(Condition.ColumnName('status'), {'', 'finished'}), - Condition.Node(Condition.ColumnName('liquipediatiertype'), Condition.Comparator.neq, 'Points') - } - - local allTournaments = Tournament.getAllTournaments(lpdbFilter, function(tournament) - return isWithinDateRange(tournament) - end) - - ---@param phase TournamentPhase - ---@return fun(tournament: StandardTournament): boolean - local function filterByPhase(phase) - return function(tournament) - return tournament.phase == phase - end - end - - ---@param a StandardTournament - ---@param b StandardTournament - ---@param dateProperty 'endDate' | 'startDate' - ---@param operator fun(a: integer, b: integer): boolean - ---@return boolean? - local function sortByDateProperty(a, b, dateProperty, operator) - if not a[dateProperty] and not b[dateProperty] then - return nil - end - if not a[dateProperty] then - return true - end - if not b[dateProperty] then - return false - end - if a[dateProperty].timestamp ~= b[dateProperty].timestamp then - return operator(a[dateProperty].timestamp, b[dateProperty].timestamp) - end - return nil - end - - ---@param a StandardTournament - ---@param b StandardTournament - ---@return boolean - local function sortByDate(a, b) - local endDateSort = sortByDateProperty(a, b, 'endDate', Operator.gt) - if endDateSort ~= nil then - return endDateSort - end - local startDateSort = sortByDateProperty(a, b, 'startDate', Operator.gt) - if startDateSort ~= nil then - return startDateSort - end - return a.pageName < b.pageName - end - - ---@param a StandardTournament - ---@param b StandardTournament - ---@return boolean - local function sortByDateUpcoming(a, b) - local endDateSort = sortByDateProperty(a, b, 'startDate', Operator.gt) - if endDateSort ~= nil then - return endDateSort - end - local startDateSort = sortByDateProperty(a, b, 'endDate', Operator.gt) - if startDateSort ~= nil then - return startDateSort - end - return a.pageName < b.pageName - end - - local upcomingTournaments = Array.filter(allTournaments, filterByPhase('UPCOMING')) - local ongoingTournaments = Array.filter(allTournaments, filterByPhase('ONGOING')) - local completedTournaments = Array.filter(allTournaments, filterByPhase('FINISHED')) - table.sort(upcomingTournaments, sortByDateUpcoming) - table.sort(ongoingTournaments, sortByDate) - table.sort(completedTournaments, sortByDate) + local data = TickerData.get(self.props) + local displayGameIcons = Logic.readBool(self.props.displayGameIcons) local fallbackElement = HtmlWidgets.Div{ attributes = { @@ -164,21 +44,18 @@ function TournamentsTickerWidget:render() } } - - local displayGameIcons = Logic.readBool(self.props.displayGameIcons) - return HtmlWidgets.Div{ children = { - HtmlWidgets.Ul{ + HtmlWidgets.Div{ classes = {'tournaments-list'}, attributes = { ['data-filter-hideable-group'] = '', ['data-filter-effect'] = 'fade', }, children = { - Sublist{title = 'Upcoming', tournaments = upcomingTournaments, displayGameIcons = displayGameIcons} , - Sublist{title = 'Ongoing', tournaments = ongoingTournaments, displayGameIcons = displayGameIcons}, - Sublist{title = 'Completed', tournaments = completedTournaments, displayGameIcons = displayGameIcons}, + Sublist{title = 'Upcoming', tournaments = data.upcoming, displayGameIcons = displayGameIcons}, + Sublist{title = 'Ongoing', tournaments = data.ongoing, displayGameIcons = displayGameIcons}, + Sublist{title = 'Completed', tournaments = data.completed, displayGameIcons = displayGameIcons}, fallbackElement } } diff --git a/lua/wikis/commons/Widget/Tournaments/Ticker/List.lua b/lua/wikis/commons/Widget/Tournaments/Ticker/List.lua new file mode 100644 index 00000000000..30fdc8af848 --- /dev/null +++ b/lua/wikis/commons/Widget/Tournaments/Ticker/List.lua @@ -0,0 +1,103 @@ +--- +-- @Liquipedia +-- page=Module:Widget/Tournaments/Ticker/List +-- +-- Please see https://github.com/Liquipedia/Lua-Modules to contribute +-- + +local Lua = require('Module:Lua') + +local Class = Lua.import('Module:Class') +local I18n = Lua.import('Module:I18n') +local Widget = Lua.import('Module:Widget') +local ContentSwitch = Lua.import('Module:Widget/ContentSwitch') +local HtmlWidgets = Lua.import('Module:Widget/Html/All') +local ListItem = Lua.import('Module:Widget/Tournaments/Ticker/ListItem') +local PhaseCollapsible = Lua.import('Module:Widget/Tournaments/Ticker/PhaseCollapsible') +local Sublist = Lua.import('Module:Widget/Tournaments/Ticker/Sublist') +local TickerData = Lua.import('Module:TournamentsTicker/Data') + +---@class TournamentsTickerListWidgetProps: TournamentsTickerDataProps +---@field variant 'tabs'|'collapsible'? +---@field displayGameIcons boolean? +---@field tierColorScheme string? + +---@class TournamentsTickerListWidget: Widget +---@operator call(TournamentsTickerListWidgetProps): TournamentsTickerListWidget +---@field props TournamentsTickerListWidgetProps +local TournamentsTickerListWidget = Class.new(Widget) +TournamentsTickerListWidget.defaultProps = { + upcomingDays = 5, + completedDays = 5, + variant = 'tabs', +} + +---@return Widget +function TournamentsTickerListWidget:render() + local data = TickerData.get(self.props) + local displayGameIcons = self.props.displayGameIcons + + ---@param tournament StandardTournament + ---@return Widget + local function createItem(tournament) + return ListItem{ + tournament = tournament, + displayGameIcon = displayGameIcons, + tierColorScheme = self.props.tierColorScheme, + } + end + + ---@param tournaments StandardTournament[] + ---@return Widget + local function buildSublist(tournaments) + return Sublist{ + tournaments = tournaments, + createItem = createItem, + fallback = HtmlWidgets.Div{ + attributes = { + ['data-filter-hideable-group-fallback'] = '', + ['data-filter-effect'] = 'fade', + }, + children = HtmlWidgets.Center{ + css = {margin = '1.5rem 0', ['font-style'] = 'italic'}, + children = I18n.translate('tournament-ticker-no-tournaments'), + }, + }, + } + end + + local tabsWidget = ContentSwitch{ + css = { margin = '0.75rem'}, + switchGroup = 'tournament-list-phase', + defaultActive = 2, + storeValue = false, + tabs = { + {label = 'Upcoming', value = 'upcoming', content = buildSublist(data.upcoming)}, + {label = 'Ongoing', value = 'ongoing', content = buildSublist(data.ongoing)}, + {label = 'Completed', value = 'completed', content = buildSublist(data.completed)}, + }, + } + + if self.props.variant ~= 'collapsible' then + return tabsWidget + end + + return HtmlWidgets.Div{ + children = { + HtmlWidgets.Div{ + classes = {'tournaments-list--tabs'}, + children = tabsWidget, + }, + HtmlWidgets.Div{ + classes = {'tournaments-list--collapsible'}, + children = { + PhaseCollapsible{label = 'Ongoing', children = buildSublist(data.ongoing)}, + PhaseCollapsible{label = 'Upcoming', children = buildSublist(data.upcoming)}, + PhaseCollapsible{label = 'Completed', collapsed = true, children = buildSublist(data.completed)}, + }, + }, + }, + } +end + +return TournamentsTickerListWidget diff --git a/lua/wikis/commons/Widget/Tournaments/Ticker/ListItem.lua b/lua/wikis/commons/Widget/Tournaments/Ticker/ListItem.lua new file mode 100644 index 00000000000..41d148d6dfb --- /dev/null +++ b/lua/wikis/commons/Widget/Tournaments/Ticker/ListItem.lua @@ -0,0 +1,98 @@ +--- +-- @Liquipedia +-- page=Module:Widget/Tournaments/Ticker/ListItem +-- +-- Please see https://github.com/Liquipedia/Lua-Modules to contribute +-- + +local Lua = require('Module:Lua') + +local Class = Lua.import('Module:Class') +local Game = Lua.import('Module:Game') +local LeagueIcon = Lua.import('Module:LeagueIcon') +local WidgetUtil = Lua.import('Module:Widget/Util') +local Widget = Lua.import('Module:Widget') +local HtmlWidgets = Lua.import('Module:Widget/Html/All') +local DateRange = Lua.import('Module:Widget/Misc/DateRange') +local Link = Lua.import('Module:Widget/Basic/Link') +local TierPill = Lua.import('Module:Widget/Tournament/TierPill') + +---@class TournamentsTickerListItemProps +---@field tournament StandardTournament +---@field displayGameIcon boolean +---@field tierColorScheme string? + +---@class TournamentsTickerListItemWidget: Widget +---@operator call(TournamentsTickerListItemProps): TournamentsTickerListItemWidget +---@field props TournamentsTickerListItemProps +local TournamentsTickerListItemWidget = Class.new(Widget) + +---@return Widget? +function TournamentsTickerListItemWidget:render() + local tournament = self.props.tournament + if not tournament then + return + end + + local iconWidget = LeagueIcon.display{ + icon = tournament.icon, + iconDark = tournament.iconDark, + series = tournament.series, + link = tournament.pageName, + options = {noTemplate = true}, + } + + local badgeChildren = WidgetUtil.collect( + iconWidget, + self.props.displayGameIcon and Game.icon{ + game = tournament.game, + noLink = true, + spanClass = 'tournaments-list-item__game-icon', + } or nil, + TierPill{ + tournament = tournament, + variant = 'subtle', + colorScheme = self.props.tierColorScheme, + } + ) + + return HtmlWidgets.Div{ + classes = {'tournaments-list-item'}, + children = { + HtmlWidgets.Span{ + classes = {'tournament-icon'}, + children = iconWidget, + }, + HtmlWidgets.Div{ + classes = {'tournaments-list-item__content'}, + children = { + HtmlWidgets.Div{ + classes = {'tournaments-list-item__name'}, + children = Link{ + link = tournament.pageName, + children = tournament.displayName, + }, + }, + HtmlWidgets.Div{ + classes = {'tournaments-list-item__meta'}, + children = { + HtmlWidgets.Div{ + classes = {'tournaments-list-item__badges'}, + children = badgeChildren, + }, + HtmlWidgets.Div{ + classes = {'tournaments-list-item__date'}, + children = DateRange{ + startDate = tournament.startDate, + endDate = tournament.endDate, + }, + }, + }, + }, + }, + }, + }, + } +end + +return TournamentsTickerListItemWidget diff --git a/lua/wikis/commons/Widget/Tournaments/Ticker/PhaseCollapsible.lua b/lua/wikis/commons/Widget/Tournaments/Ticker/PhaseCollapsible.lua new file mode 100644 index 00000000000..868246c6440 --- /dev/null +++ b/lua/wikis/commons/Widget/Tournaments/Ticker/PhaseCollapsible.lua @@ -0,0 +1,49 @@ +--- +-- @Liquipedia +-- page=Module:Widget/Tournaments/Ticker/PhaseCollapsible +-- +-- Please see https://github.com/Liquipedia/Lua-Modules to contribute +-- + +local Lua = require('Module:Lua') + +local Class = Lua.import('Module:Class') + +local Widget = Lua.import('Module:Widget') +local ChevronToggle = Lua.import('Module:Widget/GeneralCollapsible/ChevronToggle') +local GeneralCollapsible = Lua.import('Module:Widget/GeneralCollapsible/Default') +local HtmlWidgets = Lua.import('Module:Widget/Html/All') + +---@class TournamentsTickerPhaseCollapsibleProps +---@field label string +---@field children Widget|Widget[] +---@field collapsed boolean? + +---@class TournamentsTickerPhaseCollapsible: Widget +---@operator call(TournamentsTickerPhaseCollapsibleProps): TournamentsTickerPhaseCollapsible +---@field props TournamentsTickerPhaseCollapsibleProps +local TournamentsTickerPhaseCollapsible = Class.new(Widget) + +---@return Widget +function TournamentsTickerPhaseCollapsible:render() + return GeneralCollapsible{ + classes = {'tournaments-phase-collapsible'}, + shouldCollapse = self.props.collapsed, + titleWidget = HtmlWidgets.Div{ + classes = {'tournaments-phase-collapsible__header'}, + attributes = { + ['data-collapsible-click-region'] = 'true', + }, + children = { + HtmlWidgets.Span{ + classes = {'tournaments-phase-collapsible__label'}, + children = self.props.label, + }, + ChevronToggle{}, + }, + }, + children = self.props.children, + } +end + +return TournamentsTickerPhaseCollapsible diff --git a/lua/wikis/commons/Widget/Tournaments/Ticker/Sublist.lua b/lua/wikis/commons/Widget/Tournaments/Ticker/Sublist.lua index d13d1bcd0df..6c81f26c3c8 100644 --- a/lua/wikis/commons/Widget/Tournaments/Ticker/Sublist.lua +++ b/lua/wikis/commons/Widget/Tournaments/Ticker/Sublist.lua @@ -11,14 +11,17 @@ local Array = Lua.import('Module:Array') local Class = Lua.import('Module:Class') local Widget = Lua.import('Module:Widget') +local WidgetUtil = Lua.import('Module:Widget/Util') local HtmlWidgets = Lua.import('Module:Widget/Html/All') local TournamentLabel = Lua.import('Module:Widget/Tournament/Label') local FilterConfig = Lua.import('Module:FilterButtons/Config') ---@class TournamentsTickerSublistWidgetProps ----@field title string +---@field title string? ---@field tournaments StandardTournament[] ---@field displayGameIcons boolean +---@field createItem (fun(tournament: StandardTournament): Widget)? +---@field fallback Widget? ---@class TournamentsTickerSublistWidget: Widget ---@operator call(TournamentsTickerSublistWidgetProps): TournamentsTickerSublistWidget @@ -31,6 +34,13 @@ function TournamentsTickerSublistWidget:render() return end + local createItem = self.props.createItem or function(tournament) + return TournamentLabel{ + tournament = tournament, + displayGameIcon = self.props.displayGameIcons, + } + end + ---@param tournament StandardTournament ---@param child Widget ---@return Widget @@ -54,27 +64,23 @@ function TournamentsTickerSublistWidget:render() local list = HtmlWidgets.Ul{ classes = {'tournaments-list-type-list'}, children = Array.map(self.props.tournaments, function(tournament) - return HtmlWidgets.Li{children = createFilterWrapper(tournament, TournamentLabel{ - tournament = tournament, - displayGameIcon = self.props.displayGameIcons - })} + return HtmlWidgets.Li{children = createFilterWrapper(tournament, createItem(tournament))} end), } - return HtmlWidgets.Li{ + return HtmlWidgets.Div{ attributes = { ['data-filter-hideable-group'] = '', ['data-filter-effect'] = 'fade', }, - children = { - HtmlWidgets.Span{ + children = WidgetUtil.collect( + self.props.title and HtmlWidgets.Span{ classes = {'tournaments-list-heading'}, children = self.props.title, - }, - HtmlWidgets.Div{ - children = list, - } - }, + } or nil, + list, + self.props.fallback + ), } end diff --git a/lua/wikis/fortnite/MainPageLayout/data.lua b/lua/wikis/fortnite/MainPageLayout/data.lua index b0827e447cd..7383f7eeaf1 100644 --- a/lua/wikis/fortnite/MainPageLayout/data.lua +++ b/lua/wikis/fortnite/MainPageLayout/data.lua @@ -10,7 +10,7 @@ local Lua = require('Module:Lua') local MainPageLayoutUtil = Lua.import('Module:MainPageLayout/Util') local FilterButtonsWidget = Lua.import('Module:Widget/FilterButtons') -local TournamentsTicker = Lua.import('Module:Widget/Tournaments/Ticker') +local TournamentsTicker = Lua.import('Module:Widget/Tournaments/Ticker/List') local HtmlWidgets = Lua.import('Module:Widget/Html/All') local Div = HtmlWidgets.Div @@ -62,9 +62,10 @@ local CONTENT = { heading = 'Tournaments', body = TournamentsTicker{ upcomingDays = 14, - completedDays = 7 + completedDays = 7, + variant = 'collapsible', }, - padding = true, + padding = false, boxid = MainPageLayoutUtil.BoxId.TOURNAMENTS_TICKER, }, } diff --git a/lua/wikis/leagueoflegends/MainPageLayout/data.lua b/lua/wikis/leagueoflegends/MainPageLayout/data.lua index a9c3188f4df..30285ff75e7 100644 --- a/lua/wikis/leagueoflegends/MainPageLayout/data.lua +++ b/lua/wikis/leagueoflegends/MainPageLayout/data.lua @@ -16,7 +16,7 @@ local Comparator = Condition.Comparator local ColumnName = Condition.ColumnName local FilterButtonsWidget = Lua.import('Module:Widget/FilterButtons') -local TournamentsTicker = Lua.import('Module:Widget/Tournaments/Ticker') +local TournamentsTicker = Lua.import('Module:Widget/Tournaments/Ticker/List') local HtmlWidgets = Lua.import('Module:Widget/Html/All') local Div = HtmlWidgets.Div @@ -102,9 +102,10 @@ local CONTENT = { modifierTypeQualifier = -2, modifierTier1 = 55, modifierTier2 = 55, - modifierTier3 = 10 + modifierTier3 = 10, + tierColorScheme = 'top3', }, - padding = true, + padding = false, boxid = MainPageLayoutUtil.BoxId.TOURNAMENTS_TICKER, }, headlines = { diff --git a/lua/wikis/pubgmobile/MainPageLayout/data.lua b/lua/wikis/pubgmobile/MainPageLayout/data.lua index 7ee8d5ddfe8..e54fa7e6046 100644 --- a/lua/wikis/pubgmobile/MainPageLayout/data.lua +++ b/lua/wikis/pubgmobile/MainPageLayout/data.lua @@ -10,7 +10,7 @@ local Lua = require('Module:Lua') local MainPageLayoutUtil = Lua.import('Module:MainPageLayout/Util') local FilterButtonsWidget = Lua.import('Module:Widget/FilterButtons') -local TournamentsTicker = Lua.import('Module:Widget/Tournaments/Ticker') +local TournamentsTicker = Lua.import('Module:Widget/Tournaments/Ticker/List') local HtmlWidgets = Lua.import('Module:Widget/Html/All') local Div = HtmlWidgets.Div @@ -68,9 +68,9 @@ local CONTENT = { heading = 'Tournaments', body = TournamentsTicker{ upcomingDays = 30, - completedDays = 30 + completedDays = 30, }, - padding = true, + padding = false, boxid = MainPageLayoutUtil.BoxId.TOURNAMENTS_TICKER, }, headlines = { diff --git a/lua/wikis/trackmania/MainPageLayout/data.lua b/lua/wikis/trackmania/MainPageLayout/data.lua index 8adcb77ca84..1adfe826969 100644 --- a/lua/wikis/trackmania/MainPageLayout/data.lua +++ b/lua/wikis/trackmania/MainPageLayout/data.lua @@ -11,7 +11,7 @@ local MainPageLayoutUtil = Lua.import('Module:MainPageLayout/Util') local FilterButtonsWidget = Lua.import('Module:Widget/FilterButtons') local MatchTicker = Lua.import('Module:Widget/MainPage/MatchTicker') -local TournamentsTicker = Lua.import('Module:Widget/Tournaments/Ticker') +local TournamentsTicker = Lua.import('Module:Widget/Tournaments/Ticker/List') local HtmlWidgets = Lua.import('Module:Widget/Html/All') local Div = HtmlWidgets.Div @@ -72,8 +72,10 @@ local CONTENT = { upcomingDays = 30, modifierTier1 = 50, completedDays = 20, + tierColorScheme = 'top3', + variant = 'collapsible', }, - padding = true, + padding = false, boxid = MainPageLayoutUtil.BoxId.TOURNAMENTS_TICKER, }, } diff --git a/stylesheets/commons/Mainpage.scss b/stylesheets/commons/Mainpage.scss index e3bad969567..f57d3a1ee70 100644 --- a/stylesheets/commons/Mainpage.scss +++ b/stylesheets/commons/Mainpage.scss @@ -84,6 +84,190 @@ div.main-page-banner .main-page-banner-bottom-row { list-style-type: none; } +.tournaments-list-item { + display: flex; + align-items: center; + gap: 0.25rem; + padding: 0.5rem; + border-bottom: 1px solid; + border-color: var( --clr-on-surface-light-primary-8 ); + + .theme--dark & { + border-color: var( --clr-on-surface-dark-primary-8 ); + } + + > .tournament-icon { + display: flex; + justify-content: center; + align-items: center; + padding: 0.25rem; + width: 2.25rem; + height: 2.25rem; + + @media ( max-width: 359px ) { + display: none; + } + } + + &__content { + flex: 1; + min-width: 0; + display: flex; + flex-direction: column; + font-weight: bold; + gap: 0.125rem; + } + + &__name { + font-size: 0.8125rem; + white-space: nowrap; + overflow: hidden; + text-overflow: ellipsis; + } + + &__meta { + display: flex; + justify-content: space-between; + } + + &__badges { + display: flex; + gap: 0.25rem; + + > .league-icon-small-image { + width: 1.25rem; + height: 1.25rem; + min-width: unset; + min-height: unset; + padding: 0.125rem; + background-color: var( --clr-on-surface-light-primary-4 ); + border-radius: 0.25rem; + flex-shrink: 0; + overflow: hidden; + + @media ( min-width: 360px ) { + display: none; + } + + img { + max-width: 1rem; + max-height: 1rem; + } + + i { + font-size: 0.75rem; + } + + .theme--dark & { + background-color: var( --clr-on-surface-dark-primary-4 ); + } + } + } + + &__game-icon { + display: flex; + align-items: center; + justify-content: center; + width: 1.25rem; + height: 1.25rem; + background-color: var( --clr-on-surface-light-primary-4 ); + border-radius: 0.25rem; + padding: 0.125rem; + + .theme--dark & { + background-color: var( --clr-on-surface-dark-primary-4 ); + } + + img { + max-width: 100%; + max-height: 100%; + } + } + + &__date { + font-size: 0.6875rem; + line-height: 1rem; + background-color: var( --clr-on-surface-light-primary-8 ); + color: var( --clr-secondary-25 ); + border-radius: 0.25rem; + padding: 0.125rem 0.5rem; + + .theme--dark & { + background-color: var( --clr-on-surface-dark-primary-8 ); + color: var( --clr-secondary-100 ); + } + } +} + +.tournaments-list--collapsible { + display: none; + + @media ( min-width: 1024px ) { + display: block; + } +} + +@media ( min-width: 1024px ) { + .tournaments-list--tabs { + display: none; + } +} + +.tournaments-phase-collapsible { + &:last-child.collapsed { + padding-bottom: 0.75rem; + } + + &__header { + background-color: var( --clr-on-surface-light-primary-4 ); + border-radius: 0.625rem; + padding: 0.625rem 0.75rem; + display: flex; + align-items: center; + cursor: pointer; + user-select: none; + margin: 0.75rem 0.75rem 0.25rem; + + & + & { + margin-top: 0; + } + + &:hover { + background-color: var( --clr-on-surface-light-primary-12 ); + } + + .theme--dark & { + background-color: var( --clr-on-surface-dark-primary-4 ); + + &:hover { + background-color: var( --clr-on-surface-dark-primary-12 ); + } + } + } + + &__label { + flex: 1; + font-size: 0.875rem; + font-weight: bold; + color: var( --clr-secondary-25 ); + + .theme--dark & { + color: var( --clr-secondary-100 ); + } + } + + .general-collapsible-expand-button, + .general-collapsible-collapse-button { + outline: unset; + color: var( --clr-secondary-25 ); + + .theme--dark & { + color: var( --clr-secondary-100 ); + opacity: 0.7; + } + } +} + .tournaments-list-dates { float: right; padding-right: 10px; diff --git a/stylesheets/commons/Miscellaneous.scss b/stylesheets/commons/Miscellaneous.scss index 3916c85b756..1f49ac0ac1f 100644 --- a/stylesheets/commons/Miscellaneous.scss +++ b/stylesheets/commons/Miscellaneous.scss @@ -363,6 +363,10 @@ span.icon-small { a { display: contents; } + + i { + font-size: 1.125rem; + } } /* LeagueIconSmall dark mode support */ diff --git a/stylesheets/commons/TournamentTags.scss b/stylesheets/commons/TournamentTags.scss index bc3a8a23852..0df3da88922 100644 --- a/stylesheets/commons/TournamentTags.scss +++ b/stylesheets/commons/TournamentTags.scss @@ -139,3 +139,140 @@ $showmatch-border-color: var( --tournament-tag-showmatch-badge-border-color, #00 justify-content: center; } } + +.tournament-badge--subtle { + height: auto; + width: auto; + background-color: var( --clr-on-surface-light-primary-4 ) !important; + border-width: 0; + border-left: 0.25rem solid; + border-left-color: var( --tournament-badge-color ) !important; + border-radius: 0.25rem; + padding: 0.125rem 0.5rem; + gap: 0.375rem; + + .theme--dark & { + background-color: var( --clr-on-surface-dark-primary-4 ) !important; + } + + .tournament-badge__chip { + width: auto; + height: auto; + padding: 0 0.375rem; + background-color: var( --clr-on-surface-light-primary-8 ); + color: var( --clr-secondary-25 ); + font-size: 0.6875rem; + font-weight: bold; + line-height: 1rem; + margin-left: -0.25rem; + + .theme--dark & { + background-color: var( --clr-on-surface-dark-primary-8 ); + color: var( --clr-secondary-100 ); + } + } + + &.badge--tier1 { + --tournament-badge-color: #ef6532; + + .theme--dark & { + --tournament-badge-color: #ff4500; + } + } + + &.badge--tier2 { + --tournament-badge-color: #b38f00; + + .theme--dark & { + --tournament-badge-color: #ffcc00; + } + } + + &.badge--tier3 { + --tournament-badge-color: #0097ca; + + .theme--dark & { + --tournament-badge-color: #00bfff; + } + } + + &.badge--tier4 { + --tournament-badge-color: #2d712f; + + .theme--dark & { + --tournament-badge-color: #65c877; + } + } + + &.badge--tier5 { + --tournament-badge-color: #9166ff; + + .theme--dark & { + --tournament-badge-color: #8858ff; + } + } + + &.badge--misc, + &.badge--qualifier, + &.badge--monthly, + &.badge--weekly, + &.badge--showmatch { + --tournament-badge-color: #949494; + + .theme--dark & { + --tournament-badge-color: #707176; + } + } + + &.tournament-badge--top3 { + &.badge--tier1 { + --tournament-badge-color: #f16c0e; + + .theme--dark & { + --tournament-badge-color: #ffa500; + } + } + + &.badge--tier2 { + --tournament-badge-color: #2563eb; + + .theme--dark & { + --tournament-badge-color: #3b82f6; + } + } + + &.badge--tier3 { + --tournament-badge-color: #262626; + + .theme--dark & { + --tournament-badge-color: #ffffff; + } + } + + &.badge--tier4, + &.badge--tier5, + &.badge--misc, + &.badge--qualifier, + &.badge--monthly, + &.badge--weekly, + &.badge--showmatch { + --tournament-badge-color: #949494; + + .theme--dark & { + --tournament-badge-color: #8d8d8d; + } + } + } + + .tournament-badge__text { + color: var( --clr-secondary-25 ); + font-size: 0.6875rem; + font-weight: bold; + line-height: 1rem; + justify-content: flex-start; + + .theme--dark & { + color: var( --clr-secondary-100 ); + } + } +}