2
import React, { Component } from 'react';
3
import { Button, Input, InputGroup, InputGroupAddon } from 'reactstrap';
5
import moment from 'moment-timezone';
7
import 'tempusdominus-core';
8
import 'tempusdominus-bootstrap-4';
9
import 'tempusdominus-bootstrap-4/build/css/tempusdominus-bootstrap-4.min.css';
11
import { dom, library } from '@fortawesome/fontawesome-svg-core';
12
import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
20
} from '@fortawesome/free-solid-svg-icons';
22
library.add(faChevronLeft, faChevronRight, faCalendarCheck, faArrowUp, faArrowDown, faTimes);
23
// Sadly needed to also replace <i> within the date picker, since it's not a React component.
26
interface TimeInputProps {
27
time: number | null; // Timestamp in milliseconds.
28
useLocalTime: boolean;
29
range: number; // Range in seconds.
31
onChangeTime: (time: number | null) => void;
34
class TimeInput extends Component<TimeInputProps> {
35
private timeInputRef = React.createRef<HTMLInputElement>();
36
// eslint-disable-next-line @typescript-eslint/no-explicit-any
37
private $time: any = null;
39
getBaseTime = (): number => {
40
return this.props.time || moment().valueOf();
43
calcShiftRange = (): number => this.props.range / 2;
45
increaseTime = (): void => {
46
const time = this.getBaseTime() + this.calcShiftRange();
47
this.props.onChangeTime(time);
50
decreaseTime = (): void => {
51
const time = this.getBaseTime() - this.calcShiftRange();
52
this.props.onChangeTime(time);
55
clearTime = (): void => {
56
this.props.onChangeTime(null);
59
timezone = (): string => {
60
return this.props.useLocalTime ? moment.tz.guess() : 'UTC';
63
componentDidMount(): void {
64
if (!this.timeInputRef.current) {
67
this.$time = $(this.timeInputRef.current);
69
this.$time.datetimepicker({
71
today: 'fas fa-calendar-check',
79
format: 'YYYY-MM-DD HH:mm:ss',
81
timeZone: this.timezone(),
82
defaultDate: this.props.time,
85
// eslint-disable-next-line @typescript-eslint/no-explicit-any
86
this.$time.on('change.datetimepicker', (e: any) => {
87
// The end time can also be set by dragging a section on the graph,
88
// and that value will have decimal places.
89
if (e.date && e.date.valueOf() !== Math.trunc(this.props.time?.valueOf() || NaN)) {
90
this.props.onChangeTime(e.date.valueOf());
95
componentWillUnmount(): void {
96
this.$time.datetimepicker('destroy');
99
componentDidUpdate(prevProps: TimeInputProps): void {
100
const { time, useLocalTime } = this.props;
101
if (prevProps.time !== time) {
102
this.$time.datetimepicker('date', time ? moment(time) : null);
104
if (prevProps.useLocalTime !== useLocalTime) {
105
this.$time.datetimepicker('options', { timeZone: this.timezone(), defaultDate: null });
109
render(): JSX.Element {
111
<InputGroup className="time-input" size="sm">
112
<InputGroupAddon addonType="prepend">
113
<Button title="Decrease time" onClick={this.decreaseTime}>
114
<FontAwesomeIcon icon={faChevronLeft} fixedWidth />
119
placeholder={this.props.placeholder}
120
innerRef={this.timeInputRef}
121
onFocus={() => this.$time.datetimepicker('show')}
122
onBlur={() => this.$time.datetimepicker('hide')}
123
onKeyDown={(e) => ['Escape', 'Enter'].includes(e.key) && this.$time.datetimepicker('hide')}
126
{/* CAUTION: While the datetimepicker also has an option to show a 'clear' button,
127
that functionality is broken, so we create an external solution instead. */}
128
{this.props.time && (
129
<InputGroupAddon addonType="append">
130
<Button outline className="clear-time-btn" title="Clear time" onClick={this.clearTime}>
131
<FontAwesomeIcon icon={faTimes} fixedWidth />
136
<InputGroupAddon addonType="append">
137
<Button title="Increase time" onClick={this.increaseTime}>
138
<FontAwesomeIcon icon={faChevronRight} fixedWidth />
146
export default TimeInput;