Skip to content

Commit 8a100b5

Browse files
committedMar 3, 2025
[Fix]: Address Issues in Time-Only Column Type Table Component (lowcoder-org#1549)
1 parent 76a3b3e commit 8a100b5

File tree

1 file changed

+98
-40
lines changed
  • client/packages/lowcoder/src/comps/comps/tableComp/column/columnTypeComps

1 file changed

+98
-40
lines changed
 

‎client/packages/lowcoder/src/comps/comps/tableComp/column/columnTypeComps/columnTimeComp.tsx

+98-40
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
import { default as TimePicker } from "antd/es/time-picker";
12
import {
23
ColumnTypeCompBuilder,
34
ColumnTypeViewFn,
@@ -7,63 +8,126 @@ import { StringControl } from "comps/controls/codeControl";
78
import { withDefault } from "comps/generators";
89
import { formatPropertyView } from "comps/utils/propertyUtils";
910
import { trans } from "i18n";
10-
import {
11-
TIME_FORMAT,
12-
formatTimestamp,
13-
timestampToHumanReadable,
14-
} from "util/dateTimeUtils";
15-
import { DateEdit } from "./columnDateComp";
16-
import { IconControl } from "comps/controls/iconControl";
17-
import { hasIcon } from "comps/utils";
11+
import dayjs from "dayjs";
12+
import { useEffect, useRef, useState } from "react";
13+
import styled from "styled-components";
14+
import { TIME_FORMAT } from "util/dateTimeUtils";
15+
16+
const TimePickerStyled = styled(TimePicker)<{ $open: boolean }>`
17+
width: 100%;
18+
height: 100%;
19+
position: absolute;
20+
top: 0;
21+
padding: 0;
22+
padding-left: 11px;
23+
.ant-picker-input {
24+
height: 100%;
25+
}
26+
input {
27+
padding-right: 18px;
28+
cursor: pointer;
29+
}
30+
&.ant-picker-focused .ant-picker-suffix svg g {
31+
stroke: ${(props) => props.$open && "#315EFB"};
32+
}
33+
.ant-picker-suffix {
34+
height: calc(100% - 1px);
35+
position: absolute;
36+
right: 0;
37+
top: 0.5px;
38+
display: flex;
39+
align-items: center;
40+
padding: 0 3px;
41+
}
42+
`;
43+
44+
const Wrapper = styled.div`
45+
background: transparent !important;
46+
`;
47+
48+
export function formatTime(time: string, format: string) {
49+
const parsedTime = dayjs(time, TIME_FORMAT);
50+
return parsedTime.isValid() ? parsedTime.format(format) : "";
51+
}
1852

1953
const childrenMap = {
2054
text: StringControl,
2155
format: withDefault(StringControl, TIME_FORMAT),
2256
inputFormat: withDefault(StringControl, TIME_FORMAT),
23-
prefixIcon: IconControl,
24-
suffixIcon: IconControl,
2557
};
2658

2759
let inputFormat = TIME_FORMAT;
2860

29-
const getBaseValue: ColumnTypeViewFn<typeof childrenMap, string, string> = (props) =>
30-
props.text;
61+
const getBaseValue: ColumnTypeViewFn<typeof childrenMap, string, string> = (props) => props.text;
62+
63+
type TimeEditProps = {
64+
value: string;
65+
onChange: (value: string) => void;
66+
onChangeEnd: () => void;
67+
inputFormat: string;
68+
};
69+
70+
export const TimeEdit = (props: TimeEditProps) => {
71+
const pickerRef = useRef<any>();
72+
const [panelOpen, setPanelOpen] = useState(true);
73+
let value = dayjs(props.value, TIME_FORMAT);
74+
if (!value.isValid()) {
75+
value = dayjs("00:00:00", TIME_FORMAT);
76+
}
77+
78+
const [tempValue, setTempValue] = useState<dayjs.Dayjs | null>(value);
79+
80+
useEffect(() => {
81+
const value = props.value ? dayjs(props.value, TIME_FORMAT) : null;
82+
setTempValue(value);
83+
}, [props.value]);
84+
85+
return (
86+
<Wrapper
87+
onKeyDown={(e) => {
88+
if (e.key === "Enter" && !panelOpen) {
89+
props.onChangeEnd();
90+
}
91+
}}
92+
onMouseDown={(e) => {
93+
e.stopPropagation();
94+
e.preventDefault();
95+
}}
96+
>
97+
<TimePickerStyled
98+
ref={pickerRef}
99+
$open={panelOpen}
100+
format={props.inputFormat}
101+
allowClear={true}
102+
autoFocus
103+
value={tempValue}
104+
defaultOpen={true}
105+
onOpenChange={(open) => setPanelOpen(open)}
106+
onChange={(value, timeString) => {
107+
props.onChange(timeString as string);
108+
}}
109+
onBlur={() => props.onChangeEnd()}
110+
/>
111+
</Wrapper>
112+
);
113+
};
31114

32115
export const TimeComp = (function () {
33116
return new ColumnTypeCompBuilder(
34117
childrenMap,
35118
(props, dispatch) => {
36119
inputFormat = props.inputFormat;
37120
const value = props.changeValue ?? getBaseValue(props, dispatch);
38-
39-
// Convert value to a number if it's a valid timestamp
40-
const timestamp = Number(value);
41-
const formattedValue = !isNaN(timestamp)
42-
? formatTimestamp(timestamp)
43-
: timestampToHumanReadable(timestamp) ?? value;
44-
45-
return (
46-
<>
47-
{hasIcon(props.prefixIcon) && <span>{props.prefixIcon}</span>}
48-
<span>{formattedValue}</span>
49-
{hasIcon(props.suffixIcon) && <span>{props.suffixIcon}</span>}
50-
</>
51-
);
52-
},
53-
(nodeValue) => {
54-
const timestamp = Number(nodeValue.text.value);
55-
return !isNaN(timestamp)
56-
? timestampToHumanReadable(timestamp)
57-
: nodeValue.text.value;
121+
return formatTime(value, props.format);
58122
},
123+
(nodeValue) => formatTime(nodeValue.text.value, nodeValue.format.value),
59124
getBaseValue
60125
)
61126
.setEditViewFn((props) => (
62-
<DateEdit
127+
<TimeEdit
63128
value={props.value}
64129
onChange={props.onChange}
65130
onChangeEnd={props.onChangeEnd}
66-
showTime={true} // Ensures only time is shown
67131
inputFormat={inputFormat}
68132
/>
69133
))
@@ -73,12 +137,6 @@ export const TimeComp = (function () {
73137
label: trans("table.columnValue"),
74138
tooltip: ColumnValueTooltip,
75139
})}
76-
{children.prefixIcon.propertyView({
77-
label: trans("button.prefixIcon"),
78-
})}
79-
{children.suffixIcon.propertyView({
80-
label: trans("button.suffixIcon"),
81-
})}
82140
{formatPropertyView({ children, placeholder: TIME_FORMAT })}
83141
</>
84142
))

0 commit comments

Comments
 (0)
Failed to load comments.