- Added responsive.lua with fluent builder API (:when()/:apply()/:otherwise()) for creating responsive layouts that react to parent size or custom conditions - All elements now use getResolved() to check active states, enabling multiple responsive rules to coexist
157 lines
5.4 KiB
Lua
157 lines
5.4 KiB
Lua
local errorManager = require("errorManager")
|
|
---@configDefault false
|
|
|
|
--- This is the responsive plugin. It provides a fluent builder API for creating responsive states with an intuitive when/apply/otherwise syntax.
|
|
---@class BaseElement
|
|
local BaseElement = {}
|
|
|
|
--- Creates a responsive builder for defining responsive states
|
|
--- @shortDescription Creates a responsive state builder
|
|
--- @param self BaseElement The element to create the builder for
|
|
--- @return ResponsiveBuilder builder The responsive builder instance
|
|
function BaseElement:responsive()
|
|
local builder = {
|
|
_element = self,
|
|
_rules = {},
|
|
_currentStateName = nil,
|
|
_currentCondition = nil,
|
|
_stateCounter = 0
|
|
}
|
|
|
|
--- Defines a condition for responsive behavior
|
|
--- @param condition string|function The condition as string expression or function
|
|
--- @return ResponsiveBuilder self For method chaining
|
|
function builder:when(condition)
|
|
if self._currentCondition then
|
|
errorManager.header = "Responsive Builder Error"
|
|
errorManager.error("Previous when() must be followed by apply() before starting a new when()")
|
|
end
|
|
|
|
self._stateCounter = self._stateCounter + 1
|
|
self._currentStateName = "__responsive_" .. self._stateCounter
|
|
self._currentCondition = condition
|
|
|
|
return self
|
|
end
|
|
|
|
--- Applies properties when the current condition is met
|
|
--- @param properties table The properties to apply {property = value, ...}
|
|
--- @return ResponsiveBuilder self For method chaining
|
|
function builder:apply(properties)
|
|
if not self._currentCondition then
|
|
errorManager.header = "Responsive Builder Error"
|
|
errorManager.error("apply() must follow a when() call")
|
|
end
|
|
|
|
if type(properties) ~= "table" then
|
|
errorManager.header = "Responsive Builder Error"
|
|
errorManager.error("apply() requires a table of properties")
|
|
end
|
|
|
|
self._element:registerResponsiveState(
|
|
self._currentStateName,
|
|
self._currentCondition,
|
|
100
|
|
)
|
|
|
|
for propName, value in pairs(properties) do
|
|
local capitalizedName = propName:sub(1,1):upper() .. propName:sub(2)
|
|
local setter = "set" .. capitalizedName .. "State"
|
|
|
|
if self._element[setter] then
|
|
self._element[setter](self._element, self._currentStateName, value)
|
|
else
|
|
errorManager.header = "Responsive Builder Error"
|
|
errorManager.error("Unknown property: " .. propName)
|
|
end
|
|
end
|
|
|
|
table.insert(self._rules, {
|
|
stateName = self._currentStateName,
|
|
condition = self._currentCondition,
|
|
properties = properties
|
|
})
|
|
|
|
self._currentCondition = nil
|
|
self._currentStateName = nil
|
|
|
|
return self
|
|
end
|
|
|
|
--- Defines a fallback condition (else case)
|
|
--- @param properties table The properties to apply when no other conditions match
|
|
--- @return ResponsiveBuilder self For method chaining
|
|
function builder:otherwise(properties)
|
|
if self._currentCondition then
|
|
errorManager.header = "Responsive Builder Error"
|
|
errorManager.error("otherwise() cannot be used after when() without apply()")
|
|
end
|
|
|
|
if type(properties) ~= "table" then
|
|
errorManager.header = "Responsive Builder Error"
|
|
errorManager.error("otherwise() requires a table of properties")
|
|
end
|
|
|
|
self._stateCounter = self._stateCounter + 1
|
|
local otherwiseStateName = "__responsive_otherwise_" .. self._stateCounter
|
|
|
|
local otherRules = {}
|
|
for _, rule in ipairs(self._rules) do
|
|
table.insert(otherRules, rule.condition)
|
|
end
|
|
|
|
local otherwiseCondition
|
|
if type(otherRules[1]) == "string" then
|
|
local negatedExprs = {}
|
|
for _, cond in ipairs(otherRules) do
|
|
table.insert(negatedExprs, "not (" .. cond .. ")")
|
|
end
|
|
otherwiseCondition = table.concat(negatedExprs, " and ")
|
|
else
|
|
otherwiseCondition = function(elem)
|
|
for _, cond in ipairs(otherRules) do
|
|
if cond(elem) then
|
|
return false
|
|
end
|
|
end
|
|
return true
|
|
end
|
|
end
|
|
|
|
self._element:registerResponsiveState(
|
|
otherwiseStateName,
|
|
otherwiseCondition,
|
|
50
|
|
)
|
|
|
|
for propName, value in pairs(properties) do
|
|
local capitalizedName = propName:sub(1,1):upper() .. propName:sub(2)
|
|
local setter = "set" .. capitalizedName .. "State"
|
|
|
|
if self._element[setter] then
|
|
self._element[setter](self._element, otherwiseStateName, value)
|
|
else
|
|
errorManager.header = "Responsive Builder Error"
|
|
errorManager.error("Unknown property: " .. propName)
|
|
end
|
|
end
|
|
|
|
return self
|
|
end
|
|
|
|
--- Completes the builder (optional, for clarity)
|
|
--- @return BaseElement element The original element
|
|
function builder:done()
|
|
if self._currentCondition then
|
|
errorManager.header = "Responsive Builder Error"
|
|
errorManager.error("Unfinished when() without apply()")
|
|
end
|
|
return self._element
|
|
end
|
|
|
|
return builder
|
|
end
|
|
|
|
return {
|
|
BaseElement = BaseElement
|
|
} |