forked from deephaven/web-client-ui
-
Notifications
You must be signed in to change notification settings - Fork 0
/
DateTimeInput.tsx
96 lines (84 loc) · 2.97 KB
/
DateTimeInput.tsx
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
import React, { useCallback, useState } from 'react';
import classNames from 'classnames';
import Log from '@deephaven/log';
import MaskedInput, { SelectionSegment } from './MaskedInput';
import { getNextSegmentValue } from './DateInputUtils';
const log = Log.module('DateTimeInput');
// This could be more restrictive and restrict days to the number of days in the month...
// But then gotta take leap year into account and everything.
const DATE_PATTERN = '[12][0-9]{3}-(0[1-9]|1[0-2])-(0[1-9]|[1-2][0-9]|3[0-1])';
// Put zero width spaces in the nanosecond part of the date to allow jumping between segments
const TIME_PATTERN =
'([01][0-9]|2[0-3]):[0-5][0-9]:[0-5][0-9]\\.[0-9]{3}\u200B[0-9]{3}\u200B[0-9]{3}';
const FULL_DATE_PATTERN = `${DATE_PATTERN} ${TIME_PATTERN}`;
const DEFAULT_VALUE_STRING = '2022-01-01 00:00:00.000000000';
const FULL_DATE_FORMAT = 'YYYY-MM-DD HH:MM:SS.SSSSSSSSS';
type DateTimeInputProps = {
className?: string;
onChange?(value?: string): void;
defaultValue?: string;
onFocus?(): void;
onBlur?(): void;
'data-testid'?: string;
};
export function addSeparators(value: string): string {
const dateTimeMillis = value.substring(0, 23);
const micros = value.substring(23, 26);
const nanos = value.substring(26);
return [dateTimeMillis, micros, nanos].filter(v => v !== '').join('\u200B');
}
const removeSeparators = (value: string) => value.replace(/\u200B/g, '');
const EXAMPLES = [addSeparators(DEFAULT_VALUE_STRING)];
const DateTimeInput = React.forwardRef<HTMLInputElement, DateTimeInputProps>(
(props: DateTimeInputProps, ref) => {
const {
className = '',
onChange = () => undefined,
defaultValue = '',
onFocus = () => undefined,
onBlur = () => undefined,
'data-testid': dataTestId,
} = props;
const [value, setValue] = useState(
defaultValue.length > 0 ? addSeparators(defaultValue) : ''
);
const [selection, setSelection] = useState<SelectionSegment>();
const handleChange = useCallback(
(newValue: string): void => {
log.debug('handleChange', newValue);
setValue(newValue);
onChange(removeSeparators(newValue));
},
[onChange]
);
return (
<div className="d-flex flex-row align-items-center">
<MaskedInput
ref={ref}
className={classNames(className)}
example={EXAMPLES}
getNextSegmentValue={getNextSegmentValue}
onChange={handleChange}
onSelect={setSelection}
pattern={FULL_DATE_PATTERN}
placeholder={FULL_DATE_FORMAT}
selection={selection}
value={value}
onFocus={onFocus}
onBlur={onBlur}
data-testid={dataTestId}
/>
</div>
);
}
);
DateTimeInput.displayName = 'DateTimeInput';
DateTimeInput.defaultProps = {
className: '',
onChange: () => undefined,
defaultValue: '',
onFocus: () => undefined,
onBlur: () => undefined,
'data-testid': undefined,
};
export default DateTimeInput;