UNPKG

15.9 kBJavaScriptView Raw
1/*
2 * Copyright 2015 Palantir Technologies, Inc. All rights reserved.
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16import { __assign, __extends } from "tslib";
17import classNames from "classnames";
18import * as React from "react";
19import { Classes as CoreClasses, Utils as CoreUtils, DISPLAYNAME_PREFIX, HTMLSelect, Icon, Intent, Keys, } from "@blueprintjs/core";
20import * as Classes from "./common/classes";
21import * as DateUtils from "./common/dateUtils";
22import { getDefaultMaxTime, getDefaultMinTime, getTimeUnit, getTimeUnitClassName, getTimeUnitMax, getTimeUnitPrintStr, isTimeUnitValid, setTimeUnit, TimeUnit, wrapTimeAtUnit, } from "./common/timeUnit";
23import * as Utils from "./common/utils";
24export var TimePrecision = {
25 MILLISECOND: "millisecond",
26 MINUTE: "minute",
27 SECOND: "second",
28};
29/**
30 * Time picker component.
31 *
32 * @see https://blueprintjs.com/docs/#datetime/timepicker
33 */
34var TimePicker = /** @class */ (function (_super) {
35 __extends(TimePicker, _super);
36 function TimePicker(props, context) {
37 var _a;
38 var _this = _super.call(this, props, context) || this;
39 _this.timeInputIds = (_a = {},
40 _a[TimeUnit.HOUR_24] = CoreUtils.uniqueId(TimeUnit.HOUR_24 + "-input"),
41 _a[TimeUnit.HOUR_12] = CoreUtils.uniqueId(TimeUnit.HOUR_12 + "-input"),
42 _a[TimeUnit.MINUTE] = CoreUtils.uniqueId(TimeUnit.MINUTE + "-input"),
43 _a[TimeUnit.SECOND] = CoreUtils.uniqueId(TimeUnit.SECOND + "-input"),
44 _a[TimeUnit.MS] = CoreUtils.uniqueId(TimeUnit.MS + "-input"),
45 _a);
46 // begin method definitions: event handlers
47 _this.getInputChangeHandler = function (unit) { return function (e) {
48 var text = getStringValueFromInputEvent(e);
49 switch (unit) {
50 case TimeUnit.HOUR_12:
51 case TimeUnit.HOUR_24:
52 _this.setState({ hourText: text });
53 break;
54 case TimeUnit.MINUTE:
55 _this.setState({ minuteText: text });
56 break;
57 case TimeUnit.SECOND:
58 _this.setState({ secondText: text });
59 break;
60 case TimeUnit.MS:
61 _this.setState({ millisecondText: text });
62 break;
63 }
64 }; };
65 _this.getInputBlurHandler = function (unit) { return function (e) {
66 var _a, _b;
67 var text = getStringValueFromInputEvent(e);
68 _this.updateTime(parseInt(text, 10), unit);
69 (_b = (_a = _this.props).onBlur) === null || _b === void 0 ? void 0 : _b.call(_a, e, unit);
70 }; };
71 _this.getInputFocusHandler = function (unit) { return function (e) {
72 var _a, _b;
73 if (_this.props.selectAllOnFocus) {
74 e.currentTarget.select();
75 }
76 (_b = (_a = _this.props).onFocus) === null || _b === void 0 ? void 0 : _b.call(_a, e, unit);
77 }; };
78 _this.getInputKeyDownHandler = function (unit) { return function (e) {
79 var _a;
80 var _b, _c;
81 handleKeyEvent(e, (_a = {},
82 _a[Keys.ARROW_UP] = function () { return _this.incrementTime(unit); },
83 _a[Keys.ARROW_DOWN] = function () { return _this.decrementTime(unit); },
84 _a[Keys.ENTER] = function () {
85 e.currentTarget.blur();
86 },
87 _a));
88 (_c = (_b = _this.props).onKeyDown) === null || _c === void 0 ? void 0 : _c.call(_b, e, unit);
89 }; };
90 _this.getInputKeyUpHandler = function (unit) { return function (e) {
91 var _a, _b;
92 (_b = (_a = _this.props).onKeyUp) === null || _b === void 0 ? void 0 : _b.call(_a, e, unit);
93 }; };
94 _this.handleAmPmChange = function (e) {
95 var isNextPm = e.currentTarget.value === "pm";
96 if (isNextPm !== _this.state.isPm) {
97 var hour_1 = DateUtils.convert24HourMeridiem(_this.state.value.getHours(), isNextPm);
98 _this.setState({ isPm: isNextPm }, function () { return _this.updateTime(hour_1, TimeUnit.HOUR_24); });
99 }
100 };
101 _this.incrementTime = function (unit) { return _this.shiftTime(unit, 1); };
102 _this.decrementTime = function (unit) { return _this.shiftTime(unit, -1); };
103 _this.state = _this.getFullStateFromValue(_this.getInitialValue(), props.useAmPm);
104 return _this;
105 }
106 TimePicker.prototype.render = function () {
107 var _a;
108 var shouldRenderMilliseconds = this.props.precision === TimePrecision.MILLISECOND;
109 var shouldRenderSeconds = shouldRenderMilliseconds || this.props.precision === TimePrecision.SECOND;
110 var hourUnit = this.props.useAmPm ? TimeUnit.HOUR_12 : TimeUnit.HOUR_24;
111 var classes = classNames(Classes.TIMEPICKER, this.props.className, (_a = {},
112 _a[CoreClasses.DISABLED] = this.props.disabled,
113 _a));
114 return (React.createElement("div", { className: classes },
115 React.createElement("div", { className: Classes.TIMEPICKER_ARROW_ROW },
116 this.maybeRenderArrowButton(true, hourUnit),
117 this.maybeRenderArrowButton(true, TimeUnit.MINUTE),
118 shouldRenderSeconds && this.maybeRenderArrowButton(true, TimeUnit.SECOND),
119 shouldRenderMilliseconds && this.maybeRenderArrowButton(true, TimeUnit.MS)),
120 React.createElement("div", { className: Classes.TIMEPICKER_INPUT_ROW },
121 this.renderInput(Classes.TIMEPICKER_HOUR, hourUnit, this.state.hourText),
122 this.renderDivider(),
123 this.renderInput(Classes.TIMEPICKER_MINUTE, TimeUnit.MINUTE, this.state.minuteText),
124 shouldRenderSeconds && this.renderDivider(),
125 shouldRenderSeconds &&
126 this.renderInput(Classes.TIMEPICKER_SECOND, TimeUnit.SECOND, this.state.secondText),
127 shouldRenderMilliseconds && this.renderDivider("."),
128 shouldRenderMilliseconds &&
129 this.renderInput(Classes.TIMEPICKER_MILLISECOND, TimeUnit.MS, this.state.millisecondText)),
130 this.maybeRenderAmPm(),
131 React.createElement("div", { className: Classes.TIMEPICKER_ARROW_ROW },
132 this.maybeRenderArrowButton(false, hourUnit),
133 this.maybeRenderArrowButton(false, TimeUnit.MINUTE),
134 shouldRenderSeconds && this.maybeRenderArrowButton(false, TimeUnit.SECOND),
135 shouldRenderMilliseconds && this.maybeRenderArrowButton(false, TimeUnit.MS))));
136 };
137 TimePicker.prototype.componentDidUpdate = function (prevProps) {
138 var didMinTimeChange = prevProps.minTime !== this.props.minTime;
139 var didMaxTimeChange = prevProps.maxTime !== this.props.maxTime;
140 var didBoundsChange = didMinTimeChange || didMaxTimeChange;
141 var didPropValueChange = prevProps.value !== this.props.value;
142 var shouldStateUpdate = didBoundsChange || didPropValueChange;
143 var value = this.state.value;
144 if (this.props.value == null) {
145 value = this.getInitialValue();
146 }
147 if (didBoundsChange) {
148 value = DateUtils.getTimeInRange(this.state.value, this.props.minTime, this.props.maxTime);
149 }
150 if (this.props.value != null && !DateUtils.areSameTime(this.props.value, prevProps.value)) {
151 value = this.props.value;
152 }
153 if (shouldStateUpdate) {
154 this.setState(this.getFullStateFromValue(value, this.props.useAmPm));
155 }
156 };
157 // begin method definitions: rendering
158 TimePicker.prototype.maybeRenderArrowButton = function (isDirectionUp, timeUnit) {
159 var _this = this;
160 if (!this.props.showArrowButtons) {
161 return null;
162 }
163 var classes = classNames(Classes.TIMEPICKER_ARROW_BUTTON, getTimeUnitClassName(timeUnit));
164 var onClick = function () { return (isDirectionUp ? _this.incrementTime : _this.decrementTime)(timeUnit); };
165 var label = "".concat(isDirectionUp ? "Increase" : "Decrease", " ").concat(getTimeUnitPrintStr(timeUnit));
166 // set tabIndex=-1 to ensure a valid FocusEvent relatedTarget when focused
167 return (React.createElement("span", { "aria-controls": this.timeInputIds[timeUnit], "aria-label": label, tabIndex: -1, className: classes, onClick: onClick },
168 React.createElement(Icon, { icon: isDirectionUp ? "chevron-up" : "chevron-down", title: label })));
169 };
170 TimePicker.prototype.renderDivider = function (text) {
171 if (text === void 0) { text = ":"; }
172 return React.createElement("span", { className: Classes.TIMEPICKER_DIVIDER_TEXT }, text);
173 };
174 TimePicker.prototype.renderInput = function (className, unit, value) {
175 var _a;
176 var valueNumber = parseInt(value, 10);
177 var isValid = isTimeUnitValid(unit, valueNumber);
178 var isHour = unit === TimeUnit.HOUR_12 || unit === TimeUnit.HOUR_24;
179 return (React.createElement("input", { "aria-label": getTimeUnitPrintStr(unit), "aria-valuemin": 0, "aria-valuenow": valueNumber, "aria-valuemax": getTimeUnitMax(unit), className: classNames(Classes.TIMEPICKER_INPUT, (_a = {}, _a[CoreClasses.intentClass(Intent.DANGER)] = !isValid, _a), className), id: this.timeInputIds[unit], onBlur: this.getInputBlurHandler(unit), onChange: this.getInputChangeHandler(unit), onFocus: this.getInputFocusHandler(unit), onKeyDown: this.getInputKeyDownHandler(unit), onKeyUp: this.getInputKeyUpHandler(unit), role: this.props.showArrowButtons ? "spinbutton" : undefined, value: value, disabled: this.props.disabled, autoFocus: isHour && this.props.autoFocus }));
180 };
181 TimePicker.prototype.maybeRenderAmPm = function () {
182 if (!this.props.useAmPm) {
183 return null;
184 }
185 return (React.createElement(HTMLSelect, { className: Classes.TIMEPICKER_AMPM_SELECT, disabled: this.props.disabled, onChange: this.handleAmPmChange, value: this.state.isPm ? "pm" : "am" },
186 React.createElement("option", { value: "am" }, "AM"),
187 React.createElement("option", { value: "pm" }, "PM")));
188 };
189 // begin method definitions: state modification
190 /**
191 * Generates a full ITimePickerState object with all text fields set to formatted strings based on value
192 */
193 TimePicker.prototype.getFullStateFromValue = function (value, useAmPm) {
194 var timeInRange = DateUtils.getTimeInRange(value, this.props.minTime, this.props.maxTime);
195 var hourUnit = useAmPm ? TimeUnit.HOUR_12 : TimeUnit.HOUR_24;
196 /* tslint:disable:object-literal-sort-keys */
197 return {
198 hourText: formatTime(timeInRange.getHours(), hourUnit),
199 minuteText: formatTime(timeInRange.getMinutes(), TimeUnit.MINUTE),
200 secondText: formatTime(timeInRange.getSeconds(), TimeUnit.SECOND),
201 millisecondText: formatTime(timeInRange.getMilliseconds(), TimeUnit.MS),
202 value: timeInRange,
203 isPm: DateUtils.getIsPmFrom24Hour(timeInRange.getHours()),
204 };
205 /* tslint:enable:object-literal-sort-keys */
206 };
207 TimePicker.prototype.shiftTime = function (unit, amount) {
208 if (this.props.disabled) {
209 return;
210 }
211 var newTime = getTimeUnit(unit, this.state.value) + amount;
212 this.updateTime(wrapTimeAtUnit(unit, newTime), unit);
213 };
214 TimePicker.prototype.updateTime = function (time, unit) {
215 var newValue = DateUtils.clone(this.state.value);
216 if (isTimeUnitValid(unit, time)) {
217 setTimeUnit(unit, time, newValue, this.state.isPm);
218 if (DateUtils.isTimeInRange(newValue, this.props.minTime, this.props.maxTime)) {
219 this.updateState({ value: newValue });
220 }
221 else {
222 this.updateState(this.getFullStateFromValue(this.state.value, this.props.useAmPm));
223 }
224 }
225 else {
226 this.updateState(this.getFullStateFromValue(this.state.value, this.props.useAmPm));
227 }
228 };
229 TimePicker.prototype.updateState = function (state) {
230 var _a, _b;
231 var newState = state;
232 var hasNewValue = newState.value != null && !DateUtils.areSameTime(newState.value, this.state.value);
233 if (this.props.value == null) {
234 // component is uncontrolled
235 if (hasNewValue) {
236 newState = this.getFullStateFromValue(newState.value, this.props.useAmPm);
237 }
238 this.setState(newState);
239 }
240 else {
241 // component is controlled, and there's a new value
242 // so set inputs' text based off of _old_ value and later fire onChange with new value
243 if (hasNewValue) {
244 this.setState(this.getFullStateFromValue(this.state.value, this.props.useAmPm));
245 }
246 else {
247 // no new value, this means only text has changed (from user typing)
248 // we want inputs to change, so update state with new text for the inputs
249 // but don't change actual value
250 this.setState(__assign(__assign({}, newState), { value: DateUtils.clone(this.state.value) }));
251 }
252 }
253 if (hasNewValue) {
254 (_b = (_a = this.props).onChange) === null || _b === void 0 ? void 0 : _b.call(_a, newState.value);
255 }
256 };
257 TimePicker.prototype.getInitialValue = function () {
258 var value = this.props.minTime;
259 if (this.props.value != null) {
260 value = this.props.value;
261 }
262 else if (this.props.defaultValue != null) {
263 value = this.props.defaultValue;
264 }
265 return value;
266 };
267 TimePicker.defaultProps = {
268 autoFocus: false,
269 disabled: false,
270 maxTime: getDefaultMaxTime(),
271 minTime: getDefaultMinTime(),
272 precision: TimePrecision.MINUTE,
273 selectAllOnFocus: false,
274 showArrowButtons: false,
275 useAmPm: false,
276 };
277 TimePicker.displayName = "".concat(DISPLAYNAME_PREFIX, ".TimePicker");
278 return TimePicker;
279}(React.Component));
280export { TimePicker };
281function formatTime(time, unit) {
282 switch (unit) {
283 case TimeUnit.HOUR_24:
284 return time.toString();
285 case TimeUnit.HOUR_12:
286 return DateUtils.get12HourFrom24Hour(time).toString();
287 case TimeUnit.MINUTE:
288 case TimeUnit.SECOND:
289 return Utils.padWithZeroes(time.toString(), 2);
290 case TimeUnit.MS:
291 return Utils.padWithZeroes(time.toString(), 3);
292 default:
293 throw Error("Invalid TimeUnit");
294 }
295}
296function getStringValueFromInputEvent(e) {
297 return e.target.value;
298}
299function handleKeyEvent(e, actions, preventDefault) {
300 if (preventDefault === void 0) { preventDefault = true; }
301 for (var _i = 0, _a = Object.keys(actions); _i < _a.length; _i++) {
302 var k = _a[_i];
303 var key = Number(k);
304 // HACKHACK: https://github.com/palantir/blueprint/issues/4165
305 // eslint-disable-next-line deprecation/deprecation
306 if (e.which === key) {
307 if (preventDefault) {
308 e.preventDefault();
309 }
310 actions[key]();
311 }
312 }
313}
314//# sourceMappingURL=timePicker.js.map
\No newline at end of file