Skip to content

Commit 383c716

Browse files
authored
Merge branch 'dev' into module_loading_issues
2 parents 264b2f0 + 06c0f5b commit 383c716

File tree

7 files changed

+479
-4
lines changed

7 files changed

+479
-4
lines changed
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
import { MultiCompBuilder } from "comps/generators";
2+
import { SimpleContainerComp } from "../containerBase/simpleContainerComp";
3+
4+
const children = {
5+
view: SimpleContainerComp,
6+
// FIXME: keep extensible
7+
};
8+
9+
export const ContainerBodyChildComp = new MultiCompBuilder(children, (props, dispatch) => {
10+
return {
11+
...props,
12+
dispatch: dispatch,
13+
};
14+
})
15+
// TODO
16+
.setPropertyViewFn(() => <></>)
17+
.build();
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,201 @@
1+
import { ContainerStyleType, heightCalculator, widthCalculator } from "comps/controls/styleControlConstants";
2+
import { EditorContext } from "comps/editorState";
3+
import { BackgroundColorContext } from "comps/utils/backgroundColorContext";
4+
import { HintPlaceHolder, ScrollBar } from "lowcoder-design";
5+
import { ReactNode, useContext } from "react";
6+
import styled, { css } from "styled-components";
7+
import { checkIsMobile } from "util/commonUtils";
8+
import { gridItemCompToGridItems, InnerGrid } from "../containerComp/containerView";
9+
import { LayoutViewProps } from "./layoutCompBuilder";
10+
11+
const getStyle = (style: ContainerStyleType) => {
12+
return css`
13+
border-color: ${style.border};
14+
border-width: ${style.borderWidth};
15+
border-radius: ${style.radius};
16+
overflow: hidden;
17+
padding: ${style.padding};
18+
${style.background && `background-color: ${style.background};`}
19+
${style.backgroundImage && `background-image: ${style.backgroundImage};`}
20+
${style.backgroundImageRepeat && `background-repeat: ${style.backgroundImageRepeat};`}
21+
${style.backgroundImageSize && `background-size: ${style.backgroundImageSize};`}
22+
${style.backgroundImagePosition && `background-position: ${style.backgroundImagePosition};`}
23+
${style.backgroundImageOrigin && `background-origin: ${style.backgroundImageOrigin};`}
24+
`;
25+
};
26+
27+
const Wrapper = styled.div<{ $style: ContainerStyleType }>`
28+
display: flex;
29+
flex-flow: column;
30+
height: 100%;
31+
border: 1px solid #d7d9e0;
32+
border-radius: 4px;
33+
${(props) => props.$style && getStyle(props.$style)}
34+
`;
35+
36+
const HeaderInnerGrid = styled(InnerGrid)<{
37+
$backgroundColor: string
38+
$headerBackgroundImage: string;
39+
$headerBackgroundImageRepeat: string;
40+
$headerBackgroundImageSize: string;
41+
$headerBackgroundImagePosition: string;
42+
$headerBackgroundImageOrigin: string;
43+
}>`
44+
overflow: visible;
45+
${(props) => props.$backgroundColor && `background-color: ${props.$backgroundColor};`}
46+
border-radius: 0;
47+
${(props) => props.$headerBackgroundImage && `background-image: ${props.$headerBackgroundImage};`}
48+
${(props) => props.$headerBackgroundImageRepeat && `background-repeat: ${props.$headerBackgroundImageRepeat};`}
49+
${(props) => props.$headerBackgroundImageSize && `background-size: ${props.$headerBackgroundImageSize};`}
50+
${(props) => props.$headerBackgroundImagePosition && `background-position: ${props.$headerBackgroundImagePosition};`}
51+
${(props) => props.$headerBackgroundImageOrigin && `background-origin: ${props.$headerBackgroundImageOrigin};`}
52+
`;
53+
54+
const BodyInnerGrid = styled(InnerGrid)<{
55+
$showBorder: boolean;
56+
$backgroundColor: string;
57+
$borderColor: string;
58+
$borderWidth: string;
59+
$backgroundImage: string;
60+
$backgroundImageRepeat: string;
61+
$backgroundImageSize: string;
62+
$backgroundImagePosition: string;
63+
$backgroundImageOrigin: string;
64+
}>`
65+
border-top: ${(props) => `${props.$showBorder ? props.$borderWidth : 0} solid ${props.$borderColor}`};
66+
flex: 1;
67+
${(props) => props.$backgroundColor && `background-color: ${props.$backgroundColor};`}
68+
border-radius: 0;
69+
${(props) => props.$backgroundImage && `background-image: ${props.$backgroundImage};`}
70+
${(props) => props.$backgroundImageRepeat && `background-repeat: ${props.$backgroundImageRepeat};`}
71+
${(props) => props.$backgroundImageSize && `background-size: ${props.$backgroundImageSize};`}
72+
${(props) => props.$backgroundImagePosition && `background-position: ${props.$backgroundImagePosition};`}
73+
${(props) => props.$backgroundImageOrigin && `background-origin: ${props.$backgroundImageOrigin};`}
74+
`;
75+
76+
const FooterInnerGrid = styled(InnerGrid)<{
77+
$showBorder: boolean;
78+
$backgroundColor: string;
79+
$borderColor: string;
80+
$borderWidth: string;
81+
$footerBackgroundImage: string;
82+
$footerBackgroundImageRepeat: string;
83+
$footerBackgroundImageSize: string;
84+
$footerBackgroundImagePosition: string;
85+
$footerBackgroundImageOrigin: string;
86+
}>`
87+
border-top: ${(props) => `${props.$showBorder ? props.$borderWidth : 0} solid ${props.$borderColor}`};
88+
overflow: visible;
89+
${(props) => props.$backgroundColor && `background-color: ${props.$backgroundColor};`}
90+
border-radius: 0;
91+
${(props) => props.$footerBackgroundImage && `background-image: ${props.$footerBackgroundImage};`}
92+
${(props) => props.$footerBackgroundImageRepeat && `background-repeat: ${props.$footerBackgroundImageRepeat};`}
93+
${(props) => props.$footerBackgroundImageSize && `background-size: ${props.$footerBackgroundImageSize};`}
94+
${(props) => props.$footerBackgroundImagePosition && `background-position: ${props.$footerBackgroundImagePosition};`}
95+
${(props) => props.$footerBackgroundImageOrigin && `background-origin: ${props.$footerBackgroundImageOrigin};`}
96+
`;
97+
98+
export type LayoutProps = LayoutViewProps & {
99+
hintPlaceholder?: ReactNode;
100+
};
101+
102+
export function Layout(props: LayoutProps) {
103+
const { container } = props;
104+
const { showHeader, showFooter } = container;
105+
// When the header and footer are not displayed, the body must be displayed
106+
const showBody = container.showBody || (!showHeader && !showFooter);
107+
const scrollbars = container.scrollbars;
108+
109+
const { items: headerItems, ...otherHeaderProps } = container.header;
110+
const { items: bodyItems, ...otherBodyProps } = container.body["0"].children.view.getView();
111+
const { items: footerItems, ...otherFooterProps } = container.footer;
112+
const {
113+
style,
114+
headerStyle,
115+
bodyStyle,
116+
footerStyle,
117+
} = container;
118+
119+
const editorState = useContext(EditorContext);
120+
const maxWidth = editorState.getAppSettings().maxWidth;
121+
const isMobile = checkIsMobile(maxWidth);
122+
const paddingWidth = isMobile ? 8 : 0;
123+
124+
return (
125+
<div style={{padding: style.margin, height: '100%'}}>
126+
<Wrapper $style={style}>
127+
{showHeader && (
128+
<BackgroundColorContext.Provider value={headerStyle.headerBackground}>
129+
<HeaderInnerGrid
130+
{...otherHeaderProps}
131+
items={gridItemCompToGridItems(headerItems)}
132+
autoHeight={true}
133+
emptyRows={5}
134+
minHeight="46px"
135+
containerPadding={[paddingWidth, 3]}
136+
showName={{ bottom: showBody || showFooter ? 20 : 0 }}
137+
$backgroundColor={headerStyle?.headerBackground || 'transparent'}
138+
$headerBackgroundImage={headerStyle?.headerBackgroundImage}
139+
$headerBackgroundImageRepeat={headerStyle?.headerBackgroundImageRepeat}
140+
$headerBackgroundImageSize={headerStyle?.headerBackgroundImageSize}
141+
$headerBackgroundImagePosition={headerStyle?.headerBackgroundImagePosition}
142+
$headerBackgroundImageOrigin={headerStyle?.headerBackgroundImageOrigin}
143+
style={{padding: headerStyle.containerHeaderPadding}}
144+
145+
/>
146+
</BackgroundColorContext.Provider>
147+
)}
148+
{showBody && (
149+
<BackgroundColorContext.Provider value={bodyStyle.background}>
150+
<ScrollBar style={{ height: container.autoHeight ? "auto" : "100%", margin: "0px", padding: "0px" }} hideScrollbar={!scrollbars}>
151+
<BodyInnerGrid
152+
$showBorder={showHeader}
153+
{...otherBodyProps}
154+
items={gridItemCompToGridItems(bodyItems)}
155+
autoHeight={container.autoHeight}
156+
emptyRows={14}
157+
minHeight={showHeader ? "143px" : "142px"}
158+
containerPadding={
159+
(showHeader && showFooter) || showHeader ? [paddingWidth, 11.5] : [paddingWidth, 11]
160+
}
161+
hintPlaceholder={props.hintPlaceholder ?? HintPlaceHolder}
162+
$backgroundColor={bodyStyle?.background || 'transparent'}
163+
$borderColor={style?.border}
164+
$borderWidth={style?.borderWidth}
165+
$backgroundImage={bodyStyle?.backgroundImage}
166+
$backgroundImageRepeat={bodyStyle?.backgroundImageRepeat}
167+
$backgroundImageSize={bodyStyle?.backgroundImageSize}
168+
$backgroundImagePosition={bodyStyle?.backgroundImagePosition}
169+
$backgroundImageOrigin={bodyStyle?.backgroundImageOrigin}
170+
style={{padding: bodyStyle.containerBodyPadding}}
171+
/>
172+
</ScrollBar>
173+
</BackgroundColorContext.Provider>
174+
)}
175+
{showFooter && (
176+
<BackgroundColorContext.Provider value={footerStyle.footerBackground}>
177+
<FooterInnerGrid
178+
$showBorder={showHeader || showBody}
179+
{...otherFooterProps}
180+
items={gridItemCompToGridItems(footerItems)}
181+
autoHeight={true}
182+
emptyRows={5}
183+
minHeight={showBody ? "47px" : "46px"}
184+
containerPadding={showBody || showHeader ? [paddingWidth, 3.5] : [paddingWidth, 3]}
185+
showName={{ top: showHeader || showBody ? 20 : 0 }}
186+
$backgroundColor={footerStyle?.footerBackground || 'transparent'}
187+
$footerBackgroundImage={footerStyle?.footerBackgroundImage}
188+
$footerBackgroundImageRepeat={footerStyle?.footerBackgroundImageRepeat}
189+
$footerBackgroundImageSize={footerStyle?.footerBackgroundImageSize}
190+
$footerBackgroundImagePosition={footerStyle?.footerBackgroundImagePosition}
191+
$footerBackgroundImageOrigin={footerStyle?.footerBackgroundImageOrigin}
192+
$borderColor={style?.border}
193+
$borderWidth={style?.borderWidth}
194+
style={{padding: footerStyle.containerFooterPadding}}
195+
/>
196+
</BackgroundColorContext.Provider>
197+
)}
198+
</Wrapper>
199+
</div>
200+
);
201+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,169 @@
1+
import { JSONValue } from "util/jsonTypes";
2+
import { AutoHeightControl } from "comps/controls/autoHeightControl";
3+
import { BoolControl } from "comps/controls/boolControl";
4+
import { styleControl } from "comps/controls/styleControl";
5+
import {
6+
ContainerStyle,
7+
ContainerHeaderStyle,
8+
ContainerBodyStyle,
9+
ContainerFooterStyle,
10+
} from "comps/controls/styleControlConstants";
11+
import { MultiCompBuilder, sameTypeMap, withDefault } from "comps/generators";
12+
import { migrateOldData } from "comps/generators/simpleGenerators";
13+
import { NameGenerator } from "comps/utils";
14+
import { fromRecord, Node } from "lowcoder-core";
15+
import { nodeIsRecord } from "lowcoder-core";
16+
import _ from "lodash";
17+
import { ReactNode } from "react";
18+
import { lastValueIfEqual } from "util/objectUtils";
19+
import {
20+
CompTree,
21+
fixOldStyleData,
22+
IContainer,
23+
mergeCompTrees,
24+
traverseCompTree,
25+
} from "../containerBase";
26+
import { SimpleContainerComp } from "../containerBase/simpleContainerComp";
27+
import { ContainerBodyChildComp } from "./containerBodyChildComp";
28+
import { trans } from "i18n";
29+
import { ControlNode } from "lowcoder-design";
30+
31+
const childrenMap = {
32+
header: SimpleContainerComp,
33+
// Support future tab or step container expansion
34+
body: withDefault(sameTypeMap(ContainerBodyChildComp), {
35+
0: { view: { layout: {}, items: {} } },
36+
}),
37+
footer: SimpleContainerComp,
38+
showHeader: BoolControl.DEFAULT_TRUE,
39+
showBody: BoolControl.DEFAULT_TRUE,
40+
showFooter: BoolControl,
41+
autoHeight: AutoHeightControl,
42+
scrollbars: withDefault(BoolControl, false),
43+
style: styleControl(ContainerStyle),
44+
headerStyle: styleControl(ContainerHeaderStyle),
45+
bodyStyle: styleControl(ContainerBodyStyle),
46+
footerStyle: styleControl(ContainerFooterStyle),
47+
};
48+
49+
// Compatible with old style data 2022-8-15
50+
const LayoutBaseComp = migrateOldData(
51+
new MultiCompBuilder(childrenMap, (props, dispatch) => {
52+
return { ...props, dispatch };
53+
}).build(),
54+
fixOldStyleData
55+
);
56+
57+
export class LayoutComp extends LayoutBaseComp implements IContainer {
58+
scrollbars: any;
59+
private allContainers() {
60+
return [
61+
this.children.header,
62+
...Object.values(this.children.body.getView()).map((c) => c.children.view),
63+
this.children.footer,
64+
];
65+
}
66+
realSimpleContainer(key?: string): SimpleContainerComp | undefined {
67+
// FIXME: When the tab or step container supports header, footer, modify it to the current tab
68+
if (_.isNil(key)) return this.children.body.getView()["0"].children.view;
69+
return this.allContainers().find((container) => container.realSimpleContainer(key));
70+
}
71+
getCompTree(): CompTree {
72+
return mergeCompTrees(this.allContainers().map((c) => c.getCompTree()));
73+
}
74+
findContainer(key: string): IContainer | undefined {
75+
for (const container of this.allContainers()) {
76+
const foundContainer = container.findContainer(key);
77+
if (foundContainer) {
78+
return foundContainer === container ? this : foundContainer;
79+
}
80+
}
81+
return undefined;
82+
}
83+
getPasteValue(nameGenerator: NameGenerator): JSONValue {
84+
return {
85+
...this.toJsonValue(),
86+
header: this.children.header.getPasteValue(nameGenerator),
87+
body: _.mapValues(this.children.body.getView(), (comp) => {
88+
return {
89+
...comp.toJsonValue(),
90+
view: comp.children.view.getPasteValue(nameGenerator),
91+
};
92+
}),
93+
footer: this.children.footer.getPasteValue(nameGenerator),
94+
};
95+
}
96+
override autoHeight(): boolean {
97+
return this.children.autoHeight.getView();
98+
}
99+
100+
exposingNode() {
101+
// The exposingNodes of the container subcomponents are put together
102+
const allNodes: Record<string, Node<unknown>> = {};
103+
traverseCompTree(this.getCompTree(), (item) => {
104+
const comp = item.children.comp;
105+
let node = comp.exposingNode();
106+
// plus formDataKey
107+
if (nodeIsRecord(node) && !node.children.hasOwnProperty("formDataKey")) {
108+
const formDataKey = comp.children["formDataKey"];
109+
if (formDataKey) {
110+
node = fromRecord({ ...node.children, formDataKey: formDataKey.exposingNode() });
111+
}
112+
}
113+
allNodes[item.children.name.getView()] = node;
114+
return true;
115+
});
116+
return lastValueIfEqual(this, "exposing_node", fromRecord(allNodes), checkEquals);
117+
}
118+
119+
getPropertyView(): ControlNode {
120+
return [this.areaPropertyView(), this.heightPropertyView()];
121+
}
122+
123+
areaPropertyView() {
124+
return [
125+
this.children.showHeader.propertyView({ label: trans("prop.showHeader") }),
126+
this.children.showBody.propertyView({ label: trans("prop.showBody") }),
127+
this.children.showFooter.propertyView({ label: trans("prop.showFooter") }),
128+
129+
];
130+
}
131+
132+
heightPropertyView() {
133+
return [
134+
this.children.autoHeight.getPropertyView(),
135+
(!this.children.autoHeight.getView()) && this.children.scrollbars.propertyView({ label: trans("prop.scrollbar") })
136+
];
137+
}
138+
139+
stylePropertyView() {
140+
return this.children.style.getPropertyView();
141+
}
142+
143+
headerStylePropertyView() {
144+
return this.children.headerStyle.getPropertyView();
145+
}
146+
147+
bodyStylePropertyView() {
148+
return this.children.bodyStyle.getPropertyView();
149+
}
150+
151+
footerStylePropertyView() {
152+
return this.children.footerStyle.getPropertyView();
153+
}
154+
}
155+
156+
function checkEquals(node1: Node<unknown>, node2: Node<unknown>): boolean {
157+
if (node1 === node2) {
158+
return true;
159+
}
160+
if (node1 && node2 && nodeIsRecord(node1) && nodeIsRecord(node2)) {
161+
const a = node1.children;
162+
const b = node2.children;
163+
const keys = Object.keys(a);
164+
return (
165+
keys.length === Object.keys(b).length && keys.every((key) => checkEquals(a[key], b[key]))
166+
);
167+
}
168+
return false;
169+
}

0 commit comments

Comments
 (0)