UNPKG

@fluentui/react-northstar

Version:
427 lines (423 loc) 19.8 kB
"use strict"; var _interopRequireDefault = require("@babel/runtime/helpers/interopRequireDefault"); exports.__esModule = true; exports.datepickerClassName = exports.Datepicker = void 0; var _invoke2 = _interopRequireDefault(require("lodash/invoke")); var _accessibility = require("@fluentui/accessibility"); var _dateTimeUtilities = require("../../utils/date-time-utilities"); var _reactBindings = require("@fluentui/react-bindings"); var _reactIconsNorthstar = require("@fluentui/react-icons-northstar"); var customPropTypes = _interopRequireWildcard(require("@fluentui/react-proptypes")); var _reactComponentRef = require("@fluentui/react-component-ref"); var PropTypes = _interopRequireWildcard(require("prop-types")); var React = _interopRequireWildcard(require("react")); var _utils = require("../../utils"); var _Button = require("../Button/Button"); var _Input = require("../Input/Input"); var _Popup = require("../Popup/Popup"); var _DatepickerCalendar = require("./DatepickerCalendar"); var _DatepickerCalendarCell = require("./DatepickerCalendarCell"); var _DatepickerCalendarCellButton = require("./DatepickerCalendarCellButton"); var _DatepickerCalendarHeader = require("./DatepickerCalendarHeader"); var _DatepickerCalendarHeaderAction = require("./DatepickerCalendarHeaderAction"); var _DatepickerCalendarHeaderCell = require("./DatepickerCalendarHeaderCell"); var _DatepickerCalendarGrid = require("./DatepickerCalendarGrid"); var _DatepickerCalendarGridRow = require("./DatepickerCalendarGridRow"); var _validateDate = require("./validateDate"); function _getRequireWildcardCache(nodeInterop) { if (typeof WeakMap !== "function") return null; var cacheBabelInterop = new WeakMap(); var cacheNodeInterop = new WeakMap(); return (_getRequireWildcardCache = function _getRequireWildcardCache(nodeInterop) { return nodeInterop ? cacheNodeInterop : cacheBabelInterop; })(nodeInterop); } function _interopRequireWildcard(obj, nodeInterop) { if (!nodeInterop && obj && obj.__esModule) { return obj; } if (obj === null || typeof obj !== "object" && typeof obj !== "function") { return { default: obj }; } var cache = _getRequireWildcardCache(nodeInterop); if (cache && cache.has(obj)) { return cache.get(obj); } var newObj = {}; var hasPropertyDescriptor = Object.defineProperty && Object.getOwnPropertyDescriptor; for (var key in obj) { if (key !== "default" && Object.prototype.hasOwnProperty.call(obj, key)) { var desc = hasPropertyDescriptor ? Object.getOwnPropertyDescriptor(obj, key) : null; if (desc && (desc.get || desc.set)) { Object.defineProperty(newObj, key, desc); } else { newObj[key] = obj[key]; } } } newObj.default = obj; if (cache) { cache.set(obj, newObj); } return newObj; } var datepickerClassName = 'ui-datepicker'; exports.datepickerClassName = datepickerClassName; var formatRestrictedInput = function formatRestrictedInput(restrictedOptions, localizationStrings) { var formattedString = ''; if (!!restrictedOptions.minDate && !!restrictedOptions.maxDate) { formattedString = (0, _utils.format)(localizationStrings.inputBoundedFormatString, localizationStrings.formatMonthDayYear(restrictedOptions.minDate, localizationStrings), localizationStrings.formatMonthDayYear(restrictedOptions.maxDate, localizationStrings)); } else if (!!restrictedOptions.minDate) { formattedString = (0, _utils.format)(localizationStrings.inputMinBoundedFormatString, localizationStrings.formatMonthDayYear(restrictedOptions.minDate, localizationStrings)); } else if (!!restrictedOptions.maxDate) { formattedString = (0, _utils.format)(localizationStrings.inputMaxBoundedFormatString, localizationStrings.formatMonthDayYear(restrictedOptions.maxDate, localizationStrings)); } else { formattedString = localizationStrings.inputAriaLabel; } return formattedString; }; /** * A Datepicker is a control which is used to display dates grid and allow user to select them. * * @accessibilityIssues * [NVDA - Aria-selected is not narrated for the gridcell](https://github.com/nvaccess/nvda/issues/11986) */ var Datepicker = /*#__PURE__*/React.forwardRef(function (props, ref) { var _props$today; var context = (0, _reactBindings.useFluentContext)(); var _useTelemetry = (0, _reactBindings.useTelemetry)(Datepicker.displayName, context.telemetry), setStart = _useTelemetry.setStart, setEnd = _useTelemetry.setEnd; setStart(); var _inputRef = React.useRef(); // FIXME: This object is created every render, causing a cascade of useCallback/useEffect re-runs. // Needs to be reworked by someone who understands the intent for when various updates ought to happen. // eslint-disable-next-line react-hooks/exhaustive-deps var dateFormatting = { formatDay: props.formatDay, formatYear: props.formatYear, formatMonthDayYear: props.formatMonthDayYear, formatMonthYear: props.formatMonthYear, parseDate: props.parseDate, months: props.months, shortMonths: props.shortMonths, days: props.days, shortDays: props.shortDays, isRequiredErrorMessage: props.isRequiredErrorMessage, invalidInputErrorMessage: props.invalidInputErrorMessage, isOutOfBoundsErrorMessage: props.isOutOfBoundsErrorMessage, openCalendarTitle: props.openCalendarTitle, inputPlaceholder: props.inputPlaceholder, prevMonthAriaLabel: props.prevMonthAriaLabel, nextMonthAriaLabel: props.nextMonthAriaLabel, prevYearAriaLabel: props.prevYearAriaLabel, nextYearAriaLabel: props.nextYearAriaLabel, prevYearRangeAriaLabel: props.prevYearRangeAriaLabel, nextYearRangeAriaLabel: props.nextYearRangeAriaLabel, monthPickerHeaderAriaLabel: props.monthPickerHeaderAriaLabel, yearPickerHeaderAriaLabel: props.yearPickerHeaderAriaLabel, closeButtonAriaLabel: props.closeButtonAriaLabel, weekNumberFormatString: props.weekNumberFormatString, selectedDateFormatString: props.selectedDateFormatString, todayDateFormatString: props.todayDateFormatString, inputAriaLabel: props.inputAriaLabel, inputBoundedFormatString: props.inputBoundedFormatString, inputMinBoundedFormatString: props.inputMinBoundedFormatString, inputMaxBoundedFormatString: props.inputMaxBoundedFormatString }; var calendar = props.calendar, popup = props.popup, input = props.input, className = props.className, design = props.design, styles = props.styles, variables = props.variables, formatMonthDayYear = props.formatMonthDayYear, allowManualInput = props.allowManualInput, ariaLabelledby = props['aria-labelledby'], ariaInvalid = props['aria-invalid']; var valueFormatter = React.useCallback(function (date) { return date ? formatMonthDayYear(date, { months: dateFormatting.months, shortMonths: dateFormatting.shortMonths, days: dateFormatting.days, shortDays: dateFormatting.shortDays }) : ''; }, [dateFormatting.days, dateFormatting.months, dateFormatting.shortDays, dateFormatting.shortMonths, formatMonthDayYear]); var _useAutoControlled = (0, _reactBindings.useAutoControlled)({ defaultValue: props.defaultCalendarOpenState, value: props.calendarOpenState, initialValue: false }), openState = _useAutoControlled[0], setOpenState = _useAutoControlled[1]; var _useAutoControlled2 = (0, _reactBindings.useAutoControlled)({ defaultValue: props.defaultSelectedDate, value: props.selectedDate, initialValue: undefined }), selectedDate = _useAutoControlled2[0], setSelectedDate = _useAutoControlled2[1]; var _React$useState = React.useState(valueFormatter(selectedDate)), formattedDate = _React$useState[0], setFormattedDate = _React$useState[1]; React.useEffect(function () { setFormattedDate(valueFormatter(selectedDate)); }, [selectedDate, valueFormatter]); var restrictedDatesOptions = { minDate: props.minDate, maxDate: props.maxDate, restrictedDates: props.restrictedDates }; var _React$useState2 = React.useState(function () { return !!props.selectedDate || !!props.defaultSelectedDate ? (0, _validateDate.validateDate)(selectedDate, formattedDate, restrictedDatesOptions, dateFormatting, props.required) : ''; }), error = _React$useState2[0], setError = _React$useState2[1]; var trySetOpenState = function trySetOpenState(newValue, event) { setOpenState(newValue); (0, _invoke2.default)(props, 'onCalendarOpenStateChange', event, Object.assign({}, props, { calendarOpenState: newValue })); }; var calendarOptions = Object.assign({ selectedDate: selectedDate, navigatedDate: !!selectedDate && !error ? selectedDate : (_props$today = props.today) != null ? _props$today : new Date(), firstDayOfWeek: props.firstDayOfWeek, firstWeekOfYear: props.firstWeekOfYear, dateRangeType: props.dateRangeType, daysToSelectInDayView: props.daysToSelectInDayView, today: props.today, showWeekNumbers: props.showWeekNumbers, workWeekDays: props.workWeekDays }, restrictedDatesOptions); var ElementType = (0, _reactBindings.getElementType)(props); var unhandledProps = (0, _reactBindings.useUnhandledProps)(Datepicker.handledProps, props); var getA11yProps = (0, _reactBindings.useAccessibility)(props.accessibility, { debugName: Datepicker.displayName, actionHandlers: { open: function open(e) { if (allowManualInput) { trySetOpenState(!openState, e); } else { // Keep popup open in case we can only enter the date through calendar. trySetOpenState(true, e); } e.preventDefault(); } }, mapPropsToBehavior: function mapPropsToBehavior() { return { 'aria-invalid': ariaInvalid, 'aria-labelledby': ariaLabelledby, allowManualInput: allowManualInput }; }, rtl: context.rtl }); var _useStyles = (0, _reactBindings.useStyles)(Datepicker.displayName, { className: datepickerClassName, mapPropsToStyles: function mapPropsToStyles() { return { allowManualInput: allowManualInput }; }, mapPropsToInlineStyles: function mapPropsToInlineStyles() { return { className: className, design: design, styles: styles, variables: variables }; }, rtl: context.rtl }), classes = _useStyles.classes; var overrideDatepickerCalendarProps = function overrideDatepickerCalendarProps(predefinedProps) { return { onDateChange: function onDateChange(e, itemProps) { var targetDay = itemProps.value; setSelectedDate(targetDay.originalDate); trySetOpenState(false, e); setError(''); setFormattedDate(valueFormatter(targetDay.originalDate)); (0, _invoke2.default)(props, 'onDateChange', e, { itemProps: itemProps, value: targetDay.originalDate }); } }; }; var calendarElement = (0, _utils.createShorthand)(_DatepickerCalendar.DatepickerCalendar, calendar, { defaultProps: function defaultProps() { return getA11yProps('calendar', Object.assign({}, calendarOptions, dateFormatting)); }, overrideProps: overrideDatepickerCalendarProps }); var overrideInputProps = function overrideInputProps(predefinedProps) { return { onClick: function onClick(e) { if (allowManualInput) { trySetOpenState(!openState, e); } else { // Keep popup open in case we can only enter the date through calendar. trySetOpenState(true, e); } (0, _invoke2.default)(predefinedProps, 'onClick', e, predefinedProps); }, onChange: function onChange(e, target) { var parsedDate = props.parseDate(target.value); var validationError = (0, _validateDate.validateDate)(parsedDate, target.value, calendarOptions, dateFormatting, props.required); setError(validationError); setFormattedDate(target.value); if (!!validationError) { (0, _invoke2.default)(props, 'onDateChangeError', e, Object.assign({}, props, { error: validationError })); } else { setSelectedDate(parsedDate); (0, _invoke2.default)(props, 'onDateChange', e, Object.assign({}, props, { value: parsedDate })); } (0, _invoke2.default)(predefinedProps, 'onChange', e, predefinedProps); }, onBlur: function onBlur(e) { if (props.fallbackToLastCorrectDateOnBlur && !!error) { var futureFormattedDate = valueFormatter(selectedDate); var validationError = (0, _validateDate.validateDate)(selectedDate, futureFormattedDate, calendarOptions, dateFormatting, props.required); setError(validationError); setFormattedDate(futureFormattedDate); if (!!validationError) { (0, _invoke2.default)(props, 'onDateChangeError', e, Object.assign({}, props, { error: validationError })); } } (0, _invoke2.default)(predefinedProps, 'onBlur', e, predefinedProps); }, inputRef: function inputRef(node) { (0, _reactComponentRef.handleRef)(predefinedProps.inputRef, node); _inputRef.current = node; } }; }; var triggerButtonElement = props.inputOnly ? null : /*#__PURE__*/React.createElement(_Button.Button, { icon: /*#__PURE__*/React.createElement(_reactIconsNorthstar.CalendarIcon, null), title: props.openCalendarTitle, iconOnly: true, disabled: props.disabled, type: "button" }); var element = getA11yProps.unstable_wrapWithFocusZone( /*#__PURE__*/React.createElement(ElementType, getA11yProps('root', Object.assign({ className: classes.root, ref: ref }, unhandledProps)), !props.buttonOnly && (0, _utils.createShorthand)(_Input.Input, input, { defaultProps: function defaultProps() { return getA11yProps('input', { placeholder: props.inputPlaceholder, disabled: props.disabled, error: !!error, value: formattedDate, readOnly: !allowManualInput, required: props.required, 'aria-label': formatRestrictedInput(restrictedDatesOptions, dateFormatting) }); }, overrideProps: overrideInputProps }), (0, _utils.createShorthand)(_Popup.Popup, popup, { defaultProps: function defaultProps() { return { open: openState && !props.disabled, trapFocus: { disableFirstFocus: true }, position: 'below', align: 'start' }; }, overrideProps: function overrideProps(predefinedProps) { var _predefinedProps$trig; return { trigger: (_predefinedProps$trig = predefinedProps.trigger) != null ? _predefinedProps$trig : triggerButtonElement, target: props.buttonOnly ? null : _inputRef.current, content: calendarElement, onOpenChange: function onOpenChange(e, _ref) { var open = _ref.open; // In case the event is a click on input, we ignore such events as it should be directly handled by input. if (!(e.type === 'click' && e.target === (_inputRef == null ? void 0 : _inputRef.current))) { trySetOpenState(open, e); (0, _invoke2.default)(predefinedProps, 'onOpenChange', e, { open: open }); } } }; } }))); setEnd(); return element; }); exports.Datepicker = Datepicker; Datepicker.displayName = 'Datepicker'; Datepicker.propTypes = Object.assign({}, _utils.commonPropTypes.createCommon(), { calendar: customPropTypes.itemShorthand, popup: customPropTypes.itemShorthand, input: customPropTypes.itemShorthand, disabled: PropTypes.bool, required: PropTypes.bool, onDateChange: PropTypes.func, onDateChangeError: PropTypes.func, allowManualInput: PropTypes.bool, fallbackToLastCorrectDateOnBlur: PropTypes.bool, defaultCalendarOpenState: PropTypes.bool, calendarOpenState: PropTypes.bool, onCalendarOpenStateChange: PropTypes.func, selectedDate: PropTypes.instanceOf(Date), defaultSelectedDate: PropTypes.instanceOf(Date), inputOnly: PropTypes.bool, buttonOnly: PropTypes.bool, minDate: PropTypes.instanceOf(Date), maxDate: PropTypes.instanceOf(Date), restrictedDates: PropTypes.arrayOf(PropTypes.instanceOf(Date)), firstDayOfWeek: PropTypes.oneOf(Object.keys(_dateTimeUtilities.DayOfWeek).map(function (name) { return _dateTimeUtilities.DayOfWeek[name]; })), firstWeekOfYear: PropTypes.oneOf(Object.keys(_dateTimeUtilities.FirstWeekOfYear).map(function (name) { return _dateTimeUtilities.FirstWeekOfYear[name]; })), dateRangeType: PropTypes.oneOf(Object.keys(_dateTimeUtilities.DateRangeType).map(function (name) { return _dateTimeUtilities.DateRangeType[name]; })), daysToSelectInDayView: PropTypes.number, today: PropTypes.instanceOf(Date), showWeekNumbers: PropTypes.bool, workWeekDays: PropTypes.arrayOf(PropTypes.oneOf(Object.keys(_dateTimeUtilities.DayOfWeek).map(function (name) { return _dateTimeUtilities.DayOfWeek[name]; }))), formatDay: PropTypes.func, formatYear: PropTypes.func, formatMonthDayYear: PropTypes.func, formatMonthYear: PropTypes.func, parseDate: PropTypes.func, months: PropTypes.arrayOf(PropTypes.string), shortMonths: PropTypes.arrayOf(PropTypes.string), days: PropTypes.arrayOf(PropTypes.string), shortDays: PropTypes.arrayOf(PropTypes.string), isRequiredErrorMessage: PropTypes.string, invalidInputErrorMessage: PropTypes.string, isOutOfBoundsErrorMessage: PropTypes.string, openCalendarTitle: PropTypes.string, inputPlaceholder: PropTypes.string, prevMonthAriaLabel: PropTypes.string, nextMonthAriaLabel: PropTypes.string, prevYearAriaLabel: PropTypes.string, nextYearAriaLabel: PropTypes.string, prevYearRangeAriaLabel: PropTypes.string, nextYearRangeAriaLabel: PropTypes.string, monthPickerHeaderAriaLabel: PropTypes.string, yearPickerHeaderAriaLabel: PropTypes.string, closeButtonAriaLabel: PropTypes.string, weekNumberFormatString: PropTypes.string, selectedDateFormatString: PropTypes.string, todayDateFormatString: PropTypes.string, inputAriaLabel: PropTypes.string, inputBoundedFormatString: PropTypes.string, inputMinBoundedFormatString: PropTypes.string, inputMaxBoundedFormatString: PropTypes.string, 'aria-labelledby': PropTypes.string, 'aria-invalid': PropTypes.bool }); Datepicker.defaultProps = Object.assign({ accessibility: _accessibility.datepickerBehavior, inputOnly: false, buttonOnly: false, calendar: {}, popup: {}, input: {}, firstDayOfWeek: _dateTimeUtilities.DayOfWeek.Monday, firstWeekOfYear: _dateTimeUtilities.FirstWeekOfYear.FirstDay, dateRangeType: _dateTimeUtilities.DateRangeType.Day, fallbackToLastCorrectDateOnBlur: true, allowManualInput: true, required: false }, _dateTimeUtilities.DEFAULT_CALENDAR_STRINGS); Datepicker.handledProps = Object.keys(Datepicker.propTypes); Datepicker.create = (0, _utils.createShorthandFactory)({ Component: Datepicker }); Datepicker.Calendar = _DatepickerCalendar.DatepickerCalendar; Datepicker.CalendarHeader = _DatepickerCalendarHeader.DatepickerCalendarHeader; Datepicker.CalendarHeaderAction = _DatepickerCalendarHeaderAction.DatepickerCalendarHeaderAction; Datepicker.CalendarHeaderCell = _DatepickerCalendarHeaderCell.DatepickerCalendarHeaderCell; Datepicker.CalendarCell = _DatepickerCalendarCell.DatepickerCalendarCell; Datepicker.CalendarCellButton = _DatepickerCalendarCellButton.DatepickerCalendarCellButton; Datepicker.CalendarGrid = _DatepickerCalendarGrid.DatepickerCalendarGrid; Datepicker.CalendarGridRow = _DatepickerCalendarGridRow.DatepickerCalendarGridRow; Datepicker.Input = _Input.Input; //# sourceMappingURL=Datepicker.js.map