1+ import { default as TimePicker } from "antd/es/time-picker" ;
12import {
23ColumnTypeCompBuilder ,
34ColumnTypeViewFn ,
@@ -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 = {
2054text :StringControl ,
2155format :withDefault ( StringControl , TIME_FORMAT ) ,
2256inputFormat :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 ( ) {
33116return new ColumnTypeCompBuilder (
34117childrenMap ,
35118( props , dispatch ) => {
36119inputFormat = props . inputFormat ;
37120const 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 ) ,
59124getBaseValue
60125)
61126. setEditViewFn ( ( props ) => (
62- < DateEdit
127+ < TimeEdit
63128value = { props . value }
64129onChange = { props . onChange }
65130onChangeEnd = { props . onChangeEnd }
66- showTime = { true } // Ensures only time is shown
67131inputFormat = { inputFormat }
68132/>
69133) )
@@ -73,12 +137,6 @@ export const TimeComp = (function () {
73137label :trans ( "table.columnValue" ) ,
74138tooltip :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) )