baseui
Version:
A React Component library implementing the Base design language
655 lines (649 loc) • 26.1 kB
JavaScript
"use strict";
Object.defineProperty(exports, "__esModule", {
value: true
});
exports.default = void 0;
var React = _interopRequireWildcard(require("react"));
var _formControl = require("../form-control");
var _locale = require("../locale");
var _select = require("../select");
var _calendarHeader = _interopRequireDefault(require("./calendar-header"));
var _month = _interopRequireDefault(require("./month"));
var _timepicker = _interopRequireDefault(require("../timepicker/timepicker"));
var _styledComponents = require("./styled-components");
var _dateFnsAdapter = _interopRequireDefault(require("./utils/date-fns-adapter"));
var _dateHelpers = _interopRequireDefault(require("./utils/date-helpers"));
var _overrides = require("../helpers/overrides");
var _constants = require("./constants");
var _button = require("../button");
var _buttonDock = require("../button-dock");
function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
function _getRequireWildcardCache(e) { if ("function" != typeof WeakMap) return null; var r = new WeakMap(), t = new WeakMap(); return (_getRequireWildcardCache = function (e) { return e ? t : r; })(e); }
function _interopRequireWildcard(e, r) { if (!r && e && e.__esModule) return e; if (null === e || "object" != typeof e && "function" != typeof e) return { default: e }; var t = _getRequireWildcardCache(r); if (t && t.has(e)) return t.get(e); var n = { __proto__: null }, a = Object.defineProperty && Object.getOwnPropertyDescriptor; for (var u in e) if ("default" !== u && Object.prototype.hasOwnProperty.call(e, u)) { var i = a ? Object.getOwnPropertyDescriptor(e, u) : null; i && (i.get || i.set) ? Object.defineProperty(n, u, i) : n[u] = e[u]; } return n.default = e, t && t.set(e, n), n; }
function _extends() { _extends = Object.assign ? Object.assign.bind() : function (target) { for (var i = 1; i < arguments.length; i++) { var source = arguments[i]; for (var key in source) { if (Object.prototype.hasOwnProperty.call(source, key)) { target[key] = source[key]; } } } return target; }; return _extends.apply(this, arguments); }
function _defineProperty(obj, key, value) { key = _toPropertyKey(key); if (key in obj) { Object.defineProperty(obj, key, { value: value, enumerable: true, configurable: true, writable: true }); } else { obj[key] = value; } return obj; }
function _toPropertyKey(t) { var i = _toPrimitive(t, "string"); return "symbol" == typeof i ? i : String(i); }
function _toPrimitive(t, r) { if ("object" != typeof t || !t) return t; var e = t[Symbol.toPrimitive]; if (void 0 !== e) { var i = e.call(t, r || "default"); if ("object" != typeof i) return i; throw new TypeError("@@toPrimitive must return a primitive value."); } return ("string" === r ? String : Number)(t); } /*
Copyright (c) Uber Technologies, Inc.
This source code is licensed under the MIT license found in the
LICENSE file in the root directory of this source tree.
*/
class Calendar extends React.Component {
constructor(props) {
super(props);
_defineProperty(this, "dateHelpers", void 0);
// @ts-ignore
_defineProperty(this, "calendar", void 0);
_defineProperty(this, "getDateInView", () => {
const {
highlightedDate,
value
} = this.props;
// @ts-ignore
const minDate = this.dateHelpers.getEffectiveMinDate(this.props);
// @ts-ignore
const maxDate = this.dateHelpers.getEffectiveMaxDate(this.props);
const current = this.dateHelpers.date();
const initialDate = this.getSingleDate(value) || highlightedDate;
if (initialDate) {
return initialDate;
} else {
if (minDate && this.dateHelpers.isBefore(current, minDate)) {
return minDate;
} else if (maxDate && this.dateHelpers.isAfter(current, maxDate)) {
return maxDate;
}
}
return current;
});
_defineProperty(this, "handleMonthChange", date => {
this.setHighlightedDate(this.dateHelpers.getStartOfMonth(date));
if (this.props.onMonthChange) {
this.props.onMonthChange({
date
});
}
});
_defineProperty(this, "handleYearChange", date => {
this.setHighlightedDate(date);
if (this.props.onYearChange) {
this.props.onYearChange({
date
});
}
});
_defineProperty(this, "changeMonth", ({
date
}) => {
this.setState({
date: date
}, () => this.handleMonthChange(this.state.date));
});
_defineProperty(this, "changeYear", ({
date
}) => {
this.setState({
date: date
}, () => this.handleYearChange(this.state.date));
});
_defineProperty(this, "renderCalendarHeader", (date = this.state.date, order) => {
return /*#__PURE__*/React.createElement(_calendarHeader.default, _extends({}, this.props, {
key: `month-header-${order}`,
date: date,
order: order,
onMonthChange: this.changeMonth,
onYearChange: this.changeYear
}));
});
_defineProperty(this, "onKeyDown", event => {
switch (event.key) {
case 'ArrowUp':
case 'ArrowDown':
case 'ArrowLeft':
case 'ArrowRight':
case 'Home':
case 'End':
case 'PageUp':
case 'PageDown':
this.handleArrowKey(event.key);
event.preventDefault();
event.stopPropagation();
break;
}
});
_defineProperty(this, "handleArrowKey", key => {
const {
highlightedDate: oldDate
} = this.state;
let highlightedDate = oldDate;
const currentDate = this.dateHelpers.date();
switch (key) {
case 'ArrowLeft':
// adding `new Date()` as the last option to satisfy Flow
highlightedDate = this.dateHelpers.subDays(highlightedDate ? highlightedDate : currentDate, 1);
break;
case 'ArrowRight':
highlightedDate = this.dateHelpers.addDays(
// adding `new Date()` as the last option to satisfy Flow
highlightedDate ? highlightedDate : currentDate, 1);
break;
case 'ArrowUp':
highlightedDate = this.dateHelpers.subWeeks(
// adding `new Date()` as the last option to satisfy Flow
highlightedDate ? highlightedDate : currentDate, 1);
break;
case 'ArrowDown':
highlightedDate = this.dateHelpers.addWeeks(
// adding `new Date()` as the last option to satisfy Flow
highlightedDate ? highlightedDate : currentDate, 1);
break;
case 'Home':
highlightedDate = this.dateHelpers.getStartOfWeek(
// adding `new Date()` as the last option to satisfy Flow
highlightedDate ? highlightedDate : currentDate);
break;
case 'End':
highlightedDate = this.dateHelpers.getEndOfWeek(
// adding `new Date()` as the last option to satisfy Flow
highlightedDate ? highlightedDate : currentDate);
break;
case 'PageUp':
highlightedDate = this.dateHelpers.subMonths(
// adding `new Date()` as the last option to satisfy Flow
highlightedDate ? highlightedDate : currentDate, 1);
break;
case 'PageDown':
highlightedDate = this.dateHelpers.addMonths(
// adding `new Date()` as the last option to satisfy Flow
highlightedDate ? highlightedDate : currentDate, 1);
break;
}
this.setState({
highlightedDate,
date: highlightedDate
});
});
_defineProperty(this, "focusCalendar", () => {
if (!this.state.focused) {
this.setState({
focused: true
});
}
});
_defineProperty(this, "blurCalendar", () => {
if (typeof document !== 'undefined') {
const activeElm = document.activeElement;
if (this.calendar && !this.calendar.contains(activeElm)) {
this.setState({
focused: false
});
}
}
});
_defineProperty(this, "handleTabbing", event => {
if (typeof document !== 'undefined') {
if (event.keyCode === 9) {
const activeElm = document.activeElement;
// need to look for any tabindex >= 0 and ideally for not disabled
// focusable by default elements like input, button, etc.
const focusable = this.state.rootElement ? this.state.rootElement.querySelectorAll('[tabindex="0"]') : null;
const length = focusable ? focusable.length : 0;
if (event.shiftKey) {
if (focusable && activeElm === focusable[0]) {
event.preventDefault();
focusable[length - 1].focus();
}
} else {
if (focusable && activeElm === focusable[length - 1]) {
event.preventDefault();
focusable[0].focus();
}
}
}
}
});
_defineProperty(this, "onDayFocus", data => {
const {
date
} = data;
this.setState({
highlightedDate: date
});
this.focusCalendar();
this.props.onDayFocus && this.props.onDayFocus(data);
});
_defineProperty(this, "onDayMouseOver", data => {
const {
date
} = data;
this.setState({
highlightedDate: date
});
this.props.onDayMouseOver && this.props.onDayMouseOver(data);
});
_defineProperty(this, "onDayMouseLeave", data => {
const {
date
} = data;
const {
value
} = this.props;
const selected = this.getSingleDate(value);
this.setState({
highlightedDate: selected || date
});
this.props.onDayMouseLeave && this.props.onDayMouseLeave(data);
});
/** Responsible for merging time values into date values. Note: the 'Day' component
* determines how the days themselves change when a new day is selected. */
_defineProperty(this, "handleDateChange", data => {
// eslint-disable-next-line @typescript-eslint/no-unused-vars
const {
onChange = params => {}
} = this.props;
let updatedDate = data.date;
// Apply the currently selected time values (saved in state) to the updated date
if (Array.isArray(data.date)) {
// We'll need to update the date in time values of internal state
const newTimeState = [...this.state.time];
const start = data.date[0] ? this.dateHelpers.applyDateToTime(newTimeState[0], data.date[0]) : null;
const end = data.date[1] ? this.dateHelpers.applyDateToTime(newTimeState[1], data.date[1]) : null;
newTimeState[0] = start;
if (end) {
updatedDate = [start, end];
newTimeState[1] = end;
} else {
updatedDate = [start];
}
// Update the date in time values of internal state
this.setState({
time: newTimeState
});
} else if (!Array.isArray(this.props.value) && data.date) {
const newTimeState = this.dateHelpers.applyDateToTime(this.state.time[0], data.date);
updatedDate = newTimeState;
// Update the date in time values of internal state
this.setState({
time: [newTimeState]
});
}
onChange({
date: updatedDate
});
});
_defineProperty(this, "handleTimeChange", (time, index) => {
// eslint-disable-next-line @typescript-eslint/no-unused-vars
const {
onChange = params => {}
} = this.props;
// Save/update the time value in internal state
const newTimeState = [...this.state.time];
newTimeState[index] = this.dateHelpers.applyTimeToDate(newTimeState[index], time);
this.setState({
time: newTimeState
});
// Time change calls calendar's onChange handler
// with the date value set to the date with updated time
if (Array.isArray(this.props.value)) {
const dates = this.props.value.map((date, i) => {
if (date && index === i) {
return this.dateHelpers.applyTimeToDate(date, time);
}
return date;
});
onChange({
date: [dates[0], dates[1]]
});
} else {
const date = this.dateHelpers.applyTimeToDate(this.props.value, time);
onChange({
date
});
}
});
_defineProperty(this, "renderMonths", translations => {
const {
overrides = {},
orientation
} = this.props;
const monthList = [];
const [CalendarContainer, calendarContainerProps] = (0, _overrides.getOverrides)(overrides.CalendarContainer, _styledComponents.StyledCalendarContainer);
const [MonthContainer, monthContainerProps] = (0, _overrides.getOverrides)(overrides.MonthContainer, _styledComponents.StyledMonthContainer);
for (let i = 0; i < (this.props.monthsShown || 1); ++i) {
const monthSubComponents = [];
const monthDate = this.dateHelpers.addMonths(this.state.date, i);
const monthKey = `month-${i}`;
// @ts-ignore
monthSubComponents.push(this.renderCalendarHeader(monthDate, i));
monthSubComponents.push(
/*#__PURE__*/
// @ts-ignore
React.createElement(CalendarContainer, _extends({
key: monthKey
// @ts-ignore
,
ref: calendar => {
this.calendar = calendar;
},
role: "grid",
"aria-roledescription": translations.ariaRoleDescCalMonth,
"aria-multiselectable": this.props.range || null,
onKeyDown: this.onKeyDown
}, calendarContainerProps, {
$density: this.props.density
}), /*#__PURE__*/React.createElement(_month.default, {
adapter: this.props.adapter,
date: monthDate,
dateLabel: this.props.dateLabel,
density: this.props.density,
excludeDates: this.props.excludeDates,
filterDate: this.props.filterDate,
highlightedDate: this.state.highlightedDate,
includeDates: this.props.includeDates,
focusedCalendar: this.state.focused,
range: this.props.range,
locale: this.props.locale,
maxDate: this.props.maxDate,
minDate: this.props.minDate,
month: this.dateHelpers.getMonth(this.state.date),
onDayBlur: this.blurCalendar,
onDayFocus: this.onDayFocus,
onDayClick: this.props.onDayClick,
onDayMouseOver: this.onDayMouseOver,
onDayMouseLeave: this.onDayMouseLeave,
onChange: this.handleDateChange,
overrides: overrides,
value: this.props.value,
peekNextMonth: this.props.peekNextMonth,
fixedHeight: this.props.fixedHeight,
hasLockedBehavior: !!this.props.hasLockedBehavior,
selectedInput: this.props.selectedInput
})));
// @ts-ignore
monthList.push( /*#__PURE__*/React.createElement("div", {
key: `month-component-${i}`
}, monthSubComponents));
}
return /*#__PURE__*/React.createElement(MonthContainer, _extends({
$orientation: orientation
}, monthContainerProps), monthList);
});
_defineProperty(this, "renderTimeSelect", (value, onChange, label) => {
const {
overrides = {}
} = this.props;
const [TimeSelectContainer, timeSelectContainerProps] = (0, _overrides.getOverrides)(overrides.TimeSelectContainer, _styledComponents.StyledSelectorContainer);
const [TimeSelectFormControl, timeSelectFormControlProps] = (0, _overrides.getOverrides)(overrides.TimeSelectFormControl, _formControl.FormControl);
const [TimeSelect, timeSelectProps] = (0, _overrides.getOverrides)(overrides.TimeSelect, _timepicker.default);
return /*#__PURE__*/React.createElement(TimeSelectContainer, timeSelectContainerProps, /*#__PURE__*/React.createElement(TimeSelectFormControl, _extends({
label: label
}, timeSelectFormControlProps), /*#__PURE__*/React.createElement(TimeSelect, _extends({
value: value ? this.dateHelpers.date(value) : value,
onChange: onChange,
nullable: true
}, timeSelectProps))));
});
_defineProperty(this, "renderQuickSelect", () => {
const {
overrides = {}
} = this.props;
const [QuickSelectContainer, quickSelectContainerProps] = (0, _overrides.getOverrides)(overrides.QuickSelectContainer, _styledComponents.StyledSelectorContainer);
const [QuickSelectFormControl, quickSelectFormControlProps] = (0, _overrides.getOverrides)(overrides.QuickSelectFormControl, _formControl.FormControl);
const [QuickSelect, {
overrides: quickSelectOverrides,
...restQuickSelectProps
}] = (0, _overrides.getOverrides)(
//
overrides.QuickSelect, _select.Select);
if (!this.props.quickSelect) {
return null;
}
const NOW = this.dateHelpers.set(this.dateHelpers.date(), {
hours: 12,
minutes: 0,
seconds: 0
});
return /*#__PURE__*/React.createElement(_locale.LocaleContext.Consumer, null, locale => /*#__PURE__*/React.createElement(QuickSelectContainer, quickSelectContainerProps, /*#__PURE__*/React.createElement(QuickSelectFormControl, _extends({
label: locale.datepicker.quickSelectLabel
}, quickSelectFormControlProps), /*#__PURE__*/React.createElement(QuickSelect, _extends({
"aria-label": locale.datepicker.quickSelectAriaLabel,
labelKey: "id"
// @ts-ignore
,
onChange: params => {
if (!params.option) {
this.setState({
quickSelectId: null
});
this.props.onChange && this.props.onChange({
date: []
});
} else {
this.setState({
quickSelectId: params.option.id
});
if (this.props.onChange) {
if (this.props.range) {
this.props.onChange({
date: [params.option.beginDate, params.option.endDate || NOW]
});
} else {
this.props.onChange({
date: params.option.beginDate
});
}
}
}
if (this.props.onQuickSelectChange) {
this.props.onQuickSelectChange(params.option);
}
},
options: this.props.quickSelectOptions || [{
id: locale.datepicker.pastWeek,
beginDate: this.dateHelpers.subWeeks(NOW, 1)
}, {
id: locale.datepicker.pastMonth,
beginDate: this.dateHelpers.subMonths(NOW, 1)
}, {
id: locale.datepicker.pastThreeMonths,
beginDate: this.dateHelpers.subMonths(NOW, 3)
}, {
id: locale.datepicker.pastSixMonths,
beginDate: this.dateHelpers.subMonths(NOW, 6)
}, {
id: locale.datepicker.pastYear,
beginDate: this.dateHelpers.subYears(NOW, 1)
}, {
id: locale.datepicker.pastTwoYears,
beginDate: this.dateHelpers.subYears(NOW, 2)
}],
placeholder: locale.datepicker.quickSelectPlaceholder,
value: this.state.quickSelectId && [{
id: this.state.quickSelectId
}],
overrides: (0, _overrides.mergeOverrides)({
Dropdown: {
style: {
textAlign: 'start'
}
}
}, quickSelectOverrides)
}, restQuickSelectProps)))));
});
_defineProperty(this, "renderActionBar", () => {
const {
overrides = {},
primaryButton,
secondaryButton
} = this.props;
const [ButtonDockComponent, buttonDockProps] = (0, _overrides.getOverrides)(overrides.ButtonDock, _buttonDock.ButtonDock);
const [PrimaryButtonComponent, primaryButtonProps] = (0, _overrides.getOverrides)(overrides.PrimaryButton, _button.Button);
const [SecondaryButtonComponent, secondaryButtonProps] = (0, _overrides.getOverrides)(overrides.SecondaryButton, _button.Button);
const primaryButtonComponent = primaryButton != null ? /*#__PURE__*/React.createElement(PrimaryButtonComponent, _extends({
onClick: () => primaryButton.onClick()
}, primaryButtonProps), primaryButton.label) : null;
const secondaryButtonComponent = secondaryButton != null ? /*#__PURE__*/React.createElement(SecondaryButtonComponent, _extends({
onClick: () => secondaryButton.onClick(),
kind: _button.KIND.tertiary
}, secondaryButtonProps), secondaryButton.label) : null;
if (primaryButtonComponent || secondaryButtonComponent) {
return /*#__PURE__*/React.createElement(ButtonDockComponent, _extends({
primaryAction: primaryButtonComponent,
dismissiveAction: secondaryButtonComponent,
overrides: (0, _overrides.mergeOverrides)({
ActionSubContainer: {
style: {
flexDirection: 'row-reverse'
}
}
}, buttonDockProps.overrides)
}, buttonDockProps));
}
return null;
});
const {
highlightedDate: _highlightedDate,
value: _value,
adapter
} = this.props;
// @ts-ignore
this.dateHelpers = new _dateHelpers.default(adapter);
const dateInView = this.getDateInView();
// @ts-ignore
let _time = [];
if (Array.isArray(_value)) {
// @ts-ignore
_time = [..._value];
} else if (_value) {
// @ts-ignore
_time = [_value];
}
this.state = {
highlightedDate: this.getSingleDate(_value) || (_highlightedDate && this.dateHelpers.isSameMonth(dateInView, _highlightedDate) ? _highlightedDate : this.dateHelpers.date()),
focused: false,
date: dateInView,
quickSelectId: null,
rootElement: null,
// @ts-ignore
time: _time
};
}
componentDidMount() {
if (this.props.autoFocusCalendar) {
this.focusCalendar();
}
}
componentDidUpdate(prevProps) {
if (this.props.highlightedDate && !this.dateHelpers.isSameDay(this.props.highlightedDate, prevProps.highlightedDate)) {
this.setState({
date: this.props.highlightedDate
});
}
if (this.props.autoFocusCalendar && this.props.autoFocusCalendar !== prevProps.autoFocusCalendar) {
this.focusCalendar();
}
if (prevProps.value !== this.props.value) {
const nextDate = this.getDateInView();
if (!this.isInView(nextDate)) {
this.setState({
date: nextDate
});
}
}
}
isInView(date) {
// we calculate the month delta between the date arg and the date in the state.
const currentDate = this.state.date;
// First we get the year delta
const yearDelta = this.dateHelpers.getYear(date) - this.dateHelpers.getYear(currentDate);
// then we convert it to months. Then we simply add the date-without-year month delta back in.
const monthDelta = yearDelta * 12 + this.dateHelpers.getMonth(date) - this.dateHelpers.getMonth(currentDate);
// we just check that the delta is between the range given by "this month" (i.e. 0) and "the last month" (i.e. monthsShown)
return monthDelta >= 0 && monthDelta < (this.props.monthsShown || 1);
}
getSingleDate(value) {
// need to check this.props.range but flow would complain
// at the return value in the else clause
if (Array.isArray(value)) {
return value[0] || null;
}
return value;
}
setHighlightedDate(date) {
const {
value
} = this.props;
const selected = this.getSingleDate(value);
let nextState;
if (selected && this.dateHelpers.isSameMonth(selected, date) && this.dateHelpers.isSameYear(selected, date)) {
nextState = {
highlightedDate: selected
};
} else {
nextState = {
highlightedDate: date
};
}
this.setState(nextState);
}
render() {
const {
overrides = {}
} = this.props;
const [Root, rootProps] = (0, _overrides.getOverrides)(overrides.Root, _styledComponents.StyledRoot);
// @ts-ignore
const [startDate, endDate] = [].concat(this.props.value);
return /*#__PURE__*/React.createElement(_locale.LocaleContext.Consumer, null, locale => /*#__PURE__*/React.createElement(Root, _extends({
$density: this.props.density,
"data-baseweb": "calendar",
role: "dialog",
"aria-roledescription": "date picker",
id: this.props.id
// @ts-ignore
,
ref: root => {
if (root && root instanceof HTMLElement && !this.state.rootElement) {
this.setState({
rootElement: root
});
}
},
"aria-label": locale.datepicker.ariaLabelCalendar,
onKeyDown: this.props.trapTabbing ? this.handleTabbing : null
}, rootProps), this.renderMonths({
ariaRoleDescCalMonth: locale.datepicker.ariaRoleDescriptionCalendarMonth
}), this.props.timeSelectStart && this.renderTimeSelect(startDate,
// @ts-ignore
time => this.handleTimeChange(time, 0), locale.datepicker.timeSelectStartLabel), this.props.timeSelectEnd && this.props.range && this.renderTimeSelect(endDate,
// @ts-ignore
time => this.handleTimeChange(time, 1), locale.datepicker.timeSelectEndLabel), this.renderQuickSelect(), this.renderActionBar()));
}
}
exports.default = Calendar;
_defineProperty(Calendar, "defaultProps", {
autoFocusCalendar: false,
dateLabel: null,
density: _constants.DENSITY.default,
excludeDates: null,
filterDate: null,
highlightedDate: null,
includeDates: null,
range: false,
locale: null,
maxDate: null,
minDate: null,
onDayClick: () => {},
onDayFocus: () => {},
onDayMouseOver: () => {},
onDayMouseLeave: () => {},
onMonthChange: () => {},
onYearChange: () => {},
onChange: () => {},
orientation: _constants.ORIENTATION.horizontal,
overrides: {},
peekNextMonth: false,
// @ts-ignore
adapter: _dateFnsAdapter.default,
value: null,
trapTabbing: false
});