"use strict";

var _interopRequireDefault = require("@babel/runtime/helpers/interopRequireDefault");
Object.defineProperty(exports, "__esModule", {
  value: true
});
exports.DatePicker = DatePicker;
exports.default = void 0;
var _dateFns = require("date-fns");
var _i18n = require("@wordpress/i18n");
var _icons = require("@wordpress/icons");
var _date = require("@wordpress/date");
var _element = require("@wordpress/element");
var _useLilius = require("./use-lilius");
var _styles = require("./styles");
var _utils = require("../utils");
var _button = _interopRequireDefault(require("../../button"));
var _constants = require("../constants");
var _jsxRuntime = require("react/jsx-runtime");
/**
 * External dependencies
 */

/**
 * WordPress dependencies
 */

/**
 * Internal dependencies
 */

/**
 * DatePicker is a React component that renders a calendar for date selection.
 *
 * ```jsx
 * import { DatePicker } from '@wordpress/components';
 * import { useState } from '@wordpress/element';
 *
 * const MyDatePicker = () => {
 *   const [ date, setDate ] = useState( new Date() );
 *
 *   return (
 *     <DatePicker
 *       currentDate={ date }
 *       onChange={ ( newDate ) => setDate( newDate ) }
 *     />
 *   );
 * };
 * ```
 */
