wix-style-react
Version:
515 lines (440 loc) • 20.4 kB
JavaScript
import _extends from "@babel/runtime/helpers/extends";
import _classCallCheck from "@babel/runtime/helpers/classCallCheck";
import _createClass from "@babel/runtime/helpers/createClass";
import _assertThisInitialized from "@babel/runtime/helpers/assertThisInitialized";
import _inherits from "@babel/runtime/helpers/inherits";
import _possibleConstructorReturn from "@babel/runtime/helpers/possibleConstructorReturn";
import _getPrototypeOf from "@babel/runtime/helpers/getPrototypeOf";
import _defineProperty from "@babel/runtime/helpers/defineProperty";
function ownKeys(object, enumerableOnly) { var keys = Object.keys(object); if (Object.getOwnPropertySymbols) { var symbols = Object.getOwnPropertySymbols(object); if (enumerableOnly) { symbols = symbols.filter(function (sym) { return Object.getOwnPropertyDescriptor(object, sym).enumerable; }); } keys.push.apply(keys, symbols); } return keys; }
function _objectSpread(target) { for (var i = 1; i < arguments.length; i++) { var source = arguments[i] != null ? arguments[i] : {}; if (i % 2) { ownKeys(Object(source), true).forEach(function (key) { _defineProperty(target, key, source[key]); }); } else if (Object.getOwnPropertyDescriptors) { Object.defineProperties(target, Object.getOwnPropertyDescriptors(source)); } else { ownKeys(Object(source)).forEach(function (key) { Object.defineProperty(target, key, Object.getOwnPropertyDescriptor(source, key)); }); } } return target; }
function _createSuper(Derived) { var hasNativeReflectConstruct = _isNativeReflectConstruct(); return function _createSuperInternal() { var Super = _getPrototypeOf(Derived), result; if (hasNativeReflectConstruct) { var NewTarget = _getPrototypeOf(this).constructor; result = Reflect.construct(Super, arguments, NewTarget); } else { result = Super.apply(this, arguments); } return _possibleConstructorReturn(this, result); }; }
function _isNativeReflectConstruct() { if (typeof Reflect === "undefined" || !Reflect.construct) return false; if (Reflect.construct.sham) return false; if (typeof Proxy === "function") return true; try { Boolean.prototype.valueOf.call(Reflect.construct(Boolean, [], function () {})); return true; } catch (e) { return false; } }
import { st, classes, cssStates } from './BaseCalendar.st.css';
import React from 'react';
import PropTypes from 'prop-types';
import DayPicker from 'react-day-picker';
import localeUtilsFactory from '../../common/LocaleUtils/LocaleUtils'; // This file is used for backward compatibility. Since the `locale` prop allows passing `date-fns locale object` as value we still need to support it!
import dateFnsLocaleUtilsFactory from '../../common/LocaleUtils/DateFnsLocaleUtils';
import legacyParse from '../../common/LocaleUtils/legacyParse';
import { WixStyleReactEnvironmentContext } from '../../WixStyleReactEnvironmentProvider/context';
import { supportedWixlocales } from 'wix-design-systems-locale-utils';
var BaseCalendar = /*#__PURE__*/function (_React$PureComponent) {
_inherits(BaseCalendar, _React$PureComponent);
var _super = _createSuper(BaseCalendar);
function BaseCalendar() {
var _this;
_classCallCheck(this, BaseCalendar);
for (var _len = arguments.length, args = new Array(_len), _key = 0; _key < _len; _key++) {
args[_key] = arguments[_key];
}
_this = _super.call.apply(_super, [this].concat(args));
_defineProperty(_assertThisInitialized(_this), "_renderDay", function (day, modifiers) {
var dateIndication = _this.props.dateIndication;
var isOutsideDay = !!modifiers[cssStates({
outside: true
})];
var isSelectedDay = !!modifiers[cssStates({
selected: true
})];
var dateIndicationNode = dateIndication && dateIndication({
date: day,
isSelected: isSelectedDay
});
var shouldHasIndication = dateIndicationNode && !isOutsideDay;
return /*#__PURE__*/React.createElement("div", {
className: st(classes.dayWrapper, {
hasIndication: shouldHasIndication
}),
"data-date": "".concat(day.getFullYear(), "-").concat(day.getMonth(), "-").concat(day.getDate()),
"data-outsideday": isOutsideDay
}, /*#__PURE__*/React.createElement("div", {
className: classes.dayText
}, day.getDate()), shouldHasIndication ? /*#__PURE__*/React.createElement("div", {
className: classes.dayIndicationContainer
}, dateIndicationNode) : null);
});
_defineProperty(_assertThisInitialized(_this), "_handleDayClick", function (value) {
var modifiers = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {};
var event = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : null;
_this._preventActionEventDefault(event);
var propsValue = _this.props.value || {};
var _this$props = _this.props,
onChange = _this$props.onChange,
shouldCloseOnSelect = _this$props.shouldCloseOnSelect;
if (_this.props.selectionMode === 'range') {
if (!propsValue.from && !propsValue.to || propsValue.from && propsValue.to) {
onChange({
from: value
}, modifiers);
} else {
var anchor = propsValue.from || propsValue.to;
var newVal = anchor < value ? {
from: anchor,
to: value
} : {
from: value,
to: anchor
};
onChange(newVal, modifiers);
shouldCloseOnSelect && _this.props.onClose(event);
}
} else {
onChange(value, modifiers);
shouldCloseOnSelect && _this.props.onClose(event);
}
});
_defineProperty(_assertThisInitialized(_this), "_preventActionEventDefault", function () {
var event = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : null;
// We should not prevent "TAB"/"ESC" key
if (event && (!event.key || !_this.keyHandlers[event.key])) {
event.preventDefault();
}
});
_defineProperty(_assertThisInitialized(_this), "_createWeekdayElement", function (localeUtils) {
return function (_ref) {
var className = _ref.className,
weekday = _ref.weekday;
var weekdayShort = localeUtils.formatWeekdayShort(weekday);
var weekdayLong = localeUtils.formatWeekdayLong(weekday);
return /*#__PURE__*/React.createElement("div", {
className: className,
"aria-label": weekdayLong,
role: "columnheader"
}, /*#__PURE__*/React.createElement("abbr", {
"data-hook": "weekday-day"
}, weekdayShort));
};
});
_defineProperty(_assertThisInitialized(_this), "_createDayPickerProps", function () {
var _objectSpread2;
var _this$props2 = _this.props,
filterDate = _this$props2.filterDate,
excludePastDates = _this$props2.excludePastDates,
numOfMonths = _this$props2.numOfMonths,
firstDayOfWeek = _this$props2.firstDayOfWeek,
rtl = _this$props2.rtl,
today = _this$props2.today,
onDisplayedViewChange = _this$props2.onDisplayedViewChange,
displayedMonth = _this$props2.displayedMonth,
captionElement = _this$props2.captionElement,
allowSelectingOutsideDays = _this$props2.allowSelectingOutsideDays;
var locale = _this._getLocale();
var value = BaseCalendar.parseValue(_this.props.value);
var localeUtils = _this._getLocaleUtilsFactory(locale, firstDayOfWeek);
var _ref2 = value || {},
from = _ref2.from,
to = _ref2.to;
var singleDay = !from && !to && value;
var firstOfMonth = [new Date(displayedMonth.getFullYear(), displayedMonth.getMonth(), 1), new Date(displayedMonth.getFullYear(), displayedMonth.getMonth() + 1, 1)];
var lastOfMonth = [new Date(displayedMonth.getFullYear(), displayedMonth.getMonth() + 1, 0), new Date(displayedMonth.getFullYear(), displayedMonth.getMonth() + 2, 0)];
var selectedDays = _this._getSelectedDays(value);
var weekdayElement = _this._createWeekdayElement(localeUtils);
var modifiers = _objectSpread((_objectSpread2 = {}, _defineProperty(_objectSpread2, cssStates({
start: true
}), from), _defineProperty(_objectSpread2, cssStates({
end: true
}), to), _defineProperty(_objectSpread2, cssStates({
firstOfMonth: true
}), firstOfMonth), _defineProperty(_objectSpread2, cssStates({
lastOfMonth: true
}), lastOfMonth), _defineProperty(_objectSpread2, cssStates({
singleDay: true
}), singleDay), _objectSpread2), _this.props.modifiers);
if (today) {
modifiers[cssStates({
today: true
})] = BaseCalendar.parseValue(today);
} // We must add the dummy state since ReactDayPicker use it as a selector in their code
var outsideCssState = allowSelectingOutsideDays ? cssStates({
dummyOutside: true
}) : cssStates({
outside: true
});
return {
disabledDays: [function (date) {
return !filterDate(new Date(date));
}, excludePastDates ? {
before: new Date()
} : {}],
initialMonth: displayedMonth,
initialYear: displayedMonth,
selectedDays: selectedDays,
month: displayedMonth,
year: displayedMonth,
locale: typeof locale === 'string' ? locale : '',
fixedWeeks: true,
onKeyDown: _this._handleKeyDown,
onDayClick: _this._handleDayClick,
localeUtils: localeUtils,
navbarElement: function navbarElement() {
return null;
},
captionElement: captionElement,
onCaptionClick: _this._preventActionEventDefault,
onDayKeyDown: _this._handleDayKeyDown,
numberOfMonths: numOfMonths,
modifiers: modifiers,
renderDay: _this._renderDay,
dir: rtl ? 'rtl' : 'ltr',
weekdayElement: weekdayElement,
classNames: {
/* The classes: 'DayPicker', 'DayPicker-wrapper', 'DayPicker-Month', 'DayPicker-Day', 'disabled'
are used as selectors for the elements at the drivers and at the e2e tests */
container: st('DayPicker', classes.container),
wrapper: 'DayPicker-wrapper',
interactionDisabled: 'DayPicker--interactionDisabled',
months: st(classes.months, {
twoMonths: numOfMonths > 1
}),
month: st('DayPicker-Month', classes.month),
weekdays: classes.weekdays,
weekdaysRow: classes.weekdaysRow,
weekday: classes.weekday,
body: classes.body,
week: classes.week,
weekNumber: 'DayPicker-WeekNumber',
day: st('DayPicker-Day', classes.day),
// default modifiers
today: cssStates({
today: !today
}),
selected: cssStates({
selected: true
}),
disabled: st('disabled', cssStates({
disabled: true
})),
outside: outsideCssState
},
onMonthChange: onDisplayedViewChange
};
});
_defineProperty(_assertThisInitialized(_this), "_handleKeyDown", function (event) {
var keyHandler = _this.keyHandlers[event.key];
keyHandler && keyHandler(event);
});
_defineProperty(_assertThisInitialized(_this), "keyHandlers", {
// escape
Escape: _this.props.onClose,
// tab
Tab: _this.props.onClose
});
_defineProperty(_assertThisInitialized(_this), "_focusSelectedDay", function () {
if (_this.dayPickerRef) {
var selectedDay = _this.dayPickerRef.dayPicker.querySelector(".".concat(cssStates({
selected: true
})));
if (selectedDay) {
// The 'unfocused' class is used as a selector at the drivers and e2e test
selectedDay.classList.add(cssStates({
unfocused: true
}), 'unfocused');
selectedDay.focus();
}
}
});
_defineProperty(_assertThisInitialized(_this), "_handleDayKeyDown", function (_value, _modifiers) {
var event = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : null;
_this._preventActionEventDefault(event);
var unfocusedDay = _this.dayPickerRef.dayPicker.querySelector(".".concat(cssStates({
unfocused: true
})));
if (unfocusedDay) {
// The 'unfocused' class is used as a selector at the drivers and e2e test
unfocusedDay.classList.remove(cssStates({
unfocused: true
}), 'unfocused');
}
});
_defineProperty(_assertThisInitialized(_this), "_getLocaleUtilsFactory", function (locale, firstDayOfWeek) {
// The `dateFnsLocaleUtilsFactory` is used for backward compatibility.
// In case that the user passes an `date-fns locale object` we need to use our old `LocaleUtilsFactory`.
return typeof locale === 'string' ? localeUtilsFactory(locale, firstDayOfWeek) : dateFnsLocaleUtilsFactory(locale, firstDayOfWeek);
});
return _this;
}
_createClass(BaseCalendar, [{
key: "_getSelectedDays",
value: function _getSelectedDays(value) {
var _ref3 = value || {},
from = _ref3.from,
to = _ref3.to;
if (from && to) {
return {
from: from,
to: to
};
} else if (from) {
return {
after: BaseCalendar.prevDay(from)
};
} else if (to) {
return {
before: BaseCalendar.nextDay(to)
};
} else {
// Single day OR empty value
return value;
}
}
}, {
key: "_getLocale",
value: function _getLocale() {
return this.props.locale || this.context.locale || 'en';
}
}, {
key: "componentDidMount",
value: function componentDidMount() {
this.props.autoFocus && this._focusSelectedDay();
}
}, {
key: "componentDidUpdate",
value: function componentDidUpdate(prevProps) {
if (!prevProps.autoFocus && this.props.autoFocus) {
this._focusSelectedDay();
}
}
}, {
key: "render",
value: function render() {
var _this2 = this;
var _this$props3 = this.props,
dataHook = _this$props3.dataHook,
className = _this$props3.className;
return /*#__PURE__*/React.createElement("div", {
"data-hook": dataHook,
className: st(classes.root, className),
onClick: this._preventActionEventDefault
}, /*#__PURE__*/React.createElement(DayPicker, _extends({
ref: function ref(_ref4) {
return _this2.dayPickerRef = _ref4;
}
}, this._createDayPickerProps())));
}
}]);
return BaseCalendar;
}(React.PureComponent);
_defineProperty(BaseCalendar, "displayName", 'BaseCalendar');
_defineProperty(BaseCalendar, "defaultProps", {
className: '',
filterDate: function filterDate() {
return true;
},
dateIndication: function dateIndication() {
return null;
},
shouldCloseOnSelect: true,
onClose: function onClose() {},
autoFocus: true,
excludePastDates: false,
selectionMode: 'day',
showMonthDropdown: false,
showYearDropdown: false,
numOfMonths: 1,
allowSelectingOutsideDays: false
});
_defineProperty(BaseCalendar, "optionalParse", function (dateOrString) {
return typeof dateOrString === 'string' ? legacyParse(dateOrString) : dateOrString;
});
_defineProperty(BaseCalendar, "parseValue", function (value) {
if (!value) {
return new Date();
}
if (typeof value === 'string') {
return legacyParse(value);
} else if (value instanceof Date) {
return value;
} else {
return {
from: BaseCalendar.optionalParse(value.from),
to: BaseCalendar.optionalParse(value.to)
};
}
});
_defineProperty(BaseCalendar, "nextDay", function (date) {
var day = new Date(date);
day.setDate(day.getDate() + 1);
return day;
});
_defineProperty(BaseCalendar, "prevDay", function (date) {
var day = new Date(date);
day.setDate(day.getDate() - 1);
return day;
});
export { BaseCalendar as default };
BaseCalendar.contextType = WixStyleReactEnvironmentContext;
BaseCalendar.propTypes = {
/** Applies as data-hook HTML attribute that can be used in the tests */
dataHook: PropTypes.string,
/** Focus selected day automatically when component mounts or updates */
autoFocus: PropTypes.bool,
/** Allows to display multiple months at once. Currently it shows 1 or 2 months only. */
numOfMonths: PropTypes.oneOf([1, 2]),
/** First day of the week, allowing only from 0 to 6 (Sunday to Saturday). The default value is 1 which means Monday. */
firstDayOfWeek: PropTypes.oneOf([0, 1, 2, 3, 4, 5, 6]),
/** A single CSS class name to be appended to the root element. */
className: PropTypes.string,
/** Provides a callback function when day in selected in the calendar */
onChange: PropTypes.func.isRequired,
/** Defines a callback function that is called whenever a user presses escape, clicks outside of the element or a date is selected and `shouldCloseOnSelect` is set. Receives an event as a first argument. */
onClose: PropTypes.func,
/** Specify whether past dates should be selectable or not */
excludePastDates: PropTypes.bool,
/**
* ##### Specify selectable dates:
* * `param` {Date} `date` - a date to check
* * `return` {boolean} - true if `date` should be selectable, false otherwise
*/
filterDate: PropTypes.func,
/** Defines the selected date */
value: PropTypes.oneOfType([PropTypes.string, PropTypes.instanceOf(Date), PropTypes.shape({
from: PropTypes.oneOfType([PropTypes.string, PropTypes.instanceOf(Date)]),
to: PropTypes.oneOfType([PropTypes.string, PropTypes.instanceOf(Date)])
})]),
/** Whether the user should be able to select a date range, or just a single day */
selectionMode: PropTypes.oneOf(['day', 'range']),
/** Specify whether the calendar closes on day selection */
shouldCloseOnSelect: PropTypes.bool,
/** Specify date picker instance locale */
locale: PropTypes.oneOfType([PropTypes.oneOf(supportedWixlocales), PropTypes.shape({
code: PropTypes.string,
formatDistance: PropTypes.func,
formatRelative: PropTypes.func,
localize: PropTypes.object,
formatLong: PropTypes.object,
match: PropTypes.object,
options: PropTypes.object
})]),
/** Specify whether RTL mode is enabled or not. When true, the keyboard navigation will be changed means pressing on the right arrow will navigate to the previous day, and pressing on the left arrow will navigate to the next day. */
rtl: PropTypes.bool,
/**
##### Add an indication under a specific date.
Function returns the indication node of a specific date or null if this day doesn't have an indication.
* - `param` {date: Date, isSelected: boolean }
* - `date` - a date
* - `isSelected` - whether this date is the selected date
* - `return` {React.node} - the indication node of a specific date or null if this day doesn't have an indication.
*/
dateIndication: PropTypes.func,
/** Sets today's date. The today indication is added automatically according to the user timezone but in some cases, we need the ability to add the today indication based on the business timezone. */
today: PropTypes.oneOfType([PropTypes.string, PropTypes.instanceOf(Date)]),
/** The current displayed month */
displayedMonth: PropTypes.instanceOf(Date).isRequired,
/**
* ##### A callback function that would be invoked every time that the displayed month / week would be changed.
* ##### This would be passed as a prop (onMonthChange) for the ReactDayPicker component.
* - `month` - The current displayed month
* `return` void
*/
onDisplayedViewChange: PropTypes.func.isRequired,
/** The Calendar head components which includes the navigation arrows, and the captions elements. */
captionElement: PropTypes.node.isRequired,
/** Responsible for adding a new modifier for the day elements. For example: `hidden` for dates that shouldn’t be displayed.
It should be passed as an object according to `ReactDayPicker` API.
https://react-day-picker.js.org/docs/matching-days/
*/
modifiers: PropTypes.object,
/** Allow selecting dates that are outside of the current displayed month. */
allowSelectingOutsideDays: PropTypes.bool
};