diff --git a/client/packages/lowcoder-design/src/icons/icon-text-size.svg b/client/packages/lowcoder-design/src/icons/icon-text-size.svg new file mode 100644 index 000000000..86299a0c9 --- /dev/null +++ b/client/packages/lowcoder-design/src/icons/icon-text-size.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/client/packages/lowcoder-design/src/icons/index.ts b/client/packages/lowcoder-design/src/icons/index.ts index 4be847768..833e83ded 100644 --- a/client/packages/lowcoder-design/src/icons/index.ts +++ b/client/packages/lowcoder-design/src/icons/index.ts @@ -296,4 +296,5 @@ export { ReactComponent as CommentIcon } from "icons/icon-comment-comp.svg"; export { ReactComponent as MentionIcon } from "icons/icon-mention-comp.svg"; export { ReactComponent as AutoCompleteCompIcon } from "icons/icon-autocomplete-comp.svg"; export { ReactComponent as WidthIcon } from "icons/icon-width.svg"; -export { ReactComponent as ResponsiveLayoutCompIcon } from "icons/icon-responsive-layout-comp.svg"; \ No newline at end of file +export { ReactComponent as ResponsiveLayoutCompIcon } from "icons/icon-responsive-layout-comp.svg"; +export { ReactComponent as TextSizeIcon } from "./icon-text-size.svg"; \ No newline at end of file diff --git a/client/packages/lowcoder/src/api/commonSettingApi.ts b/client/packages/lowcoder/src/api/commonSettingApi.ts index f1e1ac9f4..48a17cc2a 100644 --- a/client/packages/lowcoder/src/api/commonSettingApi.ts +++ b/client/packages/lowcoder/src/api/commonSettingApi.ts @@ -47,6 +47,7 @@ export interface ThemeDetail { margin?: string; padding?: string; gridColumns?: string; //Added By Aqib Mirza + textSize?: string; } export function getThemeDetailName(key: keyof ThemeDetail) { @@ -70,6 +71,8 @@ export function getThemeDetailName(key: keyof ThemeDetail) { //Added By Aqib Mirza case "gridColumns": return trans("themeDetail.gridColumns"); + case "textSize": + return trans("style.textSize"); } return ""; } @@ -84,6 +87,7 @@ export function isThemeColorKey(key: string) { case "margin": case "padding": case "gridColumns": //Added By Aqib Mirza + case "textSize": return true; } return false; diff --git a/client/packages/lowcoder/src/comps/comps/tableComp/column/tableColumnComp.tsx b/client/packages/lowcoder/src/comps/comps/tableComp/column/tableColumnComp.tsx index 0e2ec9639..3dcca917e 100644 --- a/client/packages/lowcoder/src/comps/comps/tableComp/column/tableColumnComp.tsx +++ b/client/packages/lowcoder/src/comps/comps/tableComp/column/tableColumnComp.tsx @@ -1,7 +1,7 @@ import { BoolControl } from "comps/controls/boolControl"; -import { NumberControl, StringControl } from "comps/controls/codeControl"; +import { ColorOrBoolCodeControl, NumberControl, RadiusControl, StringControl } from "comps/controls/codeControl"; import { dropdownControl, HorizontalAlignmentControl } from "comps/controls/dropdownControl"; -import { MultiCompBuilder, stateComp, valueComp } from "comps/generators"; +import { MultiCompBuilder, stateComp, valueComp, withContext, withDefault } from "comps/generators"; import { withSelectedMultiContext } from "comps/generators/withSelectedMultiContext"; import { genRandomKey } from "comps/utils/idGenerator"; import { trans } from "i18n"; @@ -9,6 +9,8 @@ import _ from "lodash"; import { changeChildAction, changeValueAction, + CompAction, + CompActionTypes, ConstructorToComp, ConstructorToDataType, ConstructorToNodeType, @@ -19,8 +21,11 @@ import { withFunction, wrapChildAction, } from "lowcoder-core"; -import { AlignClose, AlignLeft, AlignRight } from "lowcoder-design"; +import { AlignClose, AlignLeft, AlignRight, IconRadius, TextSizeIcon, controlItem } from "lowcoder-design"; import { ColumnTypeComp, ColumnTypeCompMap } from "./columnTypeComp"; +import { ColorControl } from "comps/controls/colorControl"; +import { JSONValue } from "util/jsonTypes"; +import styled from "styled-components"; export type Render = ReturnType["getOriginalComp"]>; export const RenderComp = withSelectedMultiContext(ColumnTypeComp); @@ -51,6 +56,31 @@ const columnFixOptions = [ }, ] as const; +const cellColorLabel = trans("table.cellColor"); +const CellColorTempComp = withContext( + new MultiCompBuilder({ color: ColorOrBoolCodeControl }, (props) => props.color) + .setPropertyViewFn((children) => + children.color.propertyView({ + label: cellColorLabel, + tooltip: trans("table.cellColorDesc"), + }) + ) + .build(), + ["currentCell"] as const +); + +// @ts-ignore +export class CellColorComp extends CellColorTempComp { + override getPropertyView() { + return controlItem({ filterText: cellColorLabel }, super.getPropertyView()); + } +} + +// fixme, should be infer from RowColorComp, but withContext type incorrect +export type CellColorViewType = (param: { + currentCell: JSONValue | undefined; //number | string; +}) => string; + export const columnChildrenMap = { // column title title: StringControl, @@ -67,8 +97,19 @@ export const columnChildrenMap = { tempHide: stateComp(false), fixed: dropdownControl(columnFixOptions, "close"), editable: BoolControl, + background: withDefault(ColorControl, ""), + text: withDefault(ColorControl, ""), + border: withDefault(ColorControl, ""), + borderWidth: withDefault(RadiusControl, ""), + radius: withDefault(RadiusControl, ""), + textSize: withDefault(RadiusControl, ""), + cellColor: CellColorComp, }; +const StyledIcon = styled.span` + margin: 0 4px 0 14px; +`; + /** * export for test. * Put it here temporarily to avoid circular dependencies @@ -90,6 +131,21 @@ const ColumnInitComp = new MultiCompBuilder(columnChildrenMap, (props, dispatch) .build(); export class ColumnComp extends ColumnInitComp { + override reduce(action: CompAction) { + let comp = super.reduce(action); + if (action.type === CompActionTypes.UPDATE_NODES_V2) { + comp = comp.setChild( + "cellColor", + comp.children.cellColor.reduce( + CellColorComp.changeContextDataAction({ + currentCell: undefined, + }) + ) + ); + } + return comp; + } + override getView() { const superView = super.getView(); const columnType = this.children.render.getSelectedComp().getComp().children.compType.getView(); @@ -143,6 +199,36 @@ export class ColumnComp extends ColumnInitComp { })} {this.children.autoWidth.getView() === "fixed" && this.children.width.propertyView({ label: trans("prop.width") })} + {controlItem({}, ( +
+ {"Style"} +
+ ))} + {this.children.background.propertyView({ + label: trans('style.background'), + })} + {this.children.text.propertyView({ + label: trans('text'), + })} + {this.children.border.propertyView({ + label: trans('style.border') + })} + {this.children.borderWidth.propertyView({ + label: trans('style.borderWidth'), + preInputNode: , + placeholder: '1px', + })} + {this.children.radius.propertyView({ + label: trans('style.borderRadius'), + preInputNode: , + placeholder: '3px', + })} + {this.children.textSize.propertyView({ + label: trans('style.textSize'), + preInputNode: , + placeholder: '14px', + })} + {this.children.cellColor.getPropertyView()} ); } diff --git a/client/packages/lowcoder/src/comps/comps/tableComp/tableCompView.tsx b/client/packages/lowcoder/src/comps/comps/tableComp/tableCompView.tsx index 4e08fbfaf..e43e45999 100644 --- a/client/packages/lowcoder/src/comps/comps/tableComp/tableCompView.tsx +++ b/client/packages/lowcoder/src/comps/comps/tableComp/tableCompView.tsx @@ -17,6 +17,8 @@ import { defaultTheme, handleToHoverRow, handleToSelectedRow, + TableColumnStyleType, + TableRowStyleType, TableStyleType, } from "comps/controls/styleControlConstants"; import { CompNameContext, EditorContext } from "comps/editorState"; @@ -34,6 +36,8 @@ import { useResizeDetector } from "react-resize-detector"; import { SlotConfigContext } from "comps/controls/slotControl"; import { EmptyContent } from "pages/common/styledComponent"; import { messageInstance } from "lowcoder-design"; +import { ReactRef, ResizeHandleAxis } from "layout/gridLayoutPropTypes"; +import { CellColorViewType } from "./column/tableColumnComp"; const TitleResizeHandle = styled.span` position: absolute; @@ -49,12 +53,15 @@ function genLinerGradient(color: string) { return `linear-gradient(${color}, ${color})`; } -const getStyle = (style: TableStyleType) => { +const getStyle = ( + style: TableStyleType, + rowStyle: TableRowStyleType, +) => { const background = genLinerGradient(style.background); - const selectedRowBackground = genLinerGradient(style.selectedRowBackground); - const hoverRowBackground = genLinerGradient(style.hoverRowBackground); - const alternateBackground = genLinerGradient(style.alternateBackground); - const isDark = isDarkColor(style.background); + const selectedRowBackground = genLinerGradient(rowStyle.selectedRowBackground); + const hoverRowBackground = genLinerGradient(rowStyle.hoverRowBackground); + const alternateBackground = genLinerGradient(rowStyle.alternateBackground); + return css` border-color: ${style.border}; border-radius: ${style.radius}; @@ -72,6 +79,7 @@ const getStyle = (style: TableStyleType) => { > .ant-table-thead { > tr > th { background-color: ${style.headerBackground}; + border-color: ${style.border}; color: ${style.headerText}; &.ant-table-column-has-sorters:hover { @@ -88,30 +96,7 @@ const getStyle = (style: TableStyleType) => { > tr:nth-of-type(2n + 1) { &, > td { - background: ${background}; - color: ${style.cellText}; - // Column type view and edit color - > div > div { - &, - > .ant-badge > .ant-badge-status-text, - > div > .markdown-body { - color: ${style.cellText}; - } - - > div > svg g { - stroke: ${style.cellText}; - } - - // dark link|links color - > a, - > div > a { - color: ${isDark && "#A6FFFF"}; - - &:hover { - color: ${isDark && "#2EE6E6"}; - } - } - } + background: ${genLinerGradient(rowStyle.background)}; } } @@ -119,52 +104,29 @@ const getStyle = (style: TableStyleType) => { &, > td { background: ${alternateBackground}; - color: ${style.cellText}; - // Column type view and edit color - > div > div { - &, - > .ant-badge > .ant-badge-status-text, - > div > .markdown-body { - color: ${style.cellText}; - } - - > div > svg g { - stroke: ${style.cellText}; - } - - // dark link|links color - > a, - > div > a { - color: ${isDark && "#A6FFFF"}; - - &:hover { - color: ${isDark && "#2EE6E6"}; - } - } - } } } // selected row > tr:nth-of-type(2n + 1).ant-table-row-selected { > td { - background: ${selectedRowBackground}, ${background}; + background: ${selectedRowBackground}, ${rowStyle.background} !important; } > td.ant-table-cell-row-hover, &:hover > td { - background: ${hoverRowBackground}, ${selectedRowBackground}, ${background}; + background: ${hoverRowBackground}, ${selectedRowBackground}, ${rowStyle.background} !important; } } > tr:nth-of-type(2n).ant-table-row-selected { > td { - background: ${selectedRowBackground}, ${alternateBackground}; + background: ${selectedRowBackground}, ${alternateBackground} !important; } > td.ant-table-cell-row-hover, &:hover > td { - background: ${hoverRowBackground}, ${selectedRowBackground}, ${alternateBackground}; + background: ${hoverRowBackground}, ${selectedRowBackground}, ${alternateBackground} !important; } } @@ -172,16 +134,20 @@ const getStyle = (style: TableStyleType) => { > tr:nth-of-type(2n + 1) > td.ant-table-cell-row-hover { &, > div:nth-of-type(2) { - background: ${hoverRowBackground}, ${background}; + background: ${hoverRowBackground}, ${rowStyle.background} !important; } } > tr:nth-of-type(2n) > td.ant-table-cell-row-hover { &, > div:nth-of-type(2) { - background: ${hoverRowBackground}, ${alternateBackground}; + background: ${hoverRowBackground}, ${alternateBackground} !important; } } + + > tr.ant-table-expanded-row > td { + background: ${background}; + } } } `; @@ -189,6 +155,7 @@ const getStyle = (style: TableStyleType) => { const TableWrapper = styled.div<{ $style: TableStyleType; + $rowStyle: TableRowStyleType; toolbarPosition: "above" | "below" | "close"; }>` overflow: hidden; @@ -219,8 +186,11 @@ const TableWrapper = styled.div<{ } .ant-table { + background: ${(props) => props.$style.background}; .ant-table-container { border-left: unset; + border-top: none !important; + border-inline-start: none !important; .ant-table-content { // A table expand row contains table @@ -276,7 +246,8 @@ const TableWrapper = styled.div<{ } } - ${(props) => props.$style && getStyle(props.$style)} + ${(props) => + props.$style && getStyle(props.$style, props.$rowStyle)} `; const TableTh = styled.th<{ width?: number }>` @@ -291,17 +262,43 @@ const TableTh = styled.th<{ width?: number }>` ${(props) => props.width && `width: ${props.width}px`}; `; -const TableTd = styled.td<{ background: string; $isEditing: boolean }>` +const TableTd = styled.td<{ + background: string; + $style: TableColumnStyleType; + $isEditing: boolean; +}>` .ant-table-row-expand-icon, .ant-table-row-indent { display: ${(props) => (props.$isEditing ? "none" : "initial")}; } + background: ${(props) => props.background} !important; + border-color: ${(props) => props.$style.border} !important; + border-width: ${(props) => props.$style.borderWidth} !important; + border-radius: ${(props) => props.$style.radius}; + + > div > div { + color: ${(props) => props.$style.text}; + font-size: ${(props) => props.$style.textSize}; + &, + > .ant-badge > .ant-badge-status-text, + > div > .markdown-body { + color: ${(props) => props.$style.text}; + } + + > div > svg g { + stroke: ${(props) => props.$style.text}; + } - ${(props) => - props.background && - ` - background: ${props.background} !important; - `}; + // dark link|links color + > a, + > div > a { + color: ${(props) => isDarkColor(props.background) && "#A6FFFF"}; + + &:hover { + color: ${(props) => isDarkColor(props.background) && "#2EE6E6"}; + } + } + } `; const ResizeableTitle = (props: any) => { @@ -345,8 +342,9 @@ const ResizeableTitle = (props: any) => { }} onResizeStop={onResizeStop} draggableOpts={{ enableUserSelectHack: false }} - handle={() => ( + handle={(axis: ResizeHandleAxis, ref: ReactRef) => ( { e.preventDefault(); e.stopPropagation(); @@ -362,41 +360,69 @@ const ResizeableTitle = (props: any) => { type CustomTableProps = Omit, "components" | "columns"> & { columns: CustomColumnType[]; viewModeResizable: boolean; - rowColor: RowColorViewType; + rowColorFn: RowColorViewType; + columnsStyle: TableColumnStyleType; }; function TableCellView(props: { record: RecordType; title: string; - rowColor: RowColorViewType; + rowColorFn: RowColorViewType; + cellColorFn: CellColorViewType; rowIndex: number; children: any; + columnsStyle: TableColumnStyleType; + columnStyle: TableColumnStyleType; }) { - const { record, title, rowIndex, rowColor, children, ...restProps } = props; + const { + record, + title, + rowIndex, + rowColorFn, + cellColorFn, + children, + columnsStyle, + columnStyle, + ...restProps + } = props; const [editing, setEditing] = useState(false); const rowContext = useContext(TableRowContext); let tdView; if (!record) { tdView = {children}; } else { - const color = rowColor({ + const rowColor = rowColorFn({ currentRow: record, currentIndex: rowIndex, currentOriginalIndex: record[OB_ROW_ORI_INDEX], columnTitle: title, }); - let background = ""; - if (color) { - background = genLinerGradient(color); + const cellColor = cellColorFn({ + currentCell: record[title.toLowerCase()], + }); + + const style: TableColumnStyleType = { + background: cellColor || rowColor || columnStyle.background || columnsStyle.background, + text: columnStyle.text || columnsStyle.text, + border: columnStyle.border || columnsStyle.border, + radius: columnStyle.radius || columnsStyle.radius, + borderWidth: columnStyle.borderWidth || columnsStyle.borderWidth, + textSize: columnStyle.textSize || columnsStyle.textSize, } - if (color && rowContext.selected) { - background = genLinerGradient(handleToSelectedRow(color)) + "," + background; + let { background } = style; + if (rowContext.selected) { + background = genLinerGradient(handleToSelectedRow(background)) + "," + background; } - if (color && rowContext.hover) { - background = genLinerGradient(handleToHoverRow(color)) + "," + background; + if (rowContext.hover) { + background = genLinerGradient(handleToHoverRow(background)) + "," + background; } tdView = ( - + {children} ); @@ -436,7 +462,7 @@ function ResizeableTable(props: CustomTableProps { - const { width, ...restCol } = col; + const { width, style, cellColorFn, ...restCol } = col; const resizeWidth = (resizeData.index === index ? resizeData.width : col.width) ?? 0; let colWidth: number | string = "auto"; let minWidth: number | string = COL_MIN_WIDTH; @@ -462,8 +488,11 @@ function ResizeableTable(props: CustomTableProps ({ record, title: col.titleText, - rowColor: props.rowColor, + rowColorFn: props.rowColorFn, + cellColorFn: cellColorFn, rowIndex: rowIndex, + columnsStyle: props.columnsStyle, + columnStyle: style, }), onHeaderCell: () => ({ width: resizeWidth, @@ -528,6 +557,8 @@ export function TableCompView(props: { const { comp, onDownload, onRefresh } = props; const compChildren = comp.children; const style = compChildren.style.getView(); + const rowStyle = compChildren.rowStyle.getView(); + const columnsStyle = compChildren.columnsStyle.getView(); const changeSet = useMemo(() => compChildren.columns.getChangeSet(), [compChildren.columns]); const hasChange = useMemo(() => !_.isEmpty(changeSet), [changeSet]); const columns = useMemo(() => compChildren.columns.getView(), [compChildren.columns]); @@ -634,7 +665,12 @@ export function TableCompView(props: { return ( - + {toolbar.position === "above" && toolbarView} expandable={{ @@ -643,8 +679,11 @@ export function TableCompView(props: { ? COLUMN_CHILDREN_KEY : "OB_CHILDREN_KEY_PLACEHOLDER", fixed: "left", + onExpand: (expanded) => { + if(expanded) handleChangeEvent('rowExpand') + } }} - rowColor={compChildren.rowColor.getView() as any} + rowColorFn={compChildren.rowColor.getView() as any} {...compChildren.selection.getView()(onEvent)} bordered={!compChildren.hideBordered.getView()} onChange={(pagination, filters, sorter, extra) => { @@ -652,6 +691,7 @@ export function TableCompView(props: { }} showHeader={!compChildren.hideHeader.getView()} columns={antdColumns} + columnsStyle={columnsStyle} viewModeResizable={compChildren.viewModeResizable.getView()} dataSource={pageDataInfo.data} size={compChildren.size.getView()} diff --git a/client/packages/lowcoder/src/comps/comps/tableComp/tablePropertyView.tsx b/client/packages/lowcoder/src/comps/comps/tableComp/tablePropertyView.tsx index 49d6ad7d0..199bf1231 100644 --- a/client/packages/lowcoder/src/comps/comps/tableComp/tablePropertyView.tsx +++ b/client/packages/lowcoder/src/comps/comps/tableComp/tablePropertyView.tsx @@ -449,9 +449,8 @@ export function compTablePropertyView tooltip: trans("table.viewModeResizableTooltip"), })} -
+
{comp.children.style.getPropertyView()} - {comp.children.rowColor.getPropertyView()} {comp.children.size.propertyView({ label: trans("table.tableSize"), radioButton: true, @@ -463,6 +462,13 @@ export function compTablePropertyView label: trans("table.hideBordered"), })}
+
+ {comp.children.rowStyle.getPropertyView()} + {comp.children.rowColor.getPropertyView()} +
+
+ {comp.children.columnsStyle.getPropertyView()} +
); } diff --git a/client/packages/lowcoder/src/comps/comps/tableComp/tableTypes.tsx b/client/packages/lowcoder/src/comps/comps/tableComp/tableTypes.tsx index cf05ffac2..e2f4c9acb 100644 --- a/client/packages/lowcoder/src/comps/comps/tableComp/tableTypes.tsx +++ b/client/packages/lowcoder/src/comps/comps/tableComp/tableTypes.tsx @@ -10,7 +10,7 @@ import { import { dropdownControl } from "comps/controls/dropdownControl"; import { eventHandlerControl } from "comps/controls/eventHandlerControl"; import { styleControl } from "comps/controls/styleControl"; -import { TableStyle } from "comps/controls/styleControlConstants"; +import { TableColumnStyle, TableRowStyle, TableStyle } from "comps/controls/styleControlConstants"; import { MultiCompBuilder, stateComp, @@ -69,6 +69,11 @@ export const TableEventOptions = [ value: "rowClick", description: trans("table.rowClick"), }, + { + label: trans("table.rowExpand"), + value: "rowExpand", + description: trans("table.rowExpand"), + }, { label: trans("table.filterChange"), value: "filterChange", @@ -138,6 +143,8 @@ const tableChildrenMap = { sort: valueComp>([]), toolbar: TableToolbarComp, style: styleControl(TableStyle), + rowStyle: styleControl(TableRowStyle), + columnsStyle: withDefault(styleControl(TableColumnStyle), {radius: '0px'}), viewModeResizable: BoolControl, // sample data for regenerating columns dataRowExample: stateComp(null), diff --git a/client/packages/lowcoder/src/comps/comps/tableComp/tableUtils.tsx b/client/packages/lowcoder/src/comps/comps/tableComp/tableUtils.tsx index a2b7445d2..e56f05f25 100644 --- a/client/packages/lowcoder/src/comps/comps/tableComp/tableUtils.tsx +++ b/client/packages/lowcoder/src/comps/comps/tableComp/tableUtils.tsx @@ -7,7 +7,7 @@ import { } from "antd/es/table/interface"; import { SortOrder } from "antd/lib/table/interface"; import { __COLUMN_DISPLAY_VALUE_FN } from "comps/comps/tableComp/column/columnTypeCompBuilder"; -import { RawColumnType, Render } from "comps/comps/tableComp/column/tableColumnComp"; +import { CellColorViewType, RawColumnType, Render } from "comps/comps/tableComp/column/tableColumnComp"; import { TableFilter, tableFilterOperatorMap } from "comps/comps/tableComp/tableToolbarComp"; import { SortValue, TableOnEventView } from "comps/comps/tableComp/tableTypes"; import _ from "lodash"; @@ -17,6 +17,7 @@ import { tryToNumber } from "util/convertUtils"; import { JSONObject, JSONValue } from "util/jsonTypes"; import { StatusType } from "./column/columnTypeComps/columnStatusComp"; import { ColumnListComp, tableDataRowExample } from "./column/tableColumnListComp"; +import { TableColumnStyleType } from "comps/controls/styleControlConstants"; export const COLUMN_CHILDREN_KEY = "children"; export const OB_ROW_ORI_INDEX = "__ob_origin_index"; @@ -173,7 +174,6 @@ export function getOriDisplayData( displayData[col.dataIndex] = colValue; } }); - // console.info("getOriDisplayData. idx: ", idx, " displayData: ", JSON.stringify(displayData)); return displayData; }); } @@ -253,6 +253,8 @@ function renderTitle(props: { title: string; editable: boolean }) { export type CustomColumnType = ColumnType & { onWidthResize?: (width: number) => void; titleText: string; + style: TableColumnStyleType; + cellColorFn: CellColorViewType; }; /** @@ -314,6 +316,15 @@ export function columnsToAntdFormat( align: column.align, width: column.autoWidth === "auto" ? 0 : column.width, fixed: column.fixed === "close" ? false : column.fixed, + style: { + background: column.background, + text: column.text, + border: column.border, + radius: column.radius, + textSize: column.textSize, + borderWidth: column.borderWidth, + }, + cellColorFn: column.cellColor, onWidthResize: column.onWidthResize, render: (value: any, record: RecordType, index: number) => { return column diff --git a/client/packages/lowcoder/src/comps/controls/styleControl.tsx b/client/packages/lowcoder/src/comps/controls/styleControl.tsx index 42311c187..278944ac9 100644 --- a/client/packages/lowcoder/src/comps/controls/styleControl.tsx +++ b/client/packages/lowcoder/src/comps/controls/styleControl.tsx @@ -6,7 +6,14 @@ import { BackgroundColorContext } from "comps/utils/backgroundColorContext"; import { ThemeContext } from "comps/utils/themeContext"; import { trans } from "i18n"; import _ from "lodash"; -import { controlItem, IconRadius, IconReset, ExpandIcon, CompressIcon, } from "lowcoder-design"; +import { + controlItem, + IconRadius, + IconReset, + ExpandIcon, + CompressIcon, + TextSizeIcon, +} from "lowcoder-design"; import { useContext } from "react"; import styled from "styled-components"; import { useIsMobile } from "util/hooks"; @@ -21,6 +28,8 @@ import { SingleColorConfig, MarginConfig, PaddingConfig, + TextSizeConfig, + BorderWidthConfig, } from "./styleControlConstants"; function isSimpleColorConfig(config: SingleColorConfig): config is SimpleColorConfig { @@ -35,6 +44,14 @@ function isRadiusConfig(config: SingleColorConfig): config is RadiusConfig { return config.hasOwnProperty("radius"); } +function isBorderWidthConfig(config: SingleColorConfig): config is BorderWidthConfig { + return config.hasOwnProperty("borderWidth"); +} + +function isTextSizeConfig(config: SingleColorConfig): config is TextSizeConfig { + return config.hasOwnProperty("textSize"); +} + function isMarginConfig(config: SingleColorConfig): config is MarginConfig { return config.hasOwnProperty("margin"); } @@ -55,6 +72,12 @@ function isEmptyColor(color: string) { function isEmptyRadius(radius: string) { return _.isEmpty(radius); } +function isEmptyBorderWidth(borderWidth: string) { + return _.isEmpty(borderWidth); +} +function isEmptyTextSize(textSize: string) { + return _.isEmpty(textSize); +} function isEmptyMargin(margin: string) { return _.isEmpty(margin); @@ -80,6 +103,14 @@ function calcColors>( if (!isEmptyRadius(props[name]) && isRadiusConfig(config)) { res[name] = props[name]; return; + } + if (!isEmptyBorderWidth(props[name]) && isBorderWidthConfig(config)) { + res[name] = props[name]; + return; + } + if (!isEmptyTextSize(props[name]) && isTextSizeConfig(config)) { + res[name] = props[name]; + return; } if (!isEmptyMargin(props[name]) && isMarginConfig(config)) { res[name] = props[name]; @@ -103,6 +134,13 @@ function calcColors>( if (isRadiusConfig(config)) { res[name] = themeWithDefault[config.radius]; } + if (isBorderWidthConfig(config)) { + res[name] = '1px'; + } + if (isTextSizeConfig(config)) { + // TODO: remove default textSize after added in theme in backend. + res[name] = themeWithDefault[config.textSize] || '14px'; + } if (isMarginConfig(config)) { res[name] = themeWithDefault[config.margin]; } @@ -222,7 +260,9 @@ margin: 0 8px 0 -2px; const PaddingIcon = styled(CompressIcon)` margin: 0 8px 0 -2px; `; - +const StyledTextSizeIcon = styled(TextSizeIcon)` +margin: 0 8px 0 -2px; +`; const ResetIcon = styled(IconReset)` &:hover g g { stroke: #315efb; @@ -235,8 +275,10 @@ export function styleControl(colorConfig colorConfigs.map((config) => { const name: Names = config.name; if ( - name === "radius" || - name === "cardRadius" + name === "radius" || + name === "borderWidth" || + name === "cardRadius" || + name === "textSize" ) { childrenMap[name] = StringControl; } else if (name === "margin" || name === "padding" || name==="containerheaderpadding" || name==="containerfooterpadding" || name==="containerbodypadding") { @@ -323,9 +365,10 @@ export function styleControl(colorConfig return controlItem( { filterText: config.label },
- {name === "radius" || + {(name === "radius" || + name === "borderWidth" || name === "gap" || - name === "cardRadius" + name === "cardRadius") ? ( children[name] as InstanceType ).propertyView({ @@ -341,10 +384,10 @@ export function styleControl(colorConfig preInputNode: , placeholder: props[name], }) - : name === "padding" || + : (name === "padding" || name === "containerheaderpadding" || name === "containerfooterpadding" || - name === "containerbodypadding" + name === "containerbodypadding") ? ( children[name] as InstanceType ).propertyView({ @@ -352,6 +395,14 @@ export function styleControl(colorConfig preInputNode: , placeholder: props[name], }) + : name === "textSize" + ? ( + 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 58b706771..f173f7d1f 100644 --- a/client/packages/lowcoder/src/comps/controls/styleControlConstants.tsx +++ b/client/packages/lowcoder/src/comps/controls/styleControlConstants.tsx @@ -17,6 +17,14 @@ export type RadiusConfig = CommonColorConfig & { readonly radius: string; }; +export type BorderWidthConfig = CommonColorConfig & { + readonly borderWidth: string; +}; + +export type TextSizeConfig = CommonColorConfig & { + readonly textSize: string; +}; + export type ContainerHeaderPaddigConfig = CommonColorConfig & { readonly containerheaderpadding: string; }; @@ -42,7 +50,7 @@ export type DepColorConfig = CommonColorConfig & { readonly depType?: DEP_TYPE; transformer: (color: string, ...rest: string[]) => string; }; -export type SingleColorConfig = SimpleColorConfig | DepColorConfig | RadiusConfig | MarginConfig | PaddingConfig | ContainerHeaderPaddigConfig | ContainerFooterPaddigConfig | ContainerBodyPaddigConfig; +export type SingleColorConfig = SimpleColorConfig | DepColorConfig | RadiusConfig | BorderWidthConfig | TextSizeConfig | MarginConfig | PaddingConfig | ContainerHeaderPaddigConfig | ContainerFooterPaddigConfig | ContainerBodyPaddigConfig; export const defaultTheme: ThemeDetail = { primary: "#3377FF", @@ -54,6 +62,7 @@ export const defaultTheme: ThemeDetail = { margin: "3px", padding: "3px", gridColumns: "24", + textSize: "14px", }; export const SURFACE_COLOR = "#FFFFFF"; @@ -67,7 +76,7 @@ export enum DEP_TYPE { } export function contrastText(color: string, textDark: string, textLight: string) { - return isDarkColor(color) ? textLight : textDark; + return isDarkColor(color) && color !== '#00000000' ? textLight : textDark; } // return similar background color @@ -248,6 +257,12 @@ const RADIUS = { radius: "borderRadius", } as const; +const BORDER_WIDTH = { + name: "borderWidth", + label: trans("style.borderWidth"), + borderWidth: "borderWidth", +} as const; + const MARGIN = { name: "margin", label: trans("style.margin"), @@ -260,6 +275,12 @@ const PADDING = { padding: "padding", } as const; +const TEXT_SIZE = { + name: "textSize", + label: trans("style.textSize"), + textSize: "textSize", +} as const; + const CONTAINERHEADERPADDING = { name: "containerheaderpadding", label: trans("style.containerheaderpadding"), @@ -638,33 +659,6 @@ export const SegmentStyle = [ export const TableStyle = [ ...BG_STATIC_BORDER_RADIUS, - { - name: "cellText", - label: trans("style.tableCellText"), - depName: "background", - depType: DEP_TYPE.CONTRAST_TEXT, - transformer: contrastText, - }, - { - name: "selectedRowBackground", - label: trans("style.selectedRowBackground"), - depName: "background", - depTheme: "primary", - transformer: handleToSelectedRow, - }, - { - name: "hoverRowBackground", - label: trans("style.hoverRowBackground"), - depName: "background", - transformer: handleToHoverRow, - }, - { - name: "alternateBackground", - label: trans("style.alternateRowBackground"), - depName: "background", - depType: DEP_TYPE.SELF, - transformer: toSelf, - }, { name: "headerBackground", label: trans("style.tableHeaderBackground"), @@ -694,6 +688,39 @@ export const TableStyle = [ }, ] as const; +export const TableRowStyle = [ + getBackground(), + { + name: "selectedRowBackground", + label: trans("style.selectedRowBackground"), + depName: "background", + depTheme: "primary", + transformer: handleToSelectedRow, + }, + { + name: "hoverRowBackground", + label: trans("style.hoverRowBackground"), + depName: "background", + transformer: handleToHoverRow, + }, + { + name: "alternateBackground", + label: trans("style.alternateRowBackground"), + depName: "background", + depType: DEP_TYPE.SELF, + transformer: toSelf, + }, +] as const; + +export const TableColumnStyle = [ + getStaticBackground("#00000000"), + getStaticBorder(), + BORDER_WIDTH, + RADIUS, + TEXT, + TEXT_SIZE, +] as const; + export const FileStyle = [...getStaticBgBorderRadiusByBg(SURFACE_COLOR), TEXT, ACCENT, MARGIN, PADDING] as const; export const FileViewerStyle = [ @@ -1001,6 +1028,8 @@ export type CheckboxStyleType = StyleConfigType; export type RadioStyleType = StyleConfigType; export type SegmentStyleType = StyleConfigType; export type TableStyleType = StyleConfigType; +export type TableRowStyleType = StyleConfigType; +export type TableColumnStyleType = StyleConfigType; export type FileStyleType = StyleConfigType; export type FileViewerStyleType = StyleConfigType; export type IframeStyleType = StyleConfigType; diff --git a/client/packages/lowcoder/src/i18n/locales/en.ts b/client/packages/lowcoder/src/i18n/locales/en.ts index cd946d31b..fa6620863 100644 --- a/client/packages/lowcoder/src/i18n/locales/en.ts +++ b/client/packages/lowcoder/src/i18n/locales/en.ts @@ -311,15 +311,16 @@ export const en = { style: { resetTooltip: "Reset styles. Delete the input's value to reset an individual field.", + textColor: "Text color", contrastText: "Contrast text color", generated: "Generated", customize: "Customize", staticText: "Static text", accent: "Accent", validate: "Validation message", - border: "Border", + border: "Border color", borderRadius: "Border radius", - borderwidth: "Border width", + borderWidth: "Border width", background: "Background", headerBackground: "Header background", footerBackground: "Footer background", @@ -361,6 +362,7 @@ export const en = { containerfooterpadding: "Footer Padding", containerbodypadding: "Body Padding", minWidth: "Minimum Width", + textSize: "Text size", }, export: { hiddenDesc: "If true, the component is hidden", @@ -1266,16 +1268,22 @@ export const en = { cancelChanges: "Cancel changes", rowSelectChange: "Row select change", rowClick: "Row click", + rowExpand: "Row expand", filterChange: "Filter change", sortChange: "Sort change", pageChange: "Page change", refresh: "Refresh", - rowColor: "Row color", + rowColor: "Conditional row color", rowColorDesc: "Conditionally set the row color based on the optional variables:\n" + "currentRow, currentOriginalIndex, currentIndex, columnTitle. \n" + "For example:\n" + `'{{ currentRow.id > 3 ? "green" : "red" }}'`, + cellColor: "Conditional cell color", + cellColorDesc: + "Conditionally set the cell color based on the cell value using currentCell:\n" + + "For example:\n" + + `'{{ currentCell == 3 ? "green" : "red" }}'`, saveChangesNotBind: "No event handler configured for saving changes. Please bind at least one event handler before click.", dynamicColumn: "Use dynamic column setting", diff --git a/client/packages/lowcoder/src/i18n/locales/zh.ts b/client/packages/lowcoder/src/i18n/locales/zh.ts index 6b97f4805..927c7a05b 100644 --- a/client/packages/lowcoder/src/i18n/locales/zh.ts +++ b/client/packages/lowcoder/src/i18n/locales/zh.ts @@ -293,14 +293,16 @@ themeDetail: { }, style: { resetTooltip: "重置样式.删除输入框的值以重置单个字段.", + textColor: "文字颜色", contrastText: "对比文本颜色", generated: "已生成", customize: "自定义", staticText: "静态文本", accent: "强调色", validate: "验证消息", - border: "边框", + border: "边框颜色", borderRadius: "边框半径", + borderWidth: "边框宽度", background: "背景", headerBackground: "头部背景", footerBackground: "底部背景", @@ -342,6 +344,7 @@ style: { containerfooterpadding: "下内边距", containerbodypadding: "内边距", minWidth: "最小宽度", + textSize: "字体大小", }, export: { hiddenDesc: "如果为true,则隐藏组件", @@ -1197,15 +1200,20 @@ table: { cancelChanges: "取消更改", rowSelectChange: "行选中变化", rowClick: "行点击", + rowExpand: "行展开", filterChange: "筛选变化", sortChange: "排序变化", pageChange: "分页变化", refresh: "刷新", - rowColor: "行颜色", + rowColor: "条件行颜色", rowColorDesc: "基于可选变量条件设置行颜色:\n" + "currentRow, currentOriginalIndex, currentIndex, columnTitle.\n" + "例如:'{{ currentRow.id > 3 ? \"green\" : \"red\" }}'", + cellColor: "条件单元格颜色", + cellColorDesc: + "使用 currentCell 根据单元格值有条件地设置单元格颜色:\n" + + "例如:'{{ currentCell == 3 ? \"green\" : \"red\" }}'", saveChangesNotBind: "未配置保存更改的事件处理程序.请在点击之前绑定至少一个事件处理程序.", dynamicColumn: "使用动态列设置",