1+ import { default as TimePicker } from "antd/es/time-picker" ;
12import {
23 ColumnTypeCompBuilder ,
34 ColumnTypeViewFn ,
@@ -7,63 +8,126 @@ import { StringControl } from "comps/controls/codeControl";
78import { withDefault } from "comps/generators" ;
89import { formatPropertyView } from "comps/utils/propertyUtils" ;
910import { 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
1953const childrenMap = {
2054 text : StringControl ,
2155 format : withDefault ( StringControl , TIME_FORMAT ) ,
2256 inputFormat : withDefault ( StringControl , TIME_FORMAT ) ,
23- prefixIcon : IconControl ,
24- suffixIcon : IconControl ,
2557} ;
2658
2759let 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
32115export 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