function DatePicker({
  currentDate,
  onChange,
  events = [],
  isInvalidDate,
  onMonthPreviewed,
  startOfWeek: weekStartsOn = 0
}) {
  const date = currentDate ? (0, _utils.inputToDate)(currentDate) : new Date();
  const {
    calendar,
    viewing,
    setSelected,
    setViewing,
    isSelected,
    viewPreviousMonth,
    viewNextMonth
  } = (0, _useLilius.useLilius)({
    selected: [(0, _dateFns.startOfDay)(date)],
    viewing: (0, _dateFns.startOfDay)(date),
    weekStartsOn
  });

  // Used to implement a roving tab index. Tracks the day that receives focus
  // when the user tabs into the calendar.
  const [focusable, setFocusable] = (0, _element.useState)((0, _dateFns.startOfDay)(date));

  // Allows us to only programmatically focus() a day when focus was already
  // within the calendar. This stops us stealing focus from e.g. a TimePicker
  // input.
  const [isFocusWithinCalendar, setIsFocusWithinCalendar] = (0, _element.useState)(false);

  // Update internal state when currentDate prop changes.
  const [prevCurrentDate, setPrevCurrentDate] = (0, _element.useState)(currentDate);
  if (currentDate !== prevCurrentDate) {
    setPrevCurrentDate(currentDate);
    setSelected([(0, _dateFns.startOfDay)(date)]);
    setViewing((0, _dateFns.startOfDay)(date));
    setFocusable((0, _dateFns.startOfDay)(date));
  }
  return /*#__PURE__*/(0, _jsxRuntime.jsxs)(_styles.Wrapper, {
    className: "components-datetime__date",
    role: "application",
    "aria-label": (0, _i18n.__)('Calendar'),
    children: [/*#__PURE__*/(0, _jsxRuntime.jsxs)(_styles.Navigator, {
      children: [/*#__PURE__*/(0, _jsxRuntime.jsx)(_button.default, {
        icon: (0, _i18n.isRTL)() ? _icons.arrowRight : _icons.arrowLeft,
        variant: "tertiary",
        "aria-label": (0, _i18n.__)('View previous month'),
        onClick: () => {
          viewPreviousMonth();
          setFocusable((0, _dateFns.subMonths)(focusable, 1));
          onMonthPreviewed?.((0, _dateFns.format)((0, _dateFns.subMonths)(viewing, 1), _constants.TIMEZONELESS_FORMAT));
        },
        size: "compact"
      }), /*#__PURE__*/(0, _jsxRuntime.jsxs)(_styles.NavigatorHeading, {
        level: 3,
        children: [/*#__PURE__*/(0, _jsxRuntime.jsx)("strong", {
          children: (0, _date.dateI18n)('F', viewing, -viewing.getTimezoneOffset())
        }), ' ', (0, _date.dateI18n)('Y', viewing, -viewing.getTimezoneOffset())]
      }), /*#__PURE__*/(0, _jsxRuntime.jsx)(_button.default, {
        icon: (0, _i18n.isRTL)() ? _icons.arrowLeft : _icons.arrowRight,
        variant: "tertiary",
        "aria-label": (0, _i18n.__)('View next month'),
        onClick: () => {
          viewNextMonth();
          setFocusable((0, _dateFns.addMonths)(focusable, 1));
          onMonthPreviewed?.((0, _dateFns.format)((0, _dateFns.addMonths)(viewing, 1), _constants.TIMEZONELESS_FORMAT));
        },
        size: "compact"
      })]
    }), /*#__PURE__*/(0, _jsxRuntime.jsxs)(_styles.Calendar, {
      onFocus: () => setIsFocusWithinCalendar(true),
      onBlur: () => setIsFocusWithinCalendar(false),
      children: [calendar[0][0].map(day => /*#__PURE__*/(0, _jsxRuntime.jsx)(_styles.DayOfWeek, {
        children: (0, _date.dateI18n)('D', day, -day.getTimezoneOffset())
      }, day.toString())), calendar[0].map(week => week.map((day, index) => {
        if (!(0, _dateFns.isSameMonth)(day, viewing)) {
          return null;
        }
        return /*#__PURE__*/(0, _jsxRuntime.jsx)(Day, {
          day: day,
          column: index + 1,
          isSelected: isSelected(day),
          isFocusable: (0, _dateFns.isEqual)(day, focusable),
          isFocusAllowed: isFocusWithinCalendar,
          isToday: (0, _dateFns.isSameDay)(day, new Date()),
          isInvalid: isInvalidDate ? isInvalidDate(day) : false,
          numEvents: events.filter(event => (0, _dateFns.isSameDay)(event.date, day)).length,
          onClick: () => {
            setSelected([day]);
            setFocusable(day);
            onChange?.((0, _dateFns.format)(
            // Don't change the selected date's time fields.
            new Date(day.getFullYear(), day.getMonth(), day.getDate(), date.getHours(), date.getMinutes(), date.getSeconds(), date.getMilliseconds()), _constants.TIMEZONELESS_FORMAT));
          },
          onKeyDown: event => {
            let nextFocusable;
            if (event.key === 'ArrowLeft') {
              nextFocusable = (0, _dateFns.addDays)(day, (0, _i18n.isRTL)() ? 1 : -1);
            }
            if (event.key === 'ArrowRight') {
              nextFocusable = (0, _dateFns.addDays)(day, (0, _i18n.isRTL)() ? -1 : 1);
            }
            if (event.key === 'ArrowUp') {
              nextFocusable = (0, _dateFns.subWeeks)(day, 1);
            }
            if (event.key === 'ArrowDown') {
              nextFocusable = (0, _dateFns.addWeeks)(day, 1);
            }
            if (event.key === 'PageUp') {
              nextFocusable = (0, _dateFns.subMonths)(day, 1);
            }
            if (event.key === 'PageDown') {
              nextFocusable = (0, _dateFns.addMonths)(day, 1);
            }
            if (event.key === 'Home') {
              nextFocusable = (0, _dateFns.startOfWeek)(day);
            }
            if (event.key === 'End') {
              nextFocusable = (0, _dateFns.startOfDay)((0, _dateFns.endOfWeek)(day));
            }
            if (nextFocusable) {
              event.preventDefault();
              setFocusable(nextFocusable);
              if (!(0, _dateFns.isSameMonth)(nextFocusable, viewing)) {
                setViewing(nextFocusable);
                onMonthPreviewed?.((0, _dateFns.format)(nextFocusable, _constants.TIMEZONELESS_FORMAT));
              }
            }
          }
        }, day.toString());
      }))]
    })]
  });
}
function Day({
  day,
  column,
  isSelected,
  isFocusable,
  isFocusAllowed,
  isToday,
  isInvalid,
  numEvents,
  onClick,
  onKeyDown
}) {
  const ref = (0, _element.useRef)();

  // Focus the day when it becomes focusable, e.g. because an arrow key is
  // pressed. Only do this if focus is allowed - this stops us stealing focus
  // from e.g. a TimePicker input.
  (0, _element.useEffect)(() => {
    if (ref.current && isFocusable && isFocusAllowed) {
      ref.current.focus();
    }
    // isFocusAllowed is not a dep as there is no point calling focus() on
    // an already focused element.
  }, [isFocusable]);
  return /*#__PURE__*/(0, _jsxRuntime.jsx)(_styles.DayButton, {
    ref: ref,
    className: "components-datetime__date__day" // Unused, for backwards compatibility.
    ,
    disabled: isInvalid,
    tabIndex: isFocusable ? 0 : -1,
    "aria-label": getDayLabel(day, isSelected, numEvents),
    column: column,
    isSelected: isSelected,
    isToday: isToday,
    hasEvents: numEvents > 0,
    onClick: onClick,
    onKeyDown: onKeyDown,
    children: (0, _date.dateI18n)('j', day, -day.getTimezoneOffset())
  });
}
function getDayLabel(date, isSelected, numEvents) {
  const {
    formats
  } = (0, _date.getSettings)();
  const localizedDate = (0, _date.dateI18n)(formats.date, date, -date.getTimezoneOffset());
  if (isSelected && numEvents > 0) {
    return (0, _i18n.sprintf)(
    // translators: 1: The calendar date. 2: Number of events on the calendar date.
    (0, _i18n._n)('%1$s. Selected. There is %2$d event', '%1$s. Selected. There are %2$d events', numEvents), localizedDate, numEvents);
  } else if (isSelected) {
    return (0, _i18n.sprintf)(
    // translators: %s: The calendar date.
    (0, _i18n.__)('%1$s. Selected'), localizedDate);
  } else if (numEvents > 0) {
    return (0, _i18n.sprintf)(
    // translators: 1: The calendar date. 2: Number of events on the calendar date.
    (0, _i18n._n)('%1$s. There is %2$d event', '%1$s. There are %2$d events', numEvents), localizedDate, numEvents);
  }
  return localizedDate;
}
var _default = exports.default = DatePicker;
//# sourceMappingURL=index.js.map