Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
37 changes: 34 additions & 3 deletions lua/wikis/commons/Widget/Component.lua
Original file line number Diff line number Diff line change
Expand Up @@ -19,8 +19,8 @@
---@alias ContextDef<T> {defaultValue: T}

---@alias Component<P> fun(props?: P, context: Context?): VNode<P>
---@alias ContextComponent<T> Component<{def: ContextDef<T>, value: T, children: Renderable[]}>
---@alias HtmlComponent Component<{classes?: string[], css?: table, attributes?: table, children?: Renderable[]}>
---@alias ContextComponent<T> Component<{def: ContextDef<T>, value: T, children?: Renderable|Renderable[]?}>
---@alias HtmlComponent Component<{classes?: string[], css?: table, attributes?: table, children?: Renderable|Renderable[]}>

local Lua = require('Module:Lua')
local Renderer = Lua.import('Module:Widget/Renderer')
Expand All @@ -40,17 +40,48 @@ ComponentCore.VNodeMT = {
}
}

--- Highly efficient check for if a node is actually an array of nodes, or just a single node
---@param node Renderable|Renderable[]|nil
---@return boolean
local function isSingleNode(node)
if type(node) ~= 'table' then
return true
end

-- VNodes always have this key
if node.renderFn ~= nil then
return true
end

---@cast node -VNode

-- Widget (render) and mw.html (_build)
if node.render ~= nil or node._build ~= nil then
return true
end

---@cast node -Html
---@cast node -Widget

-- Array is the only allowed type of Renderable left
return false
end

