();
+
+ const currentThemeColors = useThemeColors(param.allowGradient);
const input = propertyView.call(controlThis, {
placeholder: param.panelDefaultColor,
@@ -112,6 +116,7 @@ function ColorItem(props: {
}, [focus]);
const color = controlThis.getView();
+
useEffect(() => {
setShowDep(param.isDep && !focus && !color);
}, [color, focus, param.isDep]);
@@ -134,6 +139,8 @@ function ColorItem(props: {
;
animationStyle?: Record;
onMouseDown?: React.MouseEventHandler;
+ showValidationWhenEmpty?: boolean;
};
const StyledStarIcon = styled(StarIcon)`
@@ -77,7 +78,7 @@ const MainWrapper = styled.div<{
props.$position === "column" && props.$hasLabel ? "calc(100% - 4px)" : "100%"};
display: flex;
align-items: ${(props) => (props.$position === "row" ? "center" : "start")};
- flex-shrink: 0;
+ flex-shrink: 1;
`;
const LabelWrapper = styled.div<{
@@ -178,6 +179,12 @@ export const LabelControl = (function () {
return new MultiCompBuilder(childrenMap, (props) => (args: LabelViewProps) =>
{
+ const inputValue = (
+ ((args.children as ReactElement)?.props?.children as ReactElement)?.props?.value // text area comp
+ ?? (args.children as ReactElement)?.props.value?.value // number input comp
+ ?? (args.children as ReactElement)?.props.value
+ );
+
return (
- {args.help && Boolean((args.children as ReactElement)?.props.value) && (
+ {args.help && (
+ args.showValidationWhenEmpty
+ || (!args.showValidationWhenEmpty && Boolean(inputValue))
+ ) && (
,
+) => {
+ const editorState = useContext(EditorContext);
+ const {comp, compType} = useContext(CompContext);
+ const theme = useContext(ThemeContext);
+ const bgColor = useContext(BackgroundColorContext);
+ const { themeId } = theme || {};
+ const isPreviewTheme = themeId === 'preview-theme';
+ const isDefaultTheme = themeId === 'default-theme-id';
+
+ const appSettingsComp = editorState?.getAppSettingsComp();
+ const preventAppStylesOverwriting = appSettingsComp?.getView()?.preventAppStylesOverwriting;
+ const { appliedThemeId, preventStyleOverwriting } = (comp?.comp || {});
+ const appTheme = isPreviewTheme || isDefaultTheme || (!preventStyleOverwriting && !preventAppStylesOverwriting)
+ ? theme?.theme
+ : defaultTheme;
+ let compTheme: JSONValue|undefined = {};
+ if (appliedThemeId !== themeId) {
+ compTheme = isPreviewTheme || isDefaultTheme || (compType && !preventStyleOverwriting && !preventAppStylesOverwriting)
+ ? {
+ ...(omit(defaultTheme, 'components', 'chart')),
+ ...defaultTheme.components?.[compType]?.[styleKey] as unknown as Record,
+ ...(omit(theme?.theme, 'components', 'chart')),
+ ...theme?.theme?.components?.[compType]?.[styleKey] as unknown as Record,
+ }
+ : defaultTheme.components?.[compType]?.[styleKey];
+ }
+ const styleProps = (!comp && !compType) || preventStyleOverwriting || preventAppStylesOverwriting || appliedThemeId === themeId
+ ? props
+ : {};
+
+ return {
+ appTheme,
+ styleProps,
+ bgColor,
+ compTheme,
+ compType,
+ }
+};
+
export function styleControl(
colorConfigs: T,
styleKey: string = '',
@@ -888,39 +930,16 @@ export function styleControl(
return new ControlItemCompBuilder(
childrenMap as ToConstructor<{ [K in Names]: ColorControl }>,
(props) => {
- // const compType = useContext(CompTypeContext);
- const editorState = useContext(EditorContext);
- const {comp, compType} = useContext(CompContext);
- const theme = useContext(ThemeContext);
- const bgColor = useContext(BackgroundColorContext);
- const { themeId } = theme || {};
- const isPreviewTheme = themeId === 'preview-theme';
- const isDefaultTheme = themeId === 'default-theme-id';
-
-
- const appSettingsComp = editorState?.getAppSettingsComp();
- const preventAppStylesOverwriting = appSettingsComp?.getView()?.preventAppStylesOverwriting;
- const { appliedThemeId, preventStyleOverwriting } = (comp?.comp || {});
- const appTheme = isPreviewTheme || isDefaultTheme || (!preventStyleOverwriting && !preventAppStylesOverwriting)
- ? theme?.theme
- : defaultTheme;
- let compTheme: JSONValue|undefined = {};
- if (appliedThemeId !== themeId) {
- compTheme = isPreviewTheme || isDefaultTheme || (compType && !preventStyleOverwriting && !preventAppStylesOverwriting)
- ? {
- ...(omit(defaultTheme, 'components', 'chart')),
- ...defaultTheme.components?.[compType]?.[styleKey] as unknown as Record,
- ...(omit(theme?.theme, 'components', 'chart')),
- ...theme?.theme?.components?.[compType]?.[styleKey] as unknown as Record,
- }
- : defaultTheme.components?.[compType]?.[styleKey];
- }
- const styleProps = (!comp && !compType) || preventStyleOverwriting || preventAppStylesOverwriting || appliedThemeId === themeId
- ? props as ColorMap
- : {} as ColorMap;
+ const {
+ styleProps,
+ appTheme,
+ bgColor,
+ compTheme,
+ compType,
+ } = useThemeStyles(styleKey, props as Record);
return calcColors(
- styleProps,
+ styleProps as ColorMap,
colorConfigs,
appTheme,
bgColor,
@@ -932,21 +951,26 @@ export function styleControl(
)
.setControlItemData({ filterText: label, searchChild: true })
.setPropertyViewFn((children) => {
- const theme = useContext(ThemeContext);
- const compType = useContext(CompTypeContext);
- const bgColor = useContext(BackgroundColorContext);
const isMobile = useIsMobile();
- const compTheme = compType
- ? theme?.theme?.components?.[compType]?.[styleKey]
- : undefined;
+ const childrenProps = childrenToProps(children) as Record;
+ const {
+ styleProps,
+ appTheme,
+ bgColor,
+ compTheme,
+ compType,
+ } = useThemeStyles(styleKey, childrenProps);
const props = calcColors(
- childrenToProps(children) as ColorMap,
+ styleProps as ColorMap,
colorConfigs,
- theme?.theme,
+ appTheme,
bgColor,
compTheme as Record | undefined,
+ compType,
+ styleKey,
);
+
const showReset = Object.values(childrenToProps(children)).findIndex((item) => item) > -1;
return (
<>
@@ -1341,6 +1365,7 @@ export function styleControl(
isDep: true,
depMsg:
depMsg,
+ allowGradient: config.name.includes('background'),
})}
);
diff --git a/client/packages/lowcoder/src/comps/controls/styleControlConstants.tsx b/client/packages/lowcoder/src/comps/controls/styleControlConstants.tsx
index d8d6d2422..fca765ca3 100644
--- a/client/packages/lowcoder/src/comps/controls/styleControlConstants.tsx
+++ b/client/packages/lowcoder/src/comps/controls/styleControlConstants.tsx
@@ -1353,7 +1353,6 @@ export const RadioStyle = [
] as const;
export const SegmentStyle = [
- LABEL,
...STYLING_FIELDS_SEQUENCE.filter(
(style) => ["border", "borderWidth"].includes(style.name) === false
),
@@ -1986,6 +1985,7 @@ export const RichTextEditorStyle = [
] as const;
export type QRCodeStyleType = StyleConfigType;
+export type TimeLineStyleType = StyleConfigType;
export type AvatarStyleType = StyleConfigType;
export type AvatarLabelStyleType = StyleConfigType;
export type AvatarContainerStyleType = StyleConfigType<
diff --git a/client/packages/lowcoder/src/comps/hooks/drawerComp.tsx b/client/packages/lowcoder/src/comps/hooks/drawerComp.tsx
index 43fa3e5b7..6c04fa8aa 100644
--- a/client/packages/lowcoder/src/comps/hooks/drawerComp.tsx
+++ b/client/packages/lowcoder/src/comps/hooks/drawerComp.tsx
@@ -136,7 +136,7 @@ let TmpDrawerComp = (function () {
},
body: {
padding: 0,
- backgroundColor: props.style.background
+ background: props.style.background
}
}}
title={props.title}
diff --git a/client/packages/lowcoder/src/comps/hooks/modalComp.tsx b/client/packages/lowcoder/src/comps/hooks/modalComp.tsx
index ed2710ed5..2a3235f9d 100644
--- a/client/packages/lowcoder/src/comps/hooks/modalComp.tsx
+++ b/client/packages/lowcoder/src/comps/hooks/modalComp.tsx
@@ -23,6 +23,7 @@ import { NameConfig, withExposingConfigs } from "../generators/withExposing";
import { BoolControl } from "comps/controls/boolControl";
import { withDefault } from "comps/generators";
import SliderControl from "../controls/sliderControl";
+import { getBackgroundStyle } from "@lowcoder-ee/util/styleUtils";
const EventOptions = [
{ label: trans("modalComp.open"), value: "open", description: trans("modalComp.openDesc") },
@@ -35,13 +36,9 @@ const getStyle = (style: ModalStyleType, modalScrollbar: boolean) => {
border-radius: ${style.radius};
border: ${style.borderWidth} solid ${style.border};
overflow: hidden;
- background-color: ${style.background};
- ${style.backgroundImage ? `background-image: url(${style.backgroundImage}) !important; ` : ';'}
- ${style.backgroundImageRepeat ? `background-repeat: ${style.backgroundImageRepeat};` : 'no-repeat;'}
- ${style.backgroundImageSize ? `background-size: ${style.backgroundImageSize};` : 'cover'}
- ${style.backgroundImagePosition ? `background-position: ${style.backgroundImagePosition};` : 'center;'}
- ${style.backgroundImageOrigin ? `background-origin: ${style.backgroundImageOrigin};` : 'padding-box;'}
margin: ${style.margin};
+ ${getBackgroundStyle(style)}
+
.ant-modal-body > .react-resizable > .react-grid-layout {
background-color: ${style.background};
}
diff --git a/client/packages/lowcoder/src/comps/utils/appSettingContext.tsx b/client/packages/lowcoder/src/comps/utils/appSettingContext.tsx
new file mode 100644
index 000000000..8b442b3d7
--- /dev/null
+++ b/client/packages/lowcoder/src/comps/utils/appSettingContext.tsx
@@ -0,0 +1,7 @@
+import React from "react";
+
+export type AppSettingType = 'setting' | 'canvas';
+
+export const AppSettingContext = React.createContext<{
+ settingType: AppSettingType;
+}>({ settingType: 'setting' });
diff --git a/client/packages/lowcoder/src/constants/style.ts b/client/packages/lowcoder/src/constants/style.ts
index c7787ea77..e21136c11 100644
--- a/client/packages/lowcoder/src/constants/style.ts
+++ b/client/packages/lowcoder/src/constants/style.ts
@@ -1,5 +1,5 @@
// brand color
-export const PrimaryColor = "#3377ff";
+export const PrimaryColor = "#b85fff";
// module
export const ModulePrimaryColor = "#F27A24";
@@ -21,10 +21,10 @@ export const DarkActiveTextColor = "#222222";
export const ActiveTextColor = "#315efb";
// icon
-export const NormalMenuIconColor = "#8B8FA3";
+export const NormalMenuIconColor = "#b480de";
// Ui Comp
-export const UiCompBorderRadius = "2px";
+export const UiCompBorderRadius = "4px";
// Tab
export const TabActiveColor = "#222222";
diff --git a/client/packages/lowcoder/src/constants/themeConstants.ts b/client/packages/lowcoder/src/constants/themeConstants.ts
index 827602d7d..144486380 100644
--- a/client/packages/lowcoder/src/constants/themeConstants.ts
+++ b/client/packages/lowcoder/src/constants/themeConstants.ts
@@ -1,4 +1,5 @@
import { ThemeDetail } from "@lowcoder-ee/api/commonSettingApi";
+import { DEFAULT_GRID_COLUMNS, DEFAULT_ROW_COUNT, DEFAULT_ROW_HEIGHT } from "@lowcoder-ee/layout/calculateUtils";
const theme = {
primary: "#3377FF",
@@ -13,7 +14,9 @@ const theme = {
margin: "3px",
padding: "3px",
lineHeight: "18px",
- gridColumns: "24",
+ gridColumns: String(DEFAULT_GRID_COLUMNS),
+ gridRowHeight: String(DEFAULT_ROW_HEIGHT),
+ gridRowCount: DEFAULT_ROW_COUNT,
textSize: "14px",
// text: "#222222",
animation: "",
@@ -25,6 +28,10 @@ const theme = {
animationIterationCount: "",
showComponentLoadingIndicators: true,
showDataLoadingIndicators: true,
+ gridBgImageSize: "cover",
+ gridBgImagePosition: "center",
+ gridBgImageRepeat: "no-repeat",
+ gridBgImageOrigin: "padding-box",
};
const text = {
diff --git a/client/packages/lowcoder/src/i18n/locales/en.ts b/client/packages/lowcoder/src/i18n/locales/en.ts
index 108d3f8d4..61450a61f 100644
--- a/client/packages/lowcoder/src/i18n/locales/en.ts
+++ b/client/packages/lowcoder/src/i18n/locales/en.ts
@@ -163,6 +163,7 @@ export const en = {
"showSearch": "Searchable",
"defaultValue": "Default Value",
"required": "Required Field",
+ "showEmptyValidation": "Show Validation On Empty/Reset",
"readOnly": "Read Only",
"readOnlyTooltip": "Read-only components appear normal but cannot be modified.",
"minimum": "Minimum",
@@ -2580,7 +2581,8 @@ export const en = {
"chartClick": "Click",
"chartVisit": "Visit",
"chartQuery": "Query",
- "chartBuy": "Buy"
+ "chartBuy": "Buy",
+ "canvas": "Canvas Settings",
},
"themeDetail": {
"primary": "Brand Color",
@@ -2616,11 +2618,31 @@ export const en = {
"paddingDesc": "Default padding typically used for most components",
"containerHeaderPadding": "Header Padding",
"containerheaderpaddingDesc": "Default header padding typically used for most components",
- "gridColumns": "Canvas Grid Columns",
+ "gridColumns": "Grid Columns",
"gridColumnsDesc": "Default number of columns typically used for most containers",
"loadingIndicators": "Loading Indicators",
"showComponentLoadingIndicators": "Show loading indicators when component load",
- "showDataLoadingIndicators": "Show loading indicators when data load"
+ "showDataLoadingIndicators": "Show loading indicators when data load",
+ "background": "Background Styles",
+ "gridSettings": "Grid Settings",
+ "gridRowHeight": "Grid Row Height",
+ "gridRowHeightDesc": "Height of each row in grid",
+ "gridRowCount": "Grid Row Count",
+ "gridRowCountDesc": "Max. number of rows in grid",
+ "gridPaddingX": "Horizontal Padding",
+ "gridPaddingXDesc": "Canvas horizontal padding",
+ "gridPaddingY": "Vertical Padding",
+ "gridPaddingYDesc": "Canvas vertical padding",
+ "gridBgImage": "Background Image",
+ "gridBgImageDesc": "Canvas background image",
+ "gridBgImageRepeat": "Background Image Repeat",
+ "gridBgImageRepeatDesc": "Canvas background image repeat",
+ "gridBgImageSize": "Background Image Size",
+ "gridBgImageSizeDesc": "Canvas background image size",
+ "gridBgImagePosition": "Background Image Position",
+ "gridBgImagePositionDesc": "Canvas background image position",
+ "gridBgImageOrigin": "Background Image Origin",
+ "gridBgImageOriginDesc": "Canvas background image origin",
},
"pluginSetting": {
"title": "Plugins",
@@ -2784,7 +2806,18 @@ export const en = {
"appTitle": "Title",
"appDescription": "Description",
"appCategory": "Category",
- "showPublicHeader": "Show header in public view"
+ "showPublicHeader": "Show header in public view",
+ "canvas": "Canvas Settings",
+ "gridColumns": "Grid Columns",
+ "gridRowHeight": "Grid Row Height",
+ "gridRowCount": "Grid Row Count",
+ "gridPaddingX": "Canvas Horizontal Padding",
+ "gridPaddingY": "Canvas Vertical Padding",
+ "gridBgImage": "Background Image",
+ "gridBgImageRepeat": "Background Image Repeat",
+ "gridBgImageSize": "Background Image Size",
+ "gridBgImagePosition": "Background Image Position",
+ "gridBgImageOrigin": "Background Image Origin"
},
"customShortcut": {
"title": "Custom Shortcuts",
diff --git a/client/packages/lowcoder/src/layout/calculateUtils.tsx b/client/packages/lowcoder/src/layout/calculateUtils.tsx
index 587130f2d..1d919bd1b 100644
--- a/client/packages/lowcoder/src/layout/calculateUtils.tsx
+++ b/client/packages/lowcoder/src/layout/calculateUtils.tsx
@@ -7,18 +7,9 @@ export type PositionParams = Pick<
"margin" | "containerPadding" | "containerWidth" | "cols" | "rowHeight" | "maxRows"
>;
-// Added By Aqib Mirza
-let gridColumns: number;
-
-const getDefaultGridColumns = () => {
- return gridColumns;
-};
-
-export { getDefaultGridColumns };
-export const DEFAULT_GRID_COLUMNS = getDefaultGridColumns() || 24;
-//////////////////////
-
+export const DEFAULT_GRID_COLUMNS = 24;
export const DEFAULT_ROW_HEIGHT = 8;
+export const DEFAULT_ROW_COUNT = Infinity;
export const DEFAULT_POSITION_PARAMS: PositionParams = {
margin: [0, 0],
@@ -26,7 +17,7 @@ export const DEFAULT_POSITION_PARAMS: PositionParams = {
containerWidth: 0,
cols: DEFAULT_GRID_COLUMNS,
rowHeight: DEFAULT_ROW_HEIGHT,
- maxRows: Infinity,
+ maxRows: DEFAULT_ROW_COUNT,
};
// Helper for generating column width
@@ -102,6 +93,7 @@ export function calcGridItemSizePx(
const width = calcGridItemWHPx(w, colWidth, margin[0], false);
const isTouchSBound = top ? isTouchBound(maxRows, rowHeight, h, top) : false;
+ // console.log('positionParams',positionParams);
const height = calcGridItemWHPx(h, rowHeight, margin[1], isTouchSBound);
return { width, height };
}
diff --git a/client/packages/lowcoder/src/layout/compSelectionWrapper.tsx b/client/packages/lowcoder/src/layout/compSelectionWrapper.tsx
index fd812a8b2..a0923d486 100644
--- a/client/packages/lowcoder/src/layout/compSelectionWrapper.tsx
+++ b/client/packages/lowcoder/src/layout/compSelectionWrapper.tsx
@@ -35,7 +35,7 @@ const NameDiv = styled.div<{
}
return "#B8B9BF";
}};
- border-radius: ${(props) => (props.$position === "top" ? "2px 2px 0 0" : "0 0 2px 2px")};
+ border-radius: ${(props) => (props.$position === "top" ? "4px 4px 0 0" : "0 0 4px 4px")};
font-weight: 500;
color: #ffffff;
position: absolute;
@@ -89,13 +89,15 @@ function getLineStyle(
return `
border: ${GRID_ITEM_BORDER_WIDTH}px ${borderStyle} ${borderColor};
- padding: ${isHidden || !isSelected ? 0 : padding[1] - GRID_ITEM_BORDER_WIDTH}px;
- padding-left: ${padding[0] - GRID_ITEM_BORDER_WIDTH}px;
- padding-right: ${padding[0] - GRID_ITEM_BORDER_WIDTH}px;
+ padding: 0px;
+ // padding: ${isHidden || !isSelected ? 0 : padding[1] - GRID_ITEM_BORDER_WIDTH}px;
+ // padding-left: ${padding[0] - GRID_ITEM_BORDER_WIDTH}px;
+ // padding-right: ${padding[0] - GRID_ITEM_BORDER_WIDTH}px;
`;
}
// padding: ${props => props.hover || props.showDashline ? 3 : 4}px;
+
const SelectableDiv = styled.div<{
$hover?: boolean;
$showDashLine: boolean;
@@ -173,7 +175,7 @@ const dragCss = (props: DragHandleProps, handle: ResizeHandleAxis) => css`
height: 8px;
width: 8px;
border: 1px solid ${props.$compType === "module" ? ModulePrimaryColor : PrimaryColor};
- border-radius: 2px;
+ border-radius: 4px;
background-color: #f5f5f6;
z-index: 11;
pointer-events: none;
diff --git a/client/packages/lowcoder/src/layout/gridItem.tsx b/client/packages/lowcoder/src/layout/gridItem.tsx
index 432c9b453..963c1c056 100644
--- a/client/packages/lowcoder/src/layout/gridItem.tsx
+++ b/client/packages/lowcoder/src/layout/gridItem.tsx
@@ -103,15 +103,15 @@ const ResizableStyled = styled(Resizable)<{ $zIndex: number, isDroppable : boole
* An individual item within a ReactGridLayout.
*/
export const GridItem = React.memo((props: GridItemProps) => {
- const position = useMemo(() =>
- calcGridItemPosition({
+ const position = useMemo(() =>{
+ return calcGridItemPosition({
margin: props.margin,
containerPadding: props.containerPadding,
containerWidth: props.containerWidth,
cols: props.cols,
rowHeight: props.rowHeight,
maxRows: props.maxRows,
- }, props.x, props.y, props.w, props.h),
+ }, props.x, props.y, props.w, props.h)},
[
props.margin,
props.containerPadding,
@@ -126,6 +126,7 @@ export const GridItem = React.memo((props: GridItemProps) => {
calcGridItemPosition,
]
);
+
const [resizing, setResizing] = useState<{ width: number; height: number } | undefined>();
const [dragging, setDragging] = useState<{ top: number; left: number } | undefined>();
const elementRef = useRef(null);
@@ -345,6 +346,7 @@ export const GridItem = React.memo((props: GridItemProps) => {
Math.min(maxes.width, Infinity),
Math.min(maxes.height, Infinity),
];
+
return (
{
* @return {String} Container height in pixels.
*/
containerHeight(): string {
- const { margin, rowHeight } = this.props as Required;
+ const { margin, rowHeight, fixedRowCount } = this.props as Required;
const { extraHeight, emptyRows } = this.props;
const positionParams = genPositionParams(this.props);
+
const { containerPadding } = positionParams;
const layout = this.getUILayout(undefined, true);
let nbRow = bottom(layout);
- if (!_.isNil(emptyRows) && _.size(layout) === 0) {
- nbRow = emptyRows;
+ if (!_.isNil(emptyRows) && (_.size(layout) === 0 || fixedRowCount)) {
+ nbRow = emptyRows;// === Infinity ? 0 : emptyRows;
}
const containerHeight = Math.max(
nbRow * rowHeight + (nbRow - 1) * margin[1] + containerPadding[1] * 2
@@ -341,7 +343,7 @@ class GridLayout extends React.Component {
onLayoutMaybeChanged(newLayout: Layout, oldLayout?: Layout) {
// log.debug("layout: layoutMayBeChanged. oldLayout: ", oldLayout, " newLayout: ", newLayout);
if (!oldLayout) oldLayout = this.state.layout;
-
+
if (!_.isEqual(oldLayout, newLayout)) {
this.props.onLayoutChange?.(newLayout);
}
@@ -1046,6 +1048,7 @@ class GridLayout extends React.Component {
$radius={this.props.radius}
$autoHeight={this.props.autoHeight}
$overflow={this.props.overflow}
+ $maxRows={this.props.emptyRows}
tabIndex={-1}
onDrop={isDroppable ? this.onDrop : _.noop}
onDragLeave={isDroppable ? this.onDragLeave : _.noop}
@@ -1081,15 +1084,19 @@ const LayoutContainer = styled.div<{
$bgColor?: string;
$autoHeight?: boolean;
$overflow?: string;
+ $maxRows?: number;
$radius?: string;
}>`
border-radius: ${(props) => props.$radius ?? "4px"};
- background-color: ${(props) => props.$bgColor ?? "#f5f5f6"};
+ // background-color: ${(props) => props.$bgColor ?? "#f5f5f6"};
/* height: 100%; */
height: ${(props) => (props.$autoHeight ? "auto" : "100%")};
- overflow: auto;
- overflow: ${(props) => props.$overflow ?? "overlay"};
+ overflow: ${(props) =>
+ props.$maxRows !== DEFAULT_ROW_COUNT
+ ? 'hidden'
+ : props.$overflow ?? "overlay"
+ };
${(props) =>
props.$autoHeight &&
`::-webkit-scrollbar {
diff --git a/client/packages/lowcoder/src/layout/gridLayoutPropTypes.tsx b/client/packages/lowcoder/src/layout/gridLayoutPropTypes.tsx
index e7e2708d5..735b2ea94 100644
--- a/client/packages/lowcoder/src/layout/gridLayoutPropTypes.tsx
+++ b/client/packages/lowcoder/src/layout/gridLayoutPropTypes.tsx
@@ -34,6 +34,7 @@ export type GridLayoutProps = {
containerPadding?: [number, number];
rowHeight?: number;
isRowCountLocked?: boolean;
+ fixedRowCount?: boolean;
emptyRows?: number;
maxRows?: number;
isDraggable?: boolean;
diff --git a/client/packages/lowcoder/src/pages/editor/editorView.tsx b/client/packages/lowcoder/src/pages/editor/editorView.tsx
index 80a621514..d807771ef 100644
--- a/client/packages/lowcoder/src/pages/editor/editorView.tsx
+++ b/client/packages/lowcoder/src/pages/editor/editorView.tsx
@@ -14,6 +14,8 @@ import {
LeftSettingIcon,
LeftStateIcon,
LeftLayersIcon,
+ LeftColorPaletteIcon,
+ LeftJSSettingIcon,
ScrollBar,
} from "lowcoder-design";
import { useTemplateViewMode } from "util/hooks";
@@ -55,6 +57,7 @@ import { isAggregationApp } from "util/appUtils";
import EditorSkeletonView from "./editorSkeletonView";
import { getCommonSettings } from "@lowcoder-ee/redux/selectors/commonSettingSelectors";
import { isEqual, noop } from "lodash";
+import { AppSettingContext, AppSettingType } from "@lowcoder-ee/comps/utils/appSettingContext";
// import { BottomSkeleton } from "./bottom/BottomContent";
const Header = lazy(
@@ -257,6 +260,8 @@ enum SiderKey {
State = "state",
Setting = "setting",
Layout = "layout",
+ Canvas = "canvas",
+ JS = "js",
}
const standardSiderItems = [
@@ -268,6 +273,14 @@ const standardSiderItems = [
key: SiderKey.Setting,
icon: ,
},
+ {
+ key: SiderKey.Canvas,
+ icon: ,
+ },
+ {
+ key: SiderKey.JS,
+ icon: ,
+ },
{
key: SiderKey.Layout,
icon: ,
@@ -536,40 +549,59 @@ function EditorView(props: EditorViewProps) {
{panelStatus.left && editorModeStatus !== "layout" && (
{menuKey === SiderKey.State && }
- {menuKey === SiderKey.Setting && (
-
-
- {application &&
- !isAggregationApp(
- AppUILayoutType[application.applicationType]
- ) && (
- <>
- {appSettingsComp.getPropertyView()}
-
- >
- )}
- {trans("leftPanel.toolbarTitle")}
- {props.preloadComp.getPropertyView()}
- dispatch(
- setEditorExternalStateAction({
- showScriptsAndStyleModal: true,
- })
- )}
- >
-
- {trans("leftPanel.toolbarPreload")}
-
-
-
+
+ <>
+ {menuKey === SiderKey.Setting && (
+
+
+ {application &&
+ !isAggregationApp(
+ AppUILayoutType[application.applicationType]
+ ) && (
+ <>
+ {appSettingsComp.getPropertyView()}
+ >
+ )}
+
+
+ )}
+ {menuKey === SiderKey.Canvas && (
+
+
+ {application &&
+ !isAggregationApp(
+ AppUILayoutType[application.applicationType]
+ ) && (
+ <>
+ {appSettingsComp.getPropertyView()}
+ >
+ )}
+
+
+ )}
+ >
+
+ {menuKey === SiderKey.JS && (
+ <>
+ {trans("leftPanel.toolbarTitle")}
+ {props.preloadComp.getPropertyView()}
+ dispatch(
+ setEditorExternalStateAction({
+ showScriptsAndStyleModal: true,
+ })
+ )}
+ >
+
+ {trans("leftPanel.toolbarPreload")}
+
+
{props.preloadComp.getJSLibraryPropertyView()}
-
+ >
)}
-
{menuKey === SiderKey.Layout && (
)}
-
)}
diff --git a/client/packages/lowcoder/src/pages/setting/theme/detail/index.tsx b/client/packages/lowcoder/src/pages/setting/theme/detail/index.tsx
index 8013a1b7c..a6e66f5e7 100644
--- a/client/packages/lowcoder/src/pages/setting/theme/detail/index.tsx
+++ b/client/packages/lowcoder/src/pages/setting/theme/detail/index.tsx
@@ -174,7 +174,7 @@ class ThemeDetailPage extends React.Component
+
+
+
+ {trans("theme.canvas")}
+
+
+
+ (
+ <>
+ {item.title && (
+
+ {item.title}
+
+ )}
+ {item.items.map((canvasSettingItem) => (
+
+
+ {canvasSettingItem.type == "gridColumns" &&
+ {
+ this.configChange(params);
+ }}
+ />
+ }
+ {canvasSettingItem.type == "gridRowHeight" &&
+ {
+ this.configChange(params);
+ }}
+ />
+ }
+ {canvasSettingItem.type == "gridRowCount" &&
+ {
+ this.configChange(params);
+ }}
+ />
+ }
+ {canvasSettingItem.type == "gridPaddingX" &&
+ {
+ this.configChange(params);
+ }}
+ />
+ }
+ {canvasSettingItem.type == "gridPaddingY" &&
+ {
+ this.configChange(params);
+ }}
+ />
+ }
+ {canvasSettingItem.type == "canvas" &&
+ {
+ this.configChange(params);
+ }}
+ />
+ }
+ {canvasSettingItem.type == "gridBgImage" &&
+ {
+ this.configChange(params);
+ }}
+ />
+ }
+ {canvasSettingItem.type == "gridBgImageRepeat" &&
+ {
+ this.configChange(params);
+ }}
+ />
+ }
+ {canvasSettingItem.type == "gridBgImageSize" &&
+ {
+ this.configChange(params);
+ }}
+ />
+ }
+ {canvasSettingItem.type == "gridBgImagePosition" &&
+ {
+ this.configChange(params);
+ }}
+ />
+ }
+ {canvasSettingItem.type == "gridBgImageOrigin" &&
+ {
+ this.configChange(params);
+ }}
+ />
+ }
+
+
+ ))}
+ >
+ )}
+ />
+
+
+
+
+
@@ -538,23 +762,12 @@ class ThemeDetailPage extends React.Component
}
- {layoutSettingsItem.type == "gridColumns" &&
- {
- this.configChange(params);
- }}
- />
- }
{layoutSettingsItem.type == "showComponentLoadingIndicators" &&
{
- console.log('configChange', params);
this.configChange(params);
}}
/>
diff --git a/client/packages/lowcoder/src/pages/setting/theme/detail/previewDsl.ts b/client/packages/lowcoder/src/pages/setting/theme/detail/previewDsl.ts
index a868cca8f..ffeb03feb 100644
--- a/client/packages/lowcoder/src/pages/setting/theme/detail/previewDsl.ts
+++ b/client/packages/lowcoder/src/pages/setting/theme/detail/previewDsl.ts
@@ -677,6 +677,7 @@ const dsl = {
settings: {
maxWidth: { dropdown: "3200", input: "3200" },
themeId: "",
+ preventStylesOverwriting: false,
},
preload: { libs: [], script: "", css: "" },
};
diff --git a/client/packages/lowcoder/src/util/hooks.ts b/client/packages/lowcoder/src/util/hooks.ts
index 3bc5a2ed6..57fa2baa4 100644
--- a/client/packages/lowcoder/src/util/hooks.ts
+++ b/client/packages/lowcoder/src/util/hooks.ts
@@ -19,9 +19,13 @@ import { getDataSourceStructures } from "redux/selectors/datasourceSelectors";
import { DatasourceStructure } from "api/datasourceApi";
import { loadAuthSearchParams } from "pages/userAuth/authUtils";
import { ThemeContext } from "@lowcoder-ee/comps/utils/themeContext";
+import { defaultTheme } from "constants/themeConstants";
import { CompTypeContext } from "@lowcoder-ee/comps/utils/compTypeContext";
import { setInitialCompStyles } from "@lowcoder-ee/comps/utils/themeUtil";
import { CompAction, changeChildAction } from "lowcoder-core";
+import { ThemeDetail } from "@lowcoder-ee/api/commonSettingApi";
+import { uniq } from "lodash";
+import { constantColors } from "components/colorSelect/colorUtils";
export const ForceViewModeContext = React.createContext(false);
@@ -168,7 +172,6 @@ export function useMetaData(datasourceId: string) {
);
}
-
export function useMergeCompStyles(
props: Record,
dispatch: (action: CompAction) => void
@@ -226,3 +229,31 @@ export function useMergeCompStyles(
preventStyleOverwriting,
]);
}
+
+type ColorKey = 'primary' | 'textDark' | 'textLight' | 'canvas' | 'primarySurface' | 'border';
+type ColorKeys = ColorKey[];
+
+export function useThemeColors(allowGradient?: boolean) {
+ const currentTheme = useContext(ThemeContext)?.theme ?? {} as ThemeDetail;
+ const colorKeys: ColorKeys = ['primary', 'textDark', 'textLight', 'canvas', 'primarySurface', 'border'];
+
+ return useMemo(() => {
+ let colors: string[] = [];
+
+ colorKeys.forEach(colorKey => {
+ if (Boolean(defaultTheme[colorKey])) {
+ colors.push(defaultTheme[colorKey] ?? '');
+ }
+ if (Boolean(currentTheme[colorKey])) {
+ colors.push(currentTheme[colorKey] ?? '');
+ }
+ })
+ if (!allowGradient) {
+ colors = colors.concat(constantColors);
+ }
+ return uniq(colors);
+ }, [
+ currentTheme,
+ defaultTheme,
+ ]);
+}
\ No newline at end of file
diff --git a/client/packages/lowcoder/src/util/styleUtils.tsx b/client/packages/lowcoder/src/util/styleUtils.tsx
new file mode 100644
index 000000000..e59a8fc25
--- /dev/null
+++ b/client/packages/lowcoder/src/util/styleUtils.tsx
@@ -0,0 +1,32 @@
+import { isValidColor, isValidGradient } from "components/colorSelect/colorUtils"
+import { css } from "styled-components";
+
+const getBackgroundStyle = (style: Record) => {
+ return css`
+ ${isValidColor(style.background) && `background-color: ${style.background}`};
+ ${isValidGradient(style.background) && !Boolean(style.backgroundImage) && `background-image: ${style.background}`};
+ ${!isValidGradient(style.background) && Boolean(style.backgroundImage) && `background-image: ${style.backgroundImage}`};
+ ${isValidGradient(style.background) && Boolean(style.backgroundImage) && `background-image: url(${style.backgroundImage}), ${style.background}`};
+
+ ${style.backgroundImageRepeat && `background-repeat: ${style.backgroundImageRepeat};`};
+ ${style.backgroundImageSize && `background-size: ${style.backgroundImageSize};`};
+ ${style.backgroundImageOrigin && `background-origin: ${style.backgroundImageOrigin};`};
+ ${style.backgroundImagePosition && `background-position: ${style.backgroundImagePosition};`};
+ `;
+}
+
+const getTextStyle = (color?: string) => {
+ return css`
+ ${isValidColor(color) && `color: ${color};`}
+ ${isValidGradient(color) && `
+ background-image: -webkit-${color};
+ -webkit-background-clip: text;
+ -webkit-text-fill-color: transparent;
+ `}
+ `;
+}
+
+export {
+ getBackgroundStyle,
+ getTextStyle,
+}
\ No newline at end of file
diff --git a/client/packages/lowcoder/vite.config.mts b/client/packages/lowcoder/vite.config.mts
index e6618c6ee..1be2ebfd8 100644
--- a/client/packages/lowcoder/vite.config.mts
+++ b/client/packages/lowcoder/vite.config.mts
@@ -102,9 +102,9 @@ export const viteConfig: UserConfig = {
preprocessorOptions: {
less: {
modifyVars: {
- "@primary-color": "#3377FF",
+ "@primary-color": "#b480de",
"@link-color": "#3377FF",
- "@border-color-base": "#D7D9E0",
+ "@border-color-base": "#b480de",
"@border-radius-base": "4px",
},
javascriptEnabled: true,
diff --git a/client/yarn.lock b/client/yarn.lock
index f3f30d79d..7825894fc 100644
--- a/client/yarn.lock
+++ b/client/yarn.lock
@@ -6655,6 +6655,13 @@ __metadata:
languageName: node
linkType: hard
+"base64-arraybuffer@npm:^1.0.2":
+ version: 1.0.2
+ resolution: "base64-arraybuffer@npm:1.0.2"
+ checksum: 15e6400d2d028bf18be4ed97702b11418f8f8779fb8c743251c863b726638d52f69571d4cc1843224da7838abef0949c670bde46936663c45ad078e89fee5c62
+ languageName: node
+ linkType: hard
+
"base64-js@npm:^1.3.1":
version: 1.5.1
resolution: "base64-js@npm:1.5.1"
@@ -7907,6 +7914,15 @@ coolshapes-react@lowcoder-org/coolshapes-react:
languageName: node
linkType: hard
+"css-line-break@npm:^2.1.0":
+ version: 2.1.0
+ resolution: "css-line-break@npm:2.1.0"
+ dependencies:
+ utrie: ^1.0.2
+ checksum: 37b1fe632b03be7a287cd394cef8b5285666343443125c510df9cfb6a4734a2c71e154ec8f7bbff72d7c339e1e5872989b1c52d86162aed27d6cc114725bb4d0
+ languageName: node
+ linkType: hard
+
"css-loader@npm:^6.10.0":
version: 6.11.0
resolution: "css-loader@npm:6.11.0"
@@ -11285,6 +11301,16 @@ coolshapes-react@lowcoder-org/coolshapes-react:
languageName: node
linkType: hard
+"html2canvas@npm:^1.4.1":
+ version: 1.4.1
+ resolution: "html2canvas@npm:1.4.1"
+ dependencies:
+ css-line-break: ^2.1.0
+ text-segmentation: ^1.0.3
+ checksum: c134324af57f3262eecf982e436a4843fded3c6cf61954440ffd682527e4dd350e0c2fafd217c0b6f9a455fe345d0c67b4505689796ab160d4ca7c91c3766739
+ languageName: node
+ linkType: hard
+
"http-cache-semantics@npm:^4.1.1":
version: 4.1.1
resolution: "http-cache-semantics@npm:4.1.1"
@@ -13598,6 +13624,13 @@ coolshapes-react@lowcoder-org/coolshapes-react:
languageName: node
linkType: hard
+"lodash.throttle@npm:^4.1.1":
+ version: 4.1.1
+ resolution: "lodash.throttle@npm:4.1.1"
+ checksum: 129c0a28cee48b348aef146f638ef8a8b197944d4e9ec26c1890c19d9bf5a5690fe11b655c77a4551268819b32d27f4206343e30c78961f60b561b8608c8c805
+ languageName: node
+ linkType: hard
+
"lodash@npm:^3.9.1":
version: 3.10.1
resolution: "lodash@npm:3.10.1"
@@ -14000,6 +14033,7 @@ coolshapes-react@lowcoder-org/coolshapes-react:
qrcode.react: ^3.1.0
rc-trigger: ^5.3.1
react: ^18.2.0
+ react-best-gradient-color-picker: ^3.0.10
react-colorful: ^5.5.1
react-documents: ^1.2.1
react-dom: ^18.2.0
@@ -17485,6 +17519,24 @@ coolshapes-react@lowcoder-org/coolshapes-react:
languageName: node
linkType: hard
+"react-best-gradient-color-picker@npm:^3.0.10":
+ version: 3.0.10
+ resolution: "react-best-gradient-color-picker@npm:3.0.10"
+ dependencies:
+ html2canvas: ^1.4.1
+ lodash.throttle: ^4.1.1
+ tinycolor2: 1.4.2
+ peerDependencies:
+ "@types/react": ^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0
+ react: ^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0
+ react-dom: ^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0
+ peerDependenciesMeta:
+ "@types/react":
+ optional: true
+ checksum: 3f821beb164e7eed0c3247af99e492dc08bfa64b47f8d7f71d1543e5afbe1d9f01cde4a2917c9d794012dd1efbfe848ee58621b4f37801ea5655df8be806ea55
+ languageName: node
+ linkType: hard
+
"react-colorful@npm:^5.5.1":
version: 5.6.1
resolution: "react-colorful@npm:5.6.1"
@@ -20198,6 +20250,15 @@ coolshapes-react@lowcoder-org/coolshapes-react:
languageName: node
linkType: hard
+"text-segmentation@npm:^1.0.3":
+ version: 1.0.3
+ resolution: "text-segmentation@npm:1.0.3"
+ dependencies:
+ utrie: ^1.0.2
+ checksum: 2e24632d59567c55ab49ac324815e2f7a8043e63e26b109636322ac3e30692cee8679a448fd5d0f0598a345f407afd0e34ba612e22524cf576d382d84058c013
+ languageName: node
+ linkType: hard
+
"text-table@npm:^0.2.0":
version: 0.2.0
resolution: "text-table@npm:0.2.0"
@@ -20276,6 +20337,13 @@ coolshapes-react@lowcoder-org/coolshapes-react:
languageName: node
linkType: hard
+"tinycolor2@npm:1.4.2":
+ version: 1.4.2
+ resolution: "tinycolor2@npm:1.4.2"
+ checksum: 57ed262e08815a4ab0ed933edafdbc6555a17081781766149813b44a080ecbe58b3ee281e81c0e75b42e4d41679f138cfa98eabf043f829e0683c04adb12c031
+ languageName: node
+ linkType: hard
+
"tmpl@npm:1.0.5":
version: 1.0.5
resolution: "tmpl@npm:1.0.5"
@@ -21217,6 +21285,15 @@ coolshapes-react@lowcoder-org/coolshapes-react:
languageName: node
linkType: hard
+"utrie@npm:^1.0.2":
+ version: 1.0.2
+ resolution: "utrie@npm:1.0.2"
+ dependencies:
+ base64-arraybuffer: ^1.0.2
+ checksum: c96fbb7d4d8855a154327da0b18e39b7511cc70a7e4bcc3658e24f424bb884312d72b5ba777500b8858e34d365dc6b1a921dc5ca2f0d341182519c6b78e280a5
+ languageName: node
+ linkType: hard
+
"uuid@npm:^3.3.2":
version: 3.4.0
resolution: "uuid@npm:3.4.0"
diff --git a/server/api-service/lowcoder-domain/src/main/java/org/lowcoder/domain/application/model/ApplicationHistorySnapshotTS.java b/server/api-service/lowcoder-domain/src/main/java/org/lowcoder/domain/application/model/ApplicationHistorySnapshotTS.java
new file mode 100644
index 000000000..1b6d86502
--- /dev/null
+++ b/server/api-service/lowcoder-domain/src/main/java/org/lowcoder/domain/application/model/ApplicationHistorySnapshotTS.java
@@ -0,0 +1,23 @@
+package org.lowcoder.domain.application.model;
+
+import lombok.Getter;
+import lombok.NoArgsConstructor;
+import lombok.Setter;
+import lombok.ToString;
+import org.lowcoder.sdk.models.HasIdAndAuditing;
+import org.springframework.data.mongodb.core.mapping.Document;
+
+import java.util.Map;
+
+@ToString(callSuper = true)
+@Document
+@Getter
+@Setter
+@NoArgsConstructor
+public class ApplicationHistorySnapshotTS extends HasIdAndAuditing {
+
+ private String applicationId;
+ private Map dsl;
+ private Map context;
+
+}
diff --git a/server/api-service/lowcoder-domain/src/main/java/org/lowcoder/domain/application/repository/ApplicationHistoryArchivedSnapshotRepository.java b/server/api-service/lowcoder-domain/src/main/java/org/lowcoder/domain/application/repository/ApplicationHistoryArchivedSnapshotRepository.java
new file mode 100644
index 000000000..dded29c35
--- /dev/null
+++ b/server/api-service/lowcoder-domain/src/main/java/org/lowcoder/domain/application/repository/ApplicationHistoryArchivedSnapshotRepository.java
@@ -0,0 +1,26 @@
+package org.lowcoder.domain.application.repository;
+
+import org.lowcoder.domain.application.model.ApplicationHistorySnapshot;
+import org.springframework.data.domain.Pageable;
+import org.springframework.data.mongodb.repository.Query;
+import org.springframework.data.mongodb.repository.ReactiveMongoRepository;
+import org.springframework.stereotype.Repository;
+import reactor.core.publisher.Flux;
+import reactor.core.publisher.Mono;
+
+import java.time.Instant;
+
+@Repository
+public interface ApplicationHistoryArchivedSnapshotRepository extends ReactiveMongoRepository {
+
+ @Query(value = "{ 'applicationId': ?0, $and: [" +
+ "{$or: [ { 'context.operations': { $elemMatch: { 'compName': ?1 } } }, { $expr: { $eq: [?1, null] } } ]}, " +
+ "{$or: [ { 'dsl.settings.themeId': ?2 }, { $expr: { $eq: [?2, null] } } ] }, " +
+ "{$or: [ { 'createdAt': { $gte: ?3} }, { $expr: { $eq: [?3, null] } } ] }, " +
+ "{$or: [ { 'createdAt': { $lte: ?4} }, { $expr: { $eq: [?4, null] } } ] } " +
+ "]}",
+ fields = "{applicationId : 1, context: 1, createdBy : 1, createdAt : 1}")
+ Flux findAllByApplicationId(String applicationId, String compName, String theme, Instant createdAtFrom, Instant createdAtTo, Pageable pageable);
+
+ Mono countByApplicationId(String applicationId);
+}
diff --git a/server/api-service/lowcoder-domain/src/main/java/org/lowcoder/domain/application/repository/ApplicationHistorySnapshotRepository.java b/server/api-service/lowcoder-domain/src/main/java/org/lowcoder/domain/application/repository/ApplicationHistorySnapshotRepository.java
index a8e9f3d02..809decfd6 100644
--- a/server/api-service/lowcoder-domain/src/main/java/org/lowcoder/domain/application/repository/ApplicationHistorySnapshotRepository.java
+++ b/server/api-service/lowcoder-domain/src/main/java/org/lowcoder/domain/application/repository/ApplicationHistorySnapshotRepository.java
@@ -1,19 +1,26 @@
package org.lowcoder.domain.application.repository;
-import org.lowcoder.domain.application.model.ApplicationHistorySnapshot;
+import org.lowcoder.domain.application.model.ApplicationHistorySnapshotTS;
import org.springframework.data.domain.Pageable;
import org.springframework.data.mongodb.repository.Query;
import org.springframework.data.mongodb.repository.ReactiveMongoRepository;
import org.springframework.stereotype.Repository;
-
import reactor.core.publisher.Flux;
import reactor.core.publisher.Mono;
+import java.time.Instant;
+
@Repository
-public interface ApplicationHistorySnapshotRepository extends ReactiveMongoRepository {
+public interface ApplicationHistorySnapshotRepository extends ReactiveMongoRepository {
- @Query(fields = "{applicationId : 1, context: 1, createdBy : 1, createdAt : 1}")
- Flux findAllByApplicationId(String applicationId, Pageable pageable);
+ @Query(value = "{ 'applicationId': ?0, $and: [" +
+ "{$or: [ { 'context.operations': { $elemMatch: { 'compName': ?1 } } }, { $expr: { $eq: [?1, null] } } ]}, " +
+ "{$or: [ { 'dsl.settings.themeId': ?2 }, { $expr: { $eq: [?2, null] } } ] }, " +
+ "{$or: [ { 'createdAt': { $gte: ?3} }, { $expr: { $eq: [?3, null] } } ] }, " +
+ "{$or: [ { 'createdAt': { $lte: ?4} }, { $expr: { $eq: [?4, null] } } ] } " +
+ "]}",
+ fields = "{applicationId : 1, context: 1, createdBy : 1, createdAt : 1}")
+ Flux findAllByApplicationId(String applicationId, String compName, String theme, Instant createdAtFrom, Instant createdAtTo, Pageable pageable);
Mono countByApplicationId(String applicationId);
}
diff --git a/server/api-service/lowcoder-domain/src/main/java/org/lowcoder/domain/application/service/ApplicationHistorySnapshotService.java b/server/api-service/lowcoder-domain/src/main/java/org/lowcoder/domain/application/service/ApplicationHistorySnapshotService.java
index a8845dd07..fd4a79f82 100644
--- a/server/api-service/lowcoder-domain/src/main/java/org/lowcoder/domain/application/service/ApplicationHistorySnapshotService.java
+++ b/server/api-service/lowcoder-domain/src/main/java/org/lowcoder/domain/application/service/ApplicationHistorySnapshotService.java
@@ -1,22 +1,24 @@
package org.lowcoder.domain.application.service;
-import java.util.List;
-import java.util.Map;
-
import org.lowcoder.domain.application.model.ApplicationHistorySnapshot;
+import org.lowcoder.domain.application.model.ApplicationHistorySnapshotTS;
import org.springframework.data.domain.PageRequest;
-
import reactor.core.publisher.Mono;
+import java.time.Instant;
+import java.util.List;
+import java.util.Map;
+
public interface ApplicationHistorySnapshotService {
Mono createHistorySnapshot(String applicationId, Map dsl, Map context, String userId);
- Mono> listAllHistorySnapshotBriefInfo(String applicationId, PageRequest pageRequest);
+ Mono> listAllHistorySnapshotBriefInfo(String applicationId, String compName, String theme, Instant from, Instant to, PageRequest pageRequest);
+ Mono> listAllHistorySnapshotBriefInfoArchived(String applicationId, String compName, String theme, Instant from, Instant to, PageRequest pageRequest);
Mono countByApplicationId(String applicationId);
- Mono getHistorySnapshotDetail(String historySnapshotId);
+ Mono getHistorySnapshotDetail(String historySnapshotId);
- Mono getLastSnapshotByApp(String applicationId);
+ Mono getHistorySnapshotDetailArchived(String historySnapshotId);
}
diff --git a/server/api-service/lowcoder-domain/src/main/java/org/lowcoder/domain/application/service/impl/ApplicationHistorySnapshotServiceImpl.java b/server/api-service/lowcoder-domain/src/main/java/org/lowcoder/domain/application/service/impl/ApplicationHistorySnapshotServiceImpl.java
index 25a140c24..c47b39955 100644
--- a/server/api-service/lowcoder-domain/src/main/java/org/lowcoder/domain/application/service/impl/ApplicationHistorySnapshotServiceImpl.java
+++ b/server/api-service/lowcoder-domain/src/main/java/org/lowcoder/domain/application/service/impl/ApplicationHistorySnapshotServiceImpl.java
@@ -1,46 +1,53 @@
package org.lowcoder.domain.application.service.impl;
-import static org.lowcoder.sdk.exception.BizError.INVALID_HISTORY_SNAPSHOT;
-import static org.lowcoder.sdk.util.ExceptionUtils.deferredError;
-import static org.lowcoder.sdk.util.ExceptionUtils.ofException;
-
-import java.time.Instant;
-import java.util.List;
-import java.util.Map;
-
import lombok.RequiredArgsConstructor;
import org.lowcoder.domain.application.model.ApplicationHistorySnapshot;
+import org.lowcoder.domain.application.model.ApplicationHistorySnapshotTS;
+import org.lowcoder.domain.application.repository.ApplicationHistoryArchivedSnapshotRepository;
import org.lowcoder.domain.application.repository.ApplicationHistorySnapshotRepository;
import org.lowcoder.domain.application.service.ApplicationHistorySnapshotService;
import org.lowcoder.sdk.exception.BizError;
-import org.springframework.beans.factory.annotation.Autowired;
-import org.springframework.context.annotation.Lazy;
import org.springframework.data.domain.PageRequest;
import org.springframework.data.domain.Sort.Direction;
import org.springframework.stereotype.Service;
-
import reactor.core.publisher.Mono;
+import java.time.Instant;
+import java.util.List;
+import java.util.Map;
+
+import static org.lowcoder.sdk.exception.BizError.INVALID_HISTORY_SNAPSHOT;
+import static org.lowcoder.sdk.util.ExceptionUtils.deferredError;
+import static org.lowcoder.sdk.util.ExceptionUtils.ofException;
+
@RequiredArgsConstructor
@Service
public class ApplicationHistorySnapshotServiceImpl implements ApplicationHistorySnapshotService {
private final ApplicationHistorySnapshotRepository repository;
+ private final ApplicationHistoryArchivedSnapshotRepository repositoryArchived;
@Override
public Mono createHistorySnapshot(String applicationId, Map dsl, Map context, String userId) {
- ApplicationHistorySnapshot applicationHistorySnapshot = new ApplicationHistorySnapshot();
- applicationHistorySnapshot.setApplicationId(applicationId);
- applicationHistorySnapshot.setDsl(dsl);
- applicationHistorySnapshot.setContext(context);
- return repository.save(applicationHistorySnapshot)
+ ApplicationHistorySnapshotTS applicationHistorySnapshotTS = new ApplicationHistorySnapshotTS();
+ applicationHistorySnapshotTS.setApplicationId(applicationId);
+ applicationHistorySnapshotTS.setDsl(dsl);
+ applicationHistorySnapshotTS.setContext(context);
+ return repository.save(applicationHistorySnapshotTS)
.thenReturn(true)
.onErrorReturn(false);
}
@Override
- public Mono> listAllHistorySnapshotBriefInfo(String applicationId, PageRequest pageRequest) {
- return repository.findAllByApplicationId(applicationId, pageRequest.withSort(Direction.DESC, "id"))
+ public Mono> listAllHistorySnapshotBriefInfo(String applicationId, String compName, String theme, Instant from, Instant to, PageRequest pageRequest) {
+ return repository.findAllByApplicationId(applicationId, compName, theme, from, to, pageRequest.withSort(Direction.DESC, "id"))
+ .collectList()
+ .onErrorMap(Exception.class, e -> ofException(BizError.FETCH_HISTORY_SNAPSHOT_FAILURE, "FETCH_HISTORY_SNAPSHOT_FAILURE"));
+ }
+
+ @Override
+ public Mono> listAllHistorySnapshotBriefInfoArchived(String applicationId, String compName, String theme, Instant from, Instant to, PageRequest pageRequest) {
+ return repositoryArchived.findAllByApplicationId(applicationId, compName, theme, from, to, pageRequest.withSort(Direction.DESC, "id"))
.collectList()
.onErrorMap(Exception.class, e -> ofException(BizError.FETCH_HISTORY_SNAPSHOT_FAILURE, "FETCH_HISTORY_SNAPSHOT_FAILURE"));
}
@@ -54,17 +61,15 @@ public Mono countByApplicationId(String applicationId) {
@Override
- public Mono getHistorySnapshotDetail(String historySnapshotId) {
+ public Mono getHistorySnapshotDetail(String historySnapshotId) {
return repository.findById(historySnapshotId)
.switchIfEmpty(deferredError(INVALID_HISTORY_SNAPSHOT, "INVALID_HISTORY_SNAPSHOT", historySnapshotId));
}
+
@Override
- public Mono getLastSnapshotByApp(String applicationId) {
- ApplicationHistorySnapshot _default = new ApplicationHistorySnapshot();
- _default.setCreatedAt(Instant.ofEpochMilli(0));
- _default.setCreatedBy("");
- return repository.findAllByApplicationId(applicationId, PageRequest.of(0, 1).withSort(Direction.DESC, "createdAt"))
- .switchIfEmpty(Mono.just(_default)).next();
+ public Mono getHistorySnapshotDetailArchived(String historySnapshotId) {
+ return repositoryArchived.findById(historySnapshotId)
+ .switchIfEmpty(deferredError(INVALID_HISTORY_SNAPSHOT, "INVALID_HISTORY_SNAPSHOT", historySnapshotId));
}
}
diff --git a/server/api-service/lowcoder-domain/src/main/java/org/lowcoder/domain/authentication/AuthenticationServiceImpl.java b/server/api-service/lowcoder-domain/src/main/java/org/lowcoder/domain/authentication/AuthenticationServiceImpl.java
index 65fdeb8b2..99a541274 100644
--- a/server/api-service/lowcoder-domain/src/main/java/org/lowcoder/domain/authentication/AuthenticationServiceImpl.java
+++ b/server/api-service/lowcoder-domain/src/main/java/org/lowcoder/domain/authentication/AuthenticationServiceImpl.java
@@ -53,15 +53,15 @@ private Mono findAuthConfig(String orgId, Function findAllAuthConfigs(String orgId, boolean enableOnly) {
Mono emailAuthConfigMono = orgMemberService.doesAtleastOneAdminExist()
- .map(doesAtleastOneAdminExist -> {
+ .flatMap(doesAtleastOneAdminExist -> {
boolean shouldEnableRegister;
if(doesAtleastOneAdminExist) {
shouldEnableRegister = authProperties.getEmail().getEnableRegister();
} else {
shouldEnableRegister = Boolean.TRUE;
}
- return new FindAuthConfig
- (new EmailAuthConfig(AuthSourceConstants.EMAIL, authProperties.getEmail().isEnable(), shouldEnableRegister), null);
+ if(orgId == null) return Mono.just(new FindAuthConfig(new EmailAuthConfig(AuthSourceConstants.EMAIL, authProperties.getEmail().isEnable(), shouldEnableRegister), null));
+ else return organizationService.getById(orgId).map(organization -> new FindAuthConfig(new EmailAuthConfig(AuthSourceConstants.EMAIL, !organization.getIsEmailDisabled(), shouldEnableRegister), null));
});
diff --git a/server/api-service/lowcoder-domain/src/main/java/org/lowcoder/domain/organization/model/Organization.java b/server/api-service/lowcoder-domain/src/main/java/org/lowcoder/domain/organization/model/Organization.java
index 18d7d2423..80d338b48 100644
--- a/server/api-service/lowcoder-domain/src/main/java/org/lowcoder/domain/organization/model/Organization.java
+++ b/server/api-service/lowcoder-domain/src/main/java/org/lowcoder/domain/organization/model/Organization.java
@@ -51,6 +51,13 @@ public class Organization extends HasIdAndAuditing implements BeforeMongodbWrite
private String contactPhoneNumber;
+ private Boolean isEmailDisabled;
+
+ public Boolean getIsEmailDisabled() {
+ if(isEmailDisabled == null) return false;
+ else return isEmailDisabled;
+ }
+
@JsonIgnore
private String logoAssetId;
diff --git a/server/api-service/lowcoder-domain/src/main/java/org/lowcoder/domain/user/service/UserService.java b/server/api-service/lowcoder-domain/src/main/java/org/lowcoder/domain/user/service/UserService.java
index 19d1fde58..b7dc9326c 100644
--- a/server/api-service/lowcoder-domain/src/main/java/org/lowcoder/domain/user/service/UserService.java
+++ b/server/api-service/lowcoder-domain/src/main/java/org/lowcoder/domain/user/service/UserService.java
@@ -27,6 +27,7 @@ public interface UserService {
Mono