consolidated patterns

This commit is contained in:
Michael Freno
2025-11-18 16:01:39 -05:00
parent 5bb1162e06
commit 0b0f0e1eb7
9 changed files with 220 additions and 171 deletions

View File

@@ -89,25 +89,16 @@ if ImageDataReader.init then
ImageDataReader.init(errorHandlerDeps) ImageDataReader.init(errorHandlerDeps)
end end
-- Initialize Units module with Context dependency -- Initialize modules with dependencies
Units.initialize(Context) Units.init({ Context = Context, ErrorHandler = ErrorHandler })
Units.initializeErrorHandler(ErrorHandler) Color.init({ ErrorHandler = ErrorHandler })
utils.init({ ErrorHandler = ErrorHandler })
-- Initialize ErrorHandler for Color module Animation.init({ ErrorHandler = ErrorHandler, Easing = Easing, Color = Color })
Color.initializeErrorHandler(ErrorHandler) AnimationGroup.init({ ErrorHandler = ErrorHandler })
-- Initialize ErrorHandler for utils
utils.initializeErrorHandler(ErrorHandler)
-- Initialize ErrorHandler for Animation module
Animation.initializeErrorHandler(ErrorHandler)
-- Initialize ErrorHandler for AnimationGroup module
AnimationGroup.initializeErrorHandler(ErrorHandler)
-- Add version and metadata -- Add version and metadata
flexlove._VERSION = "0.2.3" flexlove._VERSION = "0.2.3"
flexlove._DESCRIPTION = "0I Library for LÖVE Framework based on flexbox" flexlove._DESCRIPTION = "UI Library for LÖVE Framework based on flexbox"
flexlove._URL = "https://github.com/mikefreno/FlexLove" flexlove._URL = "https://github.com/mikefreno/FlexLove"
flexlove._LICENSE = [[ flexlove._LICENSE = [[
MIT License MIT License

View File

@@ -1,11 +1,6 @@
--- Easing function type
---@alias EasingFunction fun(t: number): number
-- ErrorHandler dependency (injected via initializeErrorHandler)
local ErrorHandler = nil local ErrorHandler = nil
local Easing = nil
-- Easing module for easing functions local Color = nil
local Easing = require("modules.Easing")
---@class Keyframe ---@class Keyframe
---@field at number Normalized time position (0-1) ---@field at number Normalized time position (0-1)
---@field values table Property values at this keyframe ---@field values table Property values at this keyframe
@@ -231,15 +226,21 @@ end
---@param ColorModule table Color module reference ---@param ColorModule table Color module reference
---@return any interpolated Interpolated Color instance ---@return any interpolated Interpolated Color instance
local function lerpColor(startColor, finalColor, easedT, ColorModule) local function lerpColor(startColor, finalColor, easedT, ColorModule)
if not ColorModule then -- Use provided ColorModule or fall back to module-level Color or static _ColorModule
return nil local CM = ColorModule or Color or Animation._ColorModule
if not CM or not CM.parse or not CM.lerp then
if ErrorHandler then
ErrorHandler.warn("Animation", "Color module not properly initialized. Cannot interpolate colors.")
end
return startColor -- Return start color as fallback
end end
-- Parse colors if needed -- Parse colors if needed
local colorA = ColorModule.parse(startColor) local colorA = CM.parse(startColor)
local colorB = ColorModule.parse(finalColor) local colorB = CM.parse(finalColor)
return ColorModule.lerp(colorA, colorB, easedT) return CM.lerp(colorA, colorB, easedT)
end end
--- Helper function to interpolate table values (padding, margin, cornerRadius) --- Helper function to interpolate table values (padding, margin, cornerRadius)
@@ -252,8 +253,12 @@ local function lerpTable(startTable, finalTable, easedT)
-- Iterate through all keys in both tables -- Iterate through all keys in both tables
local keys = {} local keys = {}
for k in pairs(startTable) do keys[k] = true end for k in pairs(startTable) do
for k in pairs(finalTable) do keys[k] = true end keys[k] = true
end
for k in pairs(finalTable) do
keys[k] = true
end
for key in pairs(keys) do for key in pairs(keys) do
local startVal = startTable[key] local startVal = startTable[key]
@@ -305,36 +310,60 @@ function Animation:lerpKeyframes(prevFrame, nextFrame, easedT)
-- Get all unique property keys -- Get all unique property keys
local keys = {} local keys = {}
for k in pairs(prevFrame.values) do keys[k] = true end for k in pairs(prevFrame.values) do
for k in pairs(nextFrame.values) do keys[k] = true end keys[k] = true
end
for k in pairs(nextFrame.values) do
keys[k] = true
end
-- Define properties that should be animated as numbers -- Define properties that should be animated as numbers
local numericProperties = { local numericProperties = {
"width", "height", "opacity", "x", "y", "width",
"gap", "imageOpacity", "scrollbarWidth", "height",
"borderWidth", "fontSize", "lineHeight" "opacity",
"x",
"y",
"gap",
"imageOpacity",
"scrollbarWidth",
"borderWidth",
"fontSize",
"lineHeight",
} }
-- Define properties that should be animated as Colors -- Define properties that should be animated as Colors
local colorProperties = { local colorProperties = {
"backgroundColor", "borderColor", "textColor", "backgroundColor",
"scrollbarColor", "scrollbarBackgroundColor", "imageTint" "borderColor",
"textColor",
"scrollbarColor",
"scrollbarBackgroundColor",
"imageTint",
} }
-- Define properties that should be animated as tables -- Define properties that should be animated as tables
local tableProperties = { local tableProperties = {
"padding", "margin", "cornerRadius" "padding",
"margin",
"cornerRadius",
} }
-- Create lookup sets for faster property type checking -- Create lookup sets for faster property type checking
local numericSet = {} local numericSet = {}
for _, prop in ipairs(numericProperties) do numericSet[prop] = true end for _, prop in ipairs(numericProperties) do
numericSet[prop] = true
end
local colorSet = {} local colorSet = {}
for _, prop in ipairs(colorProperties) do colorSet[prop] = true end for _, prop in ipairs(colorProperties) do
colorSet[prop] = true
end
local tableSet = {} local tableSet = {}
for _, prop in ipairs(tableProperties) do tableSet[prop] = true end for _, prop in ipairs(tableProperties) do
tableSet[prop] = true
end
-- Interpolate each property -- Interpolate each property
for key in pairs(keys) do for key in pairs(keys) do
@@ -343,9 +372,9 @@ function Animation:lerpKeyframes(prevFrame, nextFrame, easedT)
if numericSet[key] and type(startVal) == "number" and type(finalVal) == "number" then if numericSet[key] and type(startVal) == "number" and type(finalVal) == "number" then
result[key] = lerpNumber(startVal, finalVal, easedT) result[key] = lerpNumber(startVal, finalVal, easedT)
elseif colorSet[key] and self._Color then elseif colorSet[key] and (self._Color or Animation._ColorModule) then
if startVal ~= nil and finalVal ~= nil then if startVal ~= nil and finalVal ~= nil then
result[key] = lerpColor(startVal, finalVal, easedT, self._Color) result[key] = lerpColor(startVal, finalVal, easedT, self._Color or Animation._ColorModule)
end end
elseif tableSet[key] and type(startVal) == "table" and type(finalVal) == "table" then elseif tableSet[key] and type(startVal) == "table" and type(finalVal) == "table" then
result[key] = lerpTable(startVal, finalVal, easedT) result[key] = lerpTable(startVal, finalVal, easedT)
@@ -375,7 +404,7 @@ function Animation:interpolate()
local t = math.min(self.elapsed / self.duration, 1) local t = math.min(self.elapsed / self.duration, 1)
-- Handle keyframe animations -- Handle keyframe animations
if self.keyframes and #self.keyframes >= 2 then if self.keyframes and type(self.keyframes) == "table" and #self.keyframes >= 2 then
local prevFrame, nextFrame = self:findKeyframes(t) local prevFrame, nextFrame = self:findKeyframes(t)
if prevFrame and nextFrame then if prevFrame and nextFrame then
@@ -433,20 +462,34 @@ function Animation:interpolate()
-- Define properties that should be animated as numbers -- Define properties that should be animated as numbers
local numericProperties = { local numericProperties = {
"width", "height", "opacity", "x", "y", "width",
"gap", "imageOpacity", "scrollbarWidth", "height",
"borderWidth", "fontSize", "lineHeight" "opacity",
"x",
"y",
"gap",
"imageOpacity",
"scrollbarWidth",
"borderWidth",
"fontSize",
"lineHeight",
} }
-- Define properties that should be animated as Colors -- Define properties that should be animated as Colors
local colorProperties = { local colorProperties = {
"backgroundColor", "borderColor", "textColor", "backgroundColor",
"scrollbarColor", "scrollbarBackgroundColor", "imageTint" "borderColor",
"textColor",
"scrollbarColor",
"scrollbarBackgroundColor",
"imageTint",
} }
-- Define properties that should be animated as tables -- Define properties that should be animated as tables
local tableProperties = { local tableProperties = {
"padding", "margin", "cornerRadius" "padding",
"margin",
"cornerRadius",
} }
-- Interpolate numeric properties -- Interpolate numeric properties
@@ -460,13 +503,14 @@ function Animation:interpolate()
end end
-- Interpolate color properties (if Color module is available) -- Interpolate color properties (if Color module is available)
if self._Color then local ColorModule = self._Color or Animation._ColorModule
if ColorModule then
for _, prop in ipairs(colorProperties) do for _, prop in ipairs(colorProperties) do
local startVal = self.start[prop] local startVal = self.start[prop]
local finalVal = self.final[prop] local finalVal = self.final[prop]
if startVal ~= nil and finalVal ~= nil then if startVal ~= nil and finalVal ~= nil then
result[prop] = lerpColor(startVal, finalVal, easedT, self._Color) result[prop] = lerpColor(startVal, finalVal, easedT, ColorModule)
end end
end end
end end
@@ -778,7 +822,7 @@ function Animation.keyframes(props)
ErrorHandler.warn("Animation", "Keyframe animation requires at least 2 keyframes. Using empty animation.") ErrorHandler.warn("Animation", "Keyframe animation requires at least 2 keyframes. Using empty animation.")
props.keyframes = { props.keyframes = {
{ at = 0, values = {} }, { at = 0, values = {} },
{at = 1, values = {}} { at = 1, values = {} },
} }
end end
@@ -790,7 +834,9 @@ function Animation.keyframes(props)
end end
end end
table.sort(sortedKeyframes, function(a, b) return a.at < b.at end) table.sort(sortedKeyframes, function(a, b)
return a.at < b.at
end)
-- Ensure keyframes start at 0 and end at 1 -- Ensure keyframes start at 0 and end at 1
if #sortedKeyframes > 0 then if #sortedKeyframes > 0 then
@@ -815,13 +861,22 @@ function Animation.keyframes(props)
}) })
end end
--- Initialize ErrorHandler dependency --- Initialize dependencies
---@param errorHandler table The ErrorHandler module ---@param deps table Dependencies: { ErrorHandler = ErrorHandler, Easing = Easing, Color = Color? }
local function initializeErrorHandler(errorHandler) function Animation.init(deps)
ErrorHandler = errorHandler if type(deps) == "table" then
ErrorHandler = deps.ErrorHandler
Easing = deps.Easing
if deps.Color then
Color = deps.Color
Animation._ColorModule = deps.Color
end
end
end end
-- Export ErrorHandler initializer -- Static method for Color module injection (for per-instance Color override)
Animation.initializeErrorHandler = initializeErrorHandler function Animation.setColorModule(ColorModule)
Animation._ColorModule = ColorModule
end
return Animation return Animation

