@@ -21,7 +21,7 @@ import { NameGenerator } from "comps/utils";
2121import { Section , controlItem , sectionNames } from "lowcoder-design" ;
2222import { HintPlaceHolder } from "lowcoder-design" ;
2323import _ from "lodash" ;
24- import React , { useEffect } from "react" ;
24+ import React , { useRef , useState , useEffect } from "react" ;
2525import styled from "styled-components" ;
2626import { IContainer } from "../containerBase/iContainer" ;
2727import { SimpleContainerComp } from "../containerBase/simpleContainerComp" ;
@@ -44,11 +44,14 @@ import { disabledPropertyView, hiddenPropertyView } from "comps/utils/propertyUt
4444import { DisabledContext } from "comps/generators/uiCompBuilder" ;
4545import SliderControl from "@lowcoder-ee/comps/controls/sliderControl" ;
4646import { getBackgroundStyle } from "@lowcoder-ee/util/styleUtils" ;
47+ import { useScreenInfo } from "../../hooks/screenInfoComp" ;
48+
4749
4850const RowWrapper = styled ( Row ) < {
4951$style :ResponsiveLayoutRowStyleType ;
5052$animationStyle :AnimationStyleType ;
51- $showScrollbar :boolean
53+ $showScrollbar :boolean ;
54+ $columnCount :number ;
5255} > `
5356${ ( props ) => props . $animationStyle }
5457 height: 100%;
@@ -57,26 +60,40 @@ const RowWrapper = styled(Row)<{
5760 border-color:${ ( props ) => props . $style ?. border } ;
5861 border-style:${ ( props ) => props . $style ?. borderStyle } ;
5962 padding:${ ( props ) => props . $style . padding } ;
60- rotate:${ props => props . $style . rotation }
63+ rotate:${ ( props ) => props . $style . rotation } ;
6164 overflow:${ ( props ) => ( props . $showScrollbar ?'auto' :'hidden' ) } ;
62- ::-webkit-scrollbar {
65+ display: flex;
66+ flex-wrap: wrap; // Ensure columns wrap properly when rowBreak = true
67+ ::-webkit-scrollbar {
6368 display:${ ( props ) => ( props . $showScrollbar ?'block' :'none' ) } ;
64- }
69+ }
6570${ props => getBackgroundStyle ( props . $style ) }
71+
72+ --columns:${ ( props ) => props . $columnCount || 3 } ;
6673` ;
6774
6875const ColWrapper = styled ( Col ) < {
69- $style :ResponsiveLayoutColStyleType | undefined ,
70- $minWidth ?:string ,
71- $matchColumnsHeight :boolean ,
76+ $style :ResponsiveLayoutColStyleType | undefined ;
77+ $minWidth ?:string ;
78+ $matchColumnsHeight :boolean ;
79+ $rowBreak :boolean ;
7280} > `
7381 display: flex;
7482 flex-direction: column;
75- flex-basis:${ ( props ) => props . $minWidth } ;
76- max-width:${ ( props ) => props . $minWidth } ;
83+ flex-grow: 1;
84+
85+ // When rowBreak is true, columns are stretched evenly based on configured number
86+ // When rowBreak is false, they stay at minWidth but break only if necessary
87+ flex-basis:${ ( props ) =>
88+ props . $rowBreak
89+ ?`calc(100% / var(--columns))` // Force exact column distribution
90+ :`clamp(${ props . $minWidth } , 100% / var(--columns), 100%)` } ; // MinWidth respected
91+
92+ min-width:${ ( props ) => props . $minWidth } ; // Ensure minWidth is respected
93+ max-width: 100%; // Prevent more columns than allowed
7794
7895 > div {
79- height:${ ( props ) => props . $matchColumnsHeight ?' 100%' :' auto' } ;
96+ height:${ ( props ) => ( props . $matchColumnsHeight ?" 100%" :" auto" ) } ;
8097 border-radius:${ ( props ) => props . $style ?. radius } ;
8198 border-width:${ ( props ) => props . $style ?. borderWidth } px;
8299 border-color:${ ( props ) => props . $style ?. border } ;
@@ -87,6 +104,8 @@ const ColWrapper = styled(Col)<{
87104 }
88105` ;
89106
107+
108+
90109const childrenMap = {
91110disabled :BoolCodeControl ,
92111columns :ColumnOptionControl ,
@@ -96,7 +115,8 @@ const childrenMap = {
96115} ) ,
97116horizontalGridCells :SliderControl ,
98117autoHeight :AutoHeightControl ,
99- rowBreak :withDefault ( BoolControl , false ) ,
118+ rowBreak :withDefault ( BoolControl , true ) ,
119+ useComponentWidth :withDefault ( BoolControl , true ) ,
100120matchColumnsHeight :withDefault ( BoolControl , true ) ,
101121style :styleControl ( ResponsiveLayoutRowStyle , 'style' ) ,
102122columnStyle :styleControl ( ResponsiveLayoutColStyle , 'columnStyle' ) ,
@@ -127,13 +147,17 @@ const ColumnContainer = (props: ColumnContainerProps) => {
127147) ;
128148} ;
129149
130-
131150const ResponsiveLayout = ( props :ResponsiveLayoutProps ) => {
151+ const screenInfo = useScreenInfo ( ) ;
152+ const containerRef = useRef < HTMLDivElement | null > ( null ) ;
153+ const [ componentWidth , setComponentWidth ] = useState < number | null > ( null ) ;
154+
132155let {
133156 columns,
134157 containers,
135158 dispatch,
136159 rowBreak,
160+ useComponentWidth,
137161 matchColumnsHeight,
138162 style,
139163 columnStyle,
@@ -148,33 +172,84 @@ const ResponsiveLayout = (props: ResponsiveLayoutProps) => {
148172 autoHeight
149173} = props ;
150174
175+ // Ensure screenInfo is initialized properly
176+ const safeScreenInfo = screenInfo && screenInfo . width
177+ ?screenInfo
178+ :{ width :window . innerWidth , height :window . innerHeight , deviceType :"desktop" } ;
179+
180+ // Get device type based on width
181+ const getDeviceType = ( width :number ) => {
182+ if ( width < 768 ) return "mobile" ;
183+ if ( width < 1024 ) return "tablet" ;
184+ return "desktop" ;
185+ } ;
186+
187+ // Observe component width dynamically
188+ useEffect ( ( ) => {
189+ if ( ! containerRef . current ) return ;
190+
191+ const resizeObserver = new ResizeObserver ( ( entries ) => {
192+ for ( let entry of entries ) {
193+ setComponentWidth ( entry . contentRect . width ) ;
194+ }
195+ } ) ;
196+
197+ resizeObserver . observe ( containerRef . current ) ;
198+ return ( ) => resizeObserver . disconnect ( ) ;
199+ } , [ ] ) ;
200+
201+ const totalColumns = columns . length ;
202+
203+ // Decide between screen width or component width
204+ const effectiveWidth = useComponentWidth ?componentWidth ?? safeScreenInfo . width :safeScreenInfo . width ;
205+ const effectiveDeviceType = useComponentWidth ?getDeviceType ( effectiveWidth || 1000 ) :safeScreenInfo . deviceType ;
206+
207+ // Get columns per row based on device type
208+ let configuredColumnsPerRow = effectiveDeviceType === "mobile"
209+ ?columnPerRowSM
210+ :effectiveDeviceType === "tablet"
211+ ?columnPerRowMD
212+ :columnPerRowLG ;
213+
214+ // Calculate max columns that fit based on minWidth
215+ let maxColumnsThatFit = componentWidth
216+ ?Math . floor ( componentWidth / Math . max ( ...columns . map ( ( col ) => parseFloat ( col . minWidth || "0" ) ) ) )
217+ :configuredColumnsPerRow ;
218+
219+ // Determine actual number of columns
220+ let numberOfColumns = rowBreak ?configuredColumnsPerRow :Math . min ( maxColumnsThatFit , totalColumns ) ;
221+
151222return (
152223< BackgroundColorContext . Provider value = { props . style . background } >
153224< DisabledContext . Provider value = { props . disabled } >
154- < div style = { { padding :style . margin , height :' 100%' } } >
225+ < div ref = { containerRef } style = { { padding :style . margin , height :" 100%" } } >
155226< RowWrapper
156- $style = { style }
227+ $style = { { ... style } }
157228$animationStyle = { animationStyle }
158229$showScrollbar = { mainScrollbar }
230+ $columnCount = { numberOfColumns }
159231wrap = { rowBreak }
160232gutter = { [ horizontalSpacing , verticalSpacing ] }
161233>
162- { columns . map ( column => {
234+ { columns . map ( ( column ) => {
163235const id = String ( column . id ) ;
164236const childDispatch = wrapDispatch ( wrapDispatch ( dispatch , "containers" ) , id ) ;
165- if ( ! containers [ id ] ) return null
237+ if ( ! containers [ id ] ) return null ;
166238const containerProps = containers [ id ] . children ;
167- const noOfColumns = columns . length ;
239+
240+ const calculatedWidth = 100 / numberOfColumns ;
241+
168242return (
169243< ColWrapper
170244key = { id }
171- lg = { 24 / ( noOfColumns < columnPerRowLG ? noOfColumns :columnPerRowLG ) }
172- md = { 24 / ( noOfColumns < columnPerRowMD ? noOfColumns :columnPerRowMD ) }
173- sm = { 24 / ( noOfColumns < columnPerRowSM ? noOfColumns :columnPerRowSM ) }
174- xs = { 24 / ( noOfColumns < columnPerRowSM ? noOfColumns :columnPerRowSM ) }
245+ lg = { rowBreak ? 24 / numberOfColumns :undefined }
246+ md = { rowBreak ? 24 / numberOfColumns :undefined }
247+ sm = { rowBreak ? 24 / numberOfColumns :undefined }
248+ xs = { rowBreak ? 24 / numberOfColumns :undefined }
175249$style = { props . columnStyle }
176- $minWidth = { column . minWidth }
250+ $minWidth = { ` ${ calculatedWidth } px` }
177251$matchColumnsHeight = { matchColumnsHeight }
252+ $rowBreak = { rowBreak }
178253>
179254< ColumnContainer
180255layout = { containerProps . layout . getView ( ) }
@@ -186,16 +261,16 @@ const ResponsiveLayout = (props: ResponsiveLayoutProps) => {
186261style = { columnStyle }
187262/>
188263</ ColWrapper >
189- )
190- } )
191- }
264+ ) ;
265+ } ) }
192266</ RowWrapper >
193267</ div >
194- </ DisabledContext . Provider >
268+ </ DisabledContext . Provider >
195269</ BackgroundColorContext . Provider >
196270) ;
197271} ;
198272
273+
199274export const ResponsiveLayoutBaseComp = ( function ( ) {
200275return new UICompBuilder ( childrenMap , ( props , dispatch ) => {
201276return (
@@ -234,6 +309,10 @@ export const ResponsiveLayoutBaseComp = (function () {
234309{ children . rowBreak . propertyView ( {
235310label :trans ( "responsiveLayout.rowBreak" )
236311} ) }
312+ { children . useComponentWidth . propertyView ( {
313+ label :trans ( "responsiveLayout.useComponentWidth" ) ,
314+ tooltip :trans ( "responsiveLayout.useComponentWidthDesc" )
315+ } ) }
237316{ controlItem ( { } , (
238317< div style = { { marginTop :'8px' } } >
239318{ trans ( "responsiveLayout.columnsPerRow" ) }