From 0d98992bb6fca0755f57281142cf93ec2e5ccce9 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 4 Feb 2025 14:50:04 +0000 Subject: [PATCH 1/5] Bump net.snowflake:snowflake-jdbc Bumps [net.snowflake:snowflake-jdbc](https://github.com/snowflakedb/snowflake-jdbc) from 3.13.33 to 3.22.0. - [Release notes](https://github.com/snowflakedb/snowflake-jdbc/releases) - [Changelog](https://github.com/snowflakedb/snowflake-jdbc/blob/master/CHANGELOG.rst) - [Commits](https://github.com/snowflakedb/snowflake-jdbc/compare/v3.13.33...v3.22.0) --- updated-dependencies: - dependency-name: net.snowflake:snowflake-jdbc dependency-type: direct:production ... Signed-off-by: dependabot[bot] --- server/api-service/lowcoder-plugins/snowflakePlugin/pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) 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 From 082e574c2ff23c8a7753870d02a55850fca107df Mon Sep 17 00:00:00 2001 From: Faran Javed Date: Sat, 1 Mar 2025 03:09:31 +0500 Subject: [PATCH 2/5] add time component in dropdown #1549 --- .../comps/tableComp/column/columnTypeComp.tsx | 7 ++ .../column/columnTypeComps/columnTimeComp.tsx | 71 +++++++++++++++++++ .../packages/lowcoder/src/i18n/locales/en.ts | 1 + 3 files changed, 79 insertions(+) create mode 100644 client/packages/lowcoder/src/comps/comps/tableComp/column/columnTypeComps/columnTimeComp.tsx 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..be29e3d14 --- /dev/null +++ b/client/packages/lowcoder/src/comps/comps/tableComp/column/columnTypeComps/columnTimeComp.tsx @@ -0,0 +1,71 @@ +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 { + TIME_FORMAT, + formatTimestamp, + timestampToHumanReadable, + } from "util/dateTimeUtils"; + import { DateEdit } from "./columnDateComp"; + + const childrenMap = { + text: StringControl, + format: withDefault(StringControl, TIME_FORMAT), + inputFormat: withDefault(StringControl, TIME_FORMAT), + }; + + let inputFormat = TIME_FORMAT; + + const getBaseValue: ColumnTypeViewFn = (props) => + props.text; + + export const TimeComp = (function () { + return new ColumnTypeCompBuilder( + childrenMap, + (props, dispatch) => { + inputFormat = props.inputFormat; + const value = props.changeValue ?? getBaseValue(props, dispatch); + + // Convert value to a number if it's a valid timestamp + const timestamp = Number(value); + if (!isNaN(timestamp)) { + return formatTimestamp(timestamp); + } + + return timestampToHumanReadable(timestamp) ?? value; // Returns readable time + }, + (nodeValue) => { + const timestamp = Number(nodeValue.text.value); + return !isNaN(timestamp) + ? timestampToHumanReadable(timestamp) // Convert to readable format if valid timestamp + : nodeValue.text.value; // Otherwise, return original value + }, + getBaseValue + ) + .setEditViewFn((props) => ( + + )) + .setPropertyViewFn((children) => ( + <> + {children.text.propertyView({ + label: trans("table.columnValue"), + tooltip: ColumnValueTooltip, + })} + {formatPropertyView({ children, placeholder: TIME_FORMAT })} + + )) + .build(); + })(); + \ No newline at end of file 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", From 76a3b3e0b652944d1bd7967d745b70df76244cbc Mon Sep 17 00:00:00 2001 From: Faran Javed Date: Sat, 1 Mar 2025 16:07:50 +0500 Subject: [PATCH 3/5] add suffix/prefix functionality in time #1549 --- .../column/columnTypeComps/columnTimeComp.tsx | 151 ++++++++++-------- 1 file changed, 83 insertions(+), 68 deletions(-) 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 index be29e3d14..e817b852b 100644 --- a/client/packages/lowcoder/src/comps/comps/tableComp/column/columnTypeComps/columnTimeComp.tsx +++ b/client/packages/lowcoder/src/comps/comps/tableComp/column/columnTypeComps/columnTimeComp.tsx @@ -1,71 +1,86 @@ 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 { - TIME_FORMAT, - formatTimestamp, - timestampToHumanReadable, - } from "util/dateTimeUtils"; - import { DateEdit } from "./columnDateComp"; - - const childrenMap = { - text: StringControl, - format: withDefault(StringControl, TIME_FORMAT), - inputFormat: withDefault(StringControl, TIME_FORMAT), - }; - - let inputFormat = TIME_FORMAT; - - const getBaseValue: ColumnTypeViewFn = (props) => - props.text; - - export const TimeComp = (function () { - return new ColumnTypeCompBuilder( - childrenMap, - (props, dispatch) => { - inputFormat = props.inputFormat; - const value = props.changeValue ?? getBaseValue(props, dispatch); - - // Convert value to a number if it's a valid timestamp - const timestamp = Number(value); - if (!isNaN(timestamp)) { - return formatTimestamp(timestamp); - } - - return timestampToHumanReadable(timestamp) ?? value; // Returns readable time - }, - (nodeValue) => { - const timestamp = Number(nodeValue.text.value); - return !isNaN(timestamp) - ? timestampToHumanReadable(timestamp) // Convert to readable format if valid timestamp - : nodeValue.text.value; // Otherwise, return original value - }, - getBaseValue - ) - .setEditViewFn((props) => ( - - )) - .setPropertyViewFn((children) => ( + 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 { + TIME_FORMAT, + formatTimestamp, + timestampToHumanReadable, +} from "util/dateTimeUtils"; +import { DateEdit } from "./columnDateComp"; +import { IconControl } from "comps/controls/iconControl"; +import { hasIcon } from "comps/utils"; + +const childrenMap = { + text: StringControl, + format: withDefault(StringControl, TIME_FORMAT), + inputFormat: withDefault(StringControl, TIME_FORMAT), + prefixIcon: IconControl, + suffixIcon: IconControl, +}; + +let inputFormat = TIME_FORMAT; + +const getBaseValue: ColumnTypeViewFn = (props) => + props.text; + +export const TimeComp = (function () { + return new ColumnTypeCompBuilder( + childrenMap, + (props, dispatch) => { + inputFormat = props.inputFormat; + const value = props.changeValue ?? getBaseValue(props, dispatch); + + // Convert value to a number if it's a valid timestamp + const timestamp = Number(value); + const formattedValue = !isNaN(timestamp) + ? formatTimestamp(timestamp) + : timestampToHumanReadable(timestamp) ?? value; + + return ( <> - {children.text.propertyView({ - label: trans("table.columnValue"), - tooltip: ColumnValueTooltip, - })} - {formatPropertyView({ children, placeholder: TIME_FORMAT })} + {hasIcon(props.prefixIcon) && {props.prefixIcon}} + {formattedValue} + {hasIcon(props.suffixIcon) && {props.suffixIcon}} - )) - .build(); - })(); - \ No newline at end of file + ); + }, + (nodeValue) => { + const timestamp = Number(nodeValue.text.value); + return !isNaN(timestamp) + ? timestampToHumanReadable(timestamp) + : nodeValue.text.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(); +})(); From 8a100b5a2e9c96beeadc66b8ec21953a0d3ed5ca Mon Sep 17 00:00:00 2001 From: Faran Javed Date: Mon, 3 Mar 2025 17:33:18 +0500 Subject: [PATCH 4/5] [Fix]: Address Issues in Time-Only Column Type Table Component (#1549) --- .../column/columnTypeComps/columnTimeComp.tsx | 138 +++++++++++++----- 1 file changed, 98 insertions(+), 40 deletions(-) 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 index e817b852b..d07fba00a 100644 --- a/client/packages/lowcoder/src/comps/comps/tableComp/column/columnTypeComps/columnTimeComp.tsx +++ b/client/packages/lowcoder/src/comps/comps/tableComp/column/columnTypeComps/columnTimeComp.tsx @@ -1,3 +1,4 @@ +import { default as TimePicker } from "antd/es/time-picker"; import { ColumnTypeCompBuilder, ColumnTypeViewFn, @@ -7,27 +8,109 @@ import { StringControl } from "comps/controls/codeControl"; import { withDefault } from "comps/generators"; import { formatPropertyView } from "comps/utils/propertyUtils"; import { trans } from "i18n"; -import { - TIME_FORMAT, - formatTimestamp, - timestampToHumanReadable, -} from "util/dateTimeUtils"; -import { DateEdit } from "./columnDateComp"; -import { IconControl } from "comps/controls/iconControl"; -import { hasIcon } from "comps/utils"; +import dayjs from "dayjs"; +import { useEffect, useRef, useState } from "react"; +import styled from "styled-components"; +import { TIME_FORMAT } from "util/dateTimeUtils"; + +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, format: withDefault(StringControl, TIME_FORMAT), inputFormat: withDefault(StringControl, TIME_FORMAT), - prefixIcon: IconControl, - suffixIcon: IconControl, }; let inputFormat = TIME_FORMAT; -const getBaseValue: ColumnTypeViewFn = (props) => - props.text; +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( @@ -35,35 +118,16 @@ export const TimeComp = (function () { (props, dispatch) => { inputFormat = props.inputFormat; const value = props.changeValue ?? getBaseValue(props, dispatch); - - // Convert value to a number if it's a valid timestamp - const timestamp = Number(value); - const formattedValue = !isNaN(timestamp) - ? formatTimestamp(timestamp) - : timestampToHumanReadable(timestamp) ?? value; - - return ( - <> - {hasIcon(props.prefixIcon) && {props.prefixIcon}} - {formattedValue} - {hasIcon(props.suffixIcon) && {props.suffixIcon}} - - ); - }, - (nodeValue) => { - const timestamp = Number(nodeValue.text.value); - return !isNaN(timestamp) - ? timestampToHumanReadable(timestamp) - : nodeValue.text.value; + return formatTime(value, props.format); }, + (nodeValue) => formatTime(nodeValue.text.value, nodeValue.format.value), getBaseValue ) .setEditViewFn((props) => ( - )) @@ -73,12 +137,6 @@ export const TimeComp = (function () { 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 })} )) From ce21056a5f525afc615363bce0afbd4a3c30533e Mon Sep 17 00:00:00 2001 From: Faran Javed Date: Mon, 3 Mar 2025 19:32:52 +0500 Subject: [PATCH 5/5] [Fix]: Add Prefix/Suffix icons in Time-Only Column Type Table Component (#1549) --- .../column/columnTypeComps/columnTimeComp.tsx | 24 ++++++++++++++++++- 1 file changed, 23 insertions(+), 1 deletion(-) 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 index d07fba00a..b4ad2d73d 100644 --- a/client/packages/lowcoder/src/comps/comps/tableComp/column/columnTypeComps/columnTimeComp.tsx +++ b/client/packages/lowcoder/src/comps/comps/tableComp/column/columnTypeComps/columnTimeComp.tsx @@ -12,6 +12,9 @@ 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%; @@ -52,6 +55,8 @@ export function formatTime(time: string, format: string) { const childrenMap = { text: StringControl, + prefixIcon: IconControl, + suffixIcon: IconControl, format: withDefault(StringControl, TIME_FORMAT), inputFormat: withDefault(StringControl, TIME_FORMAT), }; @@ -118,7 +123,18 @@ export const TimeComp = (function () { (props, dispatch) => { inputFormat = props.inputFormat; const value = props.changeValue ?? getBaseValue(props, dispatch); - return formatTime(value, props.format); + return( + <> + {hasIcon(props.prefixIcon) && ( + {props.prefixIcon} + )} + {value} + {hasIcon(props.suffixIcon) && ( + {props.suffixIcon} + )} + + ); + }, (nodeValue) => formatTime(nodeValue.text.value, nodeValue.format.value), getBaseValue @@ -137,6 +153,12 @@ export const TimeComp = (function () { 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 })} ))