View File

@@ -327,13 +327,12 @@ function AnimationGroup:apply(element)
element.animationGroup = self element.animationGroup = self
end end
--- Initialize ErrorHandler dependency --- Initialize dependencies
---@param errorHandler table The ErrorHandler module ---@param deps table Dependencies: { ErrorHandler = ErrorHandler }
local function initializeErrorHandler(errorHandler) function AnimationGroup.init(deps)
ErrorHandler = errorHandler if type(deps) == "table" then
ErrorHandler = deps.ErrorHandler
end
end end
-- Export ErrorHandler initializer
AnimationGroup.initializeErrorHandler = initializeErrorHandler
return AnimationGroup return AnimationGroup

View File

@@ -1,11 +1,5 @@
local ErrorHandler = nil local ErrorHandler = nil
--- Initialize ErrorHandler dependency
---@param errorHandler table The ErrorHandler module
local function initializeErrorHandler(errorHandler)
ErrorHandler = errorHandler
end
--- Standardized error message formatter (fallback for when ErrorHandler not available) --- Standardized error message formatter (fallback for when ErrorHandler not available)
---@param module string -- Module name (e.g., "Color", "Theme", "Units") ---@param module string -- Module name (e.g., "Color", "Theme", "Units")
---@param message string ---@param message string
@@ -461,7 +455,12 @@ function Color.lerp(colorA, colorB, t)
return Color.new(r, g, b, a) return Color.new(r, g, b, a)
end end
-- Export ErrorHandler initializer --- Initialize dependencies
Color.initializeErrorHandler = initializeErrorHandler ---@param deps table Dependencies: { ErrorHandler = ErrorHandler }
function Color.init(deps)
if type(deps) == "table" then
ErrorHandler = deps.ErrorHandler
end
end
return Color return Color

