- Added comprehensive state management with conditional states, priority-based resolution, and property overrides
- 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
This commit is contained in:
157
src/plugins/responsive.lua
Normal file
157
src/plugins/responsive.lua
Normal file
@@ -0,0 +1,157 @@
|
||||
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
|
||||
}
|
||||
Reference in New Issue
Block a user