diff --git a/lua/wikis/apexlegends/POIDraft/POIMap/Data.lua b/lua/wikis/apexlegends/POIDraft/POIMap/Data.lua new file mode 100644 index 00000000000..cfc7995aa8d --- /dev/null +++ b/lua/wikis/apexlegends/POIDraft/POIMap/Data.lua @@ -0,0 +1,266 @@ +--- +-- @Liquipedia +-- page=Module:POIDraft/POIMap/Data +-- +-- Please see https://github.com/Liquipedia/Lua-Modules to contribute +-- + +---@class POIDraftDateBoundItem +---@field startDate string? +---@field endDate string? + +---@class PoiData: POIDraftDateBoundItem +---@field name string +---@field x number +---@field y number +---@field mobileName string +---@field mobileX number? +---@field mobileY number? +---@field hideIfAny string[]? +---@field hideIfAllMissing string[]? + +---@class MapImageData: POIDraftDateBoundItem +---@field file string + +---@class PoiMapData +---@field name string +---@field image MapImageData[] +---@field width integer +---@field mobileWidth integer +---@field pois PoiData[] + +---@type table +local MAPS_DATA = { + StormPoint = { + name = 'Storm Point', + image = { + { file = 'Storm_Point_S17.png' } + }, + width = 800, + mobileWidth = 365, + pois = { + { name = 'Barometer', x = 0.370, y = 0.666, mobileName = 'Barometer', startDate = '2025-05-01' }, + { name = 'Barometer North', x = 0.370, y = 0.666, mobileName = 'Barometer N.', endDate = '2025-04-30' }, + { name = 'Barometer South', x = 0.307, y = 0.796, mobileName = 'Barometer S.', endDate = '2025-04-30' }, + { name = 'Basin Armory', x = 0.240, y = 0.581, mobileName = 'Basin Armory', startDate = '2025-08-30' }, + { name = 'Cascade Falls', x = 0.497, y = 0.396, mobileName = 'Cascade
Falls' }, + { name = 'Cenote Cave', x = 0.192, y = 0.716, mobileName = 'Cenote Cave' }, + { name = 'Ceto Station', x = 0.347, y = 0.506, mobileName = 'Ceto Station' }, + { name = 'Checkpoint', x = 0.307, y = 0.326, mobileName = 'Checkpoint', mobileY = 0.333 }, + { + name = 'Checkpoint North', + x = 0.307, + y = 0.326, + mobileName = 'Checkpoint N.', + mobileY = 0.333, + endDate = '2025-04-30' + }, + { name = 'Checkpoint South', x = 0.267, y = 0.401, mobileName = 'Checkpoint S.', endDate = '2025-04-30' }, + { name = 'Cliff Side', x = 0.880, y = 0.366, mobileName = 'Cliff Side', endDate = '2025-04-30' }, + { name = 'Coastal Camp', x = 0.477, y = 0.856, mobileName = 'Coastal
Camp' }, + { name = 'Command Center', x = 0.647, y = 0.316, mobileName = 'Command Center' }, + { name = 'Devastated Coast', x = 0.827, y = 0.776, mobileName = 'Devastated Coast' }, + { name = 'Downed Beast', x = 0.105, y = 0.296, mobileName = 'Downed Beast', mobileX = 0.120 }, + { name = 'East Trail', x = 0.880, y = 0.366, mobileName = 'East Trail', startDate = '2025-05-01' }, + { name = 'Echo HQ', x = 0.677, y = 0.846, mobileName = 'Echo HQ' }, + { + name = 'Forbidden Zone', + x = 0.477, + y = 0.581, + mobileName = 'Forbidden
Zone', + mobileY = 0.585, + startDate = '2025-05-01' + }, + { name = 'Jurassic', x = 0.477, y = 0.581, mobileName = 'Jurassic', endDate = '2025-04-30' }, + { name = 'Launch Pad', x = 0.787, y = 0.656, mobileName = 'Launch Pad' }, + { name = 'Lift', x = 0.577, y = 0.496, mobileName = 'Lift', endDate = '2025-04-30' }, + { name = 'Lightning Rod', x = 0.787, y = 0.236, mobileName = 'Lightning Rod' }, + { name = 'Mountain Lift', x = 0.577, y = 0.496, mobileName = 'Mountain
Lift', startDate = '2025-05-01' }, + { name = 'North Pad', x = 0.237, y = 0.156, mobileName = 'North Pad' }, + { name = 'Outpost North', x = 0.407, y = 0.236, mobileName = 'Outpost North', startDate = '2025-05-01' }, + { name = 'Prowler Nest', x = 0.747, y = 0.516, mobileName = 'Prowler Nest', endDate = '2025-04-30' }, + { name = 'Storm Catcher', x = 0.727, y = 0.418, mobileName = 'Storm
Catcher' }, + { name = 'The Mill', x = 0.147, y = 0.506, mobileName = 'The Mill' }, + { name = 'The Pylon', x = 0.587, y = 0.676, mobileName = 'The Pylon', mobileY = 0.690 }, + { name = 'The Wall', x = 0.527, y = 0.166, mobileName = 'The Wall' }, + { name = 'Trident', x = 0.407, y = 0.236, mobileName = 'Trident', endDate = '2025-04-30' }, + { name = 'Zeus Station', x = 0.767, y = 0.106, mobileName = 'Zeus Station' } + } + }, + WorldsEdge = { + name = "World's Edge", + image = { + { file = 'Apex legends World Edge S30 map.webp', startDate = '2026-08-04' }, + { file = 'World_Edge_S17.png', endDate = '2026-08-04' } + }, + width = 800, + mobileWidth = 365, + pois = { + { name = 'Big Maude', x = 0.345, y = 0.457, mobileName = 'Big Maude' }, + { + name = 'Climatizer', + x = 0.702, + y = 0.146, + mobileName = 'Climatizer', + startDate = '2025-02-03', + endDate = '2026-08-04' + }, + { name = 'Climatizer East', x = 0.772, y = 0.236, mobileName = 'Climatizer East', endDate = '2025-04-30' }, + { name = 'Climatizer West', x = 0.672, y = 0.126, mobileName = 'Climatizer West', endDate = '2025-04-30' }, + { name = 'Countdown', x = 0.302, y = 0.336, mobileName = 'Countdown' }, + { name = 'East Village', x = 0.752, y = 0.173, mobileName = 'East Village', startDate = '2026-08-04' }, + { + name = 'Fragment', + x = 0.642, + y = 0.476, + mobileName = 'Fragment', + hideIfAny = { 'Fragment East team', 'Fragment West team' } + }, + { + name = 'Fragment East', + x = 0.672, + y = 0.476, + mobileName = 'Fragment E.', + hideIfAllMissing = { 'Fragment East team', 'Fragment West team' } + }, + { + name = 'Fragment West', + x = 0.492, + y = 0.456, + mobileName = 'Fragment W.', + hideIfAllMissing = { 'Fragment East team', 'Fragment West team' } + }, + { name = 'Harvester', x = 0.442, y = 0.616, mobileName = 'Harvester' }, + { name = 'Landslide', x = 0.340, y = 0.456, mobileName = 'Landslide', endDate = '2026-08-04' }, + { name = 'Launch Site', x = 0.522, y = 0.896, mobileName = 'Launch
Site' }, + { name = 'Lava Fissure', x = 0.148, y = 0.400, mobileName = 'Lava Fissure' }, + { name = 'Lava Siphon', x = 0.542, y = 0.726, mobileName = 'Lava Siphon', endDate = '2026-08-04' }, + { name = 'Mirage A Trois', x = 0.149, y = 0.510, mobileName = 'Mirage A Trois' }, + { name = 'Monument', x = 0.492, y = 0.386, mobileName = 'Monument' }, + { name = 'Overlook', x = 0.822, y = 0.396, mobileName = 'Overlook' }, + { name = 'Skyhook East', x = 0.388, y = 0.166, mobileName = 'Skyhook East' }, + { name = 'Skyhook West', x = 0.200, y = 0.236, mobileName = 'Skyhook West' }, + { name = 'Sorting Factory', x = 0.574, y = 0.755, mobileName = 'Sorting
Factory', startDate = '2026-08-04' }, + { name = 'Stacks', x = 0.712, y = 0.826, mobileName = 'Stacks' }, + { name = 'Staging', x = 0.222, y = 0.596, mobileName = 'Staging' }, + { name = 'Survey Camp', x = 0.477, y = 0.231, mobileName = 'Survey Camp', endDate = '2026-08-04' }, + { name = 'The Dome', x = 0.725, y = 0.930, mobileName = 'The Dome' }, + { name = 'The Epicenter', x = 0.622, y = 0.296, mobileName = 'The Epicenter' }, + { name = 'The Geyser', x = 0.762, y = 0.596, mobileName = 'The Geyser' }, + { name = 'The Tree', x = 0.392, y = 0.826, mobileName = 'The Tree', endDate = '2026-08-04' }, + { name = 'Thermal Station', x = 0.222, y = 0.756, mobileName = 'Thermal Station' }, + { name = 'Tree Settlement', x = 0.408, y = 0.823, mobileName = 'Tree
Settlement', startDate = '2026-08-04' }, + { name = 'War Camp', x = 0.560, y = 0.200, mobileName = 'War Camp', startDate = '2026-08-04' } + } + }, + EDistrict = { + name = 'E-District', + image = { + { file = 'Apex_Legends_E-District_Map.png' } + }, + width = 800, + mobileWidth = 365, + pois = { + { name = 'Blossom Drive', x = 0.092, y = 0.516, mobileName = 'Blossom
Drive' }, + { name = 'Boardwalk', x = 0.122, y = 0.356, mobileName = 'Boardwalk' }, + { name = 'Canal Plaza', x = 0.472, y = 0.306, mobileName = 'Canal Plaza' }, + { name = 'City Hall', x = 0.300, y = 0.410, mobileName = 'City Hall' }, + { name = 'Draft Point', x = 0.442, y = 0.656, mobileName = 'Draft Point' }, + { name = 'Electro Dam', x = 0.662, y = 0.176, mobileName = 'Electro Dam' }, + { name = 'Energy Bank', x = 0.492, y = 0.426, mobileName = 'Energy
Bank' }, + { name = 'Galleria', x = 0.622, y = 0.360, mobileName = 'Galleria' }, + { name = 'Heights', x = 0.812, y = 0.346, mobileName = 'Heights' }, + { name = 'Humbert Labs', x = 0.682, y = 0.786, mobileName = 'Humbert Labs' }, + { name = 'Neon Square', x = 0.277, y = 0.540, mobileName = 'Neon Square', mobileY = 0.570 }, + { name = 'Old Town', x = 0.605, y = 0.906, mobileName = 'Old Town' }, + { name = 'Resort', x = 0.188, y = 0.236, mobileName = 'Resort' }, + { name = 'Settlement', x = 0.597, y = 0.596, mobileName = 'Settlement' }, + { name = 'Shipyard Arcade', x = 0.412, y = 0.896, mobileName = 'Shipyard
Arcade', mobileY = 0.880 }, + { name = 'Stadium', x = 0.792, y = 0.556, mobileName = 'Stadium' }, + { name = 'Street Market', x = 0.192, y = 0.706, mobileName = 'Street Market' }, + { name = 'The Lotus', x = 0.422, y = 0.146, mobileName = 'The Lotus' }, + { name = 'Uptown', x = 0.682, y = 0.476, mobileName = 'Uptown', startDate = '2025-02-03' }, + { name = 'Vibe Isle', x = 0.682, y = 0.476, mobileName = 'Vibe Isle', endDate = '2025-02-03' }, + { name = 'Viaduct', x = 0.342, y = 0.776, mobileName = 'Viaduct' } + } + }, + BrokenMoon = { + name = 'Broken Moon', + image = { + { file = 'Broken Moon S25.png' } + }, + width = 800, + mobileWidth = 365, + pois = { + { name = 'Alpha Base', x = 0.570, y = 0.160, mobileName = 'Alpha Base' }, + { name = 'Atmostation', x = 0.640, y = 0.870, mobileName = 'Atmostation' }, + { name = 'Bionomics', x = 0.880, y = 0.760, mobileName = 'Bionomics' }, + { name = 'Breaker Wharf', x = 0.800, y = 0.180, mobileName = 'Breaker Wharf' }, + { name = 'Cliff Side', x = 0.540, y = 0.410, mobileName = 'Cliff Side' }, + { name = 'Cultivation', x = 0.400, y = 0.770, mobileName = 'Cultivation' }, + { name = 'Dry Gulch', x = 0.160, y = 0.400, mobileName = 'Dry Gulch' }, + { name = 'Eternal Gardens', x = 0.830, y = 0.360, mobileName = 'Eternal Gardens' }, + { + name = 'Experimental Labs', + x = 0.130, + y = 0.720, + mobileName = 'Experimental Labs' + }, + { name = 'Hazmat Tunnel', x = 0.580, y = 0.580, mobileName = 'Hazmat Tunnel' }, + { + name = 'Production Yard', + x = 0.100, + y = 0.540, + mobileName = 'Production Yard' + }, + { + name = 'Quarantine Zone', + x = 0.400, + y = 0.560, + mobileName = 'Quarantine Zone' + }, + { name = 'Solar Pods', x = 0.310, y = 0.870, mobileName = 'Solar Pods' }, + { name = 'Space Port', x = 0.150, y = 0.270, mobileName = 'Space Port' }, + { name = 'Stasis Array', x = 0.640, y = 0.340, mobileName = 'Stasis Array' }, + { name = 'Terraformer', x = 0.720, y = 0.640, mobileName = 'Terraformer' }, + { name = 'The Core', x = 0.360, y = 0.280, mobileName = 'The Core' }, + { name = 'The Divide', x = 0.920, y = 0.540, mobileName = 'The Divide' }, + { name = 'The Foundry', x = 0.170, y = 0.870, mobileName = 'The Foundry' }, + { name = 'Underpass', x = 0.250, y = 0.610, mobileName = 'Underpass' } + } + }, + Olympus = { + name = 'Olympus', + image = { + { file = 'Apex legends Olympus S27 map.png' } + }, + width = 800, + mobileWidth = 365, + pois = { + { name = 'Bonsai Plaza', x = 0.540, y = 0.900, mobileName = 'Bonsai
Plaza' }, + { name = 'Carrier', x = 0.210, y = 0.280, mobileName = 'Carrier' }, + { name = 'Clinic', x = 0.912, y = 0.465, mobileName = 'Clinic' }, + { name = 'Dockyard', x = 0.130, y = 0.610, mobileName = 'Dockyard' }, + { name = 'Elysium', x = 0.820, y = 0.860, mobileName = 'Elysium' }, + { name = 'Estates', x = 0.310, y = 0.525, mobileName = 'Estates' }, + { name = 'Fight Night', x = 0.400, y = 0.310, mobileName = 'Fight Night' }, + { name = 'Gardens', x = 0.742, y = 0.440, mobileName = 'Gardens' }, + { name = 'Gravity Engine', x = 0.575, y = 0.430, mobileName = 'Gravity
Engine' }, + { name = 'Grow Towers', x = 0.730, y = 0.560, mobileName = 'Grow Towers' }, + { name = 'Hammond Labs', x = 0.515, y = 0.530, mobileName = 'Hammond
Labs' }, + { name = 'Hydroponics', x = 0.210, y = 0.710, mobileName = 'Hydroponics' }, + { name = 'Icarus', x = 0.730, y = 0.920, mobileName = 'Icarus' }, + { name = 'Oasis', x = 0.165, y = 0.410, mobileName = 'Oasis' }, + { name = 'Phase Driver', x = 0.390, y = 0.830, mobileName = 'Phase Driver' }, + { name = 'Power Grid', x = 0.500, y = 0.240, mobileName = 'Power Grid' }, + { name = 'Rift', x = 0.690, y = 0.260, mobileName = 'Rift' }, + { name = 'Solar Array', x = 0.620, y = 0.750, mobileName = 'Solar
Array' }, + { name = 'Somers University', x = 0.810, y = 0.720, mobileName = 'Somers University' }, + { name = 'Stabilizer', x = 0.360, y = 0.180, mobileName = 'Stabilizer' }, + { name = 'Terminal', x = 0.480, y = 0.650, mobileName = 'Terminal' }, + { name = 'Turbine', x = 0.380, y = 0.420, mobileName = 'Turbine' } + } + } +} + +return MAPS_DATA diff --git a/lua/wikis/apexlegends/Widget/POIDraft/POILabel.lua b/lua/wikis/apexlegends/Widget/POIDraft/POILabel.lua new file mode 100644 index 00000000000..81e2d53a2c3 --- /dev/null +++ b/lua/wikis/apexlegends/Widget/POIDraft/POILabel.lua @@ -0,0 +1,125 @@ +--- +-- @Liquipedia +-- page=Module:Widget/POIDraft/POILabel +-- +-- Please see https://github.com/Liquipedia/Lua-Modules to contribute +-- + +local Lua = require('Module:Lua') + +local Logic = Lua.import('Module:Logic') +local Ordinal = Lua.import('Module:Ordinal') + +local Component = Lua.import('Module:Widget/Component') +local HtmlWidgets = Lua.import('Module:Widget/Html') +local TeamDisplay = Lua.import('Module:Widget/TeamDisplay/Block') +local WidgetUtil = Lua.import('Module:Widget/Util') + +local Div = HtmlWidgets.Div +local Span = HtmlWidgets.Span + +local Helpers = {} + +---@class PoiLabelProps +---@field poiData PoiData +---@field draftArgs table +---@field date string|number? +---@field isMobile boolean +---@field scale number + +---@private +---@param displayName string +---@return HtmlNode +function Helpers._renderPoiNameNode(displayName) + return Span { + css = { ['font-size'] = '10pt' }, + children = displayName, + } +end + +---@private +---@param props PoiLabelProps +---@param displayName string +---@param teamName string +---@param seed string|number|nil +---@return Renderable|Renderable[] +function Helpers._renderPickedLabel(props, displayName, teamName, seed) + if props.isMobile then + return Helpers._renderPoiNameNode(displayName) + end + + local seedNode = Logic.isNotEmpty(seed) and ('#' .. Ordinal.toOrdinal(seed)) or nil + + return WidgetUtil.collect( + Div { + css = { + display = 'flex', + ['align-items'] = 'center', + ['justify-content'] = 'center', + gap = '0.25em', + }, + children = WidgetUtil.collect( + seedNode, + TeamDisplay { + name = teamName, + style = 'short', + date = props.date, + } + ), + }, + Helpers._renderPoiNameNode(displayName) + ) +end + +---@param props PoiLabelProps +---@return HtmlNode +local function PoiLabel(props) + local poi = props.poiData + local displayName = props.isMobile and poi.mobileName or poi.name + + local teamName = Logic.isNotEmpty(props.draftArgs[poi.name .. ' team']) + and tostring(props.draftArgs[poi.name .. ' team']) + or nil + + local content + local classes + + if teamName then + content = Helpers._renderPickedLabel(props, displayName, teamName, props.draftArgs[poi.name .. ' seed']) + classes = { + 'brkts-opponent-hover', + tostring(props.draftArgs[poi.name .. ' rotation']) == '1' + and 'poi-label-rotation-one' + or 'poi-label-rotation-two', + } + else + content = Helpers._renderPoiNameNode(displayName) + classes = { 'brkts-opponent-hover', 'poi-label-rotation-two' } + end + + local x = props.isMobile and (poi.mobileX or poi.x) or poi.x + local y = props.isMobile and (poi.mobileY or poi.y) or poi.y + + return Div { + css = { + position = 'absolute', + left = tostring(math.floor(x * props.scale)) .. 'px', + top = tostring(math.floor(y * props.scale)) .. 'px', + transform = 'translate(-50%, -50%)', + ['text-align'] = 'center', + }, + children = { + Div { + classes = classes, + attributes = teamName and { ['aria-label'] = teamName } or nil, + css = { + ['font-weight'] = 'bold', + ['line-height'] = '1', + }, + children = content, + }, + }, + } +end + +return Component.component(PoiLabel) diff --git a/lua/wikis/apexlegends/Widget/POIDraft/POIMap.lua b/lua/wikis/apexlegends/Widget/POIDraft/POIMap.lua new file mode 100644 index 00000000000..b77f1ca09cc --- /dev/null +++ b/lua/wikis/apexlegends/Widget/POIDraft/POIMap.lua @@ -0,0 +1,225 @@ +--- +-- @Liquipedia +-- page=Module:Widget/POIDraft/POIMap +-- +-- Please see https://github.com/Liquipedia/Lua-Modules to contribute +-- + +local Lua = require('Module:Lua') + +local Array = Lua.import('Module:Array') +local DateExt = Lua.import('Module:Date/Ext') +local Logic = Lua.import('Module:Logic') + +local Component = Lua.import('Module:Widget/Component') +local HtmlWidgets = Lua.import('Module:Widget/Html') +local Image = Lua.import('Module:Widget/Image/Icon/Image') +local PoiLabel = Lua.import('Module:Widget/POIDraft/POILabel') +local TableWidgets = Lua.import('Module:Widget/Table2/All') +local WidgetUtil = Lua.import('Module:Widget/Util') + +local Div = HtmlWidgets.Div + +---@type table +local MAPS_DATA = Lua.import('Module:POIDraft/POIMap/Data', { loadData = true }) + +local Helpers = {} + +---@class PoiMapProps +---@field map string +---@field [string] any + +---@private +---@param item POIDraftDateBoundItem +---@param contextTimestamp integer? +---@return boolean +local function isActiveOnDate(item, contextTimestamp) + if not item.startDate and not item.endDate then + return true + end + + if not contextTimestamp then + return false + end + + local startTimestamp = item.startDate and DateExt.readTimestampOrNil(item.startDate) + local endTimestamp = item.endDate and DateExt.readTimestampOrNil(item.endDate) + + return (not startTimestamp or contextTimestamp >= startTimestamp) + and (not endTimestamp or contextTimestamp < endTimestamp) +end + +---@generic T: POIDraftDateBoundItem +---@param items T[] +---@param date string|number? +---@return T? +function Helpers.filterActiveItem(items, date) + local contextTimestamp = DateExt.readTimestampOrNil(date) + local defaultItem + + for _, item in ipairs(items) do + if not item.startDate and not item.endDate then + defaultItem = item + elseif isActiveOnDate(item, contextTimestamp) then + return item + end + end + + return defaultItem +end + +---@generic T: POIDraftDateBoundItem +---@param items T[] +---@param date string|number? +---@return T[] +function Helpers.filterActiveItems(items, date) + local contextTimestamp = DateExt.readTimestampOrNil(date) + + return Array.filter(items, function(item) + return isActiveOnDate(item, contextTimestamp) + end) +end + +---@param pois PoiData[] +---@param args table +---@param date string|number? +---@return PoiData[] +function Helpers.getDraftPois(pois, args, date) + local activePois = Helpers.filterActiveItems(pois, date) + + return Array.filter(activePois, function(poi) + if poi.hideIfAny then + for _, argKey in ipairs(poi.hideIfAny) do + if Logic.isNotEmpty(args[argKey]) then + return false + end + end + end + + if poi.hideIfAllMissing then + local allMissing = true + for _, argKey in ipairs(poi.hideIfAllMissing) do + if Logic.isNotEmpty(args[argKey]) then + allMissing = false + break + end + end + if allMissing then + return false + end + end + + return true + end) +end + +---@private +---@param mapData PoiMapData +---@param contextDate string|number +---@return string? +function Helpers._getCurrentMapImage(mapData, contextDate) + local activeImage = Helpers.filterActiveItem(mapData.image, contextDate) + return activeImage and activeImage.file or nil +end + +---@private +---@param props PoiMapProps +---@param mapData PoiMapData +---@param currentImage string +---@param isMobile boolean +---@param contextDate string|number +---@return HtmlNode +function Helpers._renderMapContainer(props, mapData, currentImage, isMobile, contextDate) + local width = isMobile and mapData.mobileWidth or mapData.width + local poisToRender = Helpers.getDraftPois(mapData.pois, props, contextDate) + + return Div { + classes = { + isMobile and 'mobile-only' or 'mobile-hide', + 'nounderlines', + 'dynamicmap', + 'transparent-bg', + }, + css = { + position = 'relative', + width = tostring(width) .. 'px', + margin = 'auto', + }, + children = WidgetUtil.collect( + Div { + children = { + Image { + imageLight = currentImage, + size = tostring(width) .. 'px', + alt = mapData.name, + }, + }, + }, + Array.map(poisToRender, function(poiData) + return PoiLabel { + poiData = poiData, + draftArgs = props, + date = contextDate, + isMobile = isMobile, + scale = width, + } + end) + ), + } +end + +---@param props PoiMapProps +---@return Renderable? +local function PoiMap(props) + local mapData = MAPS_DATA[props.map] + if not mapData then + return nil + end + + local contextDate = DateExt.getContextualDateOrNow() + + local currentImage = Helpers._getCurrentMapImage(mapData, contextDate) + if not currentImage then + return nil + end + + return TableWidgets.Table { + columns = { + { align = 'center' }, + }, + children = { + TableWidgets.TableHeader { + children = { + TableWidgets.Row { + children = { + TableWidgets.CellHeader { children = mapData.name }, + }, + }, + }, + }, + TableWidgets.TableBody { + children = { + TableWidgets.Row { + children = { + TableWidgets.Cell { + css = { padding = 0 }, + children = WidgetUtil.collect( + Helpers._renderMapContainer(props, mapData, currentImage, false, contextDate), + Helpers._renderMapContainer(props, mapData, currentImage, true, contextDate) + ), + }, + }, + }, + }, + }, + }, + } +end + +local PoiMapComponent = Component.component(PoiMap) +---@cast PoiMapComponent +table +PoiMapComponent.filterActiveItem = Helpers.filterActiveItem +PoiMapComponent.filterActiveItems = Helpers.filterActiveItems +PoiMapComponent.getDraftPois = Helpers.getDraftPois + +return PoiMapComponent diff --git a/lua/wikis/apexlegends/Widget/POIDraft/Table.lua b/lua/wikis/apexlegends/Widget/POIDraft/Table.lua new file mode 100644 index 00000000000..e07dd8775c7 --- /dev/null +++ b/lua/wikis/apexlegends/Widget/POIDraft/Table.lua @@ -0,0 +1,131 @@ +--- +-- @Liquipedia +-- page=Module:Widget/POIDraft/Table +-- +-- Please see https://github.com/Liquipedia/Lua-Modules to contribute +-- + +local Lua = require('Module:Lua') + +local Array = Lua.import('Module:Array') +local DateExt = Lua.import('Module:Date/Ext') +local Logic = Lua.import('Module:Logic') +local Ordinal = Lua.import('Module:Ordinal') + +local Component = Lua.import('Module:Widget/Component') +local HtmlWidgets = Lua.import('Module:Widget/Html') +local PoiMap = Lua.import('Module:Widget/POIDraft/POIMap') +local TableWidgets = Lua.import('Module:Widget/Table2/All') +local Team = Lua.import('Module:Widget/TeamDisplay/Block') + +local Abbr = HtmlWidgets.Abbr +local I = HtmlWidgets.I + +---@type table +local MAPS_DATA = Lua.import('Module:POIDraft/POIMap/Data', { loadData = true }) + +local Helpers = {} + +---@class WidgetPoiDraftProps +---@field map string +---@field [string] any + +---@private +---@param props WidgetPoiDraftProps +---@param poiName string +---@param contextDate string|number +---@return Renderable? +function Helpers._row(props, poiName, contextDate) + local team = props[poiName .. ' team'] + + if Logic.isEmpty(team) then + return nil + end + + ---@cast team string + + local isFirstPick = tostring(props[poiName .. ' rotation']) == '1' + local priorityIcon = isFirstPick and I { + classes = { 'fas', 'fa-check', 'forest-green-text' }, + } or nil + + return TableWidgets.Row { + classes = { 'brkts-opponent-hover' }, + attributes = { ['aria-label'] = team }, + children = { + TableWidgets.Cell { children = priorityIcon }, + TableWidgets.Cell { children = Ordinal.toOrdinal(props[poiName .. ' seed']) }, + TableWidgets.Cell { + children = Team { + style = 'short', + name = team, + date = contextDate, + }, + }, + TableWidgets.Cell { children = poiName }, + }, + } +end + +---@param props WidgetPoiDraftProps +---@return Renderable? +local function PoiDraft(props) + local mapData = MAPS_DATA[props.map] + + if not mapData then + return nil + end + + local contextDate = DateExt.getContextualDateOrNow() + + local rows = {} + Array.forEach(PoiMap.getDraftPois(mapData.pois, props, contextDate), function(poi) + local row = Helpers._row(props, poi.name, contextDate) + if row then + table.insert(rows, row) + end + end) + + if Logic.isEmpty(rows) then + return nil + end + + return TableWidgets.Table { + striped = true, + columns = { + { shrink = true, align = 'center' }, + { shrink = true, align = 'center' }, + { align = 'left' }, + { align = 'left' }, + }, + children = { + TableWidgets.TableHeader { + children = { + TableWidgets.Row { + children = { + TableWidgets.CellHeader { + children = Abbr { + title = 'First Choice Priority Pick', + children = '1st', + }, + }, + TableWidgets.CellHeader { children = 'Pick' }, + TableWidgets.CellHeader { children = 'Team' }, + TableWidgets.CellHeader { + children = Abbr { + title = 'Point of Interest', + children = 'POI', + }, + }, + }, + }, + }, + }, + TableWidgets.TableBody { + children = rows, + }, + }, + } +end + +return Component.component(PoiDraft)