Skip to content

Commit 36a2550

Browse files
authored
date time inputs improvments (#249)
1 parent 5d61168 commit 36a2550

File tree

1 file changed

+65
-0
lines changed

1 file changed

+65
-0
lines changed

web/src/components/modals/time-range-modal.tsx

Lines changed: 65 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -48,6 +48,67 @@ export const TimeRangeModal: React.FC<TimeRangeModalProps> = ({ id, isModalOpen,
4848
: t('To date must be after From date');
4949
};
5050

51+
//this is a hack to allow user to type into date / time inputs without having to delete previous content
52+
const onInput = (e: React.FormEvent<HTMLDivElement>, type: 'date' | 'time') => {
53+
const input = e.target as HTMLInputElement;
54+
const inputEvent = e.nativeEvent as InputEvent;
55+
56+
//save cursor position
57+
let start = input.selectionStart as number;
58+
let end = input.selectionEnd as number;
59+
60+
//split date / time accordingly
61+
const delimiter = type === 'date' ? '-' : ':';
62+
const valueParts = input.value.split(delimiter);
63+
64+
//hack delimiters only when caracters are added one by one
65+
if (start === end && inputEvent.inputType === 'insertText' && inputEvent.data !== delimiter) {
66+
//new caracter has been added before the delimiter, we need to move it to the next part
67+
switch (type) {
68+
case 'date':
69+
if (start === 5 && valueParts.length > 1) {
70+
valueParts[1] = inputEvent.data + valueParts[1];
71+
start++;
72+
} else if (start === 8 && valueParts.length > 2) {
73+
valueParts[2] = inputEvent.data + valueParts[2];
74+
start++;
75+
}
76+
break;
77+
case 'time':
78+
if (start === 3 && valueParts.length > 1) {
79+
valueParts[1] = inputEvent.data + valueParts[1];
80+
start++;
81+
} else if (start === 6 && valueParts.length > 2) {
82+
valueParts[2] = inputEvent.data + valueParts[2];
83+
start++;
84+
}
85+
break;
86+
}
87+
end = start;
88+
}
89+
90+
let truncatedValue = '';
91+
for (let i = 0; i < 3; i++) {
92+
if (truncatedValue.length) {
93+
truncatedValue += delimiter;
94+
}
95+
96+
const isYear = type === 'date' && i === 0;
97+
if (valueParts.length > i) {
98+
const len = isYear ? 4 : 2;
99+
truncatedValue += valueParts[i].slice(0, len).padEnd(len, '0');
100+
} else {
101+
truncatedValue += isYear ? '0000' : '00';
102+
}
103+
}
104+
105+
//update input value
106+
input.value = truncatedValue;
107+
108+
//restore position
109+
input.setSelectionRange(start, end);
110+
};
111+
51112
const resetInputs = React.useCallback(() => {
52113
let from: Date;
53114
let to: Date;
@@ -156,6 +217,7 @@ export const TimeRangeModal: React.FC<TimeRangeModalProps> = ({ id, isModalOpen,
156217
data-test="from-date-picker"
157218
validators={[date => dateValidator(true, date)]}
158219
onChange={str => setFromDate(str)}
220+
onInput={e => onInput(e, 'date')}
159221
value={fromDate}
160222
/>
161223
</FlexItem>
@@ -166,6 +228,7 @@ export const TimeRangeModal: React.FC<TimeRangeModalProps> = ({ id, isModalOpen,
166228
includeSeconds
167229
placeholder="hh:mm:ss"
168230
onChange={setFromTime}
231+
onInput={e => onInput(e, 'time')}
169232
time={displayedFromTime}
170233
/>
171234
</FlexItem>
@@ -180,6 +243,7 @@ export const TimeRangeModal: React.FC<TimeRangeModalProps> = ({ id, isModalOpen,
180243
validators={[date => dateValidator(false, date)]}
181244
rangeStart={fromDate ? new Date(Date.parse(fromDate)) : undefined}
182245
onChange={str => setToDate(str)}
246+
onInput={e => onInput(e, 'date')}
183247
value={toDate}
184248
/>
185249
</FlexItem>
@@ -190,6 +254,7 @@ export const TimeRangeModal: React.FC<TimeRangeModalProps> = ({ id, isModalOpen,
190254
includeSeconds
191255
placeholder="hh:mm:ss"
192256
onChange={setToTime}
257+
onInput={e => onInput(e, 'time')}
193258
time={displayedToTime}
194259
/>
195260
</FlexItem>

0 commit comments

Comments
 (0)