@@ -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 = {
91110 disabled : BoolCodeControl ,
92111 columns : ColumnOptionControl ,
@@ -96,7 +115,8 @@ const childrenMap = {
96115 } ) ,
97116 horizontalGridCells : SliderControl ,
98117 autoHeight : AutoHeightControl ,
99- rowBreak : withDefault ( BoolControl , false ) ,
118+ rowBreak : withDefault ( BoolControl , true ) ,
119+ useComponentWidth : withDefault ( BoolControl , true ) ,
100120 matchColumnsHeight : withDefault ( BoolControl , true ) ,
101121 style : styleControl ( ResponsiveLayoutRowStyle , 'style' ) ,
102122 columnStyle : 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+
132155 let {
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+
151222 return (
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 }
159231 wrap = { rowBreak }
160232 gutter = { [ horizontalSpacing , verticalSpacing ] }
161233 >
162- { columns . map ( column => {
234+ { columns . map ( ( column ) => {
163235 const id = String ( column . id ) ;
164236 const childDispatch = wrapDispatch ( wrapDispatch ( dispatch , "containers" ) , id ) ;
165- if ( ! containers [ id ] ) return null
237+ if ( ! containers [ id ] ) return null ;
166238 const containerProps = containers [ id ] . children ;
167- const noOfColumns = columns . length ;
239+
240+ const calculatedWidth = 100 / numberOfColumns ;
241+
168242 return (
169243 < ColWrapper
170244 key = { 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
180255 layout = { containerProps . layout . getView ( ) }
@@ -186,16 +261,16 @@ const ResponsiveLayout = (props: ResponsiveLayoutProps) => {
186261 style = { 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 ( ) {
200275 return new UICompBuilder ( childrenMap , ( props , dispatch ) => {
201276 return (
@@ -234,6 +309,10 @@ export const ResponsiveLayoutBaseComp = (function () {
234309 { children . rowBreak . propertyView ( {
235310 label : 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" ) }
0 commit comments