Split out CustomTimeRange date picker into its own component so it can be shared between separate Dropdown and Overlay components.
parent
217695b962
commit
35d75b5d9d
|
@ -0,0 +1,97 @@
|
|||
import React, {PropTypes, Component} from 'react'
|
||||
import rome from 'rome'
|
||||
import moment from 'moment'
|
||||
|
||||
class CustomTimeRange extends Component {
|
||||
constructor(props) {
|
||||
super(props)
|
||||
|
||||
this.handleClick = ::this.handleClick
|
||||
this._formatTimeRange = ::this._formatTimeRange
|
||||
}
|
||||
|
||||
componentDidMount() {
|
||||
const {timeRange} = this.props
|
||||
|
||||
const lower = rome(this.lower, {
|
||||
initialValue: this._formatTimeRange(timeRange.lower),
|
||||
})
|
||||
const upper = rome(this.upper, {
|
||||
initialValue: this._formatTimeRange(timeRange.upper),
|
||||
})
|
||||
|
||||
this.lowerCal = lower
|
||||
this.upperCal = upper
|
||||
}
|
||||
|
||||
// If there is an upper or lower time range set, set the corresponding calendar's value.
|
||||
componentWillReceiveProps(nextProps) {
|
||||
const {lower, upper} = nextProps.timeRange
|
||||
if (lower) {
|
||||
this.lowerCal.setValue(this._formatTimeRange(lower))
|
||||
}
|
||||
|
||||
if (upper) {
|
||||
this.upperCal.setValue(this._formatTimeRange(upper))
|
||||
}
|
||||
}
|
||||
|
||||
render() {
|
||||
return (
|
||||
<div className="custom-time--container">
|
||||
<div className="custom-time--dates">
|
||||
<div className="custom-time--lower" ref={r => (this.lower = r)} />
|
||||
<div className="custom-time--upper" ref={r => (this.upper = r)} />
|
||||
</div>
|
||||
<div
|
||||
className="custom-time--apply btn btn-sm btn-primary"
|
||||
onClick={this.handleClick}
|
||||
>
|
||||
Apply
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
/*
|
||||
* Upper and lower time ranges are passed in with single quotes as part of
|
||||
* the string literal, i.e. "'2015-09-23T18:00:00.000Z'". Remove them
|
||||
* before passing the string to be parsed.
|
||||
*/
|
||||
_formatTimeRange(timeRange) {
|
||||
if (!timeRange) {
|
||||
return ''
|
||||
}
|
||||
|
||||
// If the given time range is relative, create a fixed timestamp based on its value
|
||||
if (timeRange.match(/^now/)) {
|
||||
const match = timeRange.match(/\d+\w/)[0]
|
||||
const duration = match.slice(0, match.length - 1)
|
||||
const unitOfTime = match[match.length - 1]
|
||||
return moment().subtract(duration, unitOfTime)
|
||||
}
|
||||
|
||||
return moment(timeRange.replace(/\'/g, '')).format('YYYY-MM-DD HH:mm')
|
||||
}
|
||||
|
||||
handleClick() {
|
||||
const lower = this.lowerCal.getDate().toISOString()
|
||||
const upper = this.upperCal.getDate().toISOString()
|
||||
|
||||
this.props.onApplyTimeRange({lower, upper})
|
||||
this.props.onClose()
|
||||
}
|
||||
}
|
||||
|
||||
const {func, shape, string} = PropTypes
|
||||
|
||||
CustomTimeRange.propTypes = {
|
||||
onApplyTimeRange: func.isRequired,
|
||||
timeRange: shape({
|
||||
lower: string.isRequired,
|
||||
upper: string.isRequired,
|
||||
}).isRequired,
|
||||
onClose: func.isRequired,
|
||||
}
|
||||
|
||||
export default CustomTimeRange
|
|
@ -1,48 +1,28 @@
|
|||
import React, {PropTypes, Component} from 'react'
|
||||
import rome from 'rome'
|
||||
import moment from 'moment'
|
||||
import classnames from 'classnames'
|
||||
import OnClickOutside from 'react-onclickoutside'
|
||||
|
||||
import CustomTimeRange from 'shared/components/CustomTimeRange'
|
||||
|
||||
class CustomTimeRangeDropdown extends Component {
|
||||
constructor(props) {
|
||||
super(props)
|
||||
|
||||
this.handleClick = ::this.handleClick
|
||||
}
|
||||
|
||||
handleClickOutside() {
|
||||
this.props.onClose()
|
||||
}
|
||||
|
||||
componentDidMount() {
|
||||
const {timeRange} = this.props
|
||||
|
||||
const lower = rome(this.lower, {
|
||||
initialValue: this._formatTimeRange(timeRange.lower),
|
||||
})
|
||||
const upper = rome(this.upper, {
|
||||
initialValue: this._formatTimeRange(timeRange.upper),
|
||||
})
|
||||
|
||||
this.lowerCal = lower
|
||||
this.upperCal = upper
|
||||
}
|
||||
|
||||
// If there is an upper or lower time range set, set the corresponding calendar's value.
|
||||
componentWillReceiveProps(nextProps) {
|
||||
const {lower, upper} = nextProps.timeRange
|
||||
if (lower) {
|
||||
this.lowerCal.setValue(this._formatTimeRange(lower))
|
||||
}
|
||||
|
||||
if (upper) {
|
||||
this.upperCal.setValue(this._formatTimeRange(upper))
|
||||
}
|
||||
}
|
||||
|
||||
render() {
|
||||
const {isVisible, onToggle, timeRange: {upper, lower}} = this.props
|
||||
const {
|
||||
isVisible,
|
||||
onToggle,
|
||||
onClose,
|
||||
timeRange: {upper, lower},
|
||||
timeRange,
|
||||
onApplyTimeRange,
|
||||
} = this.props
|
||||
|
||||
return (
|
||||
<div
|
||||
|
@ -60,48 +40,15 @@ class CustomTimeRangeDropdown extends Component {
|
|||
<span className="caret" />
|
||||
</button>
|
||||
<div className="custom-time--container">
|
||||
<div className="custom-time--dates">
|
||||
<div className="custom-time--lower" ref={r => (this.lower = r)} />
|
||||
<div className="custom-time--upper" ref={r => (this.upper = r)} />
|
||||
</div>
|
||||
<div
|
||||
className="custom-time--apply btn btn-sm btn-primary"
|
||||
onClick={this.handleClick}
|
||||
>
|
||||
Apply
|
||||
</div>
|
||||
<CustomTimeRange
|
||||
onApplyTimeRange={onApplyTimeRange}
|
||||
timeRange={timeRange}
|
||||
onClose={onClose}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
handleClick() {
|
||||
const lower = this.lowerCal.getDate().toISOString()
|
||||
const upper = this.upperCal.getDate().toISOString()
|
||||
|
||||
this.props.onApplyTimeRange({lower, upper})
|
||||
this.props.onClose()
|
||||
}
|
||||
/*
|
||||
* Upper and lower time ranges are passed in with single quotes as part of
|
||||
* the string literal, i.e. "'2015-09-23T18:00:00.000Z'". Remove them
|
||||
* before passing the string to be parsed.
|
||||
*/
|
||||
_formatTimeRange(timeRange) {
|
||||
if (!timeRange) {
|
||||
return ''
|
||||
}
|
||||
|
||||
// If the given time range is relative, create a fixed timestamp based on its value
|
||||
if (timeRange.match(/^now/)) {
|
||||
const match = timeRange.match(/\d+\w/)[0]
|
||||
const duration = match.slice(0, match.length - 1)
|
||||
const unitOfTime = match[match.length - 1]
|
||||
return moment().subtract(duration, unitOfTime)
|
||||
}
|
||||
|
||||
return moment(timeRange.replace(/\'/g, '')).format('YYYY-MM-DD HH:mm')
|
||||
}
|
||||
}
|
||||
|
||||
const {bool, func, shape, string} = PropTypes
|
||||
|
|
|
@ -0,0 +1,46 @@
|
|||
import React, {PropTypes, Component} from 'react'
|
||||
import OnClickOutside from 'react-onclickoutside'
|
||||
|
||||
import CustomTimeRange from 'shared/components/CustomTimeRange'
|
||||
import OverlayTechnologies from 'shared/components/OverlayTechnologies'
|
||||
|
||||
class CustomTimeRangeOverlay extends Component {
|
||||
constructor(props) {
|
||||
super(props)
|
||||
}
|
||||
|
||||
handleClickOutside() {
|
||||
this.props.onClose()
|
||||
}
|
||||
|
||||
render() {
|
||||
const {onClose, timeRange, onApplyTimeRange} = this.props
|
||||
|
||||
return (
|
||||
<OverlayTechnologies>
|
||||
<div className="custom-time--overlay-container">
|
||||
<CustomTimeRange
|
||||
onApplyTimeRange={onApplyTimeRange}
|
||||
timeRange={timeRange}
|
||||
onClose={onClose}
|
||||
/>
|
||||
</div>
|
||||
</OverlayTechnologies>
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
const {bool, func, shape, string} = PropTypes
|
||||
|
||||
CustomTimeRangeOverlay.propTypes = {
|
||||
onApplyTimeRange: func.isRequired,
|
||||
timeRange: shape({
|
||||
lower: string.isRequired,
|
||||
upper: string.isRequired,
|
||||
}).isRequired,
|
||||
isVisible: bool.isRequired,
|
||||
onToggle: func.isRequired,
|
||||
onClose: func.isRequired,
|
||||
}
|
||||
|
||||
export default OnClickOutside(CustomTimeRangeOverlay)
|
|
@ -4,6 +4,7 @@ import moment from 'moment'
|
|||
|
||||
import OnClickOutside from 'shared/components/OnClickOutside'
|
||||
import FancyScrollbar from 'shared/components/FancyScrollbar'
|
||||
import CustomTimeRangeOverlay from 'shared/components/CustomTimeRangeOverlay'
|
||||
|
||||
import timeRanges from 'hson!shared/data/timeRanges.hson'
|
||||
import {DROPDOWN_MENU_MAX_HEIGHT} from 'shared/constants/index'
|
||||
|
@ -14,11 +15,19 @@ class TimeRangeDropdown extends Component {
|
|||
this.state = {
|
||||
autobind: false,
|
||||
isOpen: false,
|
||||
isCustomTimeRangeOpen: false,
|
||||
customTimeRange: {
|
||||
lower: '',
|
||||
upper: '',
|
||||
},
|
||||
}
|
||||
this.findTimeRangeInputValue = ::this.findTimeRangeInputValue
|
||||
this.handleSelection = ::this.handleSelection
|
||||
this.toggleMenu = ::this.toggleMenu
|
||||
this.showCustomTimeRange = ::this.showCustomTimeRange
|
||||
this.handleApplyCustomTimeRange = ::this.handleApplyCustomTimeRange
|
||||
this.handleToggleCustomTimeRange = ::this.handleToggleCustomTimeRange
|
||||
this.handleCloseCustomTimeRange = ::this.handleCloseCustomTimeRange
|
||||
}
|
||||
|
||||
findTimeRangeInputValue({upper, lower}) {
|
||||
|
@ -50,47 +59,72 @@ class TimeRangeDropdown extends Component {
|
|||
this.setState({isOpen: !this.state.isOpen})
|
||||
}
|
||||
|
||||
showCustomTimeRange() {}
|
||||
showCustomTimeRange() {
|
||||
this.setState({isCustomTimeRangeOpen: true})
|
||||
}
|
||||
|
||||
handleApplyCustomTimeRange(timeRange) {
|
||||
this.setState({timeRange})
|
||||
}
|
||||
|
||||
handleToggleCustomTimeRange() {
|
||||
this.setState({isCustomTimeRangeOpen: !this.state.isCustomTimeRangeOpen})
|
||||
}
|
||||
|
||||
handleCloseCustomTimeRange() {
|
||||
this.setState({isCustomTimeRangeOpen: false})
|
||||
}
|
||||
|
||||
render() {
|
||||
const {selected} = this.props
|
||||
const {isOpen} = this.state
|
||||
const {isOpen, customTimeRange, isCustomTimeRangeOpen} = this.state
|
||||
|
||||
return (
|
||||
<div className={classnames('dropdown dropdown-160', {open: isOpen})}>
|
||||
<div
|
||||
className="btn btn-sm btn-default dropdown-toggle"
|
||||
onClick={() => this.toggleMenu()}
|
||||
>
|
||||
<span className="icon clock" />
|
||||
<span className="dropdown-selected">
|
||||
{this.findTimeRangeInputValue(selected)}
|
||||
</span>
|
||||
<span className="caret" />
|
||||
</div>
|
||||
<ul className="dropdown-menu">
|
||||
<FancyScrollbar
|
||||
autoHide={false}
|
||||
autoHeight={true}
|
||||
maxHeight={DROPDOWN_MENU_MAX_HEIGHT}
|
||||
<div>
|
||||
<div className={classnames('dropdown dropdown-160', {open: isOpen})}>
|
||||
<div
|
||||
className="btn btn-sm btn-default dropdown-toggle"
|
||||
onClick={() => this.toggleMenu()}
|
||||
>
|
||||
<li className="dropdown-header">Time Range</li>
|
||||
<li className="custom-timerange">
|
||||
<a href="#" onClick={this.showCustomTimeRange}>
|
||||
Custom Time Range
|
||||
</a>
|
||||
</li>
|
||||
{timeRanges.map(item => {
|
||||
return (
|
||||
<li className="dropdown-item" key={item.menuOption}>
|
||||
<a href="#" onClick={() => this.handleSelection(item)}>
|
||||
{item.menuOption}
|
||||
</a>
|
||||
</li>
|
||||
)
|
||||
})}
|
||||
</FancyScrollbar>
|
||||
</ul>
|
||||
<span className="icon clock" />
|
||||
<span className="dropdown-selected">
|
||||
{this.findTimeRangeInputValue(selected)}
|
||||
</span>
|
||||
<span className="caret" />
|
||||
</div>
|
||||
<ul className="dropdown-menu">
|
||||
<FancyScrollbar
|
||||
autoHide={false}
|
||||
autoHeight={true}
|
||||
maxHeight={DROPDOWN_MENU_MAX_HEIGHT}
|
||||
>
|
||||
<li className="dropdown-header">Time Range</li>
|
||||
<li className="custom-timerange">
|
||||
<a href="#" onClick={this.showCustomTimeRange}>
|
||||
Custom Time Range
|
||||
</a>
|
||||
</li>
|
||||
{timeRanges.map(item => {
|
||||
return (
|
||||
<li className="dropdown-item" key={item.menuOption}>
|
||||
<a href="#" onClick={() => this.handleSelection(item)}>
|
||||
{item.menuOption}
|
||||
</a>
|
||||
</li>
|
||||
)
|
||||
})}
|
||||
</FancyScrollbar>
|
||||
</ul>
|
||||
</div>
|
||||
{isCustomTimeRangeOpen
|
||||
? <CustomTimeRangeOverlay
|
||||
onApplyTimeRange={this.handleApplyCustomTimeRange}
|
||||
timeRange={customTimeRange}
|
||||
isVisible={isCustomTimeRangeOpen}
|
||||
onToggle={this.handleToggleCustomTimeRange}
|
||||
onClose={this.handleCloseCustomTimeRange}
|
||||
/>
|
||||
: null}
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue