@@ -3,109 +3,155 @@ import { Splitter } from "antd";
33import styled from "styled-components" ;
44import { DispatchType , RecordConstructorToView , wrapDispatch } from "lowcoder-core" ;
55import { CompAction , CompActionTypes , deleteCompAction , wrapChildAction } from "lowcoder-core" ;
6- import { ColumnOptionControl } from "comps/controls/optionsControl" ;
7- import { NumberControl } from "comps/controls/codeControl" ;
6+ import { SplitColumnOptionControl } from "comps/controls/optionsControl" ;
7+ import { NumberControl , StringControl } from "comps/controls/codeControl" ;
88import { BoolControl } from "comps/controls/boolControl" ;
9+ import { dropdownControl } from "comps/controls/dropdownControl" ;
910import { styleControl } from "comps/controls/styleControl" ;
10- import { SplitLayoutColStyle , SplitLayoutColStyleType , AnimationStyle } from "comps/controls/styleControlConstants" ;
11+ import { SplitLayoutColStyle , SplitLayoutRowStyle , SplitLayoutRowStyleType , SplitLayoutColStyleType , AnimationStyle , heightCalculator } from "comps/controls/styleControlConstants" ;
1112import { sameTypeMap , UICompBuilder , withDefault } from "comps/generators" ;
1213import { addMapChildAction } from "comps/generators/sameTypeMap" ;
1314import { BackgroundColorContext } from "comps/utils/backgroundColorContext" ;
14- import { Section , sectionNames } from "lowcoder-design" ;
15+ import { Section , sectionNames } from "lowcoder-design" ;
1516import { trans } from "i18n" ;
16- import { SimpleContainerComp } from "../containerBase/simpleContainerComp" ;
1717import { ContainerBaseProps , gridItemCompToGridItems , InnerGrid } from "../containerComp/containerView" ;
18+ import { useContext } from "react" ;
19+ import { EditorContext } from "comps/editorState" ;
20+
21+ import { disabledPropertyView , hiddenPropertyView } from "comps/utils/propertyUtils" ;
1822import { DisabledContext } from "comps/generators/uiCompBuilder" ;
19- import { useScreenInfo } from "../../hooks/screenInfoComp" ;
23+ import { JSONObject , JSONValue } from "util/jsonTypes" ;
24+ import { IContainer } from "../containerBase/iContainer" ;
25+ import { SimpleContainerComp } from "../containerBase/simpleContainerComp" ;
26+ import { CompTree , mergeCompTrees } from "../containerBase/utils" ;
27+ import { NameGenerator } from "comps/utils" ;
28+ import { AutoHeightControl } from "comps/controls/autoHeightControl" ;
29+ import { messageInstance } from "lowcoder-design/src/components/GlobalInstances" ;
30+ import { NameConfigHidden , withExposingConfigs } from "comps/generators/withExposing" ;
31+ import SliderControl from "@lowcoder-ee/comps/controls/sliderControl" ;
32+ import { getBackgroundStyle } from "@lowcoder-ee/util/styleUtils" ;
33+ import _ from "lodash" ;
2034
21- const SplitPanelWrapper = styled ( Splitter . Panel ) < { $collapsible : boolean } > `
22- flex-grow: 1;
23- ${ ( props ) => props . $collapsible && `min-width: 50px;` }
35+ import {
36+ HorizontalIcon ,
37+ VerticalIcon ,
38+ } from "lowcoder-design/src/icons" ;
39+ import { BackgroundColor } from "@lowcoder-ee/constants/style" ;
40+
41+ const SplitPanelWrapper = styled ( Splitter . Panel ) < { } > `
2442` ;
2543
26- export interface SplitterLayoutTypes {
27- orientation : "horizontal" | "vertical" ;
28- }
44+ const SplitterWrapper = styled . div < { $style : SplitLayoutRowStyleType } > `
45+ border-radius: ${ ( props ) => props . $style ?. radius || "0px" } ;
46+ border-width: ${ ( props ) => props . $style ?. borderWidth || "0px" } ;
47+ border-color: ${ ( props ) => props . $style ?. border || "transparent" } ;
48+ border-style: ${ ( props ) => props . $style ?. borderStyle || "solid" } ;
49+ margin: ${ ( props ) => props . $style ?. margin || "0px" } ;
50+ padding: ${ ( props ) => props . $style ?. padding || "0px" } ;
51+ ${ ( props ) => ( props . $style ? getBackgroundStyle ( props . $style ) : "" ) }
52+ ` ;
2953
30- /*
54+ const OrientationOptions = [
55+ {
56+ label : < HorizontalIcon /> ,
57+ value : "horizontal" ,
58+ } ,
59+ {
60+ label : < VerticalIcon /> ,
61+ value : "vertical" ,
62+ } ,
63+ ] as const ;
3164
3265const childrenMap = {
3366 disabled : BoolControl ,
34- columns: ColumnOptionControl ,
67+ columns : SplitColumnOptionControl ,
3568 containers : withDefault ( sameTypeMap ( SimpleContainerComp ) , {
3669 0 : { view : { } , layout : { } } ,
3770 1 : { view : { } , layout : { } } ,
3871 } ) ,
39- collapsiblePanels: BoolControl,
40- orientation: withDefault(ColumnOptionControl, "horizontal"),
41- panelCount: withDefault(NumberControl, 2),
72+ autoHeight : AutoHeightControl ,
73+ horizontalGridCells : SliderControl ,
74+ verticalGridCells : SliderControl ,
75+ orientation : dropdownControl ( OrientationOptions , "horizontal" ) ,
76+ matchColumnsHeight : withDefault ( BoolControl , true ) ,
4277 columnStyle : styleControl ( SplitLayoutColStyle , "columnStyle" ) ,
78+ bodyStyle : styleControl ( SplitLayoutRowStyle , 'bodyStyle' ) ,
4379 animationStyle : styleControl ( AnimationStyle , "animationStyle" ) ,
80+ mainScrollbar : withDefault ( BoolControl , false ) ,
4481} ;
4582
4683type ViewProps = RecordConstructorToView < typeof childrenMap > ;
4784type SplitLayoutProps = ViewProps & { dispatch : DispatchType } ;
85+
4886type ColumnContainerProps = Omit < ContainerBaseProps , "style" > & {
4987 style : SplitLayoutColStyleType ;
88+ matchColumnsHeight : boolean ;
89+ backgroundColor : string ;
90+ backgroundImage : string ;
91+ padding : string ;
92+ orientation : string ;
93+ margin : string ;
5094} ;
5195
5296const ColumnContainer = ( props : ColumnContainerProps ) => {
5397 return (
5498 < InnerGrid
5599 { ...props }
56- emptyRows={15}
57100 radius = { props . style . radius }
58- style={props.style}
101+ bgColor = { props . backgroundColor }
102+ style = { {
103+ ...props . style ,
104+ height : props . orientation === "horizontal"
105+ ? ( props . matchColumnsHeight ? heightCalculator ( props . margin ) : "auto" )
106+ : ( props . autoHeight ? "100%" : "auto" ) ,
107+ } }
59108 />
60109 ) ;
61110} ;
62111
63112const SplitLayout = ( props : SplitLayoutProps ) => {
64- const screenInfo = useScreenInfo();
65- const containerRef = useRef<HTMLDivElement | null>(null);
66- const [componentWidth, setComponentWidth] = useState<number | null>(null);
67-
68- let { columns, containers, dispatch, collapsiblePanels, orientation, panelCount, columnStyle } = props;
69-
70- useEffect(() => {
71- if (!containerRef.current) return;
72- const resizeObserver = new ResizeObserver((entries) => {
73- for (let entry of entries) {
74- setComponentWidth(entry.contentRect.width);
75- }
76- });
77-
78- resizeObserver.observe(containerRef.current);
79- return () => resizeObserver.disconnect();
80- }, []);
81113
82114 return (
83- <BackgroundColorContext.Provider value={props.style? .background}>
115+ < BackgroundColorContext . Provider value = { props . columnStyle . background } >
84116 < DisabledContext . Provider value = { props . disabled } >
85- <div ref={containerRef} style={{ height: "100%" }}>
86- <Splitter layout={orientation}>
87- {Array.from({ length: panelCount }, (_, index) => {
88- const id = String(index);
89- const childDispatch = wrapDispatch(wrapDispatch(dispatch, "containers"), id);
90- if (!containers[id]) return null;
91- const containerProps = containers[id].children;
92-
117+ < SplitterWrapper $style = { props . bodyStyle } >
118+ < Splitter style = { { overflow : props . mainScrollbar ? "auto" : "hidden" } } layout = { props . orientation } >
119+ { props . columns . map ( ( col , index ) => {
120+ const id = String ( col . id ) ;
121+ const childDispatch = wrapDispatch ( wrapDispatch ( props . dispatch , "containers" ) , id ) ;
122+ const containerProps = props . containers [ id ] ?. children ;
93123 return (
94- <SplitPanelWrapper key={id} $collapsible={collapsiblePanels}>
124+ < SplitPanelWrapper
125+ key = { id }
126+ collapsible = { col . collapsible }
127+ { ...( col . minWidth !== undefined ? { min : col . minWidth } : { } ) }
128+ { ...( col . maxWidth !== undefined ? { max : col . maxWidth } : { } ) }
129+ { ...( col . width !== undefined ? { defaultSize : col . width } : { } ) }
130+ >
95131 < ColumnContainer
96132 layout = { containerProps . layout . getView ( ) }
97133 items = { gridItemCompToGridItems ( containerProps . items . getView ( ) ) }
98134 positionParams = { containerProps . positionParams . getView ( ) }
99135 dispatch = { childDispatch }
100- style={columnStyle}
136+ style = { props . columnStyle }
137+ backgroundColor = { col . backgroundColor }
138+ backgroundImage = { col . backgroundImage }
139+ padding = { col . padding }
140+ autoHeight = { props . autoHeight }
141+ horizontalGridCells = { props . horizontalGridCells }
142+ emptyRows = { props . verticalGridCells }
143+ matchColumnsHeight = { props . matchColumnsHeight }
144+ orientation = { props . orientation }
145+ margin = { props . columnStyle . margin }
101146 />
102147 </ SplitPanelWrapper >
103148 ) ;
104149 } ) }
105150 </ Splitter >
106- </div >
151+ </ SplitterWrapper >
107152 </ DisabledContext . Provider >
108153 </ BackgroundColorContext . Provider >
154+
109155 ) ;
110156} ;
111157
@@ -116,27 +162,151 @@ export const SplitLayoutBaseComp = (function () {
116162 < Section name = { sectionNames . basic } >
117163 { children . columns . propertyView ( { title : trans ( "splitLayout.column" ) } ) }
118164 </ Section >
119- <Section name={sectionNames.layout}>
120- {children.panelCount.propertyView({ label: trans("splitLayout.panelCount") }) }
121- {children.collapsiblePanels.propertyView({ label: trans("splitLayout.collapsiblePanels") }) }
122- {children.orientation.propertyView({ label: trans("splitLayout.orientation") }) }
123- </Section>
124- <Section name={sectionNames.style}>
125- {children.columnStyle.getPropertyView()}
126- {children.animationStyle.getPropertyView()}
127- </Section>
165+
166+ { ( useContext ( EditorContext ) . editorModeStatus === "logic" || useContext ( EditorContext ) . editorModeStatus === "both" ) && (
167+ < Section name = { sectionNames . interaction } >
168+ { disabledPropertyView ( children ) }
169+ { hiddenPropertyView ( children ) }
170+ </ Section >
171+ ) }
172+ { [ "layout" , "both" ] . includes ( useContext ( EditorContext ) . editorModeStatus ) && (
173+ < >
174+ < Section name = { sectionNames . layout } >
175+ { children . orientation . propertyView ( {
176+ label : trans ( "splitLayout.orientation" ) ,
177+ radioButton : true ,
178+ tooltip : trans ( "splitLayout.orientationTooltip" ) ,
179+ } ) }
180+ { children . autoHeight . getPropertyView ( ) }
181+ { ( ! children . autoHeight . getView ( ) ) && children . mainScrollbar . propertyView ( {
182+ label : trans ( "prop.mainScrollbar" )
183+ } ) }
184+ { ( children . orientation . getView ( ) == "horizontal" ) &&
185+ children . matchColumnsHeight . propertyView ( { label : trans ( "splitLayout.matchColumnsHeight" ) }
186+ ) }
187+ { children . horizontalGridCells . propertyView ( {
188+ label : trans ( 'prop.horizontalGridCells' ) ,
189+ } ) }
190+ { children . verticalGridCells . propertyView ( {
191+ label : trans ( 'prop.verticalGridCells' ) ,
192+ } ) }
193+ </ Section >
194+ < Section name = { sectionNames . bodyStyle } >
195+ { children . bodyStyle . getPropertyView ( ) }
196+ </ Section >
197+ < Section name = { sectionNames . detailStyle } >
198+ { children . columnStyle . getPropertyView ( ) }
199+ </ Section >
200+ < Section name = { sectionNames . animationStyle } hasTooltip = { true } >
201+ { children . animationStyle . getPropertyView ( ) }
202+ </ Section >
203+ </ >
204+ ) }
128205 </ >
129206 ) )
130207 . build ( ) ;
131208} ) ( ) ;
132209
133- class SplitLayoutImplComp extends SplitLayoutBaseComp {
210+ class SplitLayoutImplComp extends SplitLayoutBaseComp implements IContainer {
211+ private syncContainers ( ) : this {
212+ const columns = this . children . columns . getView ( ) ;
213+ const ids : Set < string > = new Set ( columns . map ( ( column ) => String ( column . id ) ) ) ;
214+ let containers = this . children . containers . getView ( ) ;
215+ // delete
216+ const actions : CompAction [ ] = [ ] ;
217+ Object . keys ( containers ) . forEach ( ( id ) => {
218+ if ( ! ids . has ( id ) ) {
219+ // log.debug("syncContainers delete. ids=", ids, " id=", id);
220+ actions . push ( wrapChildAction ( "containers" , wrapChildAction ( id , deleteCompAction ( ) ) ) ) ;
221+ }
222+ } ) ;
223+ // new
224+ ids . forEach ( ( id ) => {
225+ if ( ! containers . hasOwnProperty ( id ) ) {
226+ // log.debug("syncContainers new containers: ", containers, " id: ", id);
227+ actions . push (
228+ wrapChildAction ( "containers" , addMapChildAction ( id , { layout : { } , items : { } } ) )
229+ ) ;
230+ }
231+ } ) ;
232+ // log.debug("syncContainers. actions: ", actions);
233+ let instance = this ;
234+ actions . forEach ( ( action ) => {
235+ instance = instance . reduce ( action ) ;
236+ } ) ;
237+ return instance ;
238+ }
239+
134240 override reduce ( action : CompAction ) : this {
241+ const columns = this . children . columns . getView ( ) ;
242+ if ( action . type === CompActionTypes . CUSTOM ) {
243+ const value = action . value as JSONObject ;
244+ if ( value . type === "push" ) {
245+ const itemValue = value . value as JSONObject ;
246+ if ( _ . isEmpty ( itemValue . key ) ) itemValue . key = itemValue . label ;
247+ action = {
248+ ...action ,
249+ value : {
250+ ...value ,
251+ value : { ...itemValue } ,
252+ } ,
253+ } as CompAction ;
254+ }
255+ const { path } = action ;
256+ if ( value . type === "delete" && path [ 0 ] === 'columns' && columns . length <= 1 ) {
257+ messageInstance . warning ( trans ( "responsiveLayout.atLeastOneColumnError" ) ) ;
258+ // at least one column
259+ return this ;
260+ }
261+ }
262+ // log.debug("before super reduce. action: ", action);
135263 let newInstance = super . reduce ( action ) ;
264+ if ( action . type === CompActionTypes . UPDATE_NODES_V2 ) {
265+ // Need eval to get the value in StringControl
266+ newInstance = newInstance . syncContainers ( ) ;
267+ }
268+ // log.debug("reduce. instance: ", this, " newInstance: ", newInstance);
136269 return newInstance ;
137270 }
138- }
139271
140- */
272+ realSimpleContainer ( key ?: string ) : SimpleContainerComp | undefined {
273+ return Object . values ( this . children . containers . children ) . find ( ( container ) =>
274+ container . realSimpleContainer ( key )
275+ ) ;
276+ }
277+
278+ getCompTree ( ) : CompTree {
279+ const containerMap = this . children . containers . getView ( ) ;
280+ const compTrees = Object . values ( containerMap ) . map ( ( container ) => container . getCompTree ( ) ) ;
281+ return mergeCompTrees ( compTrees ) ;
282+ }
283+
284+ findContainer ( key : string ) : IContainer | undefined {
285+ const containerMap = this . children . containers . getView ( ) ;
286+ for ( const container of Object . values ( containerMap ) ) {
287+ const foundContainer = container . findContainer ( key ) ;
288+ if ( foundContainer ) {
289+ return foundContainer === container ? this : foundContainer ;
290+ }
291+ }
292+ return undefined ;
293+ }
294+
295+ getPasteValue ( nameGenerator : NameGenerator ) : JSONValue {
296+ const containerMap = this . children . containers . getView ( ) ;
297+ const containerPasteValueMap = _ . mapValues ( containerMap , ( container ) =>
298+ container . getPasteValue ( nameGenerator )
299+ ) ;
300+
301+ return { ...this . toJsonValue ( ) , containers : containerPasteValueMap } ;
302+ }
303+
304+ override autoHeight ( ) : boolean {
305+ return this . children . autoHeight . getView ( ) ;
306+ }
307+ }
141308
142- export const SplitLayoutComp = null ;
309+ export const SplitLayoutComp = withExposingConfigs (
310+ SplitLayoutImplComp ,
311+ [ NameConfigHidden ]
312+ ) ;
0 commit comments