Skip to content
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.

Commit d51734a

Browse files
authoredApr 1, 2024
Merge pull request #782 from lowcoder-org/feature-pagelayout-comp
Feature pagelayout comp
2 parents 06c0f5b + f3be8fa commit d51734a

File tree

17 files changed

+915
-319
lines changed

17 files changed

+915
-319
lines changed
 

‎client/packages/lowcoder/src/base/codeEditor/codeEditor.tsx

+3-1
Original file line numberDiff line numberDiff line change
@@ -52,7 +52,7 @@ export const CodeEditorTooltipContainer = styled.div`
5252
// tooltip common
5353
.cm-tooltip {
5454
z-index: ${Layers.codeEditorTooltip};
55-
border: none;
55+
border: 1px solid #d7d9e0;
5656
}
5757
// make sure antd popover in the code editor available
5858
.ant-popover {
@@ -84,6 +84,7 @@ export const CodeEditorTooltipContainer = styled.div`
8484
border-radius: 8px;
8585
box-shadow: 0 0 10px 0 rgba(0, 0, 0, 0.1);
8686
transform: translate(-16px, 10px);
87+
z-index: ${Layers.codeEditorTooltip};
8788
}
8889
8990
// left minor tooltip
@@ -109,6 +110,7 @@ export const CodeEditorTooltipContainer = styled.div`
109110
color: #4965f2;
110111
${textStyle};
111112
font-weight: 600;
113+
z-index: ${Layers.codeEditorTooltip};
112114
}
113115
114116
.cm-tooltip > .cm-completionInfo .hintDiv:hover {
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,205 @@
1+
import { CompParams } from "lowcoder-core";
2+
import { ToDataType } from "comps/generators/multi";
3+
import { NameConfigDisabled, NameConfigHidden, withExposingConfigs, NameConfig, CompDepsConfig } from "comps/generators/withExposing";
4+
import { withMethodExposing } from "comps/generators/withMethodExposing";
5+
import { NameGenerator } from "comps/utils/nameGenerator";
6+
import { Section, sectionNames } from "lowcoder-design";
7+
import { oldContainerParamsToNew } from "../containerBase";
8+
import { toSimpleContainerData } from "../containerBase/simpleContainerComp";
9+
import { disabledPropertyView, hiddenPropertyView } from "comps/utils/propertyUtils";
10+
import { trans } from "i18n";
11+
import { BoolCodeControl } from "comps/controls/codeControl";
12+
import { DisabledContext } from "comps/generators/uiCompBuilder";
13+
import React, { useContext, useEffect, useState } from "react";
14+
import { EditorContext } from "comps/editorState";
15+
16+
import {
17+
ContainerChildren,
18+
ContainerCompBuilder,
19+
} from "../pageLayoutComp/pageLayoutCompBuilder";
20+
import { PageLayout } from "../pageLayoutComp/pageLayout";
21+
22+
23+
export const ContainerBaseComp = (function () {
24+
const childrenMap = {
25+
disabled: BoolCodeControl
26+
};
27+
28+
return new ContainerCompBuilder(childrenMap, (props, dispatch) => {
29+
30+
const [siderCollapsed, setSiderCollapsed] = useState(false);
31+
32+
return (
33+
<DisabledContext.Provider value={props.disabled}>
34+
<PageLayout {...props} siderCollapsed={siderCollapsed} setSiderCollapsed={setSiderCollapsed} />
35+
</DisabledContext.Provider>
36+
);
37+
})
38+
.setPropertyViewFn((children) => {
39+
return (
40+
<>
41+
{(useContext(EditorContext).editorModeStatus === "logic" || useContext(EditorContext).editorModeStatus === "both") && (
42+
<Section name={sectionNames.interaction}>
43+
{disabledPropertyView(children)}
44+
{hiddenPropertyView(children)}
45+
{ children.container.appSelectorPropertyView()}
46+
</Section>
47+
)}
48+
49+
{(useContext(EditorContext).editorModeStatus === "layout" || useContext(EditorContext).editorModeStatus === "both") && (
50+
<><Section name={sectionNames.layout}>
51+
{children.container.getPropertyView()}
52+
</Section>
53+
<Section name={sectionNames.style}>
54+
{ children.container.stylePropertyView() }
55+
</Section>
56+
{children.container.children.showHeader.getView() && (
57+
<Section name={"Header Style"}>
58+
{ children.container.headerStylePropertyView() }
59+
</Section>
60+
)}
61+
{children.container.children.showSider.getView() && (
62+
<Section name={"Sider Style"}>
63+
{ children.container.siderStylePropertyView() }
64+
</Section>
65+
)}
66+
<Section name={"Body Style"}>
67+
{ children.container.bodyStylePropertyView() }
68+
</Section>
69+
{children.container.children.showFooter.getView() && (
70+
<Section name={"Footer Style"}>
71+
{ children.container.footerStylePropertyView() }
72+
</Section>
73+
)}
74+
</>
75+
)}
76+
</>
77+
);
78+
})
79+
.build();
80+
})();
81+
82+
// Compatible with old data
83+
function convertOldContainerParams(params: CompParams<any>) {
84+
// convert older params to old params
85+
let tempParams = oldContainerParamsToNew(params);
86+
87+
if (tempParams.value) {
88+
const container = tempParams.value.container;
89+
// old params
90+
if (container && (container.hasOwnProperty("layout") || container.hasOwnProperty("items"))) {
91+
const autoHeight = tempParams.value.autoHeight;
92+
const scrollbars = tempParams.value.scrollbars;
93+
return {
94+
...tempParams,
95+
value: {
96+
container: {
97+
showHeader: true,
98+
body: { 0: { view: container } },
99+
showFooter: false,
100+
showSider: true,
101+
autoHeight: autoHeight,
102+
contentScrollbars: scrollbars,
103+
},
104+
},
105+
};
106+
}
107+
}
108+
return tempParams;
109+
}
110+
111+
112+
class ContainerTmpComp extends ContainerBaseComp {
113+
constructor(params: CompParams<any>) {
114+
super(convertOldContainerParams(params));
115+
}
116+
}
117+
118+
const PageLayoutCompTmP = withExposingConfigs(ContainerTmpComp, [
119+
NameConfigHidden,
120+
NameConfigDisabled,
121+
122+
new NameConfig("container", trans("export.ratingValueDesc")),
123+
new CompDepsConfig(
124+
"siderCollapsed",
125+
(comp) => ({ data : comp.children.container.children.siderCollapsed.nodeWithoutCache()}),
126+
(input) => input.data.value, trans("listView.itemsDesc")
127+
),
128+
]);
129+
130+
export const PageLayoutComp = withMethodExposing(PageLayoutCompTmP, [
131+
132+
{
133+
method: {
134+
name: "setSiderCollapsed",
135+
description: "Set the Sider of the PageLayout to be collapsed or not",
136+
params: [{ name: "collapsed", type: "boolean" }],
137+
},
138+
execute: (comp, values) => {
139+
const page = values[0] as number;
140+
if (page && page > 0) {
141+
// comp.children.pagination.children.pageNo.dispatchChangeValueAction(page);
142+
}
143+
},
144+
}
145+
]);
146+
147+
type ContainerDataType = ToDataType<ContainerChildren<{}>>;
148+
149+
export function defaultPageLayoutData(
150+
compName: string,
151+
nameGenerator: NameGenerator
152+
): ContainerDataType {
153+
return {
154+
container: {
155+
header: toSimpleContainerData([
156+
{
157+
item: {
158+
compType: "navigation",
159+
name: nameGenerator.genItemName("pageNavigation"),
160+
comp: {
161+
logoUrl: "",
162+
hidden: false,
163+
items: [
164+
{
165+
label: "Home",
166+
hidden: false,
167+
active: false,
168+
},
169+
{
170+
label: "Services",
171+
hidden: false,
172+
active: false,
173+
items: [
174+
{
175+
label: "Lowcode Platform",
176+
hidden: false,
177+
active: false,
178+
},
179+
{
180+
label: "App Development",
181+
hidden: false,
182+
active: false,
183+
},
184+
],
185+
},
186+
{
187+
label: "About",
188+
hidden: false,
189+
active: false,
190+
},
191+
],
192+
},
193+
},
194+
layoutItem: {
195+
i: "",
196+
h: 6,
197+
w: 24,
198+
x: 0,
199+
y: 0,
200+
},
201+
},
202+
]),
203+
},
204+
};
205+
}

‎client/packages/lowcoder/src/comps/comps/layout/mobileTabLayout.tsx

+2
Original file line numberDiff line numberDiff line change
@@ -191,6 +191,8 @@ MobileTabLayoutTmp = withViewFn(MobileTabLayoutTmp, (comp) => {
191191
/>
192192
);
193193

194+
//console.log("appView", appView);
195+
194196
if (readOnly) {
195197
return (
196198
<TabLayoutViewContainer>

‎client/packages/lowcoder/src/comps/comps/layoutComp/layout.tsx

-201
This file was deleted.

‎client/packages/lowcoder/src/comps/comps/pageLayoutComp/pageLayout.tsx

+442
Large diffs are not rendered by default.

‎client/packages/lowcoder/src/comps/comps/layoutComp/layoutComp.tsx renamed to ‎client/packages/lowcoder/src/comps/comps/pageLayoutComp/pageLayoutComp.tsx

+42-11
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ import {
66
ContainerStyle,
77
ContainerHeaderStyle,
88
ContainerBodyStyle,
9+
ContainerSiderStyle,
910
ContainerFooterStyle,
1011
} from "comps/controls/styleControlConstants";
1112
import { MultiCompBuilder, sameTypeMap, withDefault } from "comps/generators";
@@ -14,7 +15,6 @@ import { NameGenerator } from "comps/utils";
1415
import { fromRecord, Node } from "lowcoder-core";
1516
import { nodeIsRecord } from "lowcoder-core";
1617
import _ from "lodash";
17-
import { ReactNode } from "react";
1818
import { lastValueIfEqual } from "util/objectUtils";
1919
import {
2020
CompTree,
@@ -27,44 +27,56 @@ import { SimpleContainerComp } from "../containerBase/simpleContainerComp";
2727
import { ContainerBodyChildComp } from "./containerBodyChildComp";
2828
import { trans } from "i18n";
2929
import { ControlNode } from "lowcoder-design";
30+
import { StringControl } from "@lowcoder-ee/index.sdk";
3031

3132
const childrenMap = {
3233
header: SimpleContainerComp,
33-
// Support future tab or step container expansion
34+
sider: SimpleContainerComp,
3435
body: withDefault(sameTypeMap(ContainerBodyChildComp), {
3536
0: { view: { layout: {}, items: {} } },
3637
}),
38+
showApp: BoolControl,
39+
contentApp: StringControl,
40+
baseUrl: StringControl,
3741
footer: SimpleContainerComp,
3842
showHeader: BoolControl.DEFAULT_TRUE,
39-
showBody: BoolControl.DEFAULT_TRUE,
43+
showSider: BoolControl.DEFAULT_TRUE,
44+
innerSider: BoolControl.DEFAULT_TRUE,
45+
siderCollapsible: withDefault(BoolControl, false),
46+
siderCollapsed : withDefault(BoolControl, false),
47+
siderRight: withDefault(BoolControl, false),
48+
siderWidth: withDefault(StringControl, "20%"),
49+
siderCollapsedWidth: withDefault(StringControl, "0"),
4050
showFooter: BoolControl,
4151
autoHeight: AutoHeightControl,
42-
scrollbars: withDefault(BoolControl, false),
52+
siderScrollbars: withDefault(BoolControl, false),
53+
contentScrollbars: withDefault(BoolControl, false),
4354
style: styleControl(ContainerStyle),
4455
headerStyle: styleControl(ContainerHeaderStyle),
56+
siderStyle: styleControl(ContainerSiderStyle),
4557
bodyStyle: styleControl(ContainerBodyStyle),
4658
footerStyle: styleControl(ContainerFooterStyle),
4759
};
4860

4961
// Compatible with old style data 2022-8-15
50-
const LayoutBaseComp = migrateOldData(
62+
const layoutBaseComp = migrateOldData(
5163
new MultiCompBuilder(childrenMap, (props, dispatch) => {
5264
return { ...props, dispatch };
5365
}).build(),
5466
fixOldStyleData
5567
);
5668

57-
export class LayoutComp extends LayoutBaseComp implements IContainer {
58-
scrollbars: any;
69+
export class PageLayoutComp extends layoutBaseComp implements IContainer {
70+
// scrollbars: any;
5971
private allContainers() {
6072
return [
6173
this.children.header,
74+
this.children.sider,
6275
...Object.values(this.children.body.getView()).map((c) => c.children.view),
6376
this.children.footer,
6477
];
6578
}
6679
realSimpleContainer(key?: string): SimpleContainerComp | undefined {
67-
// FIXME: When the tab or step container supports header, footer, modify it to the current tab
6880
if (_.isNil(key)) return this.children.body.getView()["0"].children.view;
6981
return this.allContainers().find((container) => container.realSimpleContainer(key));
7082
}
@@ -84,6 +96,7 @@ export class LayoutComp extends LayoutBaseComp implements IContainer {
8496
return {
8597
...this.toJsonValue(),
8698
header: this.children.header.getPasteValue(nameGenerator),
99+
sider: this.children.sider.getPasteValue(nameGenerator),
87100
body: _.mapValues(this.children.body.getView(), (comp) => {
88101
return {
89102
...comp.toJsonValue(),
@@ -123,16 +136,30 @@ export class LayoutComp extends LayoutBaseComp implements IContainer {
123136
areaPropertyView() {
124137
return [
125138
this.children.showHeader.propertyView({ label: trans("prop.showHeader") }),
126-
this.children.showBody.propertyView({ label: trans("prop.showBody") }),
139+
this.children.showSider.propertyView({ label: trans("prop.showSider") }),
140+
this.children.siderRight.propertyView({ label: trans("prop.siderRight") }),
141+
this.children.innerSider.propertyView({ label: trans("prop.innerSider") }),
142+
this.children.siderCollapsible.propertyView({ label: trans("prop.siderCollapsible") }),
143+
this.children.siderCollapsed.propertyView({ label: trans("prop.siderCollapsed") }),
127144
this.children.showFooter.propertyView({ label: trans("prop.showFooter") }),
128-
145+
this.children.siderWidth.propertyView({ label: trans("prop.siderWidth"), tooltip : trans("prop.siderWidthTooltip") }),
146+
this.children.siderCollapsedWidth.propertyView({ label: trans("prop.siderCollapsedWidth"), tooltip : trans("prop.siderCollapsedWidthTooltip") }),
129147
];
130148
}
131149

132150
heightPropertyView() {
133151
return [
134152
this.children.autoHeight.getPropertyView(),
135-
(!this.children.autoHeight.getView()) && this.children.scrollbars.propertyView({ label: trans("prop.scrollbar") })
153+
this.children.siderScrollbars.propertyView({ label: trans("prop.siderScrollbar")}),
154+
(!this.children.autoHeight.getView()) && this.children.contentScrollbars.propertyView({ label: trans("prop.contentScrollbar") }),
155+
];
156+
}
157+
158+
appSelectorPropertyView() {
159+
return [
160+
this.children.showApp.propertyView({ label: trans("prop.showApp"), tooltip: trans("prop.showAppTooltip") }),
161+
this.children.showApp.getView() && this.children.contentApp.propertyView({ label: trans("prop.appID") }),
162+
this.children.showApp.getView() && this.children.baseUrl.propertyView({ label: trans("prop.baseURL") }),
136163
];
137164
}
138165

@@ -144,6 +171,10 @@ export class LayoutComp extends LayoutBaseComp implements IContainer {
144171
return this.children.headerStyle.getPropertyView();
145172
}
146173

174+
siderStylePropertyView() {
175+
return this.children.siderStyle.getPropertyView();
176+
}
177+
147178
bodyStylePropertyView() {
148179
return this.children.bodyStyle.getPropertyView();
149180
}

‎client/packages/lowcoder/src/comps/comps/layoutComp/layoutCompBuilder.tsx renamed to ‎client/packages/lowcoder/src/comps/comps/pageLayoutComp/pageLayoutCompBuilder.tsx

+3-3
Original file line numberDiff line numberDiff line change
@@ -11,18 +11,18 @@ import { NewChildren as UiChildren } from "comps/generators/uiCompBuilder";
1111
import { NameGenerator } from "comps/utils";
1212
import { CompTree, IContainer } from "../containerBase";
1313
import { SimpleContainerComp } from "../containerBase/simpleContainerComp";
14-
import { LayoutComp } from "./LayoutComp";
14+
import { PageLayoutComp } from "./pageLayoutComp";
1515
import { ReactNode } from "react";
1616

1717
export type ContainerChildren<ChildrenCompMap extends Record<string, Comp<unknown>>> =
1818
UiChildren<ChildrenCompMap> & {
19-
container: InstanceType<typeof LayoutComp>;
19+
container: InstanceType<typeof PageLayoutComp>;
2020
};
2121

2222
export function containerChildren<ChildrenCompMap extends Record<string, Comp<unknown>>>(
2323
childrenMap: ToConstructor<ChildrenCompMap>
2424
): ToConstructor<ContainerChildren<ChildrenCompMap>> {
25-
return { ...childrenMap, container: LayoutComp } as any;
25+
return { ...childrenMap, container: PageLayoutComp } as any;
2626
}
2727

2828
export type LayoutViewProps = ToViewReturn<ContainerChildren<{}>>;

‎client/packages/lowcoder/src/comps/comps/responsiveLayout/responsiveLayout.tsx

+65-52
Original file line numberDiff line numberDiff line change
@@ -33,11 +33,14 @@ import { BackgroundColorContext } from "comps/utils/backgroundColorContext";
3333
import { trans } from "i18n";
3434
import { messageInstance } from "lowcoder-design";
3535
import { BoolControl } from "comps/controls/boolControl";
36-
import { NumberControl } from "comps/controls/codeControl";
36+
import { BoolCodeControl, NumberControl } from "comps/controls/codeControl";
3737

3838
import { useContext } from "react";
3939
import { EditorContext } from "comps/editorState";
4040

41+
import { disabledPropertyView, hiddenPropertyView } from "comps/utils/propertyUtils";
42+
import { DisabledContext } from "comps/generators/uiCompBuilder";
43+
4144
const RowWrapper = styled(Row)<{$style: ResponsiveLayoutRowStyleType}>`
4245
height: 100%;
4346
border: 1px solid ${(props) => props.$style.border};
@@ -62,7 +65,8 @@ const ColWrapper = styled(Col)<{
6265
}
6366
`;
6467

65-
const childrenMap = {
68+
const childrenMap = {
69+
disabled: BoolCodeControl,
6670
columns: ColumnOptionControl,
6771
containers: withDefault(sameTypeMap(SimpleContainerComp), {
6872
0: { view: {}, layout: {} },
@@ -117,58 +121,60 @@ const ResponsiveLayout = (props: ResponsiveLayoutProps) => {
117121

118122
return (
119123
<BackgroundColorContext.Provider value={props.rowStyle.background}>
120-
<div style={{padding: rowStyle.margin, height: '100%'}}>
121-
<RowWrapper
122-
$style={rowStyle}
123-
wrap={rowBreak}
124-
gutter={[horizontalSpacing, verticalSpacing]}
125-
>
126-
{columns.map(column => {
127-
const id = String(column.id);
128-
const childDispatch = wrapDispatch(wrapDispatch(dispatch, "containers"), id);
129-
if(!containers[id]) return null
130-
const containerProps = containers[id].children;
124+
<DisabledContext.Provider value={props.disabled}>
125+
<div style={{padding: rowStyle.margin, height: '100%'}}>
126+
<RowWrapper
127+
$style={rowStyle}
128+
wrap={rowBreak}
129+
gutter={[horizontalSpacing, verticalSpacing]}
130+
>
131+
{columns.map(column => {
132+
const id = String(column.id);
133+
const childDispatch = wrapDispatch(wrapDispatch(dispatch, "containers"), id);
134+
if(!containers[id]) return null
135+
const containerProps = containers[id].children;
131136

132-
const columnCustomStyle = {
133-
margin: !_.isEmpty(column.margin) ? column.margin : columnStyle.margin,
134-
padding: !_.isEmpty(column.padding) ? column.padding : columnStyle.padding,
135-
radius: !_.isEmpty(column.radius) ? column.radius : columnStyle.radius,
136-
border: `1px solid ${!_.isEmpty(column.border) ? column.border : columnStyle.border}`,
137-
background: !_.isEmpty(column.background) ? column.background : columnStyle.background,
138-
}
139-
const noOfColumns = columns.length;
140-
let backgroundStyle = columnCustomStyle.background;
141-
if(!_.isEmpty(column.backgroundImage)) {
142-
backgroundStyle = `center / cover url('${column.backgroundImage}') no-repeat, ${backgroundStyle}`;
137+
const columnCustomStyle = {
138+
margin: !_.isEmpty(column.margin) ? column.margin : columnStyle.margin,
139+
padding: !_.isEmpty(column.padding) ? column.padding : columnStyle.padding,
140+
radius: !_.isEmpty(column.radius) ? column.radius : columnStyle.radius,
141+
border: `1px solid ${!_.isEmpty(column.border) ? column.border : columnStyle.border}`,
142+
background: !_.isEmpty(column.background) ? column.background : columnStyle.background,
143+
}
144+
const noOfColumns = columns.length;
145+
let backgroundStyle = columnCustomStyle.background;
146+
if(!_.isEmpty(column.backgroundImage)) {
147+
backgroundStyle = `center / cover url('${column.backgroundImage}') no-repeat, ${backgroundStyle}`;
148+
}
149+
return (
150+
<ColWrapper
151+
key={id}
152+
lg={24/(noOfColumns < columnPerRowLG ? noOfColumns : columnPerRowLG)}
153+
md={24/(noOfColumns < columnPerRowMD ? noOfColumns : columnPerRowMD)}
154+
sm={24/(noOfColumns < columnPerRowSM ? noOfColumns : columnPerRowSM)}
155+
xs={24/(noOfColumns < columnPerRowSM ? noOfColumns : columnPerRowSM)}
156+
$style={columnCustomStyle}
157+
$minWidth={column.minWidth}
158+
$matchColumnsHeight={matchColumnsHeight}
159+
>
160+
<ColumnContainer
161+
layout={containerProps.layout.getView()}
162+
items={gridItemCompToGridItems(containerProps.items.getView())}
163+
positionParams={containerProps.positionParams.getView()}
164+
dispatch={childDispatch}
165+
autoHeight={props.autoHeight}
166+
style={{
167+
...columnCustomStyle,
168+
background: backgroundStyle,
169+
}}
170+
/>
171+
</ColWrapper>
172+
)
173+
})
143174
}
144-
return (
145-
<ColWrapper
146-
key={id}
147-
lg={24/(noOfColumns < columnPerRowLG ? noOfColumns : columnPerRowLG)}
148-
md={24/(noOfColumns < columnPerRowMD ? noOfColumns : columnPerRowMD)}
149-
sm={24/(noOfColumns < columnPerRowSM ? noOfColumns : columnPerRowSM)}
150-
xs={24/(noOfColumns < columnPerRowSM ? noOfColumns : columnPerRowSM)}
151-
$style={columnCustomStyle}
152-
$minWidth={column.minWidth}
153-
$matchColumnsHeight={matchColumnsHeight}
154-
>
155-
<ColumnContainer
156-
layout={containerProps.layout.getView()}
157-
items={gridItemCompToGridItems(containerProps.items.getView())}
158-
positionParams={containerProps.positionParams.getView()}
159-
dispatch={childDispatch}
160-
autoHeight={props.autoHeight}
161-
style={{
162-
...columnCustomStyle,
163-
background: backgroundStyle,
164-
}}
165-
/>
166-
</ColWrapper>
167-
)
168-
})
169-
}
170-
</RowWrapper>
171-
</div>
175+
</RowWrapper>
176+
</div>
177+
</DisabledContext.Provider>
172178
</BackgroundColorContext.Provider>
173179
);
174180
};
@@ -189,6 +195,13 @@ export const ResponsiveLayoutBaseComp = (function () {
189195
})}
190196
</Section>
191197

198+
{(useContext(EditorContext).editorModeStatus === "logic" || useContext(EditorContext).editorModeStatus === "both") && (
199+
<Section name={sectionNames.interaction}>
200+
{disabledPropertyView(children)}
201+
{hiddenPropertyView(children)}
202+
</Section>
203+
)}
204+
192205
{["layout", "both"].includes(useContext(EditorContext).editorModeStatus) && (
193206
<>
194207
<Section name={sectionNames.layout}>

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

+38-24
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,8 @@ import { EditorContext } from "comps/editorState";
3434
import { checkIsMobile } from "util/commonUtils";
3535
import { messageInstance } from "lowcoder-design";
3636
import { BoolControl } from "comps/controls/boolControl";
37+
import { PositionControl } from "comps/controls/dropdownControl";
38+
import { NumberControl, StringControl } from "@lowcoder-ee/index.sdk";
3739

3840
const EVENT_OPTIONS = [
3941
{
@@ -52,12 +54,15 @@ const childrenMap = {
5254
}),
5355
autoHeight: AutoHeightControl,
5456
scrollbars: withDefault(BoolControl, false),
57+
placement: withDefault(PositionControl, "top"),
5558
onEvent: eventHandlerControl(EVENT_OPTIONS),
5659
disabled: BoolCodeControl,
5760
showHeader: withDefault(BoolControl, true),
5861
style: styleControl(TabContainerStyle),
5962
headerStyle: styleControl(ContainerHeaderStyle),
6063
bodyStyle: styleControl(ContainerBodyStyle),
64+
tabsGutter: withDefault(NumberControl, 32),
65+
tabsCentered: withDefault(BoolControl, false),
6166
};
6267

6368
type ViewProps = RecordConstructorToView<typeof childrenMap>;
@@ -114,6 +119,7 @@ const getStyle = (
114119
}
115120
116121
.ant-tabs-tab-btn {
122+
font-size: ${style.textSize};
117123
font-family:${style.fontFamily};
118124
font-weight:${style.textWeight};
119125
text-transform:${style.textTransform};
@@ -177,7 +183,7 @@ const StyledTabs = styled(Tabs)<{
177183

178184
const ContainerInTab = (props: ContainerBaseProps) => {
179185
return (
180-
<InnerGrid {...props} emptyRows={15} bgColor={"white"} hintPlaceholder={HintPlaceHolder} />
186+
<InnerGrid {...props} emptyRows={15} hintPlaceholder={HintPlaceHolder} />
181187
);
182188
};
183189

@@ -254,28 +260,33 @@ const TabbedContainer = (props: TabbedContainerProps) => {
254260
})
255261

256262
return (
257-
<div style={{padding: props.style.margin, height: '100%'}}>
258-
<StyledTabs
259-
// FALK: TODO tabPosition="right"
260-
activeKey={activeKey}
261-
$style={style}
262-
$headerStyle={headerStyle}
263-
$bodyStyle={bodyStyle}
264-
$showHeader={showHeader}
265-
onChange={(key) => {
266-
if (key !== props.selectedTabKey.value) {
267-
props.selectedTabKey.onChange(key);
268-
props.onEvent("change");
269-
}
270-
}}
271-
onTabClick={onTabClick}
272-
animated
273-
$isMobile={isMobile}
274-
// tabBarGutter={32}
275-
items={tabItems}
276-
>
277-
</StyledTabs>
278-
</div>
263+
<ScrollBar style={{ height: props.autoHeight ? "100%" : "auto", margin: "0px", padding: "0px" }} hideScrollbar={!props.scrollbars}>
264+
<div style={{padding: props.style.margin, height: props.autoHeight ? "100%" : "auto"}}>
265+
<BackgroundColorContext.Provider value={headerStyle.headerBackground}>
266+
<StyledTabs
267+
tabPosition={props.placement}
268+
activeKey={activeKey}
269+
$style={style}
270+
$headerStyle={headerStyle}
271+
$bodyStyle={bodyStyle}
272+
$showHeader={showHeader}
273+
onChange={(key) => {
274+
if (key !== props.selectedTabKey.value) {
275+
props.selectedTabKey.onChange(key);
276+
props.onEvent("change");
277+
}
278+
}}
279+
onTabClick={onTabClick}
280+
animated
281+
$isMobile={isMobile}
282+
items={tabItems}
283+
tabBarGutter={props.tabsGutter}
284+
centered={props.tabsCentered}
285+
>
286+
</StyledTabs>
287+
</BackgroundColorContext.Provider>
288+
</div>
289+
</ScrollBar>
279290
);
280291
};
281292

@@ -303,14 +314,17 @@ export const TabbedContainerBaseComp = (function () {
303314
<Section name={sectionNames.interaction}>
304315
{children.onEvent.getPropertyView()}
305316
{disabledPropertyView(children)}
306-
{children.showHeader.propertyView({ label: trans("prop.showHeader") })}
317+
{children.showHeader.propertyView({ label: trans("tabbedContainer.showTabs") })}
307318
{hiddenPropertyView(children)}
308319
</Section>
309320
)}
310321

311322
{["layout", "both"].includes(useContext(EditorContext).editorModeStatus) && (
312323
<>
313324
<Section name={sectionNames.layout}>
325+
{children.placement.propertyView({ label: trans("tabbedContainer.placement"), radioButton: true })}
326+
{children.tabsCentered.propertyView({ label: trans("tabbedContainer.tabsCentered")})}
327+
{ children.tabsGutter.propertyView({ label: trans("tabbedContainer.gutter"), tooltip : trans("tabbedContainer.gutterTooltip") })}
314328
{children.autoHeight.getPropertyView()}
315329
{!children.autoHeight.getView() && (
316330
children.scrollbars.propertyView({

‎client/packages/lowcoder/src/comps/controls/styleControl.tsx

+3
Original file line numberDiff line numberDiff line change
@@ -612,6 +612,7 @@ export function styleControl<T extends readonly SingleColorConfig[]>(colorConfig
612612
name === "margin" ||
613613
name === "padding" ||
614614
name === "containerHeaderPadding" ||
615+
name === "containerSiderPadding" ||
615616
name === "containerFooterPadding" ||
616617
name === "containerBodyPadding"
617618
) {
@@ -658,6 +659,7 @@ export function styleControl<T extends readonly SingleColorConfig[]>(colorConfig
658659
name === "margin" ||
659660
name === "padding" ||
660661
name === "containerHeaderPadding" ||
662+
name === "containerSiderPadding" ||
661663
name === "containerFooterPadding" ||
662664
name === "containerBodyPadding" ||
663665
name === "borderWidth" ||
@@ -750,6 +752,7 @@ export function styleControl<T extends readonly SingleColorConfig[]>(colorConfig
750752
: name === "textSize" ||
751753
name === "padding" ||
752754
name === "containerHeaderPadding" ||
755+
name === "containerSiderPadding" ||
753756
name === "containerFooterPadding" ||
754757
name === "containerBodyPadding"
755758
? (

‎client/packages/lowcoder/src/comps/controls/styleControlConstants.tsx

+59-10
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,12 @@ export type HeaderBackgroundImageSizeConfig = CommonColorConfig & { readonly hea
3535
export type HeaderBackgroundImagePositionConfig = CommonColorConfig & { readonly headerBackgroundImagePosition: string; };
3636
export type HeaderBackgroundImageOriginConfig = CommonColorConfig & { readonly headerBackgroundImageOrigin: string; };
3737

38+
export type SiderBackgroundImageConfig = CommonColorConfig & { readonly siderBackgroundImage: string; };
39+
export type SiderBackgroundImageRepeatConfig = CommonColorConfig & { readonly siderBackgroundImageRepeat: string; };
40+
export type SiderBackgroundImageSizeConfig = CommonColorConfig & { readonly siderBackgroundImageSize: string; };
41+
export type SiderBackgroundImagePositionConfig = CommonColorConfig & { readonly siderBackgroundImagePosition: string; };
42+
export type SiderBackgroundImageOriginConfig = CommonColorConfig & { readonly siderBackgroundImageOrigin: string; };
43+
3844
export type FooterBackgroundImageConfig = CommonColorConfig & { readonly footerBackgroundImage: string; };
3945
export type FooterBackgroundImageRepeatConfig = CommonColorConfig & { readonly footerBackgroundImageRepeat: string; };
4046
export type FooterBackgroundImageSizeConfig = CommonColorConfig & { readonly footerBackgroundImageSize: string; };
@@ -65,6 +71,10 @@ export type ContainerHeaderPaddingConfig = CommonColorConfig & {
6571
readonly containerHeaderPadding: string;
6672
};
6773

74+
export type ContainerSiderPaddingConfig = CommonColorConfig & {
75+
readonly containerSiderPadding: string;
76+
};
77+
6878
export type ContainerBodyPaddingConfig = CommonColorConfig & {
6979
readonly containerBodyPadding: string;
7080
};
@@ -96,7 +106,7 @@ export type DepColorConfig = CommonColorConfig & {
96106
transformer: (color: string, ...rest: string[]) => string;
97107
};
98108

99-
export type SingleColorConfig = SimpleColorConfig | DepColorConfig | RadiusConfig | BorderWidthConfig | borderStyleConfig | BackgroundImageConfig | BackgroundImageRepeatConfig | BackgroundImageSizeConfig | BackgroundImagePositionConfig | BackgroundImageOriginConfig | TextSizeConfig | TextWeightConfig | TextTransformConfig | TextDecorationConfig | FontFamilyConfig | FontStyleConfig | MarginConfig | PaddingConfig | ContainerHeaderPaddingConfig | ContainerFooterPaddingConfig | ContainerBodyPaddingConfig | HeaderBackgroundImageConfig | HeaderBackgroundImageRepeatConfig | HeaderBackgroundImageSizeConfig | HeaderBackgroundImagePositionConfig | HeaderBackgroundImageOriginConfig | FooterBackgroundImageConfig | FooterBackgroundImageRepeatConfig | FooterBackgroundImageSizeConfig | FooterBackgroundImagePositionConfig | FooterBackgroundImageOriginConfig;
109+
export type SingleColorConfig = SimpleColorConfig | DepColorConfig | RadiusConfig | BorderWidthConfig | borderStyleConfig | BackgroundImageConfig | BackgroundImageRepeatConfig | BackgroundImageSizeConfig | BackgroundImagePositionConfig | BackgroundImageOriginConfig | TextSizeConfig | TextWeightConfig | TextTransformConfig | TextDecorationConfig | FontFamilyConfig | FontStyleConfig | MarginConfig | PaddingConfig | ContainerHeaderPaddingConfig | ContainerSiderPaddingConfig | ContainerFooterPaddingConfig | ContainerBodyPaddingConfig | HeaderBackgroundImageConfig | HeaderBackgroundImageRepeatConfig | HeaderBackgroundImageSizeConfig | HeaderBackgroundImagePositionConfig | HeaderBackgroundImageOriginConfig | FooterBackgroundImageConfig | FooterBackgroundImageRepeatConfig | FooterBackgroundImageSizeConfig | FooterBackgroundImagePositionConfig | FooterBackgroundImageOriginConfig | SiderBackgroundImageConfig | SiderBackgroundImageRepeatConfig | SiderBackgroundImageSizeConfig | SiderBackgroundImagePositionConfig | SiderBackgroundImageOriginConfig;
100110

101111
export const defaultTheme: ThemeDetail = {
102112
primary: "#3377FF",
@@ -390,6 +400,12 @@ const CONTAINER_HEADER_PADDING = {
390400
containerHeaderPadding: "padding",
391401
} as const;
392402

403+
const CONTAINER_SIDER_PADDING = {
404+
name: "containerSiderPadding",
405+
label: trans("style.containerSiderPadding"),
406+
containerSiderPadding: "padding",
407+
} as const;
408+
393409
const CONTAINER_FOOTER_PADDING = {
394410
name: "containerFooterPadding",
395411
label: trans("style.containerFooterPadding"),
@@ -435,6 +451,14 @@ const HEADER_BACKGROUND = {
435451
transformer: toSelf,
436452
} as const;
437453

454+
const SIDER_BACKGROUND = {
455+
name: "siderBackground",
456+
label: trans("style.siderBackground"),
457+
depName: "background",
458+
depType: DEP_TYPE.SELF,
459+
transformer: toSelf,
460+
} as const;
461+
438462
const BG_STATIC_BORDER_RADIUS = [getBackground(), getStaticBorder(), RADIUS] as const;
439463
const STYLING_FIELDS_SEQUENCE = [
440464
TEXT,
@@ -629,6 +653,36 @@ export const ContainerHeaderStyle = [
629653
},
630654
] as const;
631655

656+
export const ContainerSiderStyle = [
657+
CONTAINER_SIDER_PADDING,
658+
SIDER_BACKGROUND,
659+
{
660+
name: "siderBackgroundImage",
661+
label: trans("style.backgroundImage"),
662+
siderBackgroundImage: "siderBackgroundImage",
663+
},
664+
{
665+
name: "siderBackgroundImageRepeat",
666+
label: trans("style.backgroundImageRepeat"),
667+
siderBackgroundImageRepeat: "siderBackgroundImageRepeat",
668+
},
669+
{
670+
name: "siderBackgroundImageSize",
671+
label: trans("style.backgroundImageSize"),
672+
siderBackgroundImageSize: "siderBackgroundImageSize",
673+
},
674+
{
675+
name: "siderBackgroundImagePosition",
676+
label: trans("style.backgroundImagePosition"),
677+
siderBackgroundImagePosition: "siderBackgroundImagePosition",
678+
}
679+
, {
680+
name: "siderBackgroundImageOrigin",
681+
label: trans("style.backgroundImageOrigin"),
682+
siderBackgroundImageOrigin: "siderBackgroundImageOrigin",
683+
},
684+
] as const;
685+
632686
export const ContainerBodyStyle = [
633687
CONTAINER_BODY_PADDING,
634688
{
@@ -810,16 +864,10 @@ export const TabContainerStyle = [
810864
name: "tabText",
811865
label: trans("style.tabText"),
812866
depName: "headerBackground",
813-
depType: DEP_TYPE.CONTRAST_TEXT,
814-
transformer: contrastText,
815-
},]),
816-
{
817-
name: "accent",
818-
label: trans("style.tabAccent"),
819-
depTheme: "primary",
820-
depType: DEP_TYPE.SELF,
867+
depType: TEXT,
821868
transformer: toSelf,
822-
},
869+
},]),
870+
ACCENT
823871
] as const;
824872

825873
export const ModalStyle = [
@@ -1381,6 +1429,7 @@ export type TextStyleType = StyleConfigType<typeof TextStyle>;
13811429
export type ContainerStyleType = StyleConfigType<typeof ContainerStyle>;
13821430
export type ContainerHeaderStyleType = StyleConfigType<typeof ContainerHeaderStyle>;
13831431
export type ContainerBodyStyleType = StyleConfigType<typeof ContainerBodyStyle>;
1432+
export type ContainerSiderStyleType = StyleConfigType<typeof ContainerSiderStyle>;
13841433
export type ContainerFooterStyleType = StyleConfigType<typeof ContainerFooterStyle>;
13851434
export type SliderStyleType = StyleConfigType<typeof SliderStyle>;
13861435
export type RatingStyleType = StyleConfigType<typeof RatingStyle>;

‎client/packages/lowcoder/src/comps/index.tsx

+21-14
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,9 @@ import { ButtonComp } from "./comps/buttonComp/buttonComp";
66
import { DropdownComp } from "./comps/buttonComp/dropdownComp";
77
import { LinkComp } from "./comps/buttonComp/linkComp";
88
import { ContainerComp, defaultContainerData } from "./comps/containerComp/containerComp";
9+
import { defaultCollapsibleContainerData } from "./comps/containerComp/collapsibleContainerComp";
910
import { ContainerComp as FloatTextContainerComp } from "./comps/containerComp/textContainerComp";
11+
import { PageLayoutComp, defaultPageLayoutData } from "./comps/containerComp/pageLayoutComp";
1012
import { CustomComp } from "./comps/customComp/customComp";
1113
import { DatePickerComp, DateRangeComp } from "./comps/dateComp/dateComp";
1214
import { DividerComp } from "./comps/dividerComp";
@@ -54,7 +56,7 @@ import { VideoComp } from "./comps/mediaComp/videoComp";
5456
import { DrawerComp } from "./hooks/drawerComp";
5557
import { CarouselComp } from "./comps/carouselComp";
5658
import { ToggleButtonComp } from "./comps/buttonComp/toggleButtonComp";
57-
import { defaultCollapsibleContainerData } from "./comps/containerComp/collapsibleContainerComp";
59+
5860
import { RemoteCompInfo } from "types/remoteComp";
5961
import { ScannerComp } from "./comps/buttonComp/scannerComp";
6062
import { SignatureComp } from "./comps/signatureComp";
@@ -318,6 +320,24 @@ var uiCompMap: Registry = {
318320
},
319321
defaultDataFn: defaultCollapsibleContainerData,
320322
},
323+
pageLayout: {
324+
name: trans("uiComp.pageLayoutCompName"),
325+
enName: "Page Layout Container",
326+
description: trans("uiComp.pageLayoutCompDesc"),
327+
categories: ["layout"],
328+
icon: ContainerCompIcon,
329+
keywords: trans("uiComp.pageLayoutCompKeywords"),
330+
comp: PageLayoutComp,
331+
withoutLoading: true,
332+
layoutInfo: {
333+
w: 12,
334+
h: 50,
335+
// static: true,
336+
delayCollision: true,
337+
},
338+
defaultDataFn: defaultPageLayoutData,
339+
},
340+
321341
listView: {
322342
name: trans("uiComp.listViewCompName"),
323343
enName: "List View",
@@ -381,19 +401,6 @@ var uiCompMap: Registry = {
381401
h: 5,
382402
},
383403
},
384-
/*Layout: {
385-
name: "navLayout",
386-
enName: "navLayout",
387-
description: trans("uiComp.navigationCompDesc"),
388-
icon: NavComIcon,
389-
categories: ["layout"],
390-
keywords: trans("uiComp.navigationCompKeywords"),
391-
comp: NavLayout,
392-
layoutInfo: {
393-
w: 24,
394-
h: 5,
395-
},
396-
}, */
397404
cascader: {
398405
name: trans("uiComp.cascaderCompName"),
399406
enName: "Cascader",

‎client/packages/lowcoder/src/comps/uiCompRegistry.ts

+1
Original file line numberDiff line numberDiff line change
@@ -97,6 +97,7 @@ export type UICompType =
9797
| "form"
9898
| "jsonSchemaForm"
9999
| "container"
100+
| "pageLayout" // added by Falk Wolsky
100101
| "floatTextContainer"
101102
| "tabbedContainer"
102103
| "modal"

‎client/packages/lowcoder/src/constants/Layers.ts

+2-2
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,10 @@
11
export const Layers = {
22
// new user tutorials
3-
tutorials: 10000,
3+
tutorials: 3000,
44
//
55
videoDialog: 2010,
66
//
7-
codeEditorTooltip: 1110,
7+
codeEditorTooltip: 3000,
88
//
99
codeEditorPanel: 1100,
1010
//

‎client/packages/lowcoder/src/i18n/locales/en.ts

+28-1
Original file line numberDiff line numberDiff line change
@@ -189,11 +189,26 @@ export const en = {
189189
"showLabel": "Show Label",
190190
"showHeader": "Show Header",
191191
"showBody": "Show Body",
192+
"showSider": "Show Sider",
193+
"innerSider" : "Inner Sider",
192194
"showFooter": "Show Footer",
193195
"maskClosable": "Click Outside to Close",
194196
"showMask": "Show Mask",
195197
"textOverflow": "Text Overflow",
196198
"scrollbar" : "Show Scrollbars",
199+
"siderScrollbar" : "Show Scrollbars in Sider",
200+
"siderRight" : "Show sider on the Right",
201+
"siderWidth" : "Sider Width",
202+
"siderWidthTooltip" : "Sider width supports percentages (%) and pixels (px).",
203+
"siderCollapsedWidth" : "Sider Collapsed Width",
204+
"siderCollapsedWidthTooltip" : "Sider collapsed width supports percentages (%) and pixels (px).",
205+
"siderCollapsible" : "Sider Collapsible",
206+
"siderCollapsed" : "Sider Collapsed",
207+
"contentScrollbar" : "Show Scrollbars in Content",
208+
"appID": "App Id",
209+
"showApp": "Show an App in the content area",
210+
"showAppTooltip": "You can display whole Lowcoder Apps in the content area. Please mind, that for Modules we do not support Inputs, Outputs Events and Methods.",
211+
"baseURL": "Lowcoder API Base URL",
197212
},
198213
"autoHeightProp": {
199214
"auto": "Auto",
@@ -325,6 +340,7 @@ export const en = {
325340
"borderStyle":"Border Style",
326341
"background": "Background",
327342
"headerBackground": "Header Background",
343+
"siderBackground": "Sider Background",
328344
"footerBackground": "Footer Background",
329345
"fill": "Fill",
330346
"track": "Track",
@@ -365,6 +381,7 @@ export const en = {
365381
"marginBottom": "Margin Bottom",
366382
"containerHeaderPadding": "Header Padding",
367383
"containerFooterPadding": "Footer Padding",
384+
"containerSiderPadding": "Sider Padding",
368385
"containerBodyPadding": "Body Padding",
369386
"minWidth": "Minimum Width",
370387
"aspectRatio": "Aspect Ratio",
@@ -859,6 +876,10 @@ export const en = {
859876
"tabbedContainerCompDesc": "A container with tabbed navigation for organizing content into separate panels.",
860877
"tabbedContainerCompKeywords": "tabbed, container, navigation, panels",
861878

879+
"pageLayoutCompName": "Layout Container",
880+
"pageLayoutCompDesc": "A container which offers to create a layout with header, sider, footer and main content areas",
881+
"pageLayoutCompKeywords": "layout, container, navigation, pages",
882+
862883
"modalCompName": "Modal",
863884
"modalCompDesc": "A pop-up modal component for displaying content, alerts, or forms in focus.",
864885
"modalCompKeywords": "modal, popup, alert, form",
@@ -1663,13 +1684,19 @@ export const en = {
16631684
"orgName": "Workspace Name"
16641685
},
16651686
"freeLimit": "Free Trial",
1687+
16661688
"tabbedContainer": {
16671689
"switchTab": "Switch Tab",
16681690
"switchTabDesc": "Triggered When Switching Tabs",
16691691
"tab": "Tabs",
16701692
"atLeastOneTabError": "The Tab Container Keeps at Least One Tab",
16711693
"selectedTabKeyDesc": "Currently Selected Tab",
1672-
"iconPosition": "Icon Position"
1694+
"iconPosition": "Icon Position",
1695+
"placement" : "Tabs Placement",
1696+
"showTabs": "Show Tabs",
1697+
"gutter" : "Gap",
1698+
"gutterTooltip" : "The distance between tabs in px",
1699+
"tabsCentered" : "Centered Tabs",
16731700
},
16741701
"formComp": {
16751702
"containerPlaceholder": "Drag Components from the Right Pane or",

‎client/packages/lowcoder/src/pages/editor/editorConstants.tsx

+1
Original file line numberDiff line numberDiff line change
@@ -93,6 +93,7 @@ export const CompStateIcon: {
9393
sharingcomponent: <LeftMeeting />,
9494
controlButton: <LeftButton />,
9595
tabbedContainer: <LeftContainer />,
96+
pageLayout: <LeftContainer />,
9697
modal: <LeftModal />,
9798
listView: <LeftListView />,
9899
grid: <LeftListView />,

0 commit comments

Comments
 (0)
Please sign in to comment.