blob: 9e9ed460a2050ea84f18df6fb9dd01a0d71ccd1c [file] [log] [blame]
import React from 'react';
import PropTypes from 'prop-types';
import {
Button, ButtonGroup, FormControl, InputGroup,
Label, OverlayTrigger, Popover, Glyphicon,
} from 'react-bootstrap';
import Select from 'react-select';
import Datetime from 'react-datetime';
import 'react-datetime/css/react-datetime.css';
import moment from 'moment';
import ControlHeader from '../ControlHeader';
import PopoverSection from '../../../components/PopoverSection';
const RELATIVE_TIME_OPTIONS = ['ago', 'from now'];
const TIME_GRAIN_OPTIONS = ['seconds', 'minutes', 'days', 'weeks', 'months', 'years'];
const propTypes = {
animation: PropTypes.bool,
name: PropTypes.string.isRequired,
label: PropTypes.string,
description: PropTypes.string,
onChange: PropTypes.func,
value: PropTypes.string,
height: PropTypes.number,
};
const defaultProps = {
animation: true,
onChange: () => {},
value: '',
};
export default class DateFilterControl extends React.Component {
constructor(props) {
super(props);
const value = props.value || '';
this.state = {
num: '7',
grain: 'days',
rel: 'ago',
dttm: '',
type: 'free',
free: '',
};
const words = value.split(' ');
if (words.length >= 3 && RELATIVE_TIME_OPTIONS.indexOf(words[2]) >= 0) {
this.state.num = words[0];
this.state.grain = words[1];
this.state.rel = words[2];
this.state.type = 'rel';
} else if (moment(value).isValid()) {
this.state.dttm = value;
this.state.type = 'fix';
} else {
this.state.free = value;
this.state.type = 'free';
}
}
onControlChange(target, opt) {
this.setState({ [target]: opt.value }, this.onChange);
}
onNumberChange(event) {
this.setState({ num: event.target.value }, this.onChange);
}
onChange() {
let val;
if (this.state.type === 'rel') {
val = `${this.state.num} ${this.state.grain} ${this.state.rel}`;
} else if (this.state.type === 'fix') {
val = this.state.dttm;
} else if (this.state.type === 'free') {
val = this.state.free;
}
this.props.onChange(val);
}
onFreeChange(event) {
this.setState({ free: event.target.value }, this.onChange);
}
setType(type) {
this.setState({ type }, this.onChange);
}
setValue(val) {
this.setState({ type: 'free', free: val }, this.onChange);
this.close();
}
setDatetime(dttm) {
this.setState({ dttm: dttm.format().substring(0, 19) }, this.onChange);
}
close() {
this.refs.trigger.hide();
}
renderPopover() {
return (
<Popover id="filter-popover">
<div style={{ width: '240px' }}>
<PopoverSection
title="Fixed"
isSelected={this.state.type === 'fix'}
onSelect={this.setType.bind(this, 'fix')}
>
<InputGroup bsSize="small">
<InputGroup.Addon>
<Glyphicon glyph="calendar" />
</InputGroup.Addon>
<Datetime
inputProps={{ className: 'form-control input-sm' }}
dateFormat="YYYY-MM-DD"
defaultValue={this.state.dttm}
onFocus={this.setType.bind(this, 'fix')}
onChange={this.setDatetime.bind(this)}
timeFormat="h:mm:ss"
/>
</InputGroup>
</PopoverSection>
<PopoverSection
title="Relative"
isSelected={this.state.type === 'rel'}
onSelect={this.setType.bind(this, 'rel')}
>
<div className="clearfix">
<div style={{ width: '50px' }} className="input-inline">
<FormControl
onFocus={this.setType.bind(this, 'rel')}
value={this.state.num}
onChange={this.onNumberChange.bind(this)}
bsSize="small"
/>
</div>
<div style={{ width: '95px' }} className="input-inline">
<Select
onFocus={this.setType.bind(this, 'rel')}
value={this.state.grain}
clearable={false}
options={TIME_GRAIN_OPTIONS.map(s => ({ label: s, value: s }))}
onChange={this.onControlChange.bind(this, 'grain')}
/>
</div>
<div style={{ width: '95px' }} className="input-inline">
<Select
value={this.state.rel}
onFocus={this.setType.bind(this, 'rel')}
clearable={false}
options={RELATIVE_TIME_OPTIONS.map(s => ({ label: s, value: s }))}
onChange={this.onControlChange.bind(this, 'rel')}
/>
</div>
</div>
</PopoverSection>
<PopoverSection
title="Free form"
isSelected={this.state.type === 'free'}
onSelect={this.setType.bind(this, 'free')}
info={
'Superset supports smart date parsing. Strings like `last sunday` or ' +
'`last october` can be used.'
}
>
<FormControl
onFocus={this.setType.bind(this, 'free')}
value={this.state.free}
onChange={this.onFreeChange.bind(this)}
bsSize="small"
/>
</PopoverSection>
<div className="clearfix">
<Button
bsSize="small"
className="float-left ok"
bsStyle="primary"
onClick={this.close.bind(this)}
>
Ok
</Button>
<ButtonGroup
className="float-right"
>
<Button
bsSize="small"
onClick={this.setValue.bind(this, 'now')}
>
now
</Button>
<Button
bsSize="small"
onClick={this.setValue.bind(this, '')}
>
clear
</Button>
</ButtonGroup>
</div>
</div>
</Popover>
);
}
render() {
const value = this.props.value || '';
return (
<div>
<ControlHeader {...this.props} />
<OverlayTrigger
animation={this.props.animation}
container={document.body}
trigger="click"
rootClose
ref="trigger"
placement="right"
overlay={this.renderPopover()}
>
<Label style={{ cursor: 'pointer' }}>
{value.replace('T00:00:00', '') || '∞'}
</Label>
</OverlayTrigger>
</div>
);
}
}
DateFilterControl.propTypes = propTypes;
DateFilterControl.defaultProps = defaultProps;