diff --git a/modules/Element.lua b/modules/Element.lua index db2613e..ccef5a4 100644 --- a/modules/Element.lua +++ b/modules/Element.lua @@ -1310,6 +1310,20 @@ function Element.new(props) end -- Handle positioning properties for elements without parent + -- Warn if CSS positioning properties are used without absolute positioning + if (props.top or props.bottom or props.left or props.right) and not self._explicitlyAbsolute then + local properties = {} + if props.top then table.insert(properties, "top") end + if props.bottom then table.insert(properties, "bottom") end + if props.left then table.insert(properties, "left") end + if props.right then table.insert(properties, "right") end + Element._ErrorHandler:warn("Element", "LAY_011", { + element = self.id or "unnamed", + positioning = self._originalPositioning or "relative", + properties = table.concat(properties, ", "), + }) + end + -- Handle top positioning with units if props.top then local isCalc = Element._Calc and Element._Calc.isCalc(props.top) @@ -1484,6 +1498,18 @@ function Element.new(props) local baseX = self.parent.x + self.parent.padding.left local baseY = self.parent.y + self.parent.padding.top + -- Warn if explicit x/y is set on a child that will be positioned by flex layout + -- This position will be overridden unless the child has positioning="absolute" + local parentWillUseFlex = self.parent.positioning ~= "grid" + local childIsRelative = self.positioning ~= "absolute" or not self._explicitlyAbsolute + if parentWillUseFlex and childIsRelative and (props.x or props.y) then + Element._ErrorHandler:warn("Element", "LAY_008", { + element = self.id or "unnamed", + parent = self.parent.id or "unnamed", + properties = (props.x and props.y) and "x, y" or (props.x and "x" or "y"), + }) + end + if props.x then local isCalc = Element._Calc and Element._Calc.isCalc(props.x) if type(props.x) == "string" or isCalc then @@ -1554,6 +1580,20 @@ function Element.new(props) end -- Handle positioning properties BEFORE adding to parent (so they're available during layout) + -- Warn if CSS positioning properties are used without absolute positioning + if (props.top or props.bottom or props.left or props.right) and not self._explicitlyAbsolute then + local properties = {} + if props.top then table.insert(properties, "top") end + if props.bottom then table.insert(properties, "bottom") end + if props.left then table.insert(properties, "left") end + if props.right then table.insert(properties, "right") end + Element._ErrorHandler:warn("Element", "LAY_011", { + element = self.id or "unnamed", + positioning = self._originalPositioning or "relative", + properties = table.concat(properties, ", "), + }) + end + -- Handle top positioning with units if props.top then local isCalc = Element._Calc and Element._Calc.isCalc(props.top) @@ -1662,6 +1702,15 @@ function Element.new(props) Element._utils.validateEnum(props.justifySelf, Element._utils.enums.JustifySelf, "justifySelf") end + -- Warn if grid properties are set with flex positioning + if props.gridRows or props.gridColumns or props.gridTemplateRows or props.gridTemplateColumns then + Element._ErrorHandler:warn("Element", "LAY_010", { + element = self.id or "unnamed", + positioning = "flex", + properties = "gridRows/gridColumns/gridTemplate*", + }) + end + self.flexDirection = props.flexDirection or Element._utils.enums.FlexDirection.HORIZONTAL self.flexWrap = props.flexWrap or Element._utils.enums.FlexWrap.NOWRAP self.justifyContent = props.justifyContent or Element._utils.enums.JustifyContent.FLEX_START @@ -1672,6 +1721,15 @@ function Element.new(props) -- Grid container properties if self.positioning == Element._utils.enums.Positioning.GRID then + -- Warn if flex properties are set with grid positioning + if props.flexDirection or props.flexWrap or props.justifyContent then + Element._ErrorHandler:warn("Element", "LAY_009", { + element = self.id or "unnamed", + positioning = "grid", + properties = "flexDirection/flexWrap/justifyContent", + }) + end + self.gridRows = props.gridRows or 1 self.gridColumns = props.gridColumns or 1 self.alignItems = props.alignItems or Element._utils.enums.AlignItems.STRETCH diff --git a/modules/ErrorHandler.lua b/modules/ErrorHandler.lua index 3f3be04..9446734 100644 --- a/modules/ErrorHandler.lua +++ b/modules/ErrorHandler.lua @@ -105,6 +105,30 @@ local ErrorCodes = { description = "Grid layout error", suggestion = "Check grid template columns/rows and item placement", }, + LAY_008 = { + code = "FLEXLOVE_LAY_008", + category = "LAY", + description = "Explicit position will be ignored by flex layout", + suggestion = "Remove x/y properties (flex layout controls position), OR set positioning='absolute' with left/top/right/bottom properties. Additionally, you can use margin/padding for positional offsets in flex layouts.", + }, + LAY_009 = { + code = "FLEXLOVE_LAY_009", + category = "LAY", + description = "Flex layout properties ignored with grid positioning", + suggestion = "Remove flexDirection/justifyContent/alignItems properties, or change positioning to 'flex' or 'relative'", + }, + LAY_010 = { + code = "FLEXLOVE_LAY_010", + category = "LAY", + description = "Grid layout properties ignored without grid positioning", + suggestion = "Set positioning='grid' to use grid layout properties, or remove grid properties", + }, + LAY_011 = { + code = "FLEXLOVE_LAY_011", + category = "LAY", + description = "CSS positioning properties ignored", + suggestion = "Set positioning='absolute' to use top/bottom/left/right properties", + }, -- Rendering Errors (REN_001 - REN_099) REN_001 = { diff --git a/testing/__tests__/element_test.lua b/testing/__tests__/element_test.lua index 7bf2c78..4396880 100644 --- a/testing/__tests__/element_test.lua +++ b/testing/__tests__/element_test.lua @@ -490,10 +490,11 @@ function TestElementPositioning:test_nested_element_positions() luaunit.assertNotNil(parent) luaunit.assertNotNil(child) - -- Child positions are absolute in FlexLove, not relative to parent - -- So child.x = parent.x + relative_x = 100 + 20 = 120 - luaunit.assertEquals(child.x, 120) - luaunit.assertEquals(child.y, 130) + -- Parent uses default flex layout (positioning="relative" is default) + -- Flex layout controls child position, ignoring explicit x/y offsets on relative children + -- Child is positioned at parent's content area (parent.x + padding.left) + luaunit.assertEquals(child.x, 100) + luaunit.assertEquals(child.y, 100) end function TestElementPositioning:test_absolute_positioning_with_top_left()