-- Component Definitions
ComponentCore.ComponentMT = {
__call = function(self, props)
props = props or {}

-- Apply DefaultProps via lightweight metatable
-- Only shallow default props allowed
-- Only shallow default props allowed, or empty tables
if self.defaultProps then
setmetatable(props, { __index = self.defaultProps })
end

if isSingleNode(props.children) then
props.children = { props.children }
end

return setmetatable({
renderFn = self.renderFn,
props = props
Expand Down
12 changes: 5 additions & 7 deletions lua/wikis/commons/Widget/Contexts/Table2.lua
Original file line number Diff line number Diff line change
Expand Up @@ -6,13 +6,11 @@
--

local Lua = require('Module:Lua')

local Class = Lua.import('Module:Class')
local Context = Lua.import('Module:Widget/Context')
local Context = Lua.import('Module:Widget/ComponentContext')

return {
BodyStripe = Class.new(Context),
ColumnContext = Class.new(Context),
HeaderRowKind = Class.new(Context),
Section = Class.new(Context),
BodyStripe = Context.create('disabled'),
ColumnContext = Context.create({}),
HeaderRowKind = Context.create('title'),
Section = Context.create('head'),
}
16 changes: 8 additions & 8 deletions lua/wikis/commons/Widget/Table2/All.lua
Original file line number Diff line number Diff line change
Expand Up @@ -5,15 +5,15 @@
-- Please see https://github.com/Liquipedia/Lua-Modules to contribute
--

local Widgets = {}
local Components = {}

local Lua = require('Module:Lua')

Widgets.Table = Lua.import('Module:Widget/Table2/Table')
Widgets.TableHeader = Lua.import('Module:Widget/Table2/TableHeader')
Widgets.TableBody = Lua.import('Module:Widget/Table2/TableBody')
Widgets.Row = Lua.import('Module:Widget/Table2/Row')
Widgets.CellHeader = Lua.import('Module:Widget/Table2/CellHeader')
Widgets.Cell = Lua.import('Module:Widget/Table2/Cell')
Components.Table = Lua.import('Module:Widget/Table2/Table')
Components.TableHeader = Lua.import('Module:Widget/Table2/TableHeader')
Components.TableBody = Lua.import('Module:Widget/Table2/TableBody')
Components.Row = Lua.import('Module:Widget/Table2/Row')
Components.CellHeader = Lua.import('Module:Widget/Table2/CellHeader')
Components.Cell = Lua.import('Module:Widget/Table2/Cell')

return Widgets
return Components
39 changes: 17 additions & 22 deletions lua/wikis/commons/Widget/Table2/Cell.lua
Original file line number Diff line number Diff line change
Expand Up @@ -7,15 +7,15 @@

local Lua = require('Module:Lua')

local Class = Lua.import('Module:Class')
local Component = Lua.import('Module:Widget/Component')
local Context = Lua.import('Module:Widget/ComponentContext')

local Widget = Lua.import('Module:Widget')
local HtmlWidgets = Lua.import('Module:Widget/Html/All')
local Html = Lua.import('Module:Widget/Html')
local Table2Contexts = Lua.import('Module:Widget/Contexts/Table2')
local ColumnUtil = Lua.import('Module:Widget/Table2/ColumnUtil')

---@class Table2CellProps
---@field children Renderable[]?
---@field children Renderable[]
Copy link
Copy Markdown
Collaborator

@hjpalpha hjpalpha May 7, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

we use it with non arrays quite often currently

---@field align ('left'|'right'|'center')?
---@field shrink (string|number|boolean)?
---@field nowrap (string|number|boolean)?
Expand All @@ -29,24 +29,14 @@ local ColumnUtil = Lua.import('Module:Widget/Table2/ColumnUtil')
---@field css {[string]: string|number|nil}?
---@field attributes {[string]: any}?

---@class Table2Cell: Widget
---@operator call(Table2CellProps): Table2Cell
---@field props Table2CellProps
local Table2Cell = Class.new(Widget)

Table2Cell.defaultProps = {
nowrap = true,
}

---@return Widget
function Table2Cell:render()
local props = self.props

local columns = self:useContext(Table2Contexts.ColumnContext)
---@param props Table2CellProps
---@return Renderable
local function Table2Cell(props, context)
local columns = Context.read(context, Table2Contexts.ColumnContext)

-- Skip context lookups and property merging if there are no column definitions
if not columns then
return HtmlWidgets.Td{
if #columns == 0 then
return Html.Td{
attributes = ColumnUtil.buildCellAttributes(
props.align,
props.nowrap,
Expand Down Expand Up @@ -76,12 +66,17 @@ function Table2Cell:render()
ColumnUtil.buildAttributes(mergedProps)
)

return HtmlWidgets.Td{
return Html.Td{
classes = mergedProps.classes,
css = css,
attributes = attributes,
children = mergedProps.children,
}
end

return Table2Cell
return Component.component(
Table2Cell,
{
nowrap = true,
}
)
38 changes: 17 additions & 21 deletions lua/wikis/commons/Widget/Table2/CellHeader.lua
Original file line number Diff line number Diff line change
Expand Up @@ -7,16 +7,17 @@

local Lua = require('Module:Lua')

local Class = Lua.import('Module:Class')
local Component = Lua.import('Module:Widget/Component')
local Context = Lua.import('Module:Widget/ComponentContext')

local Logic = Lua.import('Module:Logic')

local Widget = Lua.import('Module:Widget')
local HtmlWidgets = Lua.import('Module:Widget/Html/All')
local Html = Lua.import('Module:Widget/Html')
local Table2Contexts = Lua.import('Module:Widget/Contexts/Table2')
local ColumnUtil = Lua.import('Module:Widget/Table2/ColumnUtil')

---@class Table2CellHeaderProps
---@field children Renderable[]?
---@field children Renderable[]
---@field section 'head'|'body'|'subhead'?
---@field align ('left'|'right'|'center')?
---@field shrink (string|number|boolean)?
Expand All @@ -33,29 +34,22 @@ local ColumnUtil = Lua.import('Module:Widget/Table2/ColumnUtil')
---@field rowspan integer|string?
---@field columnIndex integer|string?

---@class Table2CellHeader: Widget
---@operator call(Table2CellHeaderProps): Table2CellHeader
---@field props Table2CellHeaderProps
local Table2CellHeader = Class.new(Widget)

---@return Widget
function Table2CellHeader:render()
local props = self.props

local columns = self:useContext(Table2Contexts.ColumnContext)
local section = props.section or self:useContext(Table2Contexts.Section)
---@param props Table2CellHeaderProps
---@return Renderable
local function Table2CellHeader(props, context)
local columns = Context.read(context, Table2Contexts.ColumnContext)
local section = props.section or Context.read(context, Table2Contexts.Section)

local children = props.children

if section == 'subhead' then
children = {HtmlWidgets.Div{
children = {Html.Div{
classes = {'table2__subheader-cell'},
children = props.children,
}}
end

-- Skip context lookups and property merging if there are no column definitions
if not columns then
if #columns == 0 then
local align = props.align
local attributes = props.attributes or {}
if align == 'right' or align == 'center' then
Expand All @@ -72,7 +66,7 @@ function Table2CellHeader:render()
attributes
)

return HtmlWidgets.Th{
return Html.Th{
attributes = attributes,
children = children,
}
Expand Down Expand Up @@ -108,12 +102,14 @@ function Table2CellHeader:render()
attributes
)

return HtmlWidgets.Th{
return Html.Th{
classes = mergedProps.classes,
css = css,
attributes = attributes,
children = children,
}
end

return Table2CellHeader
return Component.component(
Table2CellHeader
)
54 changes: 30 additions & 24 deletions lua/wikis/commons/Widget/Table2/Row.lua
Original file line number Diff line number Diff line change
Expand Up @@ -6,38 +6,34 @@
--

local Lua = require('Module:Lua')
local Component = Lua.import('Module:Widget/Component')
local Context = Lua.import('Module:Widget/ComponentContext')

local Array = Lua.import('Module:Array')
local Class = Lua.import('Module:Class')
local Logic = Lua.import('Module:Logic')
local MathUtil = Lua.import('Module:MathUtil')

local Widget = Lua.import('Module:Widget')
local Table2Contexts = Lua.import('Module:Widget/Contexts/Table2')
local Table2Cell = Lua.import('Module:Widget/Table2/Cell')
local Table2CellHeader = Lua.import('Module:Widget/Table2/CellHeader')
local WidgetUtil = Lua.import('Module:Widget/Util')
local HtmlWidgets = Lua.import('Module:Widget/Html/All')
local Html = Lua.import('Module:Widget/Html')

---@class Table2RowProps
---@field children Renderable[]?
---@field children Renderable[]
---@field section 'head'|'body'|'subhead'?
---@field classes string[]?
---@field css {[string]: string|number|nil}?
---@field attributes {[string]: any}?
---@field highlighted (string|number|boolean)?

---@class Table2Row: Widget
---@operator call(Table2RowProps): Table2Row
---@field props Table2RowProps
local Table2Row = Class.new(Widget)

---@return Widget
function Table2Row:render()
local props = self.props
local section = props.section or self:useContext(Table2Contexts.Section)
local headerRowKind = self:useContext(Table2Contexts.HeaderRowKind)
local bodyStripe = self:useContext(Table2Contexts.BodyStripe)
---@param props Table2RowProps
---@param context Context
---@return Renderable
local function Table2Row(props, context)
local section = props.section or Context.read(context, Table2Contexts.Section)
local headerRowKind = Context.read(context, Table2Contexts.HeaderRowKind)
local bodyStripe = Context.read(context, Table2Contexts.BodyStripe)

local sectionClass = 'table2__row--body'
if section == 'head' or section == 'subhead' then
Expand Down Expand Up @@ -69,18 +65,24 @@ function Table2Row:render()

local children = props.children or {}
Comment thread
Rathoz marked this conversation as resolved.

local columns = self:useContext(Table2Contexts.ColumnContext)
if section == 'subhead' and columns and #children == 1 and Class.instanceOf(children[1], Table2CellHeader) then
local singleCell = children[1] --[[@as Table2CellHeader]]
local columns = Context.read(context, Table2Contexts.ColumnContext)
if section == 'subhead' and #columns > 0 and #children == 1 and
---@diagnostic disable-next-line: undefined-field
type(children[1]) == 'table' and children[1].renderFn == Table2CellHeader.renderFn then

local singleCell = children[1]
if singleCell.props.colspan == nil then
singleCell.props.colspan = #columns
end
Comment thread
Rathoz marked this conversation as resolved.
end

local columnIndex = 1
local indexedChildren = Array.map(children, function(child)
if Class.instanceOf(child, Table2Cell) or Class.instanceOf(child, Table2CellHeader) then
local cellChild = child --[[@as Table2Cell|Table2CellHeader]]
if type(child) == 'table' and
---@diagnostic disable-next-line: undefined-field
(child.renderFn == Table2Cell.renderFn or child.renderFn == Table2CellHeader.renderFn) then

local cellChild = child
local explicitIndex = MathUtil.toInteger(cellChild.props.columnIndex)
if explicitIndex and explicitIndex >= 1 then
columnIndex = math.max(columnIndex, explicitIndex)
Expand All @@ -101,8 +103,10 @@ function Table2Row:render()
local trChildren = indexedChildren
if section == 'subhead' then
trChildren = Array.map(trChildren, function(child)
if Class.instanceOf(child, Table2CellHeader) then
return Table2Contexts.Section{
---@diagnostic disable-next-line: undefined-field
if type(child) == 'table' and child.renderFn == Table2Cell.renderFn then
return Context.Provider{
def = Table2Contexts.Section,
value = 'subhead',
children = {child},
}
Comment thread
Rathoz marked this conversation as resolved.
Expand All @@ -111,12 +115,14 @@ function Table2Row:render()
end)
end

return HtmlWidgets.Tr{
return Html.Tr{
classes = WidgetUtil.collect(sectionClass, kindClass, stripeClass, highlightClass, props.classes),
css = props.css,
attributes = props.attributes,
children = trChildren,
}
end

return Table2Row
return Component.component(
Table2Row
)
Loading
Loading