diff --git a/client/packages/lowcoder-design/src/components/ScrollBar.tsx b/client/packages/lowcoder-design/src/components/ScrollBar.tsx index cf51ee68b..e4a08601e 100644 --- a/client/packages/lowcoder-design/src/components/ScrollBar.tsx +++ b/client/packages/lowcoder-design/src/components/ScrollBar.tsx @@ -56,6 +56,8 @@ interface IProps { }; $hideplaceholder?: boolean; hideScrollbar?: boolean; + prefixNode?: React.ReactNode; + suffixNode?: React.ReactNode; } export const ScrollBar = ({ @@ -65,6 +67,8 @@ export const ScrollBar = ({ scrollableNodeProps, hideScrollbar = false, $hideplaceholder = false, + prefixNode, + suffixNode, ...otherProps }: IProps) => { const height = style?.height ?? '100%'; @@ -73,12 +77,24 @@ export const ScrollBar = ({ return hideScrollbar ? ( + {prefixNode} {children} + {suffixNode} ) : ( - {children} + {({ scrollableNodeProps, contentNodeProps }) => { + return ( +
+ {prefixNode} +
+ {children} +
+ {suffixNode} +
+ ); + }}
); diff --git a/client/packages/lowcoder/src/comps/comps/selectInputComp/selectCompConstants.tsx b/client/packages/lowcoder/src/comps/comps/selectInputComp/selectCompConstants.tsx index 40ca4b668..41851324f 100644 --- a/client/packages/lowcoder/src/comps/comps/selectInputComp/selectCompConstants.tsx +++ b/client/packages/lowcoder/src/comps/comps/selectInputComp/selectCompConstants.tsx @@ -78,7 +78,9 @@ export const getStyle = ( .ant-select-selection-search { padding: ${style.padding}; } - .ant-select-selection-search-input { + .ant-select-selection-search-input, + .ant-select-selection-item, + .ant-select-selection-item .option-label { font-family:${(style as SelectStyleType).fontFamily} !important; text-transform:${(style as SelectStyleType).textTransform} !important; text-decoration:${(style as SelectStyleType).textDecoration} !important; diff --git a/client/packages/lowcoder/src/comps/comps/tableComp/tableCompView.tsx b/client/packages/lowcoder/src/comps/comps/tableComp/tableCompView.tsx index 95ae17061..5c6ad7c44 100644 --- a/client/packages/lowcoder/src/comps/comps/tableComp/tableCompView.tsx +++ b/client/packages/lowcoder/src/comps/comps/tableComp/tableCompView.tsx @@ -28,7 +28,7 @@ import { BackgroundColorContext } from "comps/utils/backgroundColorContext"; import { PrimaryColor } from "constants/style"; import { trans } from "i18n"; import _ from "lodash"; -import { darkenColor, isDarkColor } from "lowcoder-design"; +import { darkenColor, isDarkColor, ScrollBar } from "lowcoder-design"; import React, { Children, useCallback, useContext, useEffect, useMemo, useRef, useState } from "react"; import { Resizable } from "react-resizable"; import styled, { css } from "styled-components"; @@ -43,6 +43,7 @@ import { CellColorViewType } from "./column/tableColumnComp"; import { defaultTheme } from "@lowcoder-ee/constants/themeConstants"; import { useMergeCompStyles } from "@lowcoder-ee/util/hooks"; import { childrenToProps } from "@lowcoder-ee/comps/generators/multi"; +import { getVerticalMargin } from "@lowcoder-ee/util/cssUtil"; function genLinerGradient(color: string) { @@ -141,17 +142,35 @@ const TitleResizeHandle = styled.span` const BackgroundWrapper = styled.div<{ $style: TableStyleType; $tableAutoHeight: boolean; -}>` + $showHorizontalScrollbar: boolean; + $showVerticalScrollbar: boolean; +}>` + display: flex; + flex-direction: column; background: ${(props) => props.$style.background} !important; - // border: ${(props) => `${props.$style.border} !important`}; border-radius: ${(props) => props.$style.radius} !important; - // padding: unset !important; padding: ${(props) => props.$style.padding} !important; margin: ${(props) => props.$style.margin} !important; - overflow: scroll !important; border-style: ${(props) => props.$style.borderStyle} !important; border-width: ${(props) => `${props.$style.borderWidth} !important`}; - ${(props) => props.$style} + border-color: ${(props) => `${props.$style.border} !important`}; + height: calc(100% - ${(props) => getVerticalMargin(props.$style.margin.split(' '))}); + overflow: hidden; + + > div.table-scrollbar-wrapper { + height: auto; + overflow: auto; + ${(props) => !props.$showHorizontalScrollbar && ` + div.simplebar-horizontal { + visibility: hidden !important; + } + `} + ${(props) => !props.$showVerticalScrollbar && ` + div.simplebar-vertical { + visibility: hidden !important; + } + `} + } `; // TODO: find a way to limit the calc function for max-height only to first Margin value @@ -166,8 +185,6 @@ const TableWrapper = styled.div<{ $visibleResizables: boolean; $showHRowGridBorder?: boolean; }>` - overflow: unset !important; - .ant-table-wrapper { border-top: unset; border-color: inherit; @@ -193,22 +210,18 @@ const TableWrapper = styled.div<{ } .ant-table { - overflow-y:scroll; background: ${(props) =>props.$style.background}; .ant-table-container { border-left: unset; border-top: none !important; border-inline-start: none !important; - overflow-y:scroll; - height:300px &::after { box-shadow: none !important; } .ant-table-content { - overflow-y:scroll; - overflow-x:scroll; + overflow: unset !important } // A table expand row contains table @@ -220,6 +233,15 @@ const TableWrapper = styled.div<{ border-top: unset; > .ant-table-thead { + ${(props) => + props.$fixedHeader && ` + position: sticky; + position: -webkit-sticky; + // top: ${props.$fixedToolbar ? '47px' : '0'}; + top: 0; + z-index: 99; + ` + } > tr > th { background-color: ${(props) => props.$headerStyle.headerBackground}; @@ -227,14 +249,7 @@ const TableWrapper = styled.div<{ border-width: ${(props) => props.$headerStyle.borderWidth}; color: ${(props) => props.$headerStyle.headerText}; // border-inline-end: ${(props) => `${props.$headerStyle.borderWidth} solid ${props.$headerStyle.border}`} !important; - ${(props) => - props.$fixedHeader && ` - position: sticky; - position: -webkit-sticky; - top: ${props.$fixedToolbar ? '47px' : '0'}; - z-index: 99; - ` - } + > div { margin: ${(props) => props.$headerStyle.margin}; @@ -715,6 +730,8 @@ export function TableCompView(props: { const toolbarStyle = compChildren.toolbarStyle.getView(); const rowAutoHeight = compChildren.rowAutoHeight.getView(); const tableAutoHeight = comp.getTableAutoHeight(); + const showHorizontalScrollbar = compChildren.showHorizontalScrollbar.getView(); + const showVerticalScrollbar = compChildren.showVerticalScrollbar.getView(); const visibleResizables = compChildren.visibleResizables.getView(); const showHRowGridBorder = compChildren.showHRowGridBorder.getView(); const columnsStyle = compChildren.columnsStyle.getView(); @@ -832,70 +849,84 @@ export function TableCompView(props: { return ( - - {toolbar.position === "above" && toolbarView} - + {toolbar.position === "above" && toolbar.fixedToolbar && toolbarView} + - - expandable={{ - ...expansion.expandableConfig, - childrenColumnName: supportChildren - ? COLUMN_CHILDREN_KEY - : "OB_CHILDREN_KEY_PLACEHOLDER", - fixed: "left", - onExpand: (expanded) => { - if (expanded) { - handleChangeEvent('rowExpand') - } else { - handleChangeEvent('rowShrink') + + + expandable={{ + ...expansion.expandableConfig, + childrenColumnName: supportChildren + ? COLUMN_CHILDREN_KEY + : "OB_CHILDREN_KEY_PLACEHOLDER", + fixed: "left", + onExpand: (expanded) => { + if (expanded) { + handleChangeEvent('rowExpand') + } else { + handleChangeEvent('rowShrink') + } } + }} + rowColorFn={compChildren.rowColor.getView() as any} + rowHeightFn={compChildren.rowHeight.getView() as any} + {...compChildren.selection.getView()(onEvent)} + bordered={compChildren.showRowGridBorder.getView()} + onChange={(pagination, filters, sorter, extra) => { + onTableChange(pagination, filters, sorter, extra, comp.dispatch, onEvent); + }} + showHeader={!compChildren.hideHeader.getView()} + columns={antdColumns} + columnsStyle={columnsStyle} + viewModeResizable={compChildren.viewModeResizable.getView()} + visibleResizables={compChildren.visibleResizables.getView()} + dataSource={pageDataInfo.data} + size={compChildren.size.getView()} + rowAutoHeight={rowAutoHeight} + tableLayout="fixed" + loading={ + loading || + // fixme isLoading type + (compChildren.showDataLoadSpinner.getView() && + (compChildren.data as any).isLoading()) || + compChildren.loading.getView() } - }} - rowColorFn={compChildren.rowColor.getView() as any} - rowHeightFn={compChildren.rowHeight.getView() as any} - {...compChildren.selection.getView()(onEvent)} - bordered={compChildren.showRowGridBorder.getView()} - onChange={(pagination, filters, sorter, extra) => { - onTableChange(pagination, filters, sorter, extra, comp.dispatch, onEvent); - }} - showHeader={!compChildren.hideHeader.getView()} - columns={antdColumns} - columnsStyle={columnsStyle} - viewModeResizable={compChildren.viewModeResizable.getView()} - visibleResizables={compChildren.visibleResizables.getView()} - dataSource={pageDataInfo.data} - size={compChildren.size.getView()} - rowAutoHeight={rowAutoHeight} - tableLayout="fixed" - loading={ - loading || - // fixme isLoading type - (compChildren.showDataLoadSpinner.getView() && - (compChildren.data as any).isLoading()) || - compChildren.loading.getView() - } - onCellClick={(columnName: string, dataIndex: string) => { - comp.children.selectedCell.dispatchChangeValueAction({ - name: columnName, - dataIndex: dataIndex, - }); - }} - /> - - - {expansion.expandModalView} - - - {toolbar.position === "below" && toolbarView} + onCellClick={(columnName: string, dataIndex: string) => { + comp.children.selectedCell.dispatchChangeValueAction({ + name: columnName, + dataIndex: dataIndex, + }); + }} + /> + + + {expansion.expandModalView} + + + + {toolbar.position === "below" && toolbar.fixedToolbar && toolbarView} diff --git a/client/packages/lowcoder/src/comps/comps/tableComp/tablePropertyView.tsx b/client/packages/lowcoder/src/comps/comps/tableComp/tablePropertyView.tsx index f45edf49b..636721c1f 100644 --- a/client/packages/lowcoder/src/comps/comps/tableComp/tablePropertyView.tsx +++ b/client/packages/lowcoder/src/comps/comps/tableComp/tablePropertyView.tsx @@ -462,6 +462,12 @@ export function compTablePropertyView radioButton: true, })} {comp.children.autoHeight.getPropertyView()} + {comp.children.showHorizontalScrollbar.propertyView({ + label: trans("prop.showHorizontalScrollbar"), + })} + {!comp.children.autoHeight.getView() && comp.children.showVerticalScrollbar.propertyView({ + label: trans("prop.showVerticalScrollbar"), + })} {comp.children.fixedHeader.propertyView({ label: trans("table.fixedHeader"), tooltip: trans("table.fixedHeaderTooltip") diff --git a/client/packages/lowcoder/src/comps/comps/tableComp/tableTypes.tsx b/client/packages/lowcoder/src/comps/comps/tableComp/tableTypes.tsx index 987f8b1ee..310939f07 100644 --- a/client/packages/lowcoder/src/comps/comps/tableComp/tableTypes.tsx +++ b/client/packages/lowcoder/src/comps/comps/tableComp/tableTypes.tsx @@ -197,6 +197,8 @@ const tableChildrenMap = { hideHeader: BoolControl, fixedHeader: BoolControl, autoHeight: withDefault(AutoHeightControl, "auto"), + showVerticalScrollbar: BoolControl, + showHorizontalScrollbar: BoolControl, data: withIsLoadingMethod(JSONObjectArrayControl), showDataLoadSpinner: withDefault(BoolPureControl, true), columns: ColumnListComp, diff --git a/client/packages/lowcoder/src/comps/controls/styleControlConstants.tsx b/client/packages/lowcoder/src/comps/controls/styleControlConstants.tsx index 37dee183a..c82d6aa58 100644 --- a/client/packages/lowcoder/src/comps/controls/styleControlConstants.tsx +++ b/client/packages/lowcoder/src/comps/controls/styleControlConstants.tsx @@ -1512,7 +1512,6 @@ export const TableHeaderStyle = [ }, TEXT_SIZE, TEXT_WEIGHT, - FONT_FAMILY, ] as const; export const TableRowStyle = [ diff --git a/client/packages/lowcoder/src/i18n/locales/de.ts b/client/packages/lowcoder/src/i18n/locales/de.ts index 9c4538827..1850c4391 100644 --- a/client/packages/lowcoder/src/i18n/locales/de.ts +++ b/client/packages/lowcoder/src/i18n/locales/de.ts @@ -200,6 +200,8 @@ export const de: typeof en = { "className": "Klasse", "dataTestId": "Test ID", "horizontalGridCells": "Horizontale Gitterzellen", + "showHorizontalScrollbar": "Horizontale Bildlaufleiste anzeigen", + "showVerticalScrollbar": "Vertikale Bildlaufleiste anzeigen", }, "autoHeightProp": { ...en.autoHeightProp, diff --git a/client/packages/lowcoder/src/i18n/locales/en.ts b/client/packages/lowcoder/src/i18n/locales/en.ts index 08e7c7aa8..768b27c28 100644 --- a/client/packages/lowcoder/src/i18n/locales/en.ts +++ b/client/packages/lowcoder/src/i18n/locales/en.ts @@ -220,6 +220,8 @@ export const en = { "preventOverwriting": "Prevent overwriting styles", "color": "Color", "horizontalGridCells": "Horizontal Grid Cells", + "showHorizontalScrollbar": "Show Horizontal Scrollbar", + "showVerticalScrollbar": "Show Vertical Scrollbar", }, "autoHeightProp": { "auto": "Auto", diff --git a/client/packages/lowcoder/src/i18n/locales/pt.ts b/client/packages/lowcoder/src/i18n/locales/pt.ts index f8f6c41a9..80a3369b6 100644 --- a/client/packages/lowcoder/src/i18n/locales/pt.ts +++ b/client/packages/lowcoder/src/i18n/locales/pt.ts @@ -231,6 +231,8 @@ export const pt: typeof en = { "className": "Nome da Classe CSS", "dataTestId": "ID Individual", "horizontalGridCells": "Células de grade horizontal", + "showHorizontalScrollbar": "Mostrar barra de rolagem horizontal", + "showVerticalScrollbar": "Mostrar barra de rolagem vertical", }, "autoHeightProp": { ...en.autoHeightProp, diff --git a/client/packages/lowcoder/src/i18n/locales/zh.ts b/client/packages/lowcoder/src/i18n/locales/zh.ts index cd54a777a..235409561 100644 --- a/client/packages/lowcoder/src/i18n/locales/zh.ts +++ b/client/packages/lowcoder/src/i18n/locales/zh.ts @@ -220,6 +220,8 @@ export const zh: typeof en = { "horizontal": "水平", "minHorizontalWidth": "最小水平宽度", "horizontalGridCells": "水平网格单元", + "showHorizontalScrollbar": "显示水平滚动条", + "showVerticalScrollbar": "显示垂直滚动条", }, autoHeightProp: { diff --git a/client/packages/lowcoder/src/util/cssUtil.tsx b/client/packages/lowcoder/src/util/cssUtil.tsx new file mode 100644 index 000000000..eb0d5426a --- /dev/null +++ b/client/packages/lowcoder/src/util/cssUtil.tsx @@ -0,0 +1,52 @@ +type Direction = { + top: string; + right: string; + bottom: string; + left: string; +} + +export const parseMarginOrPadding = (style: string):Direction => { + const styles = style.split(' '); + if (styles.length === 1) { + return { + top: styles[0], right: styles[0], bottom: styles[0], left: styles[0], + }; + } + if (styles.length === 2) { + return { + top: styles[0], right: styles[1], bottom: styles[0], left: styles[1], + }; + } + if (styles.length === 3) { + return { + top: styles[0], right: styles[1], bottom: styles[2], left: styles[1], + }; + } + if (styles.length === 4) { + return { + top: styles[0], right: styles[1], bottom: styles[2], left: styles[3], + }; + } + // invalid margin/padding + return { + top: '0px', right: '0px', bottom: '0px', left: '0px', + }; +} + +export const getVerticalMargin = (margin: string[]) => { + if(margin.length === 1) return `(${margin[0]} + ${margin[0]})`; + if(margin.length === 2) return `(${margin[0]} + ${margin[0]})`; + if(margin.length === 3 || margin.length === 4) + return `(${margin[0]} + ${margin[2]})`; + + return '0px'; +} + +export const getHorizontalMargin = (margin: string[]) => { + if(margin.length === 1) return `(${margin[0]} + ${margin[0]})`; + if(margin.length === 2) return `(${margin[1]} + ${margin[1]})`; + if(margin.length === 3 || margin.length === 4) + return `(${margin[1]} + ${margin[3]})`; + + return '0px'; +}