Add table time input

Signed-off-by: Julius Volz <julius.volz@gmail.com>
This commit is contained in:
Julius Volz 2019-02-13 01:06:38 +01:00
parent 032bf86877
commit b86b91ef2a
4 changed files with 151 additions and 113 deletions

View file

@ -71,7 +71,7 @@ button.execute-btn {
display: block; display: block;
} }
.graph-controls { .graph-controls, .table-controls {
margin-bottom: 10px; margin-bottom: 10px;
} }
@ -84,10 +84,17 @@ button.execute-btn {
} }
.graph-controls .endtime-input input { .graph-controls .endtime-input input {
width: 160px;
border-right: none; border-right: none;
} }
div.endtime-input {
width: 270px !important;
}
.table-controls input {
text-align: center;
}
.graph-controls input.resolution-input { .graph-controls input.resolution-input {
width: 90px; width: 90px;
} }

View file

@ -8,53 +8,32 @@ import {
Input, Input,
} from 'reactstrap'; } from 'reactstrap';
import moment from 'moment-timezone';
import 'tempusdominus-core'; import 'tempusdominus-core';
import 'tempusdominus-bootstrap-4'; import 'tempusdominus-bootstrap-4';
import '../node_modules/tempusdominus-bootstrap-4/build/css/tempusdominus-bootstrap-4.min.css'; import '../node_modules/tempusdominus-bootstrap-4/build/css/tempusdominus-bootstrap-4.min.css';
import { dom, library } from '@fortawesome/fontawesome-svg-core'; import { library } from '@fortawesome/fontawesome-svg-core';
import { FontAwesomeIcon } from '@fortawesome/react-fontawesome'; import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
import { import {
faChevronLeft,
faChevronRight,
faPlus, faPlus,
faMinus, faMinus,
faChartArea, faChartArea,
faChartLine, faChartLine,
faClock,
faCalendarCheck,
faArrowUp,
faArrowDown,
faTimes,
} from '@fortawesome/free-solid-svg-icons'; } from '@fortawesome/free-solid-svg-icons';
import TimeInput from './TimeInput.js';
library.add( library.add(
faChevronLeft,
faChevronRight,
faPlus, faPlus,
faMinus, faMinus,
faChartArea, faChartArea,
faChartLine, faChartLine,
faClock,
faCalendarCheck,
faArrowUp,
faArrowDown,
faTimes,
); );
// Sadly needed to also replace <i> within the date picker, since it's not a React component.
dom.watch();
class GraphControls extends Component { class GraphControls extends Component {
constructor(props) { constructor(props) {
super(props); super(props);
this.state = {
startDate: Date.now(),
};
this.rangeRef = React.createRef(); this.rangeRef = React.createRef();
this.endTimeRef = React.createRef(); this.endTimeRef = React.createRef();
this.resolutionRef = React.createRef(); this.resolutionRef = React.createRef();
@ -67,12 +46,12 @@ class GraphControls extends Component {
'h': 60 * 60, 'h': 60 * 60,
'm': 60, 'm': 60,
's': 1 's': 1
}; }
rangeSteps = [ rangeSteps = [
'1s', '10s', '1m', '5m', '15m', '30m', '1h', '2h', '6h', '12h', '1d', '2d', '1s', '10s', '1m', '5m', '15m', '30m', '1h', '2h', '6h', '12h', '1d', '2d',
'1w', '2w', '4w', '8w', '1y', '2y' '1w', '2w', '4w', '8w', '1y', '2y'
]; ]
parseRange(rangeText) { parseRange(rangeText) {
var rangeRE = new RegExp('^([0-9]+)([ywdhms]+)$'); var rangeRE = new RegExp('^([0-9]+)([ywdhms]+)$');
@ -107,7 +86,7 @@ class GraphControls extends Component {
this.rangeRef.current.value = rangeText; this.rangeRef.current.value = rangeText;
} }
increaseRange = (event) => { increaseRange = () => {
for (let range of this.rangeSteps) { for (let range of this.rangeSteps) {
let rangeSeconds = this.parseRange(range); let rangeSeconds = this.parseRange(range);
if (this.props.range < rangeSeconds) { if (this.props.range < rangeSeconds) {
@ -118,7 +97,7 @@ class GraphControls extends Component {
} }
} }
decreaseRange = (event) => { decreaseRange = () => {
for (let range of this.rangeSteps.slice().reverse()) { for (let range of this.rangeSteps.slice().reverse()) {
let rangeSeconds = this.parseRange(range); let rangeSeconds = this.parseRange(range);
if (this.props.range > rangeSeconds) { if (this.props.range > rangeSeconds) {
@ -129,60 +108,6 @@ class GraphControls extends Component {
} }
} }
getBaseEndTime = () => {
return this.props.endTime || moment();
}
increaseEndTime = (event) => {
const endTime = moment(this.getBaseEndTime() + this.props.range*1000/2);
this.props.onChangeEndTime(endTime);
this.$endTime.datetimepicker('date', endTime);
}
decreaseEndTime = (event) => {
const endTime = moment(this.getBaseEndTime() - this.props.range*1000/2);
this.props.onChangeEndTime(endTime);
this.$endTime.datetimepicker('date', endTime);
}
clearEndTime = (event) => {
this.props.onChangeEndTime(null);
this.$endTime.datetimepicker('date', null);
}
componentDidMount() {
this.$endTime = window.$(this.endTimeRef.current);
this.$endTime.datetimepicker({
icons: {
today: 'fas fa-calendar-check',
},
buttons: {
//showClear: true,
showClose: true,
showToday: true,
},
sideBySide: true,
format: 'YYYY-MM-DD HH:mm:ss',
locale: 'en',
timeZone: 'UTC',
defaultDate: this.props.endTime,
});
this.$endTime.on('change.datetimepicker', e => {
console.log("CHANGE", e)
if (e.date) {
this.props.onChangeEndTime(e.date);
} else {
this.$endTime.datetimepicker('date', e.target.value);
}
});
}
componentWillUnmount() {
this.$endTime.datetimepicker('destroy');
}
render() { render() {
return ( return (
<Form inline className="graph-controls" onSubmit={e => e.preventDefault()}> <Form inline className="graph-controls" onSubmit={e => e.preventDefault()}>
@ -191,7 +116,6 @@ class GraphControls extends Component {
<Button title="Decrease range" onClick={this.decreaseRange}><FontAwesomeIcon icon="minus" fixedWidth/></Button> <Button title="Decrease range" onClick={this.decreaseRange}><FontAwesomeIcon icon="minus" fixedWidth/></Button>
</InputGroupAddon> </InputGroupAddon>
{/* <Input value={this.state.rangeInput} onChange={(e) => this.changeRangeInput(e.target.value)}/> */}
<Input <Input
defaultValue={this.formatRange(this.props.range)} defaultValue={this.formatRange(this.props.range)}
innerRef={this.rangeRef} innerRef={this.rangeRef}
@ -203,33 +127,7 @@ class GraphControls extends Component {
</InputGroupAddon> </InputGroupAddon>
</InputGroup> </InputGroup>
<InputGroup className="endtime-input" size="sm"> <TimeInput endTime={this.props.endTime} range={this.props.range} onChangeEndTime={this.props.onChangeEndTime} />
<InputGroupAddon addonType="prepend">
<Button title="Decrease end time" onClick={this.decreaseEndTime}><FontAwesomeIcon icon="chevron-left" fixedWidth/></Button>
</InputGroupAddon>
<Input
placeholder="End time"
// value={this.props.endTime ? this.props.endTime : ''}
innerRef={this.endTimeRef}
// onChange={this.props.onChangeEndTime}
onFocus={() => this.$endTime.datetimepicker('show')}
onBlur={() => this.$endTime.datetimepicker('hide')}
onKeyDown={(e) => ['Escape', 'Enter'].includes(e.key) && this.$endTime.datetimepicker('hide')}
/>
{/* CAUTION: While the datetimepicker also has an option to show a 'clear' button,
that functionality is broken, so we create an external solution instead. */}
{this.props.endTime &&
<InputGroupAddon addonType="append">
<Button className="clear-endtime-btn" title="Clear end time" onClick={this.clearEndTime}><FontAwesomeIcon icon="times" fixedWidth/></Button>
</InputGroupAddon>
}
<InputGroupAddon addonType="append">
<Button title="Increase end time" onClick={this.increaseEndTime}><FontAwesomeIcon icon="chevron-right" fixedWidth/></Button>
</InputGroupAddon>
</InputGroup>
<Input <Input
placeholder="Res. (s)" placeholder="Res. (s)"

View file

@ -18,6 +18,7 @@ import ExpressionInput from './ExpressionInput';
import GraphControls from './GraphControls'; import GraphControls from './GraphControls';
import Graph from './Graph'; import Graph from './Graph';
import DataTable from './DataTable'; import DataTable from './DataTable';
import TimeInput from './TimeInput';
class Panel extends Component { class Panel extends Component {
constructor(props) { constructor(props) {
@ -260,7 +261,12 @@ class Panel extends Component {
</TabPane> </TabPane>
<TabPane tabId="table"> <TabPane tabId="table">
{this.state.type === 'table' && {this.state.type === 'table' &&
<>
<div class="table-controls">
<TimeInput endTime={this.state.endTime} range={this.state.range} onChangeEndTime={this.handleChangeEndTime} />
</div>
<DataTable data={this.state.data} /> <DataTable data={this.state.data} />
</>
} }
</TabPane> </TabPane>
</TabContent> </TabContent>

127
src/TimeInput.js Normal file
View file

@ -0,0 +1,127 @@
import React, { Component } from 'react';
import { Button, InputGroup, InputGroupAddon, Input } from 'reactstrap';
import moment from 'moment-timezone';
import 'tempusdominus-core';
import 'tempusdominus-bootstrap-4';
import '../node_modules/tempusdominus-bootstrap-4/build/css/tempusdominus-bootstrap-4.min.css';
import { dom, library } from '@fortawesome/fontawesome-svg-core';
import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
import {
faChevronLeft,
faChevronRight,
faCalendarCheck,
faArrowUp,
faArrowDown,
faTimes,
} from '@fortawesome/free-solid-svg-icons';
library.add(
faChevronLeft,
faChevronRight,
faCalendarCheck,
faArrowUp,
faArrowDown,
faTimes,
);
// Sadly needed to also replace <i> within the date picker, since it's not a React component.
dom.watch();
class TimeInput extends Component {
constructor(props) {
super(props);
this.endTimeRef = React.createRef();
}
getBaseEndTime = () => {
return this.props.endTime || moment();
}
increaseEndTime = (event) => {
const endTime = moment(this.getBaseEndTime() + this.props.range*1000/2);
this.props.onChangeEndTime(endTime);
this.$endTime.datetimepicker('date', endTime);
}
decreaseEndTime = (event) => {
const endTime = moment(this.getBaseEndTime() - this.props.range*1000/2);
this.props.onChangeEndTime(endTime);
this.$endTime.datetimepicker('date', endTime);
}
clearEndTime = (event) => {
this.props.onChangeEndTime(null);
this.$endTime.datetimepicker('date', null);
}
componentDidMount() {
this.$endTime = window.$(this.endTimeRef.current);
this.$endTime.datetimepicker({
icons: {
today: 'fas fa-calendar-check',
},
buttons: {
//showClear: true,
showClose: true,
showToday: true,
},
sideBySide: true,
format: 'YYYY-MM-DD HH:mm:ss',
locale: 'en',
timeZone: 'UTC',
defaultDate: this.props.endTime,
});
this.$endTime.on('change.datetimepicker', e => {
console.log("CHANGE", e)
if (e.date) {
this.props.onChangeEndTime(e.date);
} else {
this.$endTime.datetimepicker('date', e.target.value);
}
});
}
componentWillUnmount() {
this.$endTime.datetimepicker('destroy');
}
render() {
return (
<InputGroup className="endtime-input" size="sm">
<InputGroupAddon addonType="prepend">
<Button title="Decrease end time" onClick={this.decreaseEndTime}><FontAwesomeIcon icon="chevron-left" fixedWidth/></Button>
</InputGroupAddon>
<Input
placeholder="End time"
// value={this.props.endTime ? this.props.endTime : ''}
innerRef={this.endTimeRef}
// onChange={this.props.onChangeEndTime}
onFocus={() => this.$endTime.datetimepicker('show')}
onBlur={() => this.$endTime.datetimepicker('hide')}
onKeyDown={(e) => ['Escape', 'Enter'].includes(e.key) && this.$endTime.datetimepicker('hide')}
/>
{/* CAUTION: While the datetimepicker also has an option to show a 'clear' button,
that functionality is broken, so we create an external solution instead. */}
{this.props.endTime &&
<InputGroupAddon addonType="append">
<Button className="clear-endtime-btn" title="Clear end time" onClick={this.clearEndTime}><FontAwesomeIcon icon="times" fixedWidth/></Button>
</InputGroupAddon>
}
<InputGroupAddon addonType="append">
<Button title="Increase end time" onClick={this.increaseEndTime}><FontAwesomeIcon icon="chevron-right" fixedWidth/></Button>
</InputGroupAddon>
</InputGroup>
);
}
}
export default TimeInput;