diff --git a/client/packages/lowcoder/src/comps/comps/tableComp/column/columnTypeComp.tsx b/client/packages/lowcoder/src/comps/comps/tableComp/column/columnTypeComp.tsx index 3757e6bc8..58e0b7df4 100644 --- a/client/packages/lowcoder/src/comps/comps/tableComp/column/columnTypeComp.tsx +++ b/client/packages/lowcoder/src/comps/comps/tableComp/column/columnTypeComp.tsx @@ -1,5 +1,6 @@ import { CellProps } from "components/table/EditableCell"; import { DateTimeComp } from "comps/comps/tableComp/column/columnTypeComps/columnDateTimeComp"; +import { TimeComp } from "./columnTypeComps/columnTimeComp"; import { ButtonComp } from "comps/comps/tableComp/column/simpleColumnTypeComps"; import { withType } from "comps/generators"; import { trans } from "i18n"; @@ -67,6 +68,11 @@ const actionOptions = [ label: trans("table.image"), value: "image", }, + { + label: trans("table.time"), + value: "time", + }, + { label: trans("table.date"), value: "date", @@ -116,6 +122,7 @@ export const ColumnTypeCompMap = { rating: RatingComp, progress: ProgressComp, date: DateComp, + time: TimeComp, }; type ColumnTypeMapType = typeof ColumnTypeCompMap; diff --git a/client/packages/lowcoder/src/comps/comps/tableComp/column/columnTypeComps/columnTimeComp.tsx b/client/packages/lowcoder/src/comps/comps/tableComp/column/columnTypeComps/columnTimeComp.tsx new file mode 100644 index 000000000..b4ad2d73d --- /dev/null +++ b/client/packages/lowcoder/src/comps/comps/tableComp/column/columnTypeComps/columnTimeComp.tsx @@ -0,0 +1,166 @@ +import { default as TimePicker } from "antd/es/time-picker"; +import { + ColumnTypeCompBuilder, + ColumnTypeViewFn, +} from "comps/comps/tableComp/column/columnTypeCompBuilder"; +import { ColumnValueTooltip } from "comps/comps/tableComp/column/simpleColumnTypeComps"; +import { StringControl } from "comps/controls/codeControl"; +import { withDefault } from "comps/generators"; +import { formatPropertyView } from "comps/utils/propertyUtils"; +import { trans } from "i18n"; +import dayjs from "dayjs"; +import { useEffect, useRef, useState } from "react"; +import styled from "styled-components"; +import { TIME_FORMAT } from "util/dateTimeUtils"; +import { hasIcon } from "comps/utils"; +import { IconControl } from "comps/controls/iconControl"; + + +const TimePickerStyled = styled(TimePicker)<{ $open: boolean }>` + width: 100%; + height: 100%; + position: absolute; + top: 0; + padding: 0; + padding-left: 11px; + .ant-picker-input { + height: 100%; + } + input { + padding-right: 18px; + cursor: pointer; + } + &.ant-picker-focused .ant-picker-suffix svg g { + stroke: ${(props) => props.$open && "#315EFB"}; + } + .ant-picker-suffix { + height: calc(100% - 1px); + position: absolute; + right: 0; + top: 0.5px; + display: flex; + align-items: center; + padding: 0 3px; + } +`; + +const Wrapper = styled.div` + background: transparent !important; +`; + +export function formatTime(time: string, format: string) { + const parsedTime = dayjs(time, TIME_FORMAT); + return parsedTime.isValid() ? parsedTime.format(format) : ""; +} + +const childrenMap = { + text: StringControl, + prefixIcon: IconControl, + suffixIcon: IconControl, + format: withDefault(StringControl, TIME_FORMAT), + inputFormat: withDefault(StringControl, TIME_FORMAT), +}; + +let inputFormat = TIME_FORMAT; + +const getBaseValue: ColumnTypeViewFn = (props) => props.text; + +type TimeEditProps = { + value: string; + onChange: (value: string) => void; + onChangeEnd: () => void; + inputFormat: string; +}; + +export const TimeEdit = (props: TimeEditProps) => { + const pickerRef = useRef(); + const [panelOpen, setPanelOpen] = useState(true); + let value = dayjs(props.value, TIME_FORMAT); + if (!value.isValid()) { + value = dayjs("00:00:00", TIME_FORMAT); + } + + const [tempValue, setTempValue] = useState(value); + + useEffect(() => { + const value = props.value ? dayjs(props.value, TIME_FORMAT) : null; + setTempValue(value); + }, [props.value]); + + return ( + { + if (e.key === "Enter" && !panelOpen) { + props.onChangeEnd(); + } + }} + onMouseDown={(e) => { + e.stopPropagation(); + e.preventDefault(); + }} + > + setPanelOpen(open)} + onChange={(value, timeString) => { + props.onChange(timeString as string); + }} + onBlur={() => props.onChangeEnd()} + /> + + ); +}; + +export const TimeComp = (function () { + return new ColumnTypeCompBuilder( + childrenMap, + (props, dispatch) => { + inputFormat = props.inputFormat; + const value = props.changeValue ?? getBaseValue(props, dispatch); + return( + <> + {hasIcon(props.prefixIcon) && ( + {props.prefixIcon} + )} + {value} + {hasIcon(props.suffixIcon) && ( + {props.suffixIcon} + )} + + ); + + }, + (nodeValue) => formatTime(nodeValue.text.value, nodeValue.format.value), + getBaseValue + ) + .setEditViewFn((props) => ( + + )) + .setPropertyViewFn((children) => ( + <> + {children.text.propertyView({ + label: trans("table.columnValue"), + tooltip: ColumnValueTooltip, + })} + {children.prefixIcon.propertyView({ + label: trans("button.prefixIcon"), + })} + {children.suffixIcon.propertyView({ + label: trans("button.suffixIcon"), + })} + {formatPropertyView({ children, placeholder: TIME_FORMAT })} + + )) + .build(); +})(); diff --git a/client/packages/lowcoder/src/i18n/locales/en.ts b/client/packages/lowcoder/src/i18n/locales/en.ts index 48d4cbdc4..054b58153 100644 --- a/client/packages/lowcoder/src/i18n/locales/en.ts +++ b/client/packages/lowcoder/src/i18n/locales/en.ts @@ -2029,6 +2029,7 @@ export const en = { "tag": "Tag", "select": "Select", "dropdown": "Dropdown", + "time" : "Time", "date": "Date", "dateTime": "Date Time", "badgeStatus": "Status", diff --git a/server/api-service/lowcoder-plugins/snowflakePlugin/pom.xml b/server/api-service/lowcoder-plugins/snowflakePlugin/pom.xml index a2cb0e729..9b114063b 100644 --- a/server/api-service/lowcoder-plugins/snowflakePlugin/pom.xml +++ b/server/api-service/lowcoder-plugins/snowflakePlugin/pom.xml @@ -30,7 +30,7 @@ net.snowflake snowflake-jdbc - 3.13.33 + 3.22.0 org.lowcoder