Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Sign up
Appearance settings

Commit f24a7f7

Browse files
Merge pull request #346 from raheeliftikhar5/layout-component
Layout component
2 parents 14d26d5 + bf3bc70 commit f24a7f7

File tree

14 files changed

+523
-17
lines changed

14 files changed

+523
-17
lines changed
Lines changed: 1 addition & 0 deletions
Loading[フレーム]
Lines changed: 1 addition & 0 deletions
Loading[フレーム]

‎client/packages/lowcoder-design/src/icons/index.ts‎

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -291,3 +291,5 @@ export { ReactComponent as TimeLineIcon } from "icons/icon-timeline-comp.svg"
291291
export { ReactComponent as LottieIcon } from "icons/icon-lottie.svg";
292292
export { ReactComponent as MentionIcon } from "icons/icon-mention-comp.svg";
293293
export { ReactComponent as AutoCompleteCompIcon } from "icons/icon-autocomplete-comp.svg";
294+
export { ReactComponent as WidthIcon } from "icons/icon-width.svg";
295+
export { ReactComponent as ResponsiveLayoutCompIcon } from "icons/icon-responsive-layout-comp.svg";
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
export { ResponsiveLayoutComp } from "./responsiveLayout";
Lines changed: 335 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,335 @@
1+
import { Row, Col } from "antd";
2+
import { JSONObject, JSONValue } from "util/jsonTypes";
3+
import { CompAction, CompActionTypes, deleteCompAction, wrapChildAction } from "lowcoder-core";
4+
import { DispatchType, RecordConstructorToView, wrapDispatch } from "lowcoder-core";
5+
import { AutoHeightControl } from "comps/controls/autoHeightControl";
6+
import { ColumnOptionControl } from "comps/controls/optionsControl";
7+
import { styleControl } from "comps/controls/styleControl";
8+
import {
9+
ResponsiveLayoutRowStyle,
10+
ResponsiveLayoutRowStyleType,
11+
ResponsiveLayoutColStyleType,
12+
ResponsiveLayoutColStyle
13+
} from "comps/controls/styleControlConstants";
14+
import { sameTypeMap, UICompBuilder, withDefault } from "comps/generators";
15+
import { addMapChildAction } from "comps/generators/sameTypeMap";
16+
import { NameConfigHidden, withExposingConfigs } from "comps/generators/withExposing";
17+
import { NameGenerator } from "comps/utils";
18+
import { Section, controlItem, sectionNames } from "lowcoder-design";
19+
import { HintPlaceHolder } from "lowcoder-design";
20+
import _ from "lodash";
21+
import React from "react";
22+
import styled from "styled-components";
23+
import { IContainer } from "../containerBase/iContainer";
24+
import { SimpleContainerComp } from "../containerBase/simpleContainerComp";
25+
import { CompTree, mergeCompTrees } from "../containerBase/utils";
26+
import {
27+
ContainerBaseProps,
28+
gridItemCompToGridItems,
29+
InnerGrid,
30+
} from "../containerComp/containerView";
31+
import { BackgroundColorContext } from "comps/utils/backgroundColorContext";
32+
import { trans } from "i18n";
33+
import { messageInstance } from "lowcoder-design";
34+
import { BoolControl } from "comps/controls/boolControl";
35+
import { NumberControl } from "comps/controls/codeControl";
36+
37+
const RowWrapper = styled(Row)<{$style: ResponsiveLayoutRowStyleType}>`
38+
height: 100%;
39+
border: 1px solid ${(props) => props.$style.border};
40+
border-radius: ${(props) => props.$style.radius};
41+
padding: ${(props) => props.$style.padding};
42+
background-color: ${(props) => props.$style.background};
43+
overflow-x: auto;
44+
`;
45+
46+
const ColWrapper = styled(Col)<{
47+
$style: ResponsiveLayoutColStyleType,
48+
$minWidth?: string,
49+
$matchColumnsHeight: boolean,
50+
}>`
51+
min-width: ${(props) => props.$minWidth};
52+
display: flex;
53+
flex-direction: column;
54+
55+
> div {
56+
height: ${(props) => props.$matchColumnsHeight ? '100%' : 'auto'};
57+
}
58+
`;
59+
60+
const childrenMap = {
61+
columns: ColumnOptionControl,
62+
containers: withDefault(sameTypeMap(SimpleContainerComp), {
63+
0: { view: {}, layout: {} },
64+
1: { view: {}, layout: {} },
65+
}),
66+
autoHeight: AutoHeightControl,
67+
rowBreak: withDefault(BoolControl, false),
68+
matchColumnsHeight: withDefault(BoolControl, false),
69+
rowStyle: withDefault(styleControl(ResponsiveLayoutRowStyle), {}),
70+
columnStyle: withDefault(styleControl(ResponsiveLayoutColStyle), {}),
71+
columnPerRowLG: withDefault(NumberControl, 4),
72+
columnPerRowMD: withDefault(NumberControl, 2),
73+
columnPerRowSM: withDefault(NumberControl, 1),
74+
verticalSpacing: withDefault(NumberControl, 8),
75+
horizontalSpacing: withDefault(NumberControl, 8),
76+
};
77+
78+
type ViewProps = RecordConstructorToView<typeof childrenMap>;
79+
type ResponsiveLayoutProps = ViewProps & { dispatch: DispatchType };
80+
type ColumnContainerProps = Omit<ContainerBaseProps, 'style'> & {
81+
style: ResponsiveLayoutColStyleType,
82+
}
83+
84+
const ColumnContainer = (props: ColumnContainerProps) => {
85+
return (
86+
<InnerGrid
87+
{...props}
88+
emptyRows={15}
89+
hintPlaceholder={HintPlaceHolder}
90+
radius={props.style.radius}
91+
style={props.style}
92+
/>
93+
);
94+
};
95+
96+
97+
const ResponsiveLayout = (props: ResponsiveLayoutProps) => {
98+
let {
99+
columns,
100+
containers,
101+
dispatch,
102+
rowBreak,
103+
matchColumnsHeight,
104+
rowStyle,
105+
columnStyle,
106+
columnPerRowLG,
107+
columnPerRowMD,
108+
columnPerRowSM,
109+
verticalSpacing,
110+
horizontalSpacing,
111+
} = props;
112+
113+
return (
114+
<BackgroundColorContext.Provider value={props.rowStyle.background}>
115+
<div style={{padding: rowStyle.margin, height: '100%'}}>
116+
<RowWrapper
117+
$style={rowStyle}
118+
wrap={rowBreak}
119+
gutter={[horizontalSpacing, verticalSpacing]}
120+
>
121+
{columns.map(column => {
122+
const id = String(column.id);
123+
const childDispatch = wrapDispatch(wrapDispatch(dispatch, "containers"), id);
124+
if(!containers[id]) return null
125+
const containerProps = containers[id].children;
126+
127+
const columnCustomStyle = {
128+
margin: !_.isEmpty(column.margin) ? column.margin : columnStyle.margin,
129+
padding: !_.isEmpty(column.padding) ? column.padding : columnStyle.padding,
130+
radius: !_.isEmpty(column.radius) ? column.radius : columnStyle.radius,
131+
border: `1px solid ${!_.isEmpty(column.border) ? column.border : columnStyle.border}`,
132+
background: !_.isEmpty(column.background) ? column.background : columnStyle.background,
133+
}
134+
const noOfColumns = columns.length;
135+
let backgroundStyle = columnCustomStyle.background;
136+
if(!_.isEmpty(column.backgroundImage)) {
137+
backgroundStyle = `center / cover url('${column.backgroundImage}') no-repeat, ${backgroundStyle}`;
138+
}
139+
return (
140+
<ColWrapper
141+
key={id}
142+
lg={24/(noOfColumns < columnPerRowLG ? noOfColumns : columnPerRowLG)}
143+
md={24/(noOfColumns < columnPerRowMD ? noOfColumns : columnPerRowMD)}
144+
sm={24/(noOfColumns < columnPerRowSM ? noOfColumns : columnPerRowSM)}
145+
xs={24/(noOfColumns < columnPerRowSM ? noOfColumns : columnPerRowSM)}
146+
$style={columnCustomStyle}
147+
$minWidth={column.minWidth}
148+
$matchColumnsHeight={matchColumnsHeight}
149+
>
150+
<ColumnContainer
151+
layout={containerProps.layout.getView()}
152+
items={gridItemCompToGridItems(containerProps.items.getView())}
153+
positionParams={containerProps.positionParams.getView()}
154+
dispatch={childDispatch}
155+
autoHeight={props.autoHeight}
156+
style={{
157+
...columnCustomStyle,
158+
background: backgroundStyle,
159+
}}
160+
/>
161+
</ColWrapper>
162+
)
163+
})
164+
}
165+
</RowWrapper>
166+
</div>
167+
</BackgroundColorContext.Provider>
168+
);
169+
};
170+
171+
export const ResponsiveLayoutBaseComp = (function () {
172+
return new UICompBuilder(childrenMap, (props, dispatch) => {
173+
return (
174+
<ResponsiveLayout {...props} dispatch={dispatch} />
175+
);
176+
})
177+
.setPropertyViewFn((children) => {
178+
return (
179+
<>
180+
<Section name={sectionNames.basic}>
181+
{children.columns.propertyView({
182+
title: trans("responsiveLayout.column"),
183+
newOptionLabel: "Column",
184+
})}
185+
{children.autoHeight.getPropertyView()}
186+
</Section>
187+
<Section name={trans("responsiveLayout.rowLayout")}>
188+
{children.rowBreak.propertyView({
189+
label: trans("responsiveLayout.rowBreak")
190+
})}
191+
{controlItem({}, (
192+
<div style={{marginTop: '8px'}}>
193+
{trans("responsiveLayout.columnsPerRow")}
194+
</div>
195+
))}
196+
{children.columnPerRowLG.propertyView({
197+
label: trans("responsiveLayout.desktop")
198+
})}
199+
{children.columnPerRowMD.propertyView({
200+
label: trans("responsiveLayout.tablet")
201+
})}
202+
{children.columnPerRowSM.propertyView({
203+
label: trans("responsiveLayout.mobile")
204+
})}
205+
</Section>
206+
<Section name={trans("responsiveLayout.columnsLayout")}>
207+
{children.matchColumnsHeight.propertyView({
208+
label: trans("responsiveLayout.matchColumnsHeight")
209+
})}
210+
{controlItem({}, (
211+
<div style={{marginTop: '8px'}}>
212+
{trans("responsiveLayout.columnsSpacing")}
213+
</div>
214+
))}
215+
{children.horizontalSpacing.propertyView({
216+
label: trans("responsiveLayout.horizontal")
217+
})}
218+
{children.verticalSpacing.propertyView({
219+
label: trans("responsiveLayout.vertical")
220+
})}
221+
</Section>
222+
<Section name={trans("responsiveLayout.rowStyle")}>
223+
{children.rowStyle.getPropertyView()}
224+
</Section>
225+
<Section name={trans("responsiveLayout.columnStyle")}>
226+
{children.columnStyle.getPropertyView()}
227+
</Section>
228+
</>
229+
);
230+
})
231+
.build();
232+
})();
233+
234+
class ResponsiveLayoutImplComp extends ResponsiveLayoutBaseComp implements IContainer {
235+
private syncContainers(): this {
236+
const columns = this.children.columns.getView();
237+
const ids: Set<string> = new Set(columns.map((column) => String(column.id)));
238+
let containers = this.children.containers.getView();
239+
// delete
240+
const actions: CompAction[] = [];
241+
Object.keys(containers).forEach((id) => {
242+
if (!ids.has(id)) {
243+
// log.debug("syncContainers delete. ids=", ids, " id=", id);
244+
actions.push(wrapChildAction("containers", wrapChildAction(id, deleteCompAction())));
245+
}
246+
});
247+
// new
248+
ids.forEach((id) => {
249+
if (!containers.hasOwnProperty(id)) {
250+
// log.debug("syncContainers new containers: ", containers, " id: ", id);
251+
actions.push(
252+
wrapChildAction("containers", addMapChildAction(id, { layout: {}, items: {} }))
253+
);
254+
}
255+
});
256+
// log.debug("syncContainers. actions: ", actions);
257+
let instance = this;
258+
actions.forEach((action) => {
259+
instance = instance.reduce(action);
260+
});
261+
return instance;
262+
}
263+
264+
override reduce(action: CompAction): this {
265+
const columns = this.children.columns.getView();
266+
if (action.type === CompActionTypes.CUSTOM) {
267+
const value = action.value as JSONObject;
268+
if (value.type === "push") {
269+
const itemValue = value.value as JSONObject;
270+
if (_.isEmpty(itemValue.key)) itemValue.key = itemValue.label;
271+
action = {
272+
...action,
273+
value: {
274+
...value,
275+
value: { ...itemValue },
276+
},
277+
} as CompAction;
278+
}
279+
if (value.type === "delete" && columns.length <= 1) {
280+
messageInstance.warning(trans("responsiveLayout.atLeastOneColumnError"));
281+
// at least one column
282+
return this;
283+
}
284+
}
285+
// log.debug("before super reduce. action: ", action);
286+
let newInstance = super.reduce(action);
287+
if (action.type === CompActionTypes.UPDATE_NODES_V2) {
288+
// Need eval to get the value in StringControl
289+
newInstance = newInstance.syncContainers();
290+
}
291+
// log.debug("reduce. instance: ", this, " newInstance: ", newInstance);
292+
return newInstance;
293+
}
294+
295+
realSimpleContainer(key?: string): SimpleContainerComp | undefined {
296+
return Object.values(this.children.containers.children).find((container) =>
297+
container.realSimpleContainer(key)
298+
);
299+
}
300+
301+
getCompTree(): CompTree {
302+
const containerMap = this.children.containers.getView();
303+
const compTrees = Object.values(containerMap).map((container) => container.getCompTree());
304+
return mergeCompTrees(compTrees);
305+
}
306+
307+
findContainer(key: string): IContainer | undefined {
308+
const containerMap = this.children.containers.getView();
309+
for (const container of Object.values(containerMap)) {
310+
const foundContainer = container.findContainer(key);
311+
if (foundContainer) {
312+
return foundContainer === container ? this : foundContainer;
313+
}
314+
}
315+
return undefined;
316+
}
317+
318+
getPasteValue(nameGenerator: NameGenerator): JSONValue {
319+
const containerMap = this.children.containers.getView();
320+
const containerPasteValueMap = _.mapValues(containerMap, (container) =>
321+
container.getPasteValue(nameGenerator)
322+
);
323+
324+
return { ...this.toJsonValue(), containers: containerPasteValueMap };
325+
}
326+
327+
override autoHeight(): boolean {
328+
return this.children.autoHeight.getView();
329+
}
330+
}
331+
332+
export const ResponsiveLayoutComp = withExposingConfigs(
333+
ResponsiveLayoutImplComp,
334+
[ NameConfigHidden]
335+
);

‎client/packages/lowcoder/src/comps/comps/tabs/tabbedContainerComp.tsx‎

Lines changed: 0 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -196,20 +196,6 @@ const TabbedContainer = (props: TabbedContainerProps) => {
196196
</BackgroundColorContext.Provider>
197197
)
198198
}
199-
// return (
200-
// <TabPane tab={label} key={tab.key} forceRender>
201-
// <BackgroundColorContext.Provider value={props.style.background}>
202-
// <ContainerInTab
203-
// layout={containerProps.layout.getView()}
204-
// items={gridItemCompToGridItems(containerProps.items.getView())}
205-
// positionParams={containerProps.positionParams.getView()}
206-
// dispatch={childDispatch}
207-
// autoHeight={props.autoHeight}
208-
// containerPadding={[paddingWidth, 20]}
209-
// />
210-
// </BackgroundColorContext.Provider>
211-
// </TabPane>
212-
// );
213199
})
214200

215201
return (

0 commit comments

Comments
(0)

AltStyle によって変換されたページ (->オリジナル) /