diff --git a/frontend/src/components/calendar/calendar.css b/frontend/src/components/calendar/calendar.css index 96e2e0e942b56f451c9e5fb51838a35de57445a6..7f28e1a18d2c193ed40f7d47542149d72e689b07 100644 --- a/frontend/src/components/calendar/calendar.css +++ b/frontend/src/components/calendar/calendar.css @@ -93,3 +93,39 @@ border-bottom-right-radius: 4px; border-right-color: color(var(--purple-color) shade(25%)); } + +.circle-button { + display: block; + font-size: 20px; + color: color(var(--base-grey) shade(30%)); + border: 2px solid color(var(--base-grey) shade(10%)); + border-radius: 99px; + width: 24px; + height: 24px; + background-color: white; + text-align: center; + vertical-align: middle; + line-height: 20px; +} + +.circle-button:hover { + color: var(--purple-color); + border-color: var(--purple-color); +} + +.circle-button--top { + position: absolute; + top: -12px; +} +.circle-button--bottom { + position: absolute; + bottom: -12px; +} +.circle-button--left { + position: absolute; + left: -12px; +} +.circle-button--right { + position: absolute; + right: -12px; +} diff --git a/frontend/src/css/core/inputs.css b/frontend/src/css/core/inputs.css index 78f25c424c342d35711b4a07369c3746700e84f8..8924193477a0f4b88c08acbe8ef79ffa30962c44 100644 --- a/frontend/src/css/core/inputs.css +++ b/frontend/src/css/core/inputs.css @@ -11,6 +11,10 @@ border-radius: var(--input-border-radius); } +.input--small { + padding: 0.3rem 0.4rem; +} + .input--focus, .input:focus { outline: none; diff --git a/frontend/src/query_builder/Calendar.jsx b/frontend/src/query_builder/Calendar.jsx index 0ebc25bcbf0e8d08606f19867fa7b3a67c231a4c..1c8e3f8a0685449918fa1f6e3468e7d48e16ddcf 100644 --- a/frontend/src/query_builder/Calendar.jsx +++ b/frontend/src/query_builder/Calendar.jsx @@ -24,9 +24,27 @@ export default class Calendar extends Component { static propTypes = { selected: PropTypes.object, selectedEnd: PropTypes.object, - onChange: PropTypes.func.isRequired + onChange: PropTypes.func.isRequired, + onAfterClick: PropTypes.func, + onBeforeClick: PropTypes.func, }; + componentWillReceiveProps(nextProps) { + let resetCurrent = false; + if (nextProps.selected && nextProps.selectedEnd) { + resetCurrent = + nextProps.selected.isAfter(this.state.current, "month") && + nextProps.selectedEnd.isBefore(this.state.current, "month"); + } else if (nextProps.selected) { + resetCurrent = + nextProps.selected.isAfter(this.state.current, "month") || + nextProps.selected.isBefore(this.state.current, "month"); + } + if (resetCurrent) { + this.setState({ current: nextProps.selected }); + } + } + onClickDay(date, e) { let { selected, selectedEnd } = this.props; if (!selected || selectedEnd) { @@ -110,7 +128,17 @@ export default class Calendar extends Component { } return ( - <div className="Calendar-weeks">{weeks}</div> + <div className="relative"> + <div className="Calendar-weeks"> + {weeks} + </div> + {this.props.onBeforeClick && + <a className="circle-button circle-button--top circle-button--left" onClick={this.props.onBeforeClick}>«</a> + } + {this.props.onAfterClick && + <a className="circle-button circle-button--bottom circle-button--right" onClick={this.props.onAfterClick}>»</a> + } + </div> ); } render() { diff --git a/frontend/src/query_builder/filters/pickers/SpecificDatePicker.jsx b/frontend/src/query_builder/filters/pickers/SpecificDatePicker.jsx index 96c71ef0730ae4ceafc722430cf3307b4ffa6f2c..1159ad604c14ba3f497302aedf7b5f99e1d2dffa 100644 --- a/frontend/src/query_builder/filters/pickers/SpecificDatePicker.jsx +++ b/frontend/src/query_builder/filters/pickers/SpecificDatePicker.jsx @@ -1,6 +1,9 @@ import React, { Component, PropTypes } from 'react'; import Calendar from '../../Calendar.jsx'; + +import Input from "metabase/components/Input.jsx"; + import { computeFilterTimeRange } from "metabase/lib/query_time"; import _ from "underscore"; @@ -30,11 +33,10 @@ export default class SpecificDatePicker extends Component { onChange(start, end) { let { filter } = this.props; - if (end) { + if (start && end && !moment(start).isSame(end)) { this.props.onFilterChange(["BETWEEN", filter[1], start, end]); } else { - let operator = _.contains(["=", "<", ">"], filter[0]) ? filter[0] : "="; - this.props.onFilterChange([operator, filter[1], start]); + this.props.onFilterChange(["=", filter[1], start || end]); } } @@ -49,22 +51,53 @@ export default class SpecificDatePicker extends Component { initial = start; } - if (start && start.isSame(end, "day")) { + let singleDay = start && start.isSame(end, "day"); + if (singleDay) { end = null; } + let startValue, startPlaceholder, endValue, endPlaceholder; + if (filter[0] === "<") { + startPlaceholder = "∞"; + endValue = filter[2]; + } else if (filter[0] === ">") { + startValue = filter[2]; + endPlaceholder = "∞"; + } else if (filter[0] === "BETWEEN") { + startValue = filter[2]; + endValue = filter[3]; + } else { + startValue = filter[2]; + endValue = filter[2]; + } + return ( <div> - <div className="mx1 mt1"> + <div className="mx2 mt2"> <Calendar initial={initial} selected={start} selectedEnd={end} onChange={this.onChange} + onBeforeClick={singleDay && this.toggleOperator.bind(this, "<")} + onAfterClick={singleDay && this.toggleOperator.bind(this, ">")} /> - <div className={cx("py1", { "disabled": filter[2] == null })}> - <span className={cx("inline-block text-centered text-purple-hover half py1 border-right", { "text-purple": filter[0] === "<" })} onClick={this.toggleOperator.bind(this, "<")}><< All before</span> - <span className={cx("inline-block text-centered text-purple-hover half py1", { "text-purple": filter[0] === ">" })} onClick={this.toggleOperator.bind(this, ">")}>All after >></span> + <div className="py2 text-centered"> + <Input + className="input input--small text-bold text-grey-4" + style={{width: "100px"}} + value={startValue && moment(startValue).format("MM/DD/YYYY")} + placeholder={startPlaceholder} + onBlurChange={(e) => this.onChange(moment(e.target.value).format("YYYY-MM-DD"), singleDay ? null : endValue)} + /> + <span className="px1">–</span> + <Input + className="input input--small text-bold text-grey-4" + style={{width: "100px"}} + value={endValue && moment(endValue).format("MM/DD/YYYY")} + placeholder={endPlaceholder} + onBlurChange={(e) => this.onChange(startValue, moment(e.target.value).format("YYYY-MM-DD"))} + /> </div> </div> </div>