View File

@@ -5,14 +5,17 @@ local ErrorHandler = nil
--- Initialize Units module with Context dependency --- Initialize Units module with Context dependency
---@param context table The Context module ---@param context table The Context module
function Units.initialize(context) --- Initialize dependencies
Context = context ---@param deps table Dependencies: { Context = Context?, ErrorHandler = ErrorHandler? }
function Units.init(deps)
if type(deps) == "table" then
if deps.Context then
Context = deps.Context
end
if deps.ErrorHandler then
ErrorHandler = deps.ErrorHandler
end
end end
--- Initialize ErrorHandler dependency
---@param errorHandler table The ErrorHandler module
function Units.initializeErrorHandler(errorHandler)
ErrorHandler = errorHandler
end end
---@param value string|number ---@param value string|number

View File

@@ -300,10 +300,12 @@ end
-- Validation utilities -- Validation utilities
local ErrorHandler = nil local ErrorHandler = nil
--- Initialize ErrorHandler dependency for validation utilities --- Initialize dependencies
---@param errorHandler table The ErrorHandler module ---@param deps table Dependencies: { ErrorHandler = ErrorHandler }
local function initializeErrorHandler(errorHandler) local function init(deps)
ErrorHandler = errorHandler if type(deps) == "table" then
ErrorHandler = deps.ErrorHandler
end
end end
--- Validate that a value is in an enum table --- Validate that a value is in an enum table
@@ -1104,7 +1106,7 @@ return {
resolveTextSizePreset = resolveTextSizePreset, resolveTextSizePreset = resolveTextSizePreset,
getModifiers = getModifiers, getModifiers = getModifiers,
TEXT_SIZE_PRESETS = TEXT_SIZE_PRESETS, TEXT_SIZE_PRESETS = TEXT_SIZE_PRESETS,
initializeErrorHandler = initializeErrorHandler, init = init,
validateEnum = validateEnum, validateEnum = validateEnum,
validateRange = validateRange, validateRange = validateRange,
validateType = validateType, validateType = validateType,

View File

@@ -2,18 +2,16 @@ local luaunit = require("testing.luaunit")
require("testing.loveStub") require("testing.loveStub")
local Animation = require("modules.Animation") local Animation = require("modules.Animation")
local Easing = require("modules.Easing")
local Color = require("modules.Color") local Color = require("modules.Color")
local Transform = require("modules.Transform") local Transform = require("modules.Transform")
local ErrorHandler = require("modules.ErrorHandler") local ErrorHandler = require("modules.ErrorHandler")
local ErrorCodes = require("modules.ErrorCodes") local ErrorCodes = require("modules.ErrorCodes")
-- Initialize ErrorHandler -- Initialize modules
ErrorHandler.init({ ErrorCodes = ErrorCodes }) ErrorHandler.init({ ErrorCodes = ErrorCodes })
Animation.initializeErrorHandler(ErrorHandler) Color.init({ ErrorHandler = ErrorHandler })
Color.initializeErrorHandler(ErrorHandler) Animation.init({ ErrorHandler = ErrorHandler, Easing = Easing, Color = Color })
-- Make Color module available to Animation
Animation.setColorModule(Color)
TestAnimationProperties = {} TestAnimationProperties = {}

View File

@@ -2,12 +2,13 @@ local luaunit = require("testing.luaunit")
require("testing.loveStub") require("testing.loveStub")
local Animation = require("modules.Animation") local Animation = require("modules.Animation")
local Easing = require("modules.Easing")
local ErrorHandler = require("modules.ErrorHandler") local ErrorHandler = require("modules.ErrorHandler")
local ErrorCodes = require("modules.ErrorCodes") local ErrorCodes = require("modules.ErrorCodes")
-- Initialize ErrorHandler for Animation module -- Initialize modules
ErrorHandler.init({ ErrorCodes = ErrorCodes }) ErrorHandler.init({ ErrorCodes = ErrorCodes })
Animation.initializeErrorHandler(ErrorHandler) Animation.init({ ErrorHandler = ErrorHandler, Easing = Easing })
TestAnimation = {} TestAnimation = {}

View File

@@ -2,12 +2,13 @@ local luaunit = require("testing.luaunit")
require("testing.loveStub") require("testing.loveStub")
local Animation = require("modules.Animation") local Animation = require("modules.Animation")
local Easing = require("modules.Easing")
local ErrorHandler = require("modules.ErrorHandler") local ErrorHandler = require("modules.ErrorHandler")
local ErrorCodes = require("modules.ErrorCodes") local ErrorCodes = require("modules.ErrorCodes")
-- Initialize ErrorHandler for Animation module -- Initialize modules
ErrorHandler.init({ ErrorCodes = ErrorCodes }) ErrorHandler.init({ ErrorCodes = ErrorCodes })
Animation.initializeErrorHandler(ErrorHandler) Animation.init({ ErrorHandler = ErrorHandler, Easing = Easing })
TestKeyframeAnimation = {} TestKeyframeAnimation = {}