Content-Length: 50813 | pFad | http://github.com/lowcoder-org/lowcoder/pull/1675.patch
thub.com
From 624d349a44976ce1fa2ac8419e068677799b6685 Mon Sep 17 00:00:00 2001
From: Faran Javed
Date: Thu, 1 May 2025 20:37:53 +0500
Subject: [PATCH 1/8] [FIX]: #1626 Column Layout Component Width Issue
---
.../comps/comps/columnLayout/columnLayout.tsx | 49 +++++++------------
.../packages/lowcoder/src/i18n/locales/en.ts | 1 +
2 files changed, 19 insertions(+), 31 deletions(-)
diff --git a/client/packages/lowcoder/src/comps/comps/columnLayout/columnLayout.tsx b/client/packages/lowcoder/src/comps/comps/columnLayout/columnLayout.tsx
index 7b9c80a240..37191e65e1 100644
--- a/client/packages/lowcoder/src/comps/comps/columnLayout/columnLayout.tsx
+++ b/client/packages/lowcoder/src/comps/comps/columnLayout/columnLayout.tsx
@@ -46,17 +46,13 @@ import { getBackgroundStyle } from "@lowcoder-ee/util/styleUtils";
const ContainWrapper = styled.div<{
$style: ContainerStyleType & {
display: string,
- gridTemplateColumns: string,
- columnGap: string,
- gridTemplateRows: string,
- rowGap: string,
+ flexWrap: string,
+ gap: string,
} | undefined;
}>`
display: ${(props) => props.$style?.display};
- grid-template-columns: ${(props) => props.$style?.gridTemplateColumns};
- grid-template-rows: ${(props) => props.$style?.gridTemplateRows};
- column-gap: ${(props) => props.$style?.columnGap};
- row-gap: ${(props) => props.$style?.rowGap};
+ flex-wrap: ${(props) => props.$style?.flexWrap};
+ gap: ${(props) => props.$style?.gap};
border-radius: ${(props) => props.$style?.radius};
border-width: ${(props) => props.$style?.borderWidth};
@@ -67,11 +63,14 @@ const ContainWrapper = styled.div<{
${props => props.$style && getBackgroundStyle(props.$style)}
`;
-const ColWrapper = styled(Col)<{
+const ColWrapper = styled.div<{
$style: ResponsiveLayoutColStyleType | undefined,
- $minWidth?: string,
+ $width?: string,
$matchColumnsHeight: boolean,
}>`
+ flex: ${props => props.$width ? "0 0 " + props.$width : "1 1 0"};
+ min-width: 0; /* Prevent flex items from overflowing */
+
> div {
height: ${(props) => props.$matchColumnsHeight ? `calc(100% - ${props.$style?.padding || 0} - ${props.$style?.padding || 0})` : 'auto'};
border-radius: ${(props) => props.$style?.radius};
@@ -94,11 +93,8 @@ const childrenMap = {
horizontalGridCells: SliderControl,
autoHeight: AutoHeightControl,
matchColumnsHeight: withDefault(BoolControl, true),
- templateRows: withDefault(StringControl, "1fr"),
- rowGap: withDefault(StringControl, "20px"),
- templateColumns: withDefault(StringControl, "1fr 1fr"),
+ gap: withDefault(StringControl, "20px"),
mainScrollbar: withDefault(BoolControl, false),
- columnGap: withDefault(StringControl, "20px"),
style: styleControl(ContainerStyle, 'style'),
columnStyle: styleControl(ResponsiveLayoutColStyle , 'columnStyle')
};
@@ -129,10 +125,7 @@ const ColumnLayout = (props: ColumnLayoutProps) => {
containers,
dispatch,
matchColumnsHeight,
- templateRows,
- rowGap,
- templateColumns,
- columnGap,
+ gap,
columnStyle,
horizontalGridCells,
mainScrollbar
@@ -145,24 +138,21 @@ const ColumnLayout = (props: ColumnLayoutProps) => {
{columns.map(column => {
const id = String(column.id);
const childDispatch = wrapDispatch(wrapDispatch(dispatch, "containers"), id);
if(!containers[id]) return null
const containerProps = containers[id].children;
- const noOfColumns = columns.length;
+
return (
-
+
{trans("responsiveLayout.columnsSpacing")}
))}
- {children.templateColumns.propertyView({label: trans("responsiveLayout.columnDefinition"), tooltip: trans("responsiveLayout.columnsDefinitionTooltip")})}
- {children.templateRows.propertyView({label: trans("responsiveLayout.rowDefinition"), tooltip: trans("responsiveLayout.rowsDefinitionTooltip")})}
- {children.columnGap.propertyView({label: trans("responsiveLayout.columnGap")})}
- {children.rowGap.propertyView({label: trans("responsiveLayout.rowGap")})}
+ {children.gap.propertyView({label: trans("responsiveLayout.gap")})}
>
)}
diff --git a/client/packages/lowcoder/src/i18n/locales/en.ts b/client/packages/lowcoder/src/i18n/locales/en.ts
index d1eff10d73..d8236a42d9 100644
--- a/client/packages/lowcoder/src/i18n/locales/en.ts
+++ b/client/packages/lowcoder/src/i18n/locales/en.ts
@@ -3899,6 +3899,7 @@ export const en = {
"rowDefinition": "Row Definition",
"columnGap": "Column Gap",
"rowGap": "Row Gap",
+ "gap": "Gap",
"atLeastOneColumnError": "Responsive Layout Keeps at Least One Column",
"columnsPerRow": "Columns per Row",
"columnsSpacing": "Columns Spacing (px)",
From e035ee2f092223df07cfbbdda750cb08bab5d64b Mon Sep 17 00:00:00 2001
From: Faran Javed
Date: Mon, 5 May 2025 18:45:12 +0500
Subject: [PATCH 2/8] [Fix]: #1626 Add Toggle between Grid/Flex Layout
---
.../comps/comps/columnLayout/columnLayout.tsx | 110 ++++++++++++++----
.../src/comps/controls/optionsControl.tsx | 10 +-
.../packages/lowcoder/src/i18n/locales/en.ts | 5 +-
3 files changed, 98 insertions(+), 27 deletions(-)
diff --git a/client/packages/lowcoder/src/comps/comps/columnLayout/columnLayout.tsx b/client/packages/lowcoder/src/comps/comps/columnLayout/columnLayout.tsx
index 37191e65e1..cb8c16cc3b 100644
--- a/client/packages/lowcoder/src/comps/comps/columnLayout/columnLayout.tsx
+++ b/client/packages/lowcoder/src/comps/comps/columnLayout/columnLayout.tsx
@@ -43,16 +43,34 @@ import { DisabledContext } from "comps/generators/uiCompBuilder";
import { SliderControl } from "@lowcoder-ee/comps/controls/sliderControl";
import { getBackgroundStyle } from "@lowcoder-ee/util/styleUtils";
+// Extended ContainerStyleType for our specific needs
+interface ExtendedContainerStyleType extends ContainerStyleType {
+ display?: string;
+ gridTemplateColumns?: string;
+ gridTemplateRows?: string;
+ columnGap?: string;
+ rowGap?: string;
+ [key: string]: string | undefined;
+}
+
const ContainWrapper = styled.div<{
- $style: ContainerStyleType & {
- display: string,
- flexWrap: string,
- gap: string,
- } | undefined;
+ $style: ExtendedContainerStyleType | undefined;
+ $useFlexLayout: boolean;
}>`
- display: ${(props) => props.$style?.display};
- flex-wrap: ${(props) => props.$style?.flexWrap};
- gap: ${(props) => props.$style?.gap};
+ display: ${(props) => props.$useFlexLayout ? 'flex' : props.$style?.display};
+ flex-wrap: ${(props) => props.$useFlexLayout ? 'wrap' : 'nowrap'};
+
+ ${(props) => !props.$useFlexLayout && `
+ grid-template-columns: ${props.$style?.gridTemplateColumns};
+ grid-template-rows: ${props.$style?.gridTemplateRows};
+ column-gap: ${props.$style?.columnGap};
+ row-gap: ${props.$style?.rowGap};
+ `}
+
+ ${(props) => props.$useFlexLayout && `
+ column-gap: ${props.$style?.columnGap || '0'};
+ row-gap: ${props.$style?.rowGap || '0'};
+ `}
border-radius: ${(props) => props.$style?.radius};
border-width: ${(props) => props.$style?.borderWidth};
@@ -65,11 +83,14 @@ const ContainWrapper = styled.div<{
const ColWrapper = styled.div<{
$style: ResponsiveLayoutColStyleType | undefined,
- $width?: string,
+ $width: string,
$matchColumnsHeight: boolean,
+ $useFlexLayout: boolean,
}>`
- flex: ${props => props.$width ? "0 0 " + props.$width : "1 1 0"};
- min-width: 0; /* Prevent flex items from overflowing */
+ ${props => props.$useFlexLayout ? `
+ flex: ${props.$width === '100%' ? '1 0 100%' : `0 0 ${props.$width}`};
+ max-width: ${props.$width};
+ ` : ''}
> div {
height: ${(props) => props.$matchColumnsHeight ? `calc(100% - ${props.$style?.padding || 0} - ${props.$style?.padding || 0})` : 'auto'};
@@ -93,10 +114,14 @@ const childrenMap = {
horizontalGridCells: SliderControl,
autoHeight: AutoHeightControl,
matchColumnsHeight: withDefault(BoolControl, true),
- gap: withDefault(StringControl, "20px"),
+ templateRows: withDefault(StringControl, "1fr"),
+ rowGap: withDefault(StringControl, "0"),
+ templateColumns: withDefault(StringControl, "1fr 1fr"),
mainScrollbar: withDefault(BoolControl, false),
+ columnGap: withDefault(StringControl, "0"),
style: styleControl(ContainerStyle, 'style'),
- columnStyle: styleControl(ResponsiveLayoutColStyle , 'columnStyle')
+ columnStyle: styleControl(ResponsiveLayoutColStyle , 'columnStyle'),
+ useFlexLayout: withDefault(BoolControl, true),
};
type ViewProps = RecordConstructorToView;
@@ -118,6 +143,19 @@ const ColumnContainer = (props: ColumnContainerProps) => {
);
};
+const getColumnWidth = (column: any): string => {
+ // Use explicit width if available
+ if (column.width) {
+ // For percentage values, calculate precisely to accommodate gaps
+ if (column.width.endsWith('%')) {
+ return column.width;
+ }
+ return column.width;
+ }
+
+ // If minWidth is set, use it or default to equal distribution
+ return column.minWidth || 'auto';
+};
const ColumnLayout = (props: ColumnLayoutProps) => {
let {
@@ -125,10 +163,14 @@ const ColumnLayout = (props: ColumnLayoutProps) => {
containers,
dispatch,
matchColumnsHeight,
- gap,
+ templateRows,
+ rowGap,
+ templateColumns,
+ columnGap,
columnStyle,
horizontalGridCells,
- mainScrollbar
+ mainScrollbar,
+ useFlexLayout,
} = props;
return (
@@ -136,24 +178,31 @@ const ColumnLayout = (props: ColumnLayoutProps) => {
-
+
{columns.map(column => {
const id = String(column.id);
const childDispatch = wrapDispatch(wrapDispatch(dispatch, "containers"), id);
if(!containers[id]) return null
const containerProps = containers[id].children;
+ const columnWidth = getColumnWidth(column);
return (
-
+
{children.matchColumnsHeight.propertyView({ label: trans("responsiveLayout.matchColumnsHeight")
@@ -217,7 +270,16 @@ export const ResponsiveLayoutBaseComp = (function () {
{controlItem({}, (
{trans("responsiveLayout.columnsSpacing")}
))}
- {children.gap.propertyView({label: trans("responsiveLayout.gap")})}
+ {!children.useFlexLayout.getView() && children.templateColumns.propertyView({
+ label: trans("responsiveLayout.columnDefinition"),
+ tooltip: trans("responsiveLayout.columnsDefinitionTooltip")
+ })}
+ {!children.useFlexLayout.getView() && children.templateRows.propertyView({
+ label: trans("responsiveLayout.rowDefinition"),
+ tooltip: trans("responsiveLayout.rowsDefinitionTooltip")
+ })}
+ {children.columnGap.propertyView({label: trans("responsiveLayout.columnGap")})}
+ {children.rowGap.propertyView({label: trans("responsiveLayout.rowGap")})}
>
)}
diff --git a/client/packages/lowcoder/src/comps/controls/optionsControl.tsx b/client/packages/lowcoder/src/comps/controls/optionsControl.tsx
index e32c32c09f..d6fef99e36 100644
--- a/client/packages/lowcoder/src/comps/controls/optionsControl.tsx
+++ b/client/packages/lowcoder/src/comps/controls/optionsControl.tsx
@@ -582,6 +582,7 @@ const ColumnOption = new MultiCompBuilder(
label: StringControl,
key: StringControl,
minWidth: withDefault(RadiusControl, ""),
+ width: withDefault(RadiusControl, ""),
background: withDefault(ColorControl, ""),
backgroundImage: withDefault(StringControl, ""),
border: withDefault(ColorControl, ""),
@@ -598,6 +599,11 @@ const ColumnOption = new MultiCompBuilder(
preInputNode: ,
placeholder: '3px',
})}
+ {children.width.propertyView({
+ label: trans('responsiveLayout.width'),
+ preInputNode: ,
+ placeholder: '50%',
+ })}
{children.background.propertyView({
label: trans('style.background'),
})}
@@ -630,8 +636,8 @@ const ColumnOption = new MultiCompBuilder(
export const ColumnOptionControl = manualOptionsControl(ColumnOption, {
initOptions: [
- { id: 0, key: "Column1", label: "Column1" },
- { id: 1, key: "Column2", label: "Column2" },
+ { id: 0, key: "Column1", label: "Column1", width: "50%" },
+ { id: 1, key: "Column2", label: "Column2", width: "50%" },
],
uniqField: "key",
autoIncField: "id",
diff --git a/client/packages/lowcoder/src/i18n/locales/en.ts b/client/packages/lowcoder/src/i18n/locales/en.ts
index d8236a42d9..ba248eea42 100644
--- a/client/packages/lowcoder/src/i18n/locales/en.ts
+++ b/client/packages/lowcoder/src/i18n/locales/en.ts
@@ -3899,7 +3899,6 @@ export const en = {
"rowDefinition": "Row Definition",
"columnGap": "Column Gap",
"rowGap": "Row Gap",
- "gap": "Gap",
"atLeastOneColumnError": "Responsive Layout Keeps at Least One Column",
"columnsPerRow": "Columns per Row",
"columnsSpacing": "Columns Spacing (px)",
@@ -3911,6 +3910,8 @@ export const en = {
"rowStyle": "Row Style",
"columnStyle": "Column Style",
"minWidth": "Min. Width",
+ "width": "Width",
+ "widthTooltip": "Set the column width (e.g., '300px', '50%', '100%'). When set to 100%, columns will stack vertically.",
"rowBreak": "Row Break",
"useComponentWidth" : "Use Self Size",
"useComponentWidthDesc" : "Use the container width instead the App width",
@@ -3919,6 +3920,8 @@ export const en = {
"columnsLayout": "Columns Layout",
"columnsDefinitionTooltip": "Columns can be defined freely based on the CSS columns properties. For example, 'auto auto' will create two columns with equal width. Read more here: https://css-tricks.com/almanac/properties/g/grid-template-columns",
"rowsDefinitionTooltip": "Rows can be defined freely based on the CSS rows properties. For example, 'auto auto' will create two rows with equal height. Read more here: https://css-tricks.com/almanac/properties/g/grid-template-rows",
+ "useFlexLayout": "Use Flexible Layout",
+ "useFlexLayoutTooltip": "Enable responsive behavior where columns can wrap when there's not enough space"
},
"splitLayout" : {
"column": "View Areas",
From 55157a8fe8a29ff14ce04e1802abe973508e5fbd Mon Sep 17 00:00:00 2001
From: Faran Javed
Date: Mon, 5 May 2025 19:18:43 +0500
Subject: [PATCH 3/8] [Fix]: #1626 Hide Width in Grid Mode
---
.../comps/comps/columnLayout/columnLayout.tsx | 3 +-
.../src/comps/controls/optionsControl.tsx | 99 ++++++++++---------
2 files changed, 57 insertions(+), 45 deletions(-)
diff --git a/client/packages/lowcoder/src/comps/comps/columnLayout/columnLayout.tsx b/client/packages/lowcoder/src/comps/comps/columnLayout/columnLayout.tsx
index cb8c16cc3b..5f51f6bbb3 100644
--- a/client/packages/lowcoder/src/comps/comps/columnLayout/columnLayout.tsx
+++ b/client/packages/lowcoder/src/comps/comps/columnLayout/columnLayout.tsx
@@ -121,7 +121,7 @@ const childrenMap = {
columnGap: withDefault(StringControl, "0"),
style: styleControl(ContainerStyle, 'style'),
columnStyle: styleControl(ResponsiveLayoutColStyle , 'columnStyle'),
- useFlexLayout: withDefault(BoolControl, true),
+ useFlexLayout: withDefault(BoolControl, false),
};
type ViewProps = RecordConstructorToView;
@@ -239,6 +239,7 @@ export const ResponsiveLayoutBaseComp = (function () {
{children.columns.propertyView({
title: trans("responsiveLayout.column"),
newOptionLabel: trans("responsiveLayout.addColumn"),
+ useFlexLayout: children.useFlexLayout.getView(),
})}
diff --git a/client/packages/lowcoder/src/comps/controls/optionsControl.tsx b/client/packages/lowcoder/src/comps/controls/optionsControl.tsx
index d6fef99e36..95520cc4dc 100644
--- a/client/packages/lowcoder/src/comps/controls/optionsControl.tsx
+++ b/client/packages/lowcoder/src/comps/controls/optionsControl.tsx
@@ -40,6 +40,7 @@ import { ControlItemCompBuilder } from "comps/generators/controlCompBuilder";
import { ColorControl } from "./colorControl";
import { StringStateControl } from "./codeStateControl";
import { reduceInContext } from "../utils/reduceContext";
+import React from "react";
const OptionTypes = [
{
@@ -65,10 +66,13 @@ type OptionControlParam = {
title?: string;
// The new option's label name
newOptionLabel?: string;
+ // Whether to use flex layout (for column options)
+ useFlexLayout?: boolean;
};
type OptionPropertyParam = {
autoMap?: boolean;
+ useFlexLayout?: boolean;
};
interface OptionCompProperty {
@@ -176,7 +180,7 @@ export function manualOptionsControl(
itemTitle={(comp) => comp.children.label.getView()}
popoverTitle={() => trans("edit")}
content={(comp) => {
- return hasPropertyView(comp) ? comp.propertyView({}) : comp.getPropertyView();
+ return hasPropertyView(comp) ? comp.propertyView({ useFlexLayout: param.useFlexLayout }) : comp.getPropertyView();
}}
items={manualComp.getView()}
onAdd={() => {
@@ -576,7 +580,7 @@ const StyledContent = styled.div`
}
`;
-const ColumnOption = new MultiCompBuilder(
+let ColumnOption = new MultiCompBuilder(
{
id: valueComp(-1),
label: StringControl,
@@ -591,48 +595,55 @@ const ColumnOption = new MultiCompBuilder(
padding: withDefault(StringControl, ""),
},
(props) => props
-)
-.setPropertyViewFn((children) => (
-
- {children.minWidth.propertyView({
- label: trans('responsiveLayout.minWidth'),
- preInputNode: ,
- placeholder: '3px',
- })}
- {children.width.propertyView({
- label: trans('responsiveLayout.width'),
- preInputNode: ,
- placeholder: '50%',
- })}
- {children.background.propertyView({
- label: trans('style.background'),
- })}
- {children.backgroundImage.propertyView({
- label: `Background Image`,
- // preInputNode: ,
- placeholder: 'https://temp.im/350x400',
- })}
- {children.border.propertyView({
- label: trans('style.border')
- })}
- {children.radius.propertyView({
- label: trans('style.borderRadius'),
- preInputNode: ,
- placeholder: '3px',
- })}
- {children.margin.propertyView({
- label: trans('style.margin'),
- preInputNode: ,
- placeholder: '3px',
- })}
- {children.padding.propertyView({
- label: trans('style.padding'),
- preInputNode: ,
- placeholder: '3px',
- })}
-
-))
- .build();
+).build();
+
+// Add propertyView method through class extension
+ColumnOption = class extends ColumnOption implements OptionCompProperty {
+ propertyView(param: OptionPropertyParam) {
+ const useFlexLayout = param?.useFlexLayout !== undefined ? param.useFlexLayout : true;
+
+ return (
+
+ {useFlexLayout && this.children.minWidth.propertyView({
+ label: trans('responsiveLayout.minWidth'),
+ preInputNode: ,
+ placeholder: '3px',
+ })}
+ {useFlexLayout && this.children.width.propertyView({
+ label: trans('responsiveLayout.width'),
+ preInputNode: ,
+ placeholder: '50%',
+ })}
+ {this.children.background.propertyView({
+ label: trans('style.background'),
+ })}
+ {this.children.backgroundImage.propertyView({
+ label: `Background Image`,
+ // preInputNode: ,
+ placeholder: 'https://temp.im/350x400',
+ })}
+ {this.children.border.propertyView({
+ label: trans('style.border')
+ })}
+ {this.children.radius.propertyView({
+ label: trans('style.borderRadius'),
+ preInputNode: ,
+ placeholder: '3px',
+ })}
+ {this.children.margin.propertyView({
+ label: trans('style.margin'),
+ preInputNode: ,
+ placeholder: '3px',
+ })}
+ {this.children.padding.propertyView({
+ label: trans('style.padding'),
+ preInputNode: ,
+ placeholder: '3px',
+ })}
+
+ );
+ }
+};
export const ColumnOptionControl = manualOptionsControl(ColumnOption, {
initOptions: [
From 112a7f138ff3cfa2ddac6fa7700e903436c685d8 Mon Sep 17 00:00:00 2001
From: Faran Javed
Date: Mon, 5 May 2025 23:07:36 +0500
Subject: [PATCH 4/8] [Fix]: #1626 fill available space issue
---
.../comps/comps/columnLayout/columnLayout.tsx | 32 +++++++++----------
.../src/comps/controls/optionsControl.tsx | 4 +--
2 files changed, 18 insertions(+), 18 deletions(-)
diff --git a/client/packages/lowcoder/src/comps/comps/columnLayout/columnLayout.tsx b/client/packages/lowcoder/src/comps/comps/columnLayout/columnLayout.tsx
index 5f51f6bbb3..d5b4f6f523 100644
--- a/client/packages/lowcoder/src/comps/comps/columnLayout/columnLayout.tsx
+++ b/client/packages/lowcoder/src/comps/comps/columnLayout/columnLayout.tsx
@@ -81,15 +81,27 @@ const ContainWrapper = styled.div<{
${props => props.$style && getBackgroundStyle(props.$style)}
`;
+const getColumnWidth = (column: any): string => {
+ // Use explicit width if available
+ if (column.width) {
+ return column.width;
+ }
+
+ // No explicit width - return auto to let flex handle it
+ return 'auto';
+};
+
const ColWrapper = styled.div<{
$style: ResponsiveLayoutColStyleType | undefined,
$width: string,
$matchColumnsHeight: boolean,
$useFlexLayout: boolean,
+ $hasExplicitWidth: boolean,
}>`
${props => props.$useFlexLayout ? `
- flex: ${props.$width === '100%' ? '1 0 100%' : `0 0 ${props.$width}`};
- max-width: ${props.$width};
+ ${props.$hasExplicitWidth
+ ? `flex: 0 0 ${props.$width}; max-width: ${props.$width};`
+ : 'flex: 1 1 0%; min-width: 0;'}
` : ''}
> div {
@@ -143,20 +155,6 @@ const ColumnContainer = (props: ColumnContainerProps) => {
);
};
-const getColumnWidth = (column: any): string => {
- // Use explicit width if available
- if (column.width) {
- // For percentage values, calculate precisely to accommodate gaps
- if (column.width.endsWith('%')) {
- return column.width;
- }
- return column.width;
- }
-
- // If minWidth is set, use it or default to equal distribution
- return column.minWidth || 'auto';
-};
-
const ColumnLayout = (props: ColumnLayoutProps) => {
let {
columns,
@@ -195,6 +193,7 @@ const ColumnLayout = (props: ColumnLayoutProps) => {
if(!containers[id]) return null
const containerProps = containers[id].children;
const columnWidth = getColumnWidth(column);
+ const hasExplicitWidth = !!column.width;
return (
@@ -203,6 +202,7 @@ const ColumnLayout = (props: ColumnLayoutProps) => {
$width={columnWidth}
$matchColumnsHeight={matchColumnsHeight}
$useFlexLayout={useFlexLayout}
+ $hasExplicitWidth={hasExplicitWidth}
>
Date: Mon, 5 May 2025 23:30:12 +0500
Subject: [PATCH 5/8] [Fix]: #1626 remove min-width from the UI
---
.../packages/lowcoder/src/comps/controls/optionsControl.tsx | 5 -----
1 file changed, 5 deletions(-)
diff --git a/client/packages/lowcoder/src/comps/controls/optionsControl.tsx b/client/packages/lowcoder/src/comps/controls/optionsControl.tsx
index bc1dba6ebb..8e25d053dc 100644
--- a/client/packages/lowcoder/src/comps/controls/optionsControl.tsx
+++ b/client/packages/lowcoder/src/comps/controls/optionsControl.tsx
@@ -604,11 +604,6 @@ ColumnOption = class extends ColumnOption implements OptionCompProperty {
return (
- {useFlexLayout && this.children.minWidth.propertyView({
- label: trans('responsiveLayout.minWidth'),
- preInputNode: ,
- placeholder: '3px',
- })}
{useFlexLayout && this.children.width.propertyView({
label: trans('responsiveLayout.width'),
preInputNode: ,
From 603a62d4899f65ae13208c79cc49090141667708 Mon Sep 17 00:00:00 2001
From: Faran Javed
Date: Tue, 6 May 2025 14:52:17 +0500
Subject: [PATCH 6/8] revert to Grid
---
.../comps/comps/columnLayout/columnLayout.tsx | 117 +++++-------------
.../src/comps/controls/optionsControl.tsx | 94 ++++++--------
.../packages/lowcoder/src/i18n/locales/en.ts | 4 -
3 files changed, 75 insertions(+), 140 deletions(-)
diff --git a/client/packages/lowcoder/src/comps/comps/columnLayout/columnLayout.tsx b/client/packages/lowcoder/src/comps/comps/columnLayout/columnLayout.tsx
index d5b4f6f523..12c2bf0a12 100644
--- a/client/packages/lowcoder/src/comps/comps/columnLayout/columnLayout.tsx
+++ b/client/packages/lowcoder/src/comps/comps/columnLayout/columnLayout.tsx
@@ -43,34 +43,20 @@ import { DisabledContext } from "comps/generators/uiCompBuilder";
import { SliderControl } from "@lowcoder-ee/comps/controls/sliderControl";
import { getBackgroundStyle } from "@lowcoder-ee/util/styleUtils";
-// Extended ContainerStyleType for our specific needs
-interface ExtendedContainerStyleType extends ContainerStyleType {
- display?: string;
- gridTemplateColumns?: string;
- gridTemplateRows?: string;
- columnGap?: string;
- rowGap?: string;
- [key: string]: string | undefined;
-}
-
const ContainWrapper = styled.div<{
- $style: ExtendedContainerStyleType | undefined;
- $useFlexLayout: boolean;
+ $style: ContainerStyleType & {
+ display: string,
+ gridTemplateColumns: string,
+ columnGap: string,
+ gridTemplateRows: string,
+ rowGap: string,
+ } | undefined;
}>`
- display: ${(props) => props.$useFlexLayout ? 'flex' : props.$style?.display};
- flex-wrap: ${(props) => props.$useFlexLayout ? 'wrap' : 'nowrap'};
-
- ${(props) => !props.$useFlexLayout && `
- grid-template-columns: ${props.$style?.gridTemplateColumns};
- grid-template-rows: ${props.$style?.gridTemplateRows};
- column-gap: ${props.$style?.columnGap};
- row-gap: ${props.$style?.rowGap};
- `}
-
- ${(props) => props.$useFlexLayout && `
- column-gap: ${props.$style?.columnGap || '0'};
- row-gap: ${props.$style?.rowGap || '0'};
- `}
+ display: ${(props) => props.$style?.display};
+ grid-template-columns: ${(props) => props.$style?.gridTemplateColumns};
+ grid-template-rows: ${(props) => props.$style?.gridTemplateRows};
+ column-gap: ${(props) => props.$style?.columnGap};
+ row-gap: ${(props) => props.$style?.rowGap};
border-radius: ${(props) => props.$style?.radius};
border-width: ${(props) => props.$style?.borderWidth};
@@ -81,29 +67,12 @@ const ContainWrapper = styled.div<{
${props => props.$style && getBackgroundStyle(props.$style)}
`;
-const getColumnWidth = (column: any): string => {
- // Use explicit width if available
- if (column.width) {
- return column.width;
- }
-
- // No explicit width - return auto to let flex handle it
- return 'auto';
-};
-
-const ColWrapper = styled.div<{
+const ColWrapper = styled(Col)<{
$style: ResponsiveLayoutColStyleType | undefined,
- $width: string,
+ $minWidth?: string,
$matchColumnsHeight: boolean,
- $useFlexLayout: boolean,
- $hasExplicitWidth: boolean,
}>`
- ${props => props.$useFlexLayout ? `
- ${props.$hasExplicitWidth
- ? `flex: 0 0 ${props.$width}; max-width: ${props.$width};`
- : 'flex: 1 1 0%; min-width: 0;'}
- ` : ''}
-
+ min-width: ${(props) => props.$minWidth || 'auto'};
> div {
height: ${(props) => props.$matchColumnsHeight ? `calc(100% - ${props.$style?.padding || 0} - ${props.$style?.padding || 0})` : 'auto'};
border-radius: ${(props) => props.$style?.radius};
@@ -127,13 +96,12 @@ const childrenMap = {
autoHeight: AutoHeightControl,
matchColumnsHeight: withDefault(BoolControl, true),
templateRows: withDefault(StringControl, "1fr"),
- rowGap: withDefault(StringControl, "0"),
+ rowGap: withDefault(StringControl, "20px"),
templateColumns: withDefault(StringControl, "1fr 1fr"),
mainScrollbar: withDefault(BoolControl, false),
- columnGap: withDefault(StringControl, "0"),
+ columnGap: withDefault(StringControl, "20px"),
style: styleControl(ContainerStyle, 'style'),
- columnStyle: styleControl(ResponsiveLayoutColStyle , 'columnStyle'),
- useFlexLayout: withDefault(BoolControl, false),
+ columnStyle: styleControl(ResponsiveLayoutColStyle , 'columnStyle')
};
type ViewProps = RecordConstructorToView;
@@ -155,6 +123,7 @@ const ColumnContainer = (props: ColumnContainerProps) => {
);
};
+
const ColumnLayout = (props: ColumnLayoutProps) => {
let {
columns,
@@ -167,8 +136,7 @@ const ColumnLayout = (props: ColumnLayoutProps) => {
columnGap,
columnStyle,
horizontalGridCells,
- mainScrollbar,
- useFlexLayout,
+ mainScrollbar
} = props;
return (
@@ -176,33 +144,27 @@ const ColumnLayout = (props: ColumnLayoutProps) => {
-
+
{columns.map(column => {
const id = String(column.id);
const childDispatch = wrapDispatch(wrapDispatch(dispatch, "containers"), id);
if(!containers[id]) return null
const containerProps = containers[id].children;
- const columnWidth = getColumnWidth(column);
- const hasExplicitWidth = !!column.width;
-
+ const noOfColumns = columns.length;
return (
-
+
@@ -260,10 +221,6 @@ export const ResponsiveLayoutBaseComp = (function () {
{children.horizontalGridCells.propertyView({
label: trans('prop.horizontalGridCells'),
})}
- {children.useFlexLayout.propertyView({
- label: trans("responsiveLayout.useFlexLayout"),
- tooltip: trans("responsiveLayout.useFlexLayoutTooltip")
- })}
{children.matchColumnsHeight.propertyView({ label: trans("responsiveLayout.matchColumnsHeight")
@@ -271,14 +228,8 @@ export const ResponsiveLayoutBaseComp = (function () {
{controlItem({}, (
{trans("responsiveLayout.columnsSpacing")}
))}
- {!children.useFlexLayout.getView() && children.templateColumns.propertyView({
- label: trans("responsiveLayout.columnDefinition"),
- tooltip: trans("responsiveLayout.columnsDefinitionTooltip")
- })}
- {!children.useFlexLayout.getView() && children.templateRows.propertyView({
- label: trans("responsiveLayout.rowDefinition"),
- tooltip: trans("responsiveLayout.rowsDefinitionTooltip")
- })}
+ {children.templateColumns.propertyView({label: trans("responsiveLayout.columnDefinition"), tooltip: trans("responsiveLayout.columnsDefinitionTooltip")})}
+ {children.templateRows.propertyView({label: trans("responsiveLayout.rowDefinition"), tooltip: trans("responsiveLayout.rowsDefinitionTooltip")})}
{children.columnGap.propertyView({label: trans("responsiveLayout.columnGap")})}
{children.rowGap.propertyView({label: trans("responsiveLayout.rowGap")})}
diff --git a/client/packages/lowcoder/src/comps/controls/optionsControl.tsx b/client/packages/lowcoder/src/comps/controls/optionsControl.tsx
index 8e25d053dc..e32c32c09f 100644
--- a/client/packages/lowcoder/src/comps/controls/optionsControl.tsx
+++ b/client/packages/lowcoder/src/comps/controls/optionsControl.tsx
@@ -40,7 +40,6 @@ import { ControlItemCompBuilder } from "comps/generators/controlCompBuilder";
import { ColorControl } from "./colorControl";
import { StringStateControl } from "./codeStateControl";
import { reduceInContext } from "../utils/reduceContext";
-import React from "react";
const OptionTypes = [
{
@@ -66,13 +65,10 @@ type OptionControlParam = {
title?: string;
// The new option's label name
newOptionLabel?: string;
- // Whether to use flex layout (for column options)
- useFlexLayout?: boolean;
};
type OptionPropertyParam = {
autoMap?: boolean;
- useFlexLayout?: boolean;
};
interface OptionCompProperty {
@@ -180,7 +176,7 @@ export function manualOptionsControl(
itemTitle={(comp) => comp.children.label.getView()}
popoverTitle={() => trans("edit")}
content={(comp) => {
- return hasPropertyView(comp) ? comp.propertyView({ useFlexLayout: param.useFlexLayout }) : comp.getPropertyView();
+ return hasPropertyView(comp) ? comp.propertyView({}) : comp.getPropertyView();
}}
items={manualComp.getView()}
onAdd={() => {
@@ -580,13 +576,12 @@ const StyledContent = styled.div`
}
`;
-let ColumnOption = new MultiCompBuilder(
+const ColumnOption = new MultiCompBuilder(
{
id: valueComp(-1),
label: StringControl,
key: StringControl,
minWidth: withDefault(RadiusControl, ""),
- width: withDefault(RadiusControl, ""),
background: withDefault(ColorControl, ""),
backgroundImage: withDefault(StringControl, ""),
border: withDefault(ColorControl, ""),
@@ -595,55 +590,48 @@ let ColumnOption = new MultiCompBuilder(
padding: withDefault(StringControl, ""),
},
(props) => props
-).build();
-
-// Add propertyView method through class extension
-ColumnOption = class extends ColumnOption implements OptionCompProperty {
- propertyView(param: OptionPropertyParam) {
- const useFlexLayout = param?.useFlexLayout !== undefined ? param.useFlexLayout : true;
-
- return (
-
- {useFlexLayout && this.children.width.propertyView({
- label: trans('responsiveLayout.width'),
- preInputNode: ,
- placeholder: '50%',
- })}
- {this.children.background.propertyView({
- label: trans('style.background'),
- })}
- {this.children.backgroundImage.propertyView({
- label: `Background Image`,
- // preInputNode: ,
- placeholder: 'https://temp.im/350x400',
- })}
- {this.children.border.propertyView({
- label: trans('style.border')
- })}
- {this.children.radius.propertyView({
- label: trans('style.borderRadius'),
- preInputNode: ,
- placeholder: '3px',
- })}
- {this.children.margin.propertyView({
- label: trans('style.margin'),
- preInputNode: ,
- placeholder: '3px',
- })}
- {this.children.padding.propertyView({
- label: trans('style.padding'),
- preInputNode: ,
- placeholder: '3px',
- })}
-
- );
- }
-};
+)
+.setPropertyViewFn((children) => (
+
+ {children.minWidth.propertyView({
+ label: trans('responsiveLayout.minWidth'),
+ preInputNode: ,
+ placeholder: '3px',
+ })}
+ {children.background.propertyView({
+ label: trans('style.background'),
+ })}
+ {children.backgroundImage.propertyView({
+ label: `Background Image`,
+ // preInputNode: ,
+ placeholder: 'https://temp.im/350x400',
+ })}
+ {children.border.propertyView({
+ label: trans('style.border')
+ })}
+ {children.radius.propertyView({
+ label: trans('style.borderRadius'),
+ preInputNode: ,
+ placeholder: '3px',
+ })}
+ {children.margin.propertyView({
+ label: trans('style.margin'),
+ preInputNode: ,
+ placeholder: '3px',
+ })}
+ {children.padding.propertyView({
+ label: trans('style.padding'),
+ preInputNode: ,
+ placeholder: '3px',
+ })}
+
+))
+ .build();
export const ColumnOptionControl = manualOptionsControl(ColumnOption, {
initOptions: [
- { id: 0, key: "Column1", label: "Column1", width: "" },
- { id: 1, key: "Column2", label: "Column2", width: "" },
+ { id: 0, key: "Column1", label: "Column1" },
+ { id: 1, key: "Column2", label: "Column2" },
],
uniqField: "key",
autoIncField: "id",
diff --git a/client/packages/lowcoder/src/i18n/locales/en.ts b/client/packages/lowcoder/src/i18n/locales/en.ts
index ba248eea42..d1eff10d73 100644
--- a/client/packages/lowcoder/src/i18n/locales/en.ts
+++ b/client/packages/lowcoder/src/i18n/locales/en.ts
@@ -3910,8 +3910,6 @@ export const en = {
"rowStyle": "Row Style",
"columnStyle": "Column Style",
"minWidth": "Min. Width",
- "width": "Width",
- "widthTooltip": "Set the column width (e.g., '300px', '50%', '100%'). When set to 100%, columns will stack vertically.",
"rowBreak": "Row Break",
"useComponentWidth" : "Use Self Size",
"useComponentWidthDesc" : "Use the container width instead the App width",
@@ -3920,8 +3918,6 @@ export const en = {
"columnsLayout": "Columns Layout",
"columnsDefinitionTooltip": "Columns can be defined freely based on the CSS columns properties. For example, 'auto auto' will create two columns with equal width. Read more here: https://css-tricks.com/almanac/properties/g/grid-template-columns",
"rowsDefinitionTooltip": "Rows can be defined freely based on the CSS rows properties. For example, 'auto auto' will create two rows with equal height. Read more here: https://css-tricks.com/almanac/properties/g/grid-template-rows",
- "useFlexLayout": "Use Flexible Layout",
- "useFlexLayoutTooltip": "Enable responsive behavior where columns can wrap when there's not enough space"
},
"splitLayout" : {
"column": "View Areas",
From dab7174a90fc72210b2905882faf6950ad1a9850 Mon Sep 17 00:00:00 2001
From: Faran Javed
Date: Tue, 13 May 2025 14:39:58 +0500
Subject: [PATCH 7/8] [FIX] #1626 add min-width using css grid minmax()
---
.../comps/comps/columnLayout/columnLayout.tsx | 55 ++++++++++++++++++-
1 file changed, 54 insertions(+), 1 deletion(-)
diff --git a/client/packages/lowcoder/src/comps/comps/columnLayout/columnLayout.tsx b/client/packages/lowcoder/src/comps/comps/columnLayout/columnLayout.tsx
index 12c2bf0a12..f30a3f8081 100644
--- a/client/packages/lowcoder/src/comps/comps/columnLayout/columnLayout.tsx
+++ b/client/packages/lowcoder/src/comps/comps/columnLayout/columnLayout.tsx
@@ -123,6 +123,53 @@ const ColumnContainer = (props: ColumnContainerProps) => {
);
};
+// Function to apply min-widths to grid template columns
+const applyMinWidthsToGridColumns = (columnsDef: string, minWidths: (string | null)[] = []) => {
+ // Handle empty case
+ if (!columnsDef?.trim()) return '';
+
+ // Handle repeat() functions with special parsing
+ if (columnsDef.includes('repeat(')) {
+ // For complex repeat patterns, we should return as-is to avoid breaking the layout
+ // A more complex parser would be needed to fully support repeat with minmax
+ return columnsDef;
+ }
+
+ const columns = columnsDef.trim().split(/\s+/);
+
+ const newColumns = columns.map((col, index) => {
+ const minWidth = minWidths[index];
+
+ // Skip if no minWidth provided for this column
+ if (!minWidth) {
+ return col;
+ }
+
+ // Keywords that should never be wrapped in minmax()
+ const keywords = ['auto', 'min-content', 'max-content', 'fit-content', 'subgrid'];
+ if (keywords.some(keyword => col === keyword)) {
+ return col;
+ }
+
+ // Functions that should never be wrapped in minmax()
+ if (col.includes('(') && col.includes(')')) {
+ // Already includes a function like calc(), minmax(), etc.
+ return col;
+ }
+
+ // Determine if column is flexible and can be wrapped with minmax
+ // - fr units (e.g., "1fr", "2.5fr")
+ // - percentage values (e.g., "50%")
+ // - length values (px, em, rem, etc.)
+ const isFlexible = /fr$/.test(col) ||
+ /%$/.test(col) ||
+ /^\d+(\.\d+)?(px|em|rem|vh|vw|vmin|vmax|cm|mm|in|pt|pc)$/.test(col);
+
+ return isFlexible ? `minmax(${minWidth}, ${col})` : col;
+ });
+
+ return newColumns.join(' ');
+};
const ColumnLayout = (props: ColumnLayoutProps) => {
let {
@@ -139,6 +186,12 @@ const ColumnLayout = (props: ColumnLayoutProps) => {
mainScrollbar
} = props;
+ // Extract minWidths from columns
+ const minWidths = columns.map(column => column.minWidth || null);
+
+ // Apply min-widths to grid template columns
+ const gridTemplateColumns = applyMinWidthsToGridColumns(templateColumns, minWidths);
+
return (
@@ -147,7 +200,7 @@ const ColumnLayout = (props: ColumnLayoutProps) => {
Date: Tue, 13 May 2025 16:07:09 +0500
Subject: [PATCH 8/8] [FIX] #1503 min-width for ResponsiveLayout Component
---
.../src/comps/comps/responsiveLayout/responsiveLayout.tsx | 7 ++++---
1 file changed, 4 insertions(+), 3 deletions(-)
diff --git a/client/packages/lowcoder/src/comps/comps/responsiveLayout/responsiveLayout.tsx b/client/packages/lowcoder/src/comps/comps/responsiveLayout/responsiveLayout.tsx
index 13b3b8b5c1..0ce837560c 100644
--- a/client/packages/lowcoder/src/comps/comps/responsiveLayout/responsiveLayout.tsx
+++ b/client/packages/lowcoder/src/comps/comps/responsiveLayout/responsiveLayout.tsx
@@ -87,7 +87,7 @@ const ColWrapper = styled(Col)<{
flex-basis: ${(props) =>
props.$rowBreak
? `calc(100% / var(--columns))` // Force exact column distribution
- : `clamp(${props.$minWidth}, 100% / var(--columns), 100%)`}; // MinWidth respected
+ : `clamp(${props.$minWidth || "0px"}, calc(100% / var(--columns)), 100%)`}; // MinWidth respected
min-width: ${(props) => props.$minWidth}; // Ensure minWidth is respected
max-width: 100%; // Prevent more columns than allowed
@@ -237,7 +237,8 @@ const ResponsiveLayout = (props: ResponsiveLayoutProps) => {
if (!containers[id]) return null;
const containerProps = containers[id].children;
- const calculatedWidth = 100 / numberOfColumns;
+ // Use the actual minWidth from column configuration instead of calculated width
+ const columnMinWidth = column.minWidth || `${100 / numberOfColumns}px`;
return (
{
sm={rowBreak ? 24 / numberOfColumns : undefined}
xs={rowBreak ? 24 / numberOfColumns : undefined}
$style={props.columnStyle}
- $minWidth={`${calculatedWidth}px`}
+ $minWidth={columnMinWidth}
$matchColumnsHeight={matchColumnsHeight}
$rowBreak={rowBreak}
>
--- a PPN by Garber Painting Akron. With Image Size Reduction included!Fetched URL: http://github.com/lowcoder-org/lowcoder/pull/1675.patch
Alternative Proxies:
Alternative Proxy
pFad Proxy
pFad v3 Proxy
pFad v4 Proxy