import React, { PureComponent } from 'react'; import * as rangeUtil from '@grafana/ui/src/utils/rangeutil'; import { Input, RawTimeRange, TimeRange, TIME_FORMAT } from '@grafana/ui'; import { toUtc, isDateTime, dateTime } from '@grafana/ui/src/utils/moment_wrapper'; interface TimePickerProps { isOpen?: boolean; isUtc?: boolean; range: TimeRange; onChangeTime?: (range: RawTimeRange, scanning?: boolean) => void; } interface TimePickerState { isOpen: boolean; isUtc: boolean; rangeString: string; refreshInterval?: string; initialRange: RawTimeRange; // Input-controlled text, keep these in a shape that is human-editable fromRaw: string; toRaw: string; } const getRaw = (isUtc: boolean, range: any) => { const rawRange = { from: range.raw.from, to: range.raw.to, }; if (isDateTime(rawRange.from)) { if (!isUtc) { rawRange.from = rawRange.from.local(); } rawRange.from = rawRange.from.format(TIME_FORMAT); } if (isDateTime(rawRange.to)) { if (!isUtc) { rawRange.to = rawRange.to.local(); } rawRange.to = rawRange.to.format(TIME_FORMAT); } return rawRange; }; /** * TimePicker with dropdown menu for relative dates. * * Initialize with a range that is either based on relative rawRange.strings, * or on Moment objects. * Internally the component needs to keep a string representation in `fromRaw` * and `toRaw` for the controlled inputs. * When a time is picked, `onChangeTime` is called with the new range that * is again based on relative time strings or Moment objects. */ export default class TimePicker extends PureComponent { dropdownEl: any; constructor(props) { super(props); const { range, isUtc, isOpen } = props; const rawRange = getRaw(props.isUtc, range); this.state = { isOpen: isOpen, isUtc: isUtc, rangeString: rangeUtil.describeTimeRange(range.raw), fromRaw: rawRange.from, toRaw: rawRange.to, initialRange: range.raw, refreshInterval: '', }; } //Temp solution... How do detect if ds supports table format? static getDerivedStateFromProps(props: TimePickerProps, state: TimePickerState) { if ( state.initialRange && state.initialRange.from === props.range.raw.from && state.initialRange.to === props.range.raw.to ) { return state; } const { range } = props; const rawRange = getRaw(props.isUtc, range); return { ...state, fromRaw: rawRange.from, toRaw: rawRange.to, initialRange: range.raw, rangeString: rangeUtil.describeTimeRange(range.raw), }; } move(direction: number, scanning?: boolean): RawTimeRange { const { onChangeTime, range: origRange } = this.props; const range = { from: toUtc(origRange.from), to: toUtc(origRange.to), }; const timespan = (range.to.valueOf() - range.from.valueOf()) / 2; let to, from; if (direction === -1) { to = range.to.valueOf() - timespan; from = range.from.valueOf() - timespan; } else if (direction === 1) { to = range.to.valueOf() + timespan; from = range.from.valueOf() + timespan; } else { to = range.to.valueOf(); from = range.from.valueOf(); } const nextTimeRange = { from: this.props.isUtc ? toUtc(from) : dateTime(from), to: this.props.isUtc ? toUtc(to) : dateTime(to), }; if (onChangeTime) { onChangeTime(nextTimeRange); } return nextTimeRange; } handleChangeFrom = e => { this.setState({ fromRaw: e.target.value, }); }; handleChangeTo = e => { this.setState({ toRaw: e.target.value, }); }; handleClickApply = () => { const { onChangeTime, isUtc } = this.props; let rawRange; this.setState( state => { const { toRaw, fromRaw } = this.state; rawRange = { from: fromRaw, to: toRaw, }; if (rawRange.from.indexOf('now') === -1) { rawRange.from = isUtc ? toUtc(rawRange.from, TIME_FORMAT) : dateTime(rawRange.from, TIME_FORMAT); } if (rawRange.to.indexOf('now') === -1) { rawRange.to = isUtc ? toUtc(rawRange.to, TIME_FORMAT) : dateTime(rawRange.to, TIME_FORMAT); } const rangeString = rangeUtil.describeTimeRange(rawRange); return { isOpen: false, rangeString, }; }, () => { if (onChangeTime) { onChangeTime(rawRange); } } ); }; handleClickLeft = () => this.move(-1); handleClickPicker = () => { this.setState(state => ({ isOpen: !state.isOpen, })); }; handleClickRight = () => this.move(1); handleClickRefresh = () => {}; handleClickRelativeOption = range => { const { onChangeTime } = this.props; const rangeString = rangeUtil.describeTimeRange(range); const rawRange = { from: range.from, to: range.to, }; this.setState( { toRaw: rawRange.to, fromRaw: rawRange.from, isOpen: false, rangeString, }, () => { if (onChangeTime) { onChangeTime(rawRange); } } ); }; getTimeOptions() { return rangeUtil.getRelativeTimesList({}, this.state.rangeString); } dropdownRef = el => { this.dropdownEl = el; }; renderDropdown() { const { fromRaw, isOpen, toRaw } = this.state; if (!isOpen) { return null; } const timeOptions = this.getTimeOptions(); return (
Quick ranges
{Object.keys(timeOptions).map(section => { const group = timeOptions[section]; return ( ); })}
Custom range
); } render() { const { isUtc, rangeString, refreshInterval } = this.state; return (
{this.renderDropdown()}
); } }