From 067ded9dfee63c21cf276f5644aabbadeecbcea0 Mon Sep 17 00:00:00 2001 From: FalkWolsky Date: Sun, 14 Jan 2024 18:33:02 +0100 Subject: [PATCH 1/3] Style Icons --- .../src/components/colorSelect/index.tsx | 2 +- .../lowcoder/src/components/ColorPicker.tsx | 4 +- .../src/comps/controls/styleControl.tsx | 37 ++++++++++--------- 3 files changed, 22 insertions(+), 21 deletions(-) diff --git a/client/packages/lowcoder-design/src/components/colorSelect/index.tsx b/client/packages/lowcoder-design/src/components/colorSelect/index.tsx index 8e4415676..a27733419 100644 --- a/client/packages/lowcoder-design/src/components/colorSelect/index.tsx +++ b/client/packages/lowcoder-design/src/components/colorSelect/index.tsx @@ -178,4 +178,4 @@ const ColorBlock = styled.div<{ $color: string }>` cursor: pointer; background-clip: content-box; overflow: hidden; -`; +`; \ No newline at end of file diff --git a/client/packages/lowcoder/src/components/ColorPicker.tsx b/client/packages/lowcoder/src/components/ColorPicker.tsx index a64765738..603776420 100644 --- a/client/packages/lowcoder/src/components/ColorPicker.tsx +++ b/client/packages/lowcoder/src/components/ColorPicker.tsx @@ -197,7 +197,7 @@ export default function ColorPicker(props: ColorConfigProps) { )} {colorKey === "margin" && (
- +
@@ -215,7 +215,7 @@ export default function ColorPicker(props: ColorConfigProps) { )} {colorKey === "padding" && (
- +
diff --git a/client/packages/lowcoder/src/comps/controls/styleControl.tsx b/client/packages/lowcoder/src/comps/controls/styleControl.tsx index a91429492..701f25d5d 100644 --- a/client/packages/lowcoder/src/comps/controls/styleControl.tsx +++ b/client/packages/lowcoder/src/comps/controls/styleControl.tsx @@ -13,6 +13,8 @@ import { ExpandIcon, CompressIcon, TextSizeIcon, + PencilIcon, + ShowBorderIcon, } from "lowcoder-design"; import { useContext } from "react"; import styled from "styled-components"; @@ -250,19 +252,11 @@ const StyleContent = styled.div` } `; -const RadiusIcon = styled(IconRadius)` - margin: 0 8px 0 -2px; -`; - -const MarginIcon = styled(ExpandIcon)` -margin: 0 8px 0 -2px; -`; -const PaddingIcon = styled(CompressIcon)` -margin: 0 8px 0 -2px; -`; -const StyledTextSizeIcon = styled(TextSizeIcon)` -margin: 0 8px 0 -2px; -`; +const RadiusIcon = styled(IconRadius)` margin: 0 8px 0 -2px;`; +const BorderIcon = styled(ShowBorderIcon)` margin: 0px 10px 0 3px;`; +const MarginIcon = styled(ExpandIcon)` margin: 0 8px 0 2px;`; +const PaddingIcon = styled(CompressIcon)` margin: 0 8px 0 2px;`; +const StyledTextSizeIcon = styled(TextSizeIcon)` margin: 0 8px 0 0px;`; const ResetIcon = styled(IconReset)` &:hover g g { stroke: #315efb; @@ -366,22 +360,29 @@ export function styleControl(colorConfig { filterText: config.label },
{(name === "radius" || - name === "borderWidth" || name === "gap" || name === "cardRadius") ? ( children[name] as InstanceType ).propertyView({ label: config.label, - preInputNode: , + preInputNode: , placeholder: props[name], - }) + }) + : name === "borderWidth" + ? ( + children[name] as InstanceType + ).propertyView({ + label: config.label, + preInputNode: , + placeholder: props[name], + }) : name === "margin" ? ( children[name] as InstanceType ).propertyView({ label: config.label, - preInputNode: , + preInputNode: , placeholder: props[name], }) : (name === "padding" || @@ -392,7 +393,7 @@ export function styleControl(colorConfig children[name] as InstanceType ).propertyView({ label: config.label, - preInputNode: , + preInputNode: , placeholder: props[name], }) : name === "textSize" From e1e8b3b825b55bb30efe519dba38d6eb9dabcffe Mon Sep 17 00:00:00 2001 From: FalkWolsky Date: Sun, 14 Jan 2024 21:56:58 +0100 Subject: [PATCH 2/3] Button Hover fix, Text Weight --- .../comps/buttonComp/buttonCompConstants.tsx | 19 ++++++++--- .../src/comps/controls/styleControl.tsx | 34 +++++++++++++++++-- .../comps/controls/styleControlConstants.tsx | 25 ++++++++++++-- .../packages/lowcoder/src/i18n/locales/en.ts | 3 +- .../packages/lowcoder/src/i18n/locales/zh.ts | 1 + 5 files changed, 70 insertions(+), 12 deletions(-) diff --git a/client/packages/lowcoder/src/comps/comps/buttonComp/buttonCompConstants.tsx b/client/packages/lowcoder/src/comps/comps/buttonComp/buttonCompConstants.tsx index 86fa9cb2f..79167b4e9 100644 --- a/client/packages/lowcoder/src/comps/comps/buttonComp/buttonCompConstants.tsx +++ b/client/packages/lowcoder/src/comps/comps/buttonComp/buttonCompConstants.tsx @@ -16,10 +16,11 @@ export function getButtonStyle(buttonStyle: ButtonStyleType) { margin: ${buttonStyle.margin}; padding: ${buttonStyle.padding}; &:not(:disabled) { - // click animation color --antd-wave-shadow-color: ${buttonStyle.border}; border-color: ${buttonStyle.border}; color: ${buttonStyle.text}; + font-size: ${buttonStyle.textSize}; + font-weight: ${buttonStyle.textWeight}; background-color: ${buttonStyle.background}; border-radius: ${buttonStyle.radius}; margin: ${buttonStyle.margin}; @@ -31,15 +32,14 @@ export function getButtonStyle(buttonStyle: ButtonStyleType) { background-color: ${hoverColor}; border-color: ${buttonStyle.border === buttonStyle.background ? hoverColor - : buttonStyle.border}; + : buttonStyle.border} !important; } - :active { color: ${buttonStyle.text}; background-color: ${activeColor}; border-color: ${buttonStyle.border === buttonStyle.background ? activeColor - : buttonStyle.border}; + : buttonStyle.border} !important; } } } @@ -54,11 +54,20 @@ export const Button100 = styled(Button)<{ $buttonStyle?: ButtonStyleType }>` justify-content: center; align-items: center; overflow: hidden; + gap: 6px; + &:not(:disabled) { + &:hover, + &:focus { + background-color: ${(props) => props.$buttonStyle ? genHoverColor(props.$buttonStyle.background) : ''} !important; + } + :active { + background-color: ${(props) => props.$buttonStyle ? genActiveColor(props.$buttonStyle.background) : ''} !important; + } + } span { overflow: hidden; text-overflow: ellipsis; } - gap: 6px; `; export const ButtonCompWrapper = styled.div<{ disabled: boolean }>` diff --git a/client/packages/lowcoder/src/comps/controls/styleControl.tsx b/client/packages/lowcoder/src/comps/controls/styleControl.tsx index 701f25d5d..4eff65357 100644 --- a/client/packages/lowcoder/src/comps/controls/styleControl.tsx +++ b/client/packages/lowcoder/src/comps/controls/styleControl.tsx @@ -13,7 +13,7 @@ import { ExpandIcon, CompressIcon, TextSizeIcon, - PencilIcon, + TypographyIcon, ShowBorderIcon, } from "lowcoder-design"; import { useContext } from "react"; @@ -31,8 +31,10 @@ import { MarginConfig, PaddingConfig, TextSizeConfig, + TextWeightConfig, BorderWidthConfig, } from "./styleControlConstants"; +import { faTextWidth } from "@fortawesome/free-solid-svg-icons"; function isSimpleColorConfig(config: SingleColorConfig): config is SimpleColorConfig { return config.hasOwnProperty("color"); @@ -54,6 +56,10 @@ function isTextSizeConfig(config: SingleColorConfig): config is TextSizeConfig { return config.hasOwnProperty("textSize"); } +function isTextWeightConfig(config: SingleColorConfig): config is TextWeightConfig { + return config.hasOwnProperty("textWeight"); +} + function isMarginConfig(config: SingleColorConfig): config is MarginConfig { return config.hasOwnProperty("margin"); } @@ -80,6 +86,9 @@ function isEmptyBorderWidth(borderWidth: string) { function isEmptyTextSize(textSize: string) { return _.isEmpty(textSize); } +function isEmptyTextWeight(textWeight: string) { + return _.isEmpty(textWeight); +} function isEmptyMargin(margin: string) { return _.isEmpty(margin); @@ -114,6 +123,10 @@ function calcColors>( res[name] = props[name]; return; } + if (!isEmptyTextWeight(props[name]) && isTextWeightConfig(config)) { + res[name] = props[name]; + return; + } if (!isEmptyMargin(props[name]) && isMarginConfig(config)) { res[name] = props[name]; return; @@ -143,6 +156,10 @@ function calcColors>( // TODO: remove default textSize after added in theme in backend. res[name] = themeWithDefault[config.textSize] || '14px'; } + if (isTextWeightConfig(config)) { + // TODO: remove default textWeight after added in theme in backend. + res[name] = themeWithDefault[config.textWeight] || 'regular'; + } if (isMarginConfig(config)) { res[name] = themeWithDefault[config.margin]; } @@ -257,6 +274,8 @@ const BorderIcon = styled(ShowBorderIcon)` margin: 0px 10px 0 3px;`; const MarginIcon = styled(ExpandIcon)` margin: 0 8px 0 2px;`; const PaddingIcon = styled(CompressIcon)` margin: 0 8px 0 2px;`; const StyledTextSizeIcon = styled(TextSizeIcon)` margin: 0 8px 0 0px;`; +const StyledTextWeightIcon = styled(TypographyIcon)` margin: 0 8px 0 0px;`; + const ResetIcon = styled(IconReset)` &:hover g g { stroke: #315efb; @@ -272,7 +291,8 @@ export function styleControl(colorConfig name === "radius" || name === "borderWidth" || name === "cardRadius" || - name === "textSize" + name === "textSize" || + name === "textWeight" ) { childrenMap[name] = StringControl; } else if (name === "margin" || name === "padding" || name==="containerheaderpadding" || name==="containerfooterpadding" || name==="containerbodypadding") { @@ -401,9 +421,17 @@ export function styleControl(colorConfig children[name] as InstanceType ).propertyView({ label: config.label, - preInputNode: , + preInputNode: , placeholder: props[name], }) + : name === "textWeight" + ? ( + children[name] as InstanceType + ).propertyView({ + label: config.label, + preInputNode: , + placeholder: props[name], + }) : children[name].propertyView({ label: config.label, panelDefaultColor: props[name], diff --git a/client/packages/lowcoder/src/comps/controls/styleControlConstants.tsx b/client/packages/lowcoder/src/comps/controls/styleControlConstants.tsx index cd39479fe..00148b45e 100644 --- a/client/packages/lowcoder/src/comps/controls/styleControlConstants.tsx +++ b/client/packages/lowcoder/src/comps/controls/styleControlConstants.tsx @@ -1,7 +1,7 @@ import { ThemeDetail } from "api/commonSettingApi"; import { darkenColor, isDarkColor, lightenColor, toHex } from "lowcoder-design"; import { trans } from "i18n"; -import { StyleConfigType } from "./styleControl"; +import { StyleConfigType } from "./styleControl"; type SupportPlatform = "pc" | "mobile"; @@ -25,6 +25,10 @@ export type TextSizeConfig = CommonColorConfig & { readonly textSize: string; }; +export type TextWeightConfig = CommonColorConfig & { + readonly textWeight: string; +}; + export type ContainerHeaderPaddigConfig = CommonColorConfig & { readonly containerheaderpadding: string; }; @@ -50,7 +54,7 @@ export type DepColorConfig = CommonColorConfig & { readonly depType?: DEP_TYPE; transformer: (color: string, ...rest: string[]) => string; }; -export type SingleColorConfig = SimpleColorConfig | DepColorConfig | RadiusConfig | BorderWidthConfig | TextSizeConfig | MarginConfig | PaddingConfig | ContainerHeaderPaddigConfig | ContainerFooterPaddigConfig | ContainerBodyPaddigConfig; +export type SingleColorConfig = SimpleColorConfig | DepColorConfig | RadiusConfig | BorderWidthConfig | TextSizeConfig | TextWeightConfig | MarginConfig | PaddingConfig | ContainerHeaderPaddigConfig | ContainerFooterPaddigConfig | ContainerBodyPaddigConfig; export const defaultTheme: ThemeDetail = { primary: "#3377FF", @@ -284,6 +288,12 @@ const TEXT_SIZE = { textSize: "textSize", } as const; +const TEXT_WEIGHT = { + name: "textWeight", + label: trans("style.textWeight"), + textWeight: "textWeight", +} as const; + const CONTAINERHEADERPADDING = { name: "containerheaderpadding", label: trans("style.containerheaderpadding"), @@ -370,11 +380,20 @@ function getStaticBackground(color: string) { } as const; } -export const ButtonStyle = [...getBgBorderRadiusByBg("primary"), TEXT, MARGIN, PADDING] as const; +export const ButtonStyle = [ + ...getBgBorderRadiusByBg("primary"), + TEXT, + TEXT_SIZE, + TEXT_WEIGHT, + MARGIN, + PADDING +] as const; export const ToggleButtonStyle = [ getBackground("canvas"), TEXT, + TEXT_SIZE, + TEXT_WEIGHT, { name: "border", label: trans("style.border"), diff --git a/client/packages/lowcoder/src/i18n/locales/en.ts b/client/packages/lowcoder/src/i18n/locales/en.ts index c4f32115a..e2fecc802 100644 --- a/client/packages/lowcoder/src/i18n/locales/en.ts +++ b/client/packages/lowcoder/src/i18n/locales/en.ts @@ -358,7 +358,8 @@ export const en = { "containerbodypadding": "Body Padding", "minWidth": "Minimum Width", "aspectRatio": "Aspect Ratio", - "textSize": "Text Size" + "textSize": "Text Size", + "textWeight": "Text Weight", }, "export": { "hiddenDesc": "If true, the component is hidden", diff --git a/client/packages/lowcoder/src/i18n/locales/zh.ts b/client/packages/lowcoder/src/i18n/locales/zh.ts index 44e245746..57c7f9a79 100644 --- a/client/packages/lowcoder/src/i18n/locales/zh.ts +++ b/client/packages/lowcoder/src/i18n/locales/zh.ts @@ -349,6 +349,7 @@ style: { containerbodypadding: "内边距", minWidth: "最小宽度", textSize: "字体大小", + textWeight: "字体粗细", }, export: { hiddenDesc: "如果为true,则隐藏组件", From 0ece0bf25615e3dcdaf8b30f6947efedff680ff1 Mon Sep 17 00:00:00 2001 From: FalkWolsky Date: Mon, 15 Jan 2024 23:52:29 +0100 Subject: [PATCH 3/3] Button Hover fix, Text Weight, Background, Tabs --- .../comps/buttonComp/buttonCompConstants.tsx | 15 +- .../comps/containerComp/containerComp.tsx | 6 +- .../comps/containerComp/containerView.tsx | 2 +- .../src/comps/comps/customComp/customComp.tsx | 6 +- .../comps/comps/tabs/tabbedContainerComp.tsx | 46 ++- .../lowcoder/src/comps/comps/textComp.tsx | 2 + .../comps/triContainerComp/triContainer.tsx | 55 ++- .../triContainerComp/triContainerComp.tsx | 4 +- .../src/comps/controls/styleControl.tsx | 329 +++++++++++++++++- .../comps/controls/styleControlConstants.tsx | 218 +++++++++++- .../lowcoder/src/comps/hooks/modalComp.tsx | 37 +- .../packages/lowcoder/src/i18n/locales/en.ts | 15 + .../packages/lowcoder/src/i18n/locales/zh.ts | 15 + 13 files changed, 689 insertions(+), 61 deletions(-) diff --git a/client/packages/lowcoder/src/comps/comps/buttonComp/buttonCompConstants.tsx b/client/packages/lowcoder/src/comps/comps/buttonComp/buttonCompConstants.tsx index 79167b4e9..ca3130690 100644 --- a/client/packages/lowcoder/src/comps/comps/buttonComp/buttonCompConstants.tsx +++ b/client/packages/lowcoder/src/comps/comps/buttonComp/buttonCompConstants.tsx @@ -26,15 +26,15 @@ export function getButtonStyle(buttonStyle: ButtonStyleType) { margin: ${buttonStyle.margin}; padding: ${buttonStyle.padding}; - :hover, - :focus { + &:hover, + &:focus { color: ${buttonStyle.text}; background-color: ${hoverColor}; border-color: ${buttonStyle.border === buttonStyle.background ? hoverColor : buttonStyle.border} !important; } - :active { + &:active { color: ${buttonStyle.text}; background-color: ${activeColor}; border-color: ${buttonStyle.border === buttonStyle.background @@ -55,15 +55,6 @@ export const Button100 = styled(Button)<{ $buttonStyle?: ButtonStyleType }>` align-items: center; overflow: hidden; gap: 6px; - &:not(:disabled) { - &:hover, - &:focus { - background-color: ${(props) => props.$buttonStyle ? genHoverColor(props.$buttonStyle.background) : ''} !important; - } - :active { - background-color: ${(props) => props.$buttonStyle ? genActiveColor(props.$buttonStyle.background) : ''} !important; - } - } span { overflow: hidden; text-overflow: ellipsis; diff --git a/client/packages/lowcoder/src/comps/comps/containerComp/containerComp.tsx b/client/packages/lowcoder/src/comps/comps/containerComp/containerComp.tsx index 94113e4a1..e189c466e 100644 --- a/client/packages/lowcoder/src/comps/comps/containerComp/containerComp.tsx +++ b/client/packages/lowcoder/src/comps/comps/containerComp/containerComp.tsx @@ -41,7 +41,11 @@ export const ContainerBaseComp = (function () { {(useContext(EditorContext).editorModeStatus === "layout" || useContext(EditorContext).editorModeStatus === "both") && ( <>
{children.container.getPropertyView()} -
{children.container.stylePropertyView()}
+ +
+ { children.container.stylePropertyView() } +
+ )} ); diff --git a/client/packages/lowcoder/src/comps/comps/containerComp/containerView.tsx b/client/packages/lowcoder/src/comps/comps/containerComp/containerView.tsx index 9445abd6e..26ac5561e 100644 --- a/client/packages/lowcoder/src/comps/comps/containerComp/containerView.tsx +++ b/client/packages/lowcoder/src/comps/comps/containerComp/containerView.tsx @@ -318,7 +318,7 @@ export function InnerGrid(props: ViewPropsWithSelect) { const defaultGrid = useContext(ThemeContext)?.theme?.gridColumns || defaultTheme?.gridColumns || - "24"; + "12"; ///////////////////// const isDroppable = useContext(IsDroppable) && (_.isNil(props.isDroppable) || props.isDroppable) && !readOnly; diff --git a/client/packages/lowcoder/src/comps/comps/customComp/customComp.tsx b/client/packages/lowcoder/src/comps/comps/customComp/customComp.tsx index d35c91622..990f5b072 100644 --- a/client/packages/lowcoder/src/comps/comps/customComp/customComp.tsx +++ b/client/packages/lowcoder/src/comps/comps/customComp/customComp.tsx @@ -12,6 +12,7 @@ import { EventData, EventTypeEnum } from "./types"; import { hiddenPropertyView } from "comps/utils/propertyUtils"; import { trans } from "i18n"; import { EditorContext } from "comps/editorState"; +import * as ReactDOMClient from 'react-dom/client'; // TODO: eventually to embedd in container so we have styling? // TODO: support different starter templates for different frameworks (react, ANT, Flutter, Angular, etc) @@ -59,9 +60,8 @@ const defaultCode = ` ); const ConnectedComponent = ${trans("customComp.sdkGlobalVarName")}.connect(MyCustomComponent); - - const container = document.getElementById("root"); - const root = createRoot(container); + const container = document.getElementById('root'); + const root = ReactDOMClient.createRoot(container); root.render(); diff --git a/client/packages/lowcoder/src/comps/comps/tabs/tabbedContainerComp.tsx b/client/packages/lowcoder/src/comps/comps/tabs/tabbedContainerComp.tsx index 125599b0b..9ec9e1293 100644 --- a/client/packages/lowcoder/src/comps/comps/tabs/tabbedContainerComp.tsx +++ b/client/packages/lowcoder/src/comps/comps/tabs/tabbedContainerComp.tsx @@ -3,7 +3,7 @@ import { JSONObject, JSONValue } from "util/jsonTypes"; import { CompAction, CompActionTypes, deleteCompAction, wrapChildAction } from "lowcoder-core"; import { DispatchType, RecordConstructorToView, wrapDispatch } from "lowcoder-core"; import { AutoHeightControl } from "comps/controls/autoHeightControl"; -import { stringExposingStateControl } from "comps/controls/codeStateControl"; +import { BooleanStateControl, booleanExposingStateControl, stringExposingStateControl } from "comps/controls/codeStateControl"; import { eventHandlerControl } from "comps/controls/eventHandlerControl"; import { TabsOptionControl } from "comps/controls/optionsControl"; import { styleControl } from "comps/controls/styleControl"; @@ -12,7 +12,7 @@ import { sameTypeMap, UICompBuilder, withDefault } from "comps/generators"; import { addMapChildAction } from "comps/generators/sameTypeMap"; import { NameConfig, NameConfigHidden, withExposingConfigs } from "comps/generators/withExposing"; import { NameGenerator } from "comps/utils"; -import { Section, sectionNames } from "lowcoder-design"; +import { ControlNode, Section, sectionNames } from "lowcoder-design"; import { HintPlaceHolder } from "lowcoder-design"; import _ from "lodash"; import React, { useCallback, useContext } from "react"; @@ -33,6 +33,9 @@ import { DisabledContext } from "comps/generators/uiCompBuilder"; import { EditorContext } from "comps/editorState"; import { checkIsMobile } from "util/commonUtils"; import { messageInstance } from "lowcoder-design"; +import { show } from "antd-mobile/es/components/dialog/show"; +import { BoolControl } from "@lowcoder-ee/index.sdk"; +import { Switch } from "antd"; const EVENT_OPTIONS = [ { @@ -52,27 +55,40 @@ const childrenMap = { autoHeight: AutoHeightControl, onEvent: eventHandlerControl(EVENT_OPTIONS), disabled: BoolCodeControl, + showHeader: BooleanStateControl, style: styleControl(TabContainerStyle), }; type ViewProps = RecordConstructorToView; type TabbedContainerProps = ViewProps & { dispatch: DispatchType }; - + const getStyle = (style: TabContainerStyleType) => { return css` &.ant-tabs { - border: 1px solid ${style.border}; + border: ${style.borderWidth} solid ${style.border}; border-radius: ${style.radius}; overflow: hidden; - padding: ${style.padding}; + padding: ${style.padding}; > .ant-tabs-content-holder > .ant-tabs-content > div > .react-grid-layout { background-color: ${style.background}; border-radius: 0; + + background-image: ${style.backgroundImage}; + background-repeat: ${style.backgroundImageRepeat}; + background-size: ${style.backgroundImageSize}; + background-position: ${style.backgroundImagePosition}; + background-origin: ${style.backgroundImageOrigin}; + } > .ant-tabs-nav { background-color: ${style.headerBackground}; + background-image: ${style.headerBackgroundImage}; + background-repeat: ${style.headerBackgroundImageRepeat}; + background-size: ${style.headerBackgroundImageSize}; + background-position: ${style.headerBackgroundImagePosition}; + background-origin: ${style.headerBackgroundImageOrigin}; .ant-tabs-tab { div { @@ -96,7 +112,11 @@ const getStyle = (style: TabContainerStyleType) => { `; }; -const StyledTabs = styled(Tabs)<{ $style: TabContainerStyleType; $isMobile?: boolean }>` +const StyledTabs = styled(Tabs)<{ + $style: TabContainerStyleType; + $isMobile?: boolean; + $showHeader?: boolean; +}>` &.ant-tabs { height: 100%; } @@ -111,6 +131,7 @@ const StyledTabs = styled(Tabs)<{ $style: TabContainerStyleType; $isMobile?: boo } .ant-tabs-nav { + display: ${(props) => (props.$showHeader ? "block" : "none")}; padding: 0 ${(props) => (props.$isMobile ? 16 : 24)}px; background: white; margin: 0px; @@ -158,12 +179,10 @@ const TabbedContainer = (props: TabbedContainerProps) => { const editorState = useContext(EditorContext); const maxWidth = editorState.getAppSettings().maxWidth; const isMobile = checkIsMobile(maxWidth); - const paddingWidth = isMobile ? 8 : 20; - - // log.debug("TabbedContainer. props: ", props); + const showHeader = props.showHeader.value; + const paddingWidth = isMobile ? 8 : 0; const tabItems = visibleTabs.map((tab) => { - // log.debug("Tab. tab: ", tab, " containers: ", containers); const id = String(tab.id); const childDispatch = wrapDispatch(wrapDispatch(dispatch, "containers"), id); const containerProps = containers[id].children; @@ -203,6 +222,7 @@ const TabbedContainer = (props: TabbedContainerProps) => { { if (key !== props.selectedTabKey.value) { props.selectedTabKey.onChange(key); @@ -220,6 +240,7 @@ const TabbedContainer = (props: TabbedContainerProps) => { ); }; + export const TabbedContainerBaseComp = (function () { return new UICompBuilder(childrenMap, (props, dispatch) => { return ( @@ -238,11 +259,12 @@ export const TabbedContainerBaseComp = (function () { })} {children.selectedTabKey.propertyView({ label: trans("prop.defaultValue") })} - + {["logic", "both"].includes(useContext(EditorContext).editorModeStatus) && (
{children.onEvent.getPropertyView()} {disabledPropertyView(children)} + {children.showHeader.propertyView({ label: trans("prop.showHeader") })} {hiddenPropertyView(children)}
)} @@ -364,6 +386,8 @@ class TabbedContainerImplComp extends TabbedContainerBaseComp implements IContai override autoHeight(): boolean { return this.children.autoHeight.getView(); } + + } export const TabbedContainerComp = withExposingConfigs(TabbedContainerImplComp, [ diff --git a/client/packages/lowcoder/src/comps/comps/textComp.tsx b/client/packages/lowcoder/src/comps/comps/textComp.tsx index 797edcf50..fa0649e68 100644 --- a/client/packages/lowcoder/src/comps/comps/textComp.tsx +++ b/client/packages/lowcoder/src/comps/comps/textComp.tsx @@ -26,6 +26,8 @@ const getStyle = (style: TextStyleType) => { border-radius: ${(style.radius ? style.radius : "4px")}; border: ${(style.borderWidth ? style.borderWidth : "0px")} solid ${style.border}; color: ${style.text}; + font-size: ${style.textSize} !important; + font-weight: ${style.textWeight} !important; background-color: ${style.background}; .markdown-body a { color: ${style.links}; diff --git a/client/packages/lowcoder/src/comps/comps/triContainerComp/triContainer.tsx b/client/packages/lowcoder/src/comps/comps/triContainerComp/triContainer.tsx index 0477ffb58..8435a6aae 100644 --- a/client/packages/lowcoder/src/comps/comps/triContainerComp/triContainer.tsx +++ b/client/packages/lowcoder/src/comps/comps/triContainerComp/triContainer.tsx @@ -9,8 +9,9 @@ import { gridItemCompToGridItems, InnerGrid } from "../containerComp/containerVi import { TriContainerViewProps } from "../triContainerComp/triContainerCompBuilder"; const getStyle = (style: ContainerStyleType) => { - return css` + return css` border-color: ${style.border}; + border-width: ${style.borderWidth}; border-radius: ${style.radius}; overflow: hidden; // margin: ${style.margin}; @@ -29,32 +30,64 @@ const Wrapper = styled.div<{ $style: ContainerStyleType }>` ${(props) => props.$style && getStyle(props.$style)} `; -const HeaderInnerGrid = styled(InnerGrid)<{ $backgroundColor: string }>` +const HeaderInnerGrid = styled(InnerGrid)<{ + $backgroundColor: string + $headerBackgroundImage: string; + $headerBackgroundImageRepeat: string; + $headerBackgroundImageSize: string; + $headerBackgroundImagePosition: string; + $headerBackgroundImageOrigin: string; + }>` overflow: visible; ${(props) => props.$backgroundColor && `background-color: ${props.$backgroundColor};`} border-radius: 0; + ${(props) => props.$headerBackgroundImage && `background-image: ${props.$headerBackgroundImage};`} + ${(props) => props.$headerBackgroundImageRepeat && `background-repeat: ${props.$headerBackgroundImageRepeat};`} + ${(props) => props.$headerBackgroundImageSize && `background-size: ${props.$headerBackgroundImageSize};`} + ${(props) => props.$headerBackgroundImagePosition && `background-position: ${props.$headerBackgroundImagePosition};`} + ${(props) => props.$headerBackgroundImageOrigin && `background-origin: ${props.$headerBackgroundImageOrigin};`} `; const BodyInnerGrid = styled(InnerGrid)<{ $showBorder: boolean; $backgroundColor: string; $borderColor: string; + $backgroundImage: string; + $backgroundImageRepeat: string; + $backgroundImageSize: string; + $backgroundImagePosition: string; + $backgroundImageOrigin: string; }>` border-top: ${(props) => `${props.$showBorder ? 1 : 0}px solid ${props.$borderColor}`}; flex: 1; ${(props) => props.$backgroundColor && `background-color: ${props.$backgroundColor};`} border-radius: 0; + ${(props) => props.$backgroundImage && `background-image: ${props.$backgroundImage};`} + ${(props) => props.$backgroundImageRepeat && `background-repeat: ${props.$backgroundImageRepeat};`} + ${(props) => props.$backgroundImageSize && `background-size: ${props.$backgroundImageSize};`} + ${(props) => props.$backgroundImagePosition && `background-position: ${props.$backgroundImagePosition};`} + ${(props) => props.$backgroundImageOrigin && `background-origin: ${props.$backgroundImageOrigin};`} `; const FooterInnerGrid = styled(InnerGrid)<{ $showBorder: boolean; $backgroundColor: string; $borderColor: string; + $footerBackgroundImage: string; + $footerBackgroundImageRepeat: string; + $footerBackgroundImageSize: string; + $footerBackgroundImagePosition: string; + $footerBackgroundImageOrigin: string; }>` border-top: ${(props) => `${props.$showBorder ? 1 : 0}px solid ${props.$borderColor}`}; overflow: visible; ${(props) => props.$backgroundColor && `background-color: ${props.$backgroundColor};`} border-radius: 0; + ${(props) => props.$footerBackgroundImage && `background-image: ${props.$footerBackgroundImage};`} + ${(props) => props.$footerBackgroundImageRepeat && `background-repeat: ${props.$footerBackgroundImageRepeat};`} + ${(props) => props.$footerBackgroundImageSize && `background-size: ${props.$footerBackgroundImageSize};`} + ${(props) => props.$footerBackgroundImagePosition && `background-position: ${props.$footerBackgroundImagePosition};`} + ${(props) => props.$footerBackgroundImageOrigin && `background-origin: ${props.$footerBackgroundImageOrigin};`} `; export type TriContainerProps = TriContainerViewProps & { @@ -75,7 +108,7 @@ export function TriContainer(props: TriContainerProps) { const editorState = useContext(EditorContext); const maxWidth = editorState.getAppSettings().maxWidth; const isMobile = checkIsMobile(maxWidth); - const paddingWidth = isMobile ? 7 : 19; + const paddingWidth = isMobile ? 8 : 0; return (
@@ -91,7 +124,13 @@ export function TriContainer(props: TriContainerProps) { containerPadding={[paddingWidth, 3]} showName={{ bottom: showBody || showFooter ? 20 : 0 }} $backgroundColor={style?.headerBackground} + $headerBackgroundImage={style?.headerBackgroundImage} + $headerBackgroundImageRepeat={style?.headerBackgroundImageRepeat} + $headerBackgroundImageSize={style?.headerBackgroundImageSize} + $headerBackgroundImagePosition={style?.headerBackgroundImagePosition} + $headerBackgroundImageOrigin={style?.headerBackgroundImageOrigin} style={{padding: style.containerheaderpadding}} + /> )} @@ -110,6 +149,11 @@ export function TriContainer(props: TriContainerProps) { hintPlaceholder={props.hintPlaceholder ?? HintPlaceHolder} $backgroundColor={style?.background} $borderColor={style?.border} + $backgroundImage={style?.backgroundImage} + $backgroundImageRepeat={style?.backgroundImageRepeat} + $backgroundImageSize={style?.backgroundImageSize} + $backgroundImagePosition={style?.backgroundImagePosition} + $backgroundImageOrigin={style?.backgroundImageOrigin} style={{padding: style.containerbodypadding}} /> @@ -126,6 +170,11 @@ export function TriContainer(props: TriContainerProps) { containerPadding={showBody || showHeader ? [paddingWidth, 3.5] : [paddingWidth, 3]} showName={{ top: showHeader || showBody ? 20 : 0 }} $backgroundColor={style?.footerBackground} + $footerBackgroundImage={style?.footerBackgroundImage} + $footerBackgroundImageRepeat={style?.footerBackgroundImageRepeat} + $footerBackgroundImageSize={style?.footerBackgroundImageSize} + $footerBackgroundImagePosition={style?.footerBackgroundImagePosition} + $footerBackgroundImageOrigin={style?.footerBackgroundImageOrigin} $borderColor={style?.border} style={{padding: style.containerfooterpadding}} /> diff --git a/client/packages/lowcoder/src/comps/comps/triContainerComp/triContainerComp.tsx b/client/packages/lowcoder/src/comps/comps/triContainerComp/triContainerComp.tsx index bf0373188..b76095ea5 100644 --- a/client/packages/lowcoder/src/comps/comps/triContainerComp/triContainerComp.tsx +++ b/client/packages/lowcoder/src/comps/comps/triContainerComp/triContainerComp.tsx @@ -30,7 +30,6 @@ const childrenMap = { 0: { view: { layout: {}, items: {} } }, }), footer: SimpleContainerComp, - showHeader: BoolControl.DEFAULT_TRUE, showBody: BoolControl.DEFAULT_TRUE, showFooter: BoolControl, @@ -108,7 +107,7 @@ export class TriContainerComp extends TriContainerBaseComp implements IContainer return lastValueIfEqual(this, "exposing_node", fromRecord(allNodes), checkEquals); } - getPropertyView(): ControlNode { + getPropertyView(): ControlNode { return [this.areaPropertyView(), this.heightPropertyView()]; } @@ -127,6 +126,7 @@ export class TriContainerComp extends TriContainerBaseComp implements IContainer stylePropertyView() { return this.children.style.getPropertyView(); } + } function checkEquals(node1: Node, node2: Node): boolean { diff --git a/client/packages/lowcoder/src/comps/controls/styleControl.tsx b/client/packages/lowcoder/src/comps/controls/styleControl.tsx index 4eff65357..fb97e3f2a 100644 --- a/client/packages/lowcoder/src/comps/controls/styleControl.tsx +++ b/client/packages/lowcoder/src/comps/controls/styleControl.tsx @@ -15,11 +15,12 @@ import { TextSizeIcon, TypographyIcon, ShowBorderIcon, + ImageCompIcon, } from "lowcoder-design"; import { useContext } from "react"; import styled from "styled-components"; import { useIsMobile } from "util/hooks"; -import { RadiusControl, StringControl } from "./codeControl"; +import { CSSCodeControl, ObjectControl, RadiusControl, StringControl } from "./codeControl"; import { ColorControl } from "./colorControl"; import { defaultTheme, @@ -33,8 +34,25 @@ import { TextSizeConfig, TextWeightConfig, BorderWidthConfig, + BackgroundImageConfig, + BackgroundImageRepeatConfig, + BackgroundImageSizeConfig, + BackgroundImagePositionConfig, + BackgroundImageOriginConfig, + HeaderBackgroundImageConfig, + HeaderBackgroundImageRepeatConfig, + HeaderBackgroundImageSizeConfig, + HeaderBackgroundImagePositionConfig, + HeaderBackgroundImageOriginConfig, + FooterBackgroundImageConfig, + FooterBackgroundImageRepeatConfig, + FooterBackgroundImageSizeConfig, + FooterBackgroundImagePositionConfig, + FooterBackgroundImageOriginConfig, + } from "./styleControlConstants"; import { faTextWidth } from "@fortawesome/free-solid-svg-icons"; +import appSelectControl from "./appSelectControl"; function isSimpleColorConfig(config: SingleColorConfig): config is SimpleColorConfig { return config.hasOwnProperty("color"); @@ -52,6 +70,57 @@ function isBorderWidthConfig(config: SingleColorConfig): config is BorderWidthCo return config.hasOwnProperty("borderWidth"); } +function isBackgroundImageConfig(config: SingleColorConfig): config is BackgroundImageConfig { + return config.hasOwnProperty("backgroundImage"); +} + +function isBackgroundImageRepeatConfig(config: SingleColorConfig): config is BackgroundImageRepeatConfig { + return config.hasOwnProperty("backgroundImageRepeat"); +} + +function isBackgroundImageSizeConfig(config: SingleColorConfig): config is BackgroundImageSizeConfig { + return config.hasOwnProperty("backgroundImageSize"); +} + +function isBackgroundImagePositionConfig(config: SingleColorConfig): config is BackgroundImagePositionConfig { + return config.hasOwnProperty("backgroundImagePosition"); +} + +function isBackgroundImageOriginConfig(config: SingleColorConfig): config is BackgroundImageOriginConfig { + return config.hasOwnProperty("backgroundImageOrigin"); +} + +function isHeaderBackgroundImageConfig(config: SingleColorConfig): config is HeaderBackgroundImageConfig { + return config.hasOwnProperty("headerBackgroundImage"); +} +function isHeaderBackgroundImageRepeatConfig(config: SingleColorConfig): config is HeaderBackgroundImageRepeatConfig { + return config.hasOwnProperty("headerBackgroundImageRepeat"); +} +function isHeaderBackgroundImageSizeConfig(config: SingleColorConfig): config is HeaderBackgroundImageSizeConfig { + return config.hasOwnProperty("headerBackgroundImageSize"); +} +function isHeaderBackgroundImagePositionConfig(config: SingleColorConfig): config is HeaderBackgroundImagePositionConfig { + return config.hasOwnProperty("headerBackgroundImagePosition"); +} +function isHeaderBackgroundImageOriginConfig(config: SingleColorConfig): config is HeaderBackgroundImageOriginConfig { + return config.hasOwnProperty("headerBackgroundImageOrigin"); +} +function isFooterBackgroundImageConfig(config: SingleColorConfig): config is FooterBackgroundImageConfig { + return config.hasOwnProperty("footerBackgroundImage"); +} +function isFooterBackgroundImageRepeatConfig(config: SingleColorConfig): config is FooterBackgroundImageRepeatConfig { + return config.hasOwnProperty("footerBackgroundImageRepeat"); +} +function isFooterBackgroundImageSizeConfig(config: SingleColorConfig): config is FooterBackgroundImageSizeConfig { + return config.hasOwnProperty("footerBackgroundImageSize"); +} +function isFooterBackgroundImagePositionConfig(config: SingleColorConfig): config is FooterBackgroundImagePositionConfig { + return config.hasOwnProperty("footerBackgroundImagePosition"); +} +function isFooterBackgroundImageOriginConfig(config: SingleColorConfig): config is FooterBackgroundImageOriginConfig { + return config.hasOwnProperty("footerBackgroundImageOrigin"); +} + function isTextSizeConfig(config: SingleColorConfig): config is TextSizeConfig { return config.hasOwnProperty("textSize"); } @@ -83,6 +152,52 @@ function isEmptyRadius(radius: string) { function isEmptyBorderWidth(borderWidth: string) { return _.isEmpty(borderWidth); } +function isEmptyBackgroundImageConfig(backgroundImage: string) { + return _.isEmpty(backgroundImage); +} +function isEmptyBackgroundImageRepeatConfig(backgroundImageRepeat: string) { + return _.isEmpty(backgroundImageRepeat); +} +function isEmptyBackgroundImageSizeConfig(backgroundImageSize: string) { + return _.isEmpty(backgroundImageSize); +} +function isEmptyBackgroundImagePositionConfig(backgroundImagePosition: string) { + return _.isEmpty(backgroundImagePosition); +} +function isEmptyBackgroundImageOriginConfig(backgroundImageOrigin: string) { + return _.isEmpty(backgroundImageOrigin); +} +function isEmptyHeaderBackgroundImageConfig(headerBackgroundImage: string) { + return _.isEmpty(headerBackgroundImage); +} +function isEmptyHeaderBackgroundImageRepeatConfig(headerBackgroundImageRepeat: string) { + return _.isEmpty(headerBackgroundImageRepeat); +} +function isEmptyHeaderBackgroundImageSizeConfig(headerBackgroundImageSize: string) { + return _.isEmpty(headerBackgroundImageSize); +} +function isEmptyHeaderBackgroundImagePositionConfig(headerBackgroundImagePosition: string) { + return _.isEmpty(headerBackgroundImagePosition); +} +function isEmptyHeaderBackgroundImageOriginConfig(headerBackgroundImageOrigin: string) { + return _.isEmpty(headerBackgroundImageOrigin); +} +function isEmptyFooterBackgroundImageConfig(footerBackgroundImage: string) { + return _.isEmpty(footerBackgroundImage); +} +function isEmptyFooterBackgroundImageRepeatConfig(footerBackgroundImageRepeat: string) { + return _.isEmpty(footerBackgroundImageRepeat); +} +function isEmptyFooterBackgroundImageSizeConfig(footerBackgroundImageSize: string) { + return _.isEmpty(footerBackgroundImageSize); +} +function isEmptyFooterBackgroundImagePositionConfig(footerBackgroundImagePosition: string) { + return _.isEmpty(footerBackgroundImagePosition); +} +function isEmptyFooterBackgroundImageOriginConfig(footerBackgroundImageOrigin: string) { + return _.isEmpty(footerBackgroundImageOrigin); +} + function isEmptyTextSize(textSize: string) { return _.isEmpty(textSize); } @@ -119,6 +234,66 @@ function calcColors>( res[name] = props[name]; return; } + if (!isEmptyBackgroundImageConfig(props[name]) && isBackgroundImageConfig(config)) { + res[name] = props[name]; + return; + } + if (!isEmptyBackgroundImageRepeatConfig(props[name]) && isBackgroundImageRepeatConfig(config)) { + res[name] = props[name]; + return; + } + if (!isEmptyBackgroundImageSizeConfig(props[name]) && isBackgroundImageSizeConfig(config)) { + res[name] = props[name]; + return; + } + if (!isEmptyBackgroundImagePositionConfig(props[name]) && isBackgroundImagePositionConfig(config)) { + res[name] = props[name]; + return; + } + if (!isEmptyBackgroundImageOriginConfig(props[name]) && isBackgroundImageOriginConfig(config)) { + res[name] = props[name]; + return; + } + if (!isEmptyHeaderBackgroundImageConfig(props[name]) && isHeaderBackgroundImageConfig(config)) { + res[name] = props[name]; + return; + } + if (!isEmptyHeaderBackgroundImageRepeatConfig(props[name]) && isHeaderBackgroundImageRepeatConfig(config)) { + res[name] = props[name]; + return; + } + if (!isEmptyHeaderBackgroundImageSizeConfig(props[name]) && isHeaderBackgroundImageSizeConfig(config)) { + res[name] = props[name]; + return; + } + if (!isEmptyHeaderBackgroundImagePositionConfig(props[name]) && isHeaderBackgroundImagePositionConfig(config)) { + res[name] = props[name]; + return; + } + if (!isEmptyHeaderBackgroundImageOriginConfig(props[name]) && isHeaderBackgroundImageOriginConfig(config)) { + res[name] = props[name]; + return; + } + if (!isEmptyFooterBackgroundImageConfig(props[name]) && isFooterBackgroundImageConfig(config)) { + res[name] = props[name]; + return; + } + if (!isEmptyFooterBackgroundImageRepeatConfig(props[name]) && isFooterBackgroundImageRepeatConfig(config)) { + res[name] = props[name]; + return; + } + if (!isEmptyFooterBackgroundImageSizeConfig(props[name]) && isFooterBackgroundImageSizeConfig(config)) { + res[name] = props[name]; + return; + } + if (!isEmptyFooterBackgroundImagePositionConfig(props[name]) && isFooterBackgroundImagePositionConfig(config)) { + res[name] = props[name]; + return; + } + if (!isEmptyFooterBackgroundImageOriginConfig(props[name]) && isFooterBackgroundImageOriginConfig(config)) { + res[name] = props[name]; + return; + } if (!isEmptyTextSize(props[name]) && isTextSizeConfig(config)) { res[name] = props[name]; return; @@ -152,6 +327,51 @@ function calcColors>( if (isBorderWidthConfig(config)) { res[name] = '0px'; } + if (isBackgroundImageConfig(config)) { + res[name] = ''; + } + if (isBackgroundImageRepeatConfig(config)) { + res[name] = 'no-repeat'; + } + if (isBackgroundImageSizeConfig(config)) { + res[name] = 'cover'; + } + if (isBackgroundImagePositionConfig(config)) { + res[name] = 'center'; + } + if (isBackgroundImageOriginConfig(config)) { + res[name] = 'padding-box'; + } + if (isHeaderBackgroundImageConfig(config)) { + res[name] = ''; + } + if (isHeaderBackgroundImageRepeatConfig(config)) { + res[name] = 'no-repeat'; + } + if (isHeaderBackgroundImageSizeConfig(config)) { + res[name] = 'cover'; + } + if (isHeaderBackgroundImagePositionConfig(config)) { + res[name] = 'center'; + } + if (isHeaderBackgroundImageOriginConfig(config)) { + res[name] = 'padding-box'; + } + if (isFooterBackgroundImageConfig(config)) { + res[name] = ''; + } + if (isFooterBackgroundImageRepeatConfig(config)) { + res[name] = 'no-repeat'; + } + if (isFooterBackgroundImageSizeConfig(config)) { + res[name] = 'cover'; + } + if (isFooterBackgroundImagePositionConfig(config)) { + res[name] = 'center'; + } + if (isFooterBackgroundImageOriginConfig(config)) { + res[name] = 'padding-box'; + } if (isTextSizeConfig(config)) { // TODO: remove default textSize after added in theme in backend. res[name] = themeWithDefault[config.textSize] || '14px'; @@ -275,6 +495,7 @@ const MarginIcon = styled(ExpandIcon)` margin: 0 8px 0 2px;`; const PaddingIcon = styled(CompressIcon)` margin: 0 8px 0 2px;`; const StyledTextSizeIcon = styled(TextSizeIcon)` margin: 0 8px 0 0px;`; const StyledTextWeightIcon = styled(TypographyIcon)` margin: 0 8px 0 0px;`; +const StyledBackgroundImageIcon = styled(ImageCompIcon)` margin: 0 0px 0 -12px;`; const ResetIcon = styled(IconReset)` &:hover g g { @@ -292,12 +513,30 @@ export function styleControl(colorConfig name === "borderWidth" || name === "cardRadius" || name === "textSize" || - name === "textWeight" + name === "textWeight" || + name === "backgroundImage" || + name === "backgroundImageRepeat" || + name === "backgroundImageSize" || + name === "backgroundImagePosition" || + name === "backgroundImageOrigin" || + name === "headerBackgroundImage" || + name === "headerBackgroundImageRepeat" || + name === "headerBackgroundImageSize" || + name === "headerBackgroundImagePosition" || + name === "headerBackgroundImageOrigin" || + name === "footerBackgroundImage" || + name === "footerBackgroundImageRepeat" || + name === "footerBackgroundImageSize" || + name === "footerBackgroundImagePosition" || + name === "footerBackgroundImageOrigin" || + name === "margin" || + name === "padding" || + name === "containerheaderpadding" || + name === "containerfooterpadding" || + name === "containerbodypadding" ) { childrenMap[name] = StringControl; - } else if (name === "margin" || name === "padding" || name==="containerheaderpadding" || name==="containerfooterpadding" || name==="containerbodypadding") { - childrenMap[name] = StringControl; - } else { + } else { childrenMap[name] = ColorControl; } }); @@ -338,9 +577,25 @@ export function styleControl(colorConfig name === "radius" || name === "margin" || name === "padding" || - name==="containerheaderpadding" || - name==="containerfooterpadding" || - name==="containerbodypadding" + name === "containerheaderpadding" || + name === "containerfooterpadding" || + name === "containerbodypadding" || + name === "borderWidth" || + name === "backgroundImage" || + name === "backgroundImageRepeat" || + name === "backgroundImageSize" || + name === "backgroundImagePosition" || + name === "backgroundImageOrigin" || + name === "headerBackgroundImage" || + name === "headerBackgroundImageRepeat" || + name === "headerBackgroundImageSize" || + name === "headerBackgroundImagePosition" || + name === "headerBackgroundImageOrigin" || + name === "footerBackgroundImage" || + name === "footerBackgroundImageRepeat" || + name === "footerBackgroundImageSize" || + name === "footerBackgroundImagePosition" || + name === "footerBackgroundImageOrigin" ) { children[name]?.dispatchChangeValueAction(""); } else { @@ -424,14 +679,54 @@ export function styleControl(colorConfig preInputNode: , placeholder: props[name], }) - : name === "textWeight" - ? ( - children[name] as InstanceType - ).propertyView({ - label: config.label, - preInputNode: , - placeholder: props[name], - }) + : name === "textWeight" + ? ( + children[name] as InstanceType + ).propertyView({ + label: config.label, + preInputNode: , + placeholder: props[name], + }) + : name === "backgroundImage" || name === "headerBackgroundImage" || name === "footerBackgroundImage" + ? ( + children[name] as InstanceType + ).propertyView({ + label: config.label, + preInputNode: , + placeholder: props[name], + }) + : name === "backgroundImageRepeat" || name === "headerBackgroundImageRepeat" || name === "footerBackgroundImageRepeat" + ? ( + children[name] as InstanceType + ).propertyView({ + label: config.label, + preInputNode: , + placeholder: props[name], + }) + : name === "backgroundImageSize" || name === "headerBackgroundImageSize" || name === "footerBackgroundImageSize" + ? ( + children[name] as InstanceType + ).propertyView({ + label: config.label, + preInputNode: , + placeholder: props[name], + }) + : name === "backgroundImagePosition" || name === "headerBackgroundImagePosition" || name === "footerBackgroundImagePosition" + ? ( + children[name] as InstanceType + ).propertyView({ + label: config.label, + preInputNode: , + placeholder: props[name], + }) + : name === "backgroundImageOrigin" || name === "headerBackgroundImageOrigin" || name === "footerBackgroundImageOrigin" + ? ( + children[name] as InstanceType + ).propertyView({ + label: config.label, + preInputNode: , + placeholder: props[name], + }) : children[name].propertyView({ label: config.label, panelDefaultColor: props[name], @@ -458,4 +753,4 @@ export function useStyle(colorConfigs: T props[config.name as Names] = ""; }); return calcColors(props, colorConfigs, theme?.theme, bgColor); -} +} \ No newline at end of file diff --git a/client/packages/lowcoder/src/comps/controls/styleControlConstants.tsx b/client/packages/lowcoder/src/comps/controls/styleControlConstants.tsx index 00148b45e..b12c8569e 100644 --- a/client/packages/lowcoder/src/comps/controls/styleControlConstants.tsx +++ b/client/packages/lowcoder/src/comps/controls/styleControlConstants.tsx @@ -21,6 +21,24 @@ export type BorderWidthConfig = CommonColorConfig & { readonly borderWidth: string; }; +export type BackgroundImageConfig = CommonColorConfig & { readonly backgroundImage: string; }; +export type BackgroundImageRepeatConfig = CommonColorConfig & { readonly backgroundImageRepeat: string; }; +export type BackgroundImageSizeConfig = CommonColorConfig & { readonly backgroundImageSize: string;}; +export type BackgroundImagePositionConfig = CommonColorConfig & { readonly backgroundImagePosition: string; }; +export type BackgroundImageOriginConfig = CommonColorConfig & { readonly backgroundImageOrigin: string;}; + +export type HeaderBackgroundImageConfig = CommonColorConfig & { readonly headerBackgroundImage: string; }; +export type HeaderBackgroundImageRepeatConfig = CommonColorConfig & { readonly headerBackgroundImageRepeat: string; }; +export type HeaderBackgroundImageSizeConfig = CommonColorConfig & { readonly headerBackgroundImageSize: string;}; +export type HeaderBackgroundImagePositionConfig = CommonColorConfig & { readonly headerBackgroundImagePosition: string; }; +export type HeaderBackgroundImageOriginConfig = CommonColorConfig & { readonly headerBackgroundImageOrigin: string;}; + +export type FooterBackgroundImageConfig = CommonColorConfig & { readonly footerBackgroundImage: string; }; +export type FooterBackgroundImageRepeatConfig = CommonColorConfig & { readonly footerBackgroundImageRepeat: string; }; +export type FooterBackgroundImageSizeConfig = CommonColorConfig & { readonly footerBackgroundImageSize: string;}; +export type FooterBackgroundImagePositionConfig = CommonColorConfig & { readonly footerBackgroundImagePosition: string; }; +export type FooterBackgroundImageOriginConfig = CommonColorConfig & { readonly footerBackgroundImageOrigin: string;}; + export type TextSizeConfig = CommonColorConfig & { readonly textSize: string; }; @@ -54,7 +72,7 @@ export type DepColorConfig = CommonColorConfig & { readonly depType?: DEP_TYPE; transformer: (color: string, ...rest: string[]) => string; }; -export type SingleColorConfig = SimpleColorConfig | DepColorConfig | RadiusConfig | BorderWidthConfig | TextSizeConfig | TextWeightConfig | MarginConfig | PaddingConfig | ContainerHeaderPaddigConfig | ContainerFooterPaddigConfig | ContainerBodyPaddigConfig; +export type SingleColorConfig = SimpleColorConfig | DepColorConfig | RadiusConfig | BorderWidthConfig | BackgroundImageConfig | BackgroundImageRepeatConfig | BackgroundImageSizeConfig | BackgroundImagePositionConfig | BackgroundImageOriginConfig | TextSizeConfig | TextWeightConfig | MarginConfig | PaddingConfig | ContainerHeaderPaddigConfig | ContainerFooterPaddigConfig | ContainerBodyPaddigConfig | HeaderBackgroundImageConfig | HeaderBackgroundImageRepeatConfig | HeaderBackgroundImageSizeConfig | HeaderBackgroundImagePositionConfig | HeaderBackgroundImageOriginConfig | FooterBackgroundImageConfig | FooterBackgroundImageRepeatConfig | FooterBackgroundImageSizeConfig | FooterBackgroundImagePositionConfig | FooterBackgroundImageOriginConfig; export const defaultTheme: ThemeDetail = { primary: "#3377FF", @@ -270,6 +288,36 @@ const BORDER_WIDTH = { borderWidth: "borderWidth", } as const; +const BACKGROUND_IMAGE = { + name: "backgroundImage", + label: trans("style.backgroundImage"), + backgroundImage: "backgroundImage", +} as const; + +const BACKGROUND_IMAGE_REPEAT = { + name: "backgroundImageRepeat", + label: trans("style.backgroundImageRepeat"), + backgroundImageRepeat: "backgroundImageRepeat", +} as const; + +const BACKGROUND_IMAGE_SIZE = { + name: "backgroundImageSize", + label: trans("style.backgroundImageSize"), + backgroundImageSize: "backgroundImageSize", +} as const; + +const BACKGROUND_IMAGE_POSITION = { + name: "backgroundImagePosition", + label: trans("style.backgroundImagePosition"), + backgroundImagePosition: "backgroundImagePosition", +} as const; + +const BACKGROUND_IMAGE_ORIGIN = { + name: "backgroundImageOrigin", + label: trans("style.backgroundImageOrigin"), + backgroundImageOrigin: "backgroundImageOrigin", +} as const; + const MARGIN = { name: "margin", label: trans("style.margin"), @@ -415,6 +463,8 @@ export const TextStyle = [ transformer: toSelf, }, TEXT, + TEXT_SIZE, + TEXT_WEIGHT, BORDER, MARGIN, PADDING, @@ -440,6 +490,7 @@ export const MarginStyle = [ export const ContainerStyle = [ ...BG_STATIC_BORDER_RADIUS, + BORDER_WIDTH, HEADER_BACKGROUND, { name: "footerBackground", @@ -452,7 +503,82 @@ export const ContainerStyle = [ PADDING, CONTAINERHEADERPADDING, CONTAINERFOOTERPADDING, - CONTAINERBODYPADDING + CONTAINERBODYPADDING, + { + name: "headerBackgroundImage", + label: trans("style.headerBackgroundImage"), + headerBackgroundImage: "headerBackgroundImage", + }, + { + name: "headerBackgroundImageRepeat", + label: trans("style.headerBackgroundImageRepeat"), + headerBackgroundImageRepeat: "headerBackgroundImageRepeat", + }, + { + name: "headerBackgroundImageSize", + label: trans("style.headerBackgroundImageSize"), + headerBackgroundImageSize: "headerBackgroundImageSize", + }, + { + name: "headerBackgroundImagePosition", + label: trans("style.headerBackgroundImagePosition"), + headerBackgroundImagePosition: "headerBackgroundImagePosition", + } + ,{ + name: "headerBackgroundImageOrigin", + label: trans("style.headerBackgroundImageOrigin"), + headerBackgroundImageOrigin: "headerBackgroundImageOrigin", + }, + { + name: "backgroundImage", + label: trans("style.backgroundImage"), + backgroundImage: "backgroundImage", + }, + { + name: "backgroundImageRepeat", + label: trans("style.backgroundImageRepeat"), + backgroundImageRepeat: "backgroundImageRepeat", + }, + { + name: "backgroundImageSize", + label: trans("style.backgroundImageSize"), + backgroundImageSize: "backgroundImageSize", + }, + { + name: "backgroundImagePosition", + label: trans("style.backgroundImagePosition"), + backgroundImagePosition: "backgroundImagePosition", + }, + { + name: "backgroundImageOrigin", + label: trans("style.backgroundImageOrigin"), + backgroundImageOrigin: "backgroundImageOrigin", + }, + { + name: "footerBackgroundImage", + label: trans("style.footerBackgroundImage"), + footerBackgroundImage: "footerBackgroundImage", + }, + { + name: "footerBackgroundImageRepeat", + label: trans("style.footerBackgroundImageRepeat"), + footerBackgroundImageRepeat: "footerBackgroundImageRepeat", + }, + { + name: "footerBackgroundImageSize", + label: trans("style.footerBackgroundImageSize"), + footerBackgroundImageSize: "footerBackgroundImageSize", + }, + { + name: "footerBackgroundImagePosition", + label: trans("style.footerBackgroundImagePosition"), + footerBackgroundImagePosition: "footerBackgroundImagePosition", + } + ,{ + name: "footerBackgroundImageOrigin", + label: trans("style.footerBackgroundImageOrigin"), + footerBackgroundImageOrigin: "footerBackgroundImageOrigin", + } ] as const; export const SliderStyle = [ @@ -570,6 +696,7 @@ export const MultiSelectStyle = [ export const TabContainerStyle = [ ...BG_STATIC_BORDER_RADIUS, + BORDER_WIDTH, HEADER_BACKGROUND, { name: "tabText", @@ -587,9 +714,94 @@ export const TabContainerStyle = [ }, MARGIN, PADDING, + { + name: "headerBackgroundImage", + label: trans("style.headerBackgroundImage"), + headerBackgroundImage: "headerBackgroundImage", + }, + { + name: "headerBackgroundImageRepeat", + label: trans("style.headerBackgroundImageRepeat"), + headerBackgroundImageRepeat: "headerBackgroundImageRepeat", + }, + { + name: "headerBackgroundImageSize", + label: trans("style.headerBackgroundImageSize"), + headerBackgroundImageSize: "headerBackgroundImageSize", + }, + { + name: "headerBackgroundImagePosition", + label: trans("style.headerBackgroundImagePosition"), + headerBackgroundImagePosition: "headerBackgroundImagePosition", + } + ,{ + name: "headerBackgroundImageOrigin", + label: trans("style.headerBackgroundImageOrigin"), + headerBackgroundImageOrigin: "headerBackgroundImageOrigin", + }, + { + name: "backgroundImage", + label: trans("style.backgroundImage"), + backgroundImage: "backgroundImage", + }, + { + name: "backgroundImageRepeat", + label: trans("style.backgroundImageRepeat"), + backgroundImageRepeat: "backgroundImageRepeat", + }, + { + name: "backgroundImageSize", + label: trans("style.backgroundImageSize"), + backgroundImageSize: "backgroundImageSize", + }, + { + name: "backgroundImagePosition", + label: trans("style.backgroundImagePosition"), + backgroundImagePosition: "backgroundImagePosition", + }, + { + name: "backgroundImageOrigin", + label: trans("style.backgroundImageOrigin"), + backgroundImageOrigin: "backgroundImageOrigin", + }, + { + name: "footerBackgroundImage", + label: trans("style.footerBackgroundImage"), + footerBackgroundImage: "footerBackgroundImage", + }, + { + name: "footerBackgroundImageRepeat", + label: trans("style.footerBackgroundImageRepeat"), + footerBackgroundImageRepeat: "footerBackgroundImageRepeat", + }, + { + name: "footerBackgroundImageSize", + label: trans("style.footerBackgroundImageSize"), + footerBackgroundImageSize: "footerBackgroundImageSize", + }, + { + name: "footerBackgroundImagePosition", + label: trans("style.footerBackgroundImagePosition"), + footerBackgroundImagePosition: "footerBackgroundImagePosition", + } + ,{ + name: "footerBackgroundImageOrigin", + label: trans("style.footerBackgroundImageOrigin"), + footerBackgroundImageOrigin: "footerBackgroundImageOrigin", + } ] as const; -export const ModalStyle = getBgBorderRadiusByBg(); +export const ModalStyle = [ + ...getBgBorderRadiusByBg(), + BORDER_WIDTH, + MARGIN, + PADDING, + BACKGROUND_IMAGE, + BACKGROUND_IMAGE_REPEAT, + BACKGROUND_IMAGE_SIZE, + BACKGROUND_IMAGE_POSITION, + BACKGROUND_IMAGE_ORIGIN +] as const; export const CascaderStyle = [ LABEL, diff --git a/client/packages/lowcoder/src/comps/hooks/modalComp.tsx b/client/packages/lowcoder/src/comps/hooks/modalComp.tsx index 92af6fb6d..49139eb39 100644 --- a/client/packages/lowcoder/src/comps/hooks/modalComp.tsx +++ b/client/packages/lowcoder/src/comps/hooks/modalComp.tsx @@ -26,25 +26,45 @@ const EventOptions = [ { label: trans("modalComp.close"), value: "close", description: trans("modalComp.closeDesc") }, ] as const; -const DEFAULT_WIDTH = "60%"; -const DEFAULT_HEIGHT = 222; -const DEFAULT_PADDING = 16; - const getStyle = (style: ModalStyleType) => { return css` .ant-modal-content { border-radius: ${style.radius}; - border: 1px solid ${style.border}; + border: ${style.borderWidth} solid ${style.border}; overflow: hidden; background-color: ${style.background}; - + ${style.backgroundImage ? `background-image: ${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}; .ant-modal-body > .react-resizable > .react-grid-layout { background-color: ${style.background}; } + > .ant-modal-body { + background-color: ${style.background}; + } } `; }; +const DEFAULT_WIDTH = "60%"; +const DEFAULT_HEIGHT = 222; + +function extractMarginValues(style: ModalStyleType) { + // Regular expression to match numeric values with units (like px, em, etc.) + const regex = /\d+px|\d+em|\d+%|\d+vh|\d+vw/g; + // Extract the values using the regular expression + let values = style.padding.match(regex); + // If only one value is found, duplicate it to simulate uniform margin + if (values && values.length === 1) { + values = [values[0], values[0]]; + } + // Return the array of values + return values; +} + const ModalStyled = styled.div<{ $style: ModalStyleType }>` ${(props) => props.$style && getStyle(props.$style)} `; @@ -101,6 +121,7 @@ let TmpModalComp = (function () { }, [dispatch] ); + let paddingValues = extractMarginValues(props.style); return ( @@ -129,8 +150,8 @@ let TmpModalComp = (function () { {...otherContainerProps} items={gridItemCompToGridItems(items)} autoHeight={props.autoHeight} - minHeight={DEFAULT_HEIGHT - DEFAULT_PADDING * 2 + "px"} - containerPadding={[DEFAULT_PADDING, DEFAULT_PADDING]} + minHeight={paddingValues ? DEFAULT_HEIGHT - parseInt(paddingValues[0]) * 2 + "px" : ""} + containerPadding={paddingValues ? [parseInt(paddingValues[0]), parseInt(paddingValues[1])] : [24,24]} hintPlaceholder={HintPlaceHolder} /> diff --git a/client/packages/lowcoder/src/i18n/locales/en.ts b/client/packages/lowcoder/src/i18n/locales/en.ts index e2fecc802..49cbef022 100644 --- a/client/packages/lowcoder/src/i18n/locales/en.ts +++ b/client/packages/lowcoder/src/i18n/locales/en.ts @@ -360,6 +360,21 @@ export const en = { "aspectRatio": "Aspect Ratio", "textSize": "Text Size", "textWeight": "Text Weight", + "backgroundImage": "BgImage", + "backgroundImageRepeat": "BgImage Repeat", + "backgroundImageSize": "BgImage Size", + "backgroundImagePosition": "BgImage Position", + "backgroundImageOrigin": "BgImage Origin", + "headerBackgroundImage": "Header BgImage", + "headerBackgroundImageRepeat": "Header BgImage Repeat", + "headerBackgroundImageSize": "Header BgImage Size", + "headerBackgroundImagePosition": "Header BgImage Position", + "headerBackgroundImageOrigin": "Header BgImage Origin", + "footerBackgroundImage": "Footer BgImage", + "footerBackgroundImageRepeat": "Footer BgImage Repeat", + "footerBackgroundImageSize": "Footer BgImage Size", + "footerBackgroundImagePosition": "Footer BgImage Position", + "footerBackgroundImageOrigin": "Footer BgImage Origin", }, "export": { "hiddenDesc": "If true, the component is hidden", diff --git a/client/packages/lowcoder/src/i18n/locales/zh.ts b/client/packages/lowcoder/src/i18n/locales/zh.ts index 57c7f9a79..1c1d1b760 100644 --- a/client/packages/lowcoder/src/i18n/locales/zh.ts +++ b/client/packages/lowcoder/src/i18n/locales/zh.ts @@ -350,6 +350,21 @@ style: { minWidth: "最小宽度", textSize: "字体大小", textWeight: "字体粗细", + "backgroundImage": "背景图片", + "backgroundImageRepeat" : "背景图片重复", + "backgroundImageSize" : "背景图片大小", + "backgroundImagePosition" : "背景图片位置", + "backgroundImageOrigin": "背景图片原点", + "headerBackgroundImage": "头部背景图片", + "headerBackgroundImageRepeat" : "头部背景图片重复", + "headerBackgroundImageSize" : "头部背景图片大小", + "headerBackgroundImagePosition" : "头部背景图片位置", + "headerBackgroundImageOrigin": "头部背景图片原点", + "footerBackgroundImage": "底部背景图片", + "footerBackgroundImageRepeat" : "底部背景图片重复", + "footerBackgroundImageSize" : "底部背景图片大小", + "footerBackgroundImagePosition" : "底部背景图片位置", + "footerBackgroundImageOrigin": "底部背景图片原点", }, export: { hiddenDesc: "如果为true,则隐藏组件",