UNPKG

16.8 kBJavaScriptView Raw
1"use strict";
2/*
3 * Copyright 2015 Palantir Technologies, Inc. All rights reserved.
4 *
5 * Licensed under the Apache License, Version 2.0 (the "License");
6 * you may not use this file except in compliance with the License.
7 * You may obtain a copy of the License at
8 *
9 * http://www.apache.org/licenses/LICENSE-2.0
10 *
11 * Unless required by applicable law or agreed to in writing, software
12 * distributed under the License is distributed on an "AS IS" BASIS,
13 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14 * See the License for the specific language governing permissions and
15 * limitations under the License.
16 */
17Object.defineProperty(exports, "__esModule", { value: true });
18exports.DatePicker = void 0;
19var tslib_1 = require("tslib");
20var classnames_1 = tslib_1.__importDefault(require("classnames"));
21var React = tslib_1.__importStar(require("react"));
22var react_day_picker_1 = tslib_1.__importDefault(require("react-day-picker"));
23var core_1 = require("@blueprintjs/core");
24var Classes = tslib_1.__importStar(require("./common/classes"));
25var DateUtils = tslib_1.__importStar(require("./common/dateUtils"));
26var Errors = tslib_1.__importStar(require("./common/errors"));
27var datePickerCaption_1 = require("./datePickerCaption");
28var datePickerCore_1 = require("./datePickerCore");
29var datePickerNavbar_1 = require("./datePickerNavbar");
30var shortcuts_1 = require("./shortcuts");
31var timePicker_1 = require("./timePicker");
32/**
33 * Date picker component.
34 *
35 * @see https://blueprintjs.com/docs/#datetime/datepicker
36 */
37var DatePicker = /** @class */ (function (_super) {
38 tslib_1.__extends(DatePicker, _super);
39 function DatePicker(props, context) {
40 var _this = _super.call(this, props, context) || this;
41 _this.ignoreNextMonthChange = false;
42 _this.shouldHighlightCurrentDay = function (date) {
43 var highlightCurrentDay = _this.props.highlightCurrentDay;
44 return highlightCurrentDay && DateUtils.isToday(date);
45 };
46 _this.getDatePickerModifiers = function () {
47 var modifiers = _this.props.modifiers;
48 return tslib_1.__assign({ isToday: _this.shouldHighlightCurrentDay }, modifiers);
49 };
50 _this.renderDay = function (day) {
51 var date = day.getDate();
52 return React.createElement("div", { className: Classes.DATEPICKER_DAY_WRAPPER }, date);
53 };
54 _this.disabledDays = function (day) { return !DateUtils.isDayInRange(day, [_this.props.minDate, _this.props.maxDate]); };
55 _this.getDisabledDaysModifier = function () {
56 var disabledDays = _this.props.dayPickerProps.disabledDays;
57 return Array.isArray(disabledDays) ? tslib_1.__spreadArray([_this.disabledDays], disabledDays, true) : [_this.disabledDays, disabledDays];
58 };
59 _this.renderCaption = function (props) { return (React.createElement(datePickerCaption_1.DatePickerCaption, tslib_1.__assign({}, props, { maxDate: _this.props.maxDate, minDate: _this.props.minDate, onDateChange: _this.handleMonthChange, reverseMonthAndYearMenus: _this.props.reverseMonthAndYearMenus }))); };
60 _this.renderNavbar = function (props) { return (React.createElement(datePickerNavbar_1.DatePickerNavbar, tslib_1.__assign({}, props, { maxDate: _this.props.maxDate, minDate: _this.props.minDate }))); };
61 _this.handleDayClick = function (day, modifiers, e) {
62 var _a, _b;
63 (_b = (_a = _this.props.dayPickerProps).onDayClick) === null || _b === void 0 ? void 0 : _b.call(_a, day, modifiers, e);
64 if (modifiers.disabled) {
65 return;
66 }
67 _this.updateDay(day);
68 // allow toggling selected date by clicking it again (if prop enabled)
69 var newValue = _this.props.canClearSelection && modifiers.selected ? null : DateUtils.getDateTime(day, _this.state.value);
70 _this.updateValue(newValue, true);
71 };
72 _this.handleShortcutClick = function (shortcut, selectedShortcutIndex) {
73 var _a = _this.props, onShortcutChange = _a.onShortcutChange, currentShortcutIndex = _a.selectedShortcutIndex;
74 var dateRange = shortcut.dateRange, includeTime = shortcut.includeTime;
75 var newDate = dateRange[0];
76 var newValue = includeTime ? newDate : DateUtils.getDateTime(newDate, _this.state.value);
77 _this.updateDay(newDate);
78 _this.updateValue(newValue, true);
79 if (currentShortcutIndex === undefined) {
80 _this.setState({ selectedShortcutIndex: selectedShortcutIndex });
81 }
82 var datePickerShortcut = tslib_1.__assign(tslib_1.__assign({}, shortcut), { date: shortcut.dateRange[0] });
83 onShortcutChange === null || onShortcutChange === void 0 ? void 0 : onShortcutChange(datePickerShortcut, selectedShortcutIndex);
84 };
85 _this.updateDay = function (day) {
86 if (_this.props.value === undefined) {
87 // set now if uncontrolled, otherwise they'll be updated in `componentDidUpdate`
88 _this.setState({
89 displayMonth: day.getMonth(),
90 displayYear: day.getFullYear(),
91 selectedDay: day.getDate(),
92 });
93 }
94 if (_this.state.value != null && _this.state.value.getMonth() !== day.getMonth()) {
95 _this.ignoreNextMonthChange = true;
96 }
97 };
98 _this.handleClearClick = function () { return _this.updateValue(null, true); };
99 _this.handleMonthChange = function (newDate) {
100 var _a, _b;
101 var date = _this.computeValidDateInSpecifiedMonthYear(newDate.getFullYear(), newDate.getMonth());
102 _this.setState({ displayMonth: date.getMonth(), displayYear: date.getFullYear() });
103 if (_this.state.value !== null) {
104 // if handleDayClick just got run (so this flag is set), then the
105 // user selected a date in a new month, so don't invoke onChange a
106 // second time
107 _this.updateValue(date, false, _this.ignoreNextMonthChange);
108 _this.ignoreNextMonthChange = false;
109 }
110 (_b = (_a = _this.props.dayPickerProps).onMonthChange) === null || _b === void 0 ? void 0 : _b.call(_a, date);
111 };
112 _this.handleTodayClick = function () {
113 var value = new Date();
114 var displayMonth = value.getMonth();
115 var displayYear = value.getFullYear();
116 var selectedDay = value.getDate();
117 _this.setState({ displayMonth: displayMonth, displayYear: displayYear, selectedDay: selectedDay });
118 _this.updateValue(value, true);
119 };
120 _this.handleTimeChange = function (time) {
121 var _a, _b;
122 (_b = (_a = _this.props.timePickerProps) === null || _a === void 0 ? void 0 : _a.onChange) === null || _b === void 0 ? void 0 : _b.call(_a, time);
123 var value = _this.state.value;
124 var newValue = DateUtils.getDateTime(value != null ? value : new Date(), time);
125 _this.updateValue(newValue, true);
126 };
127 var value = getInitialValue(props);
128 var initialMonth = getInitialMonth(props, value);
129 _this.state = {
130 displayMonth: initialMonth.getMonth(),
131 displayYear: initialMonth.getFullYear(),
132 selectedDay: value == null ? null : value.getDate(),
133 selectedShortcutIndex: _this.props.selectedShortcutIndex !== undefined ? _this.props.selectedShortcutIndex : -1,
134 value: value,
135 };
136 return _this;
137 }
138 DatePicker.prototype.render = function () {
139 var _a;
140 var _b = this.props, className = _b.className, dayPickerProps = _b.dayPickerProps, footerElement = _b.footerElement, locale = _b.locale, localeUtils = _b.localeUtils, maxDate = _b.maxDate, minDate = _b.minDate, showActionsBar = _b.showActionsBar;
141 var _c = this.state, displayMonth = _c.displayMonth, displayYear = _c.displayYear;
142 return (React.createElement("div", { className: (0, classnames_1.default)(Classes.DATEPICKER, className) },
143 this.maybeRenderShortcuts(),
144 React.createElement("div", { className: Classes.DATEPICKER_CONTENT },
145 React.createElement(react_day_picker_1.default, tslib_1.__assign({ showOutsideDays: true, locale: locale, localeUtils: localeUtils, modifiers: this.getDatePickerModifiers() }, dayPickerProps, { canChangeMonth: true, captionElement: this.renderCaption, navbarElement: this.renderNavbar, disabledDays: this.getDisabledDaysModifier(), fromMonth: minDate, month: new Date(displayYear, displayMonth), onDayClick: this.handleDayClick, onMonthChange: this.handleMonthChange, selectedDays: this.state.value, toMonth: maxDate, renderDay: (_a = dayPickerProps === null || dayPickerProps === void 0 ? void 0 : dayPickerProps.renderDay) !== null && _a !== void 0 ? _a : this.renderDay })),
146 this.maybeRenderTimePicker(),
147 showActionsBar && this.renderOptionsBar(),
148 footerElement)));
149 };
150 DatePicker.prototype.componentDidUpdate = function (prevProps, prevState) {
151 _super.prototype.componentDidUpdate.call(this, prevProps, prevState);
152 var value = this.props.value;
153 if (value === prevProps.value) {
154 // no action needed
155 return;
156 }
157 else if (value == null) {
158 // clear the value
159 this.setState({ value: value });
160 }
161 else {
162 this.setState({
163 displayMonth: value.getMonth(),
164 displayYear: value.getFullYear(),
165 selectedDay: value.getDate(),
166 value: value,
167 });
168 }
169 if (this.props.selectedShortcutIndex !== prevProps.selectedShortcutIndex) {
170 this.setState({ selectedShortcutIndex: this.props.selectedShortcutIndex });
171 }
172 };
173 DatePicker.prototype.validateProps = function (props) {
174 var defaultValue = props.defaultValue, initialMonth = props.initialMonth, maxDate = props.maxDate, minDate = props.minDate, value = props.value;
175 if (defaultValue != null && !DateUtils.isDayInRange(defaultValue, [minDate, maxDate])) {
176 console.error(Errors.DATEPICKER_DEFAULT_VALUE_INVALID);
177 }
178 if (initialMonth != null && !DateUtils.isMonthInRange(initialMonth, [minDate, maxDate])) {
179 console.error(Errors.DATEPICKER_INITIAL_MONTH_INVALID);
180 }
181 if (maxDate != null && minDate != null && maxDate < minDate && !DateUtils.areSameDay(maxDate, minDate)) {
182 console.error(Errors.DATEPICKER_MAX_DATE_INVALID);
183 }
184 if (value != null && !DateUtils.isDayInRange(value, [minDate, maxDate])) {
185 console.error(Errors.DATEPICKER_VALUE_INVALID);
186 }
187 };
188 DatePicker.prototype.renderOptionsBar = function () {
189 var _a = this.props, clearButtonText = _a.clearButtonText, todayButtonText = _a.todayButtonText, minDate = _a.minDate, maxDate = _a.maxDate, canClearSelection = _a.canClearSelection;
190 var todayEnabled = isTodayEnabled(minDate, maxDate);
191 return [
192 React.createElement(core_1.Divider, { key: "div" }),
193 React.createElement("div", { className: Classes.DATEPICKER_FOOTER, key: "footer" },
194 React.createElement(core_1.Button, { minimal: true, disabled: !todayEnabled, onClick: this.handleTodayClick, text: todayButtonText }),
195 React.createElement(core_1.Button, { disabled: !canClearSelection, minimal: true, onClick: this.handleClearClick, text: clearButtonText })),
196 ];
197 };
198 DatePicker.prototype.maybeRenderTimePicker = function () {
199 var _a = this.props, timePrecision = _a.timePrecision, timePickerProps = _a.timePickerProps, minDate = _a.minDate, maxDate = _a.maxDate;
200 if (timePrecision == null && timePickerProps === undefined) {
201 return null;
202 }
203 var applyMin = DateUtils.areSameDay(this.state.value, minDate);
204 var applyMax = DateUtils.areSameDay(this.state.value, maxDate);
205 return (React.createElement("div", { className: Classes.DATEPICKER_TIMEPICKER_WRAPPER },
206 React.createElement(timePicker_1.TimePicker, tslib_1.__assign({ precision: timePrecision, minTime: applyMin ? minDate : undefined, maxTime: applyMax ? maxDate : undefined }, timePickerProps, { onChange: this.handleTimeChange, value: this.state.value }))));
207 };
208 DatePicker.prototype.maybeRenderShortcuts = function () {
209 var shortcuts = this.props.shortcuts;
210 if (shortcuts == null || shortcuts === false) {
211 return null;
212 }
213 var selectedShortcutIndex = this.state.selectedShortcutIndex;
214 var _a = this.props, maxDate = _a.maxDate, minDate = _a.minDate, timePrecision = _a.timePrecision;
215 // Reuse the existing date range shortcuts and only care about start date
216 var dateRangeShortcuts = shortcuts === true
217 ? true
218 : shortcuts.map(function (shortcut) { return (tslib_1.__assign(tslib_1.__assign({}, shortcut), { dateRange: [shortcut.date, undefined] })); });
219 return [
220 React.createElement(shortcuts_1.Shortcuts, tslib_1.__assign({ key: "shortcuts" }, {
221 allowSingleDayRange: true,
222 maxDate: maxDate,
223 minDate: minDate,
224 selectedShortcutIndex: selectedShortcutIndex,
225 shortcuts: dateRangeShortcuts,
226 timePrecision: timePrecision,
227 }, { onShortcutClick: this.handleShortcutClick, useSingleDateShortcuts: true })),
228 React.createElement(core_1.Divider, { key: "div" }),
229 ];
230 };
231 DatePicker.prototype.computeValidDateInSpecifiedMonthYear = function (displayYear, displayMonth) {
232 var _a = this.props, minDate = _a.minDate, maxDate = _a.maxDate;
233 var selectedDay = this.state.selectedDay;
234 // month is 0-based, date is 1-based. date 0 is last day of previous month.
235 var maxDaysInMonth = new Date(displayYear, displayMonth + 1, 0).getDate();
236 var displayDate = selectedDay == null ? 1 : Math.min(selectedDay, maxDaysInMonth);
237 // 12:00 matches the underlying react-day-picker timestamp behavior
238 var value = DateUtils.getDateTime(new Date(displayYear, displayMonth, displayDate, 12), this.state.value);
239 // clamp between min and max dates
240 if (value < minDate) {
241 return minDate;
242 }
243 else if (value > maxDate) {
244 return maxDate;
245 }
246 return value;
247 };
248 /**
249 * Update `value` by invoking `onChange` (always) and setting state (if uncontrolled).
250 */
251 DatePicker.prototype.updateValue = function (value, isUserChange, skipOnChange) {
252 var _a, _b;
253 if (skipOnChange === void 0) { skipOnChange = false; }
254 if (!skipOnChange) {
255 (_b = (_a = this.props).onChange) === null || _b === void 0 ? void 0 : _b.call(_a, value, isUserChange);
256 }
257 if (this.props.value === undefined) {
258 this.setState({ value: value });
259 }
260 };
261 DatePicker.defaultProps = {
262 canClearSelection: true,
263 clearButtonText: "Clear",
264 dayPickerProps: {},
265 highlightCurrentDay: false,
266 maxDate: (0, datePickerCore_1.getDefaultMaxDate)(),
267 minDate: (0, datePickerCore_1.getDefaultMinDate)(),
268 reverseMonthAndYearMenus: false,
269 shortcuts: false,
270 showActionsBar: false,
271 todayButtonText: "Today",
272 };
273 DatePicker.displayName = "".concat(core_1.DISPLAYNAME_PREFIX, ".DatePicker");
274 return DatePicker;
275}(core_1.AbstractPureComponent2));
276exports.DatePicker = DatePicker;
277function getInitialValue(props) {
278 // !== because `null` is a valid value (no date)
279 if (props.value !== undefined) {
280 return props.value;
281 }
282 if (props.defaultValue !== undefined) {
283 return props.defaultValue;
284 }
285 return null;
286}
287function getInitialMonth(props, value) {
288 var today = new Date();
289 // != because we must have a real `Date` to begin the calendar on.
290 if (props.initialMonth != null) {
291 return props.initialMonth;
292 }
293 else if (value != null) {
294 return value;
295 }
296 else if (DateUtils.isDayInRange(today, [props.minDate, props.maxDate])) {
297 return today;
298 }
299 else {
300 return DateUtils.getDateBetween([props.minDate, props.maxDate]);
301 }
302}
303function isTodayEnabled(minDate, maxDate) {
304 var today = new Date();
305 return DateUtils.isDayInRange(today, [minDate, maxDate]);
306}
307//# sourceMappingURL=datePicker.js.map
\No newline at end of file