"use strict";

Object.defineProperty(exports, "__esModule", {
  value: true
});
exports.default = void 0;

function _react() {
  const data = _interopRequireWildcard(require("react"));

  _react = function () {
    return data;
  };

  return data;
}

function _tslib() {
  const data = require("tslib");

  _tslib = function () {
    return data;
  };

  return data;
}

function _jsxRuntime() {
  const data = require("react/jsx-runtime");

  _jsxRuntime = function () {
    return data;
  };

  return data;
}

function _clsx() {
  const data = _interopRequireDefault(require("clsx"));

  _clsx = function () {
    return data;
  };

  return data;
}

var _utils = require("../utils");

var _utils2 = require("./utils");

var _useRefs = _interopRequireDefault(require("../hooks/use-refs"));

var _raf = require("../utils/raf");

var _useRect = require("../hooks/use-rect");

var _toast = _interopRequireDefault(require("../toast"));

var _CalendarMonth = _interopRequireDefault(require("./CalendarMonth"));

var _button = _interopRequireDefault(require("../button"));

var _CalendarHeader = _interopRequireDefault(require("./CalendarHeader"));

var _popup = _interopRequireDefault(require("../popup"));

var _useSetState = _interopRequireDefault(require("../hooks/use-set-state"));

var _useUpdateEffect = _interopRequireDefault(require("../hooks/use-update-effect"));

var _ConfigProviderContext = _interopRequireDefault(require("../config-provider/ConfigProviderContext"));

function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }

function _getRequireWildcardCache(nodeInterop) { if (typeof WeakMap !== "function") return null; var cacheBabelInterop = new WeakMap(); var cacheNodeInterop = new WeakMap(); return (_getRequireWildcardCache = function (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; }

/* eslint-disable no-plusplus */
const Calendar = (0, _react().forwardRef)((_a, ref) => {
  var {
    className,
    style
  } = _a,
      props = (0, _tslib().__rest)(_a, ["className", "style"]);
  const {
    prefixCls,
    createNamespace,
    locale
  } = (0, _react().useContext)(_ConfigProviderContext.default);
  const [bem] = createNamespace('calendar', prefixCls);

  const limitDateRange = (date, minDate = props.minDate, maxDate = props.maxDate) => {
    if ((0, _utils2.compareDay)(date, minDate) === -1) {
      return minDate;
    }

    if ((0, _utils2.compareDay)(date, maxDate) === 1) {
      return maxDate;
    }

    return date;
  };

  const getInitialDate = (defaultDate = props.defaultDate) => {
    const {
      type,
      minDate,
      maxDate
    } = props;

    if (defaultDate === null) {
      return defaultDate;
    }

    const now = (0, _utils2.getToday)();

    if (type === 'range') {
      if (!Array.isArray(defaultDate)) {
        defaultDate = [];
      }

      const start = limitDateRange(defaultDate[0] || now, minDate, (0, _utils2.getPrevDay)(maxDate));
      const end = limitDateRange(defaultDate[1] || now, (0, _utils2.getNextDay)(minDate));
      return [start, end];
    }

    if (type === 'multiple') {
      if (Array.isArray(defaultDate)) {
        return defaultDate.map(date => limitDateRange(date));
      }

      return [limitDateRange(now)];
    }

    if (!defaultDate || Array.isArray(defaultDate)) {
      defaultDate = now;
    }

    return limitDateRange(defaultDate);
  };

  const bodyRef = (0, _react().useRef)();
  const bodyHeightRef = (0, _react().useRef)(0);
  const [state, updateState] = (0, _useSetState.default)({
    subtitle: '',
    currentDate: getInitialDate()
  });
  const [monthRefs, setMonthRefs] = (0, _useRefs.default)();
  const dayOffset = (0, _react().useMemo)(() => props.firstDayOfWeek ? +props.firstDayOfWeek % 7 : 0, [props.firstDayOfWeek, props.firstDayOfWeek]);
  const months = (0, _react().useMemo)(() => {
    const internalMonths = [];
    const cursor = new Date(props.minDate);
    cursor.setDate(1);

    do {
      internalMonths.push(new Date(cursor));
      cursor.setMonth(cursor.getMonth() + 1);
    } while ((0, _utils2.compareMonth)(cursor, props.maxDate) !== 1);

    return internalMonths;
  }, [props.minDate, props.maxDate]);
  const buttonDisabled = (0, _react().useMemo)(() => {
    const {
      currentDate
    } = state;

    if (currentDate) {
      if (props.type === 'range') {
        return !currentDate[0] || !currentDate[1];
      }

      if (props.type === 'multiple') {
        return !currentDate.length;
      }
    }

    return !currentDate;
  }, [props.type, state.currentDate]); // calculate the position of the elements
  // and find the elements that needs to be rendered

  const onScroll = () => {
    var _a;

    const top = (0, _utils.getScrollTop)(bodyRef.current);
    const bottom = top + bodyHeightRef.current;
    const heights = months.map((item, index) => monthRefs[index].getHeight());
    const heightSum = heights.reduce((a, b) => a + b, 0); // iOS scroll bounce may exceed the range

    if (bottom > heightSum && top > 0) {
      return;
    }

    let height = 0;
    let currentMonth;
    const visibleRange = [-1, -1];

    for (let i = 0; i < months.length; i++) {
      const month = monthRefs[i];
      const visible = height <= bottom && height + heights[i] >= top;

      if (visible) {
        visibleRange[1] = i;

        if (!currentMonth) {
          currentMonth = month;
          visibleRange[0] = i;
        }

        if (!monthRefs[i].showed) {
          monthRefs[i].showed = true;
          (_a = props.onMonthShow) === null || _a === void 0 ? void 0 : _a.call(props, {
            date: month.date,
            title: month.getTitle()
          });
        }
      }

      height += heights[i];
    }

    months.forEach((_, index) => {
      const visible = index >= visibleRange[0] - 1 && index <= visibleRange[1] + 1;
      monthRefs[index].setVisible(visible);
    });
    /* istanbul ignore else */

    if (currentMonth) {
      updateState({
        subtitle: currentMonth.getTitle()
      });
    }
  };

  const scrollToDate = targetDate => {
    (0, _raf.raf)(() => {
      months.some((month, index) => {
        if ((0, _utils2.compareMonth)(month, targetDate) === 0) {
          if (bodyRef.current) {
            monthRefs[index].scrollIntoView(bodyRef.current);
          }

          return true;
        }

        return false;
      });
      onScroll();
    });
  }; // scroll to current month


  const scrollIntoView = () => {
    if (props.poppable && !props.visible) {
      return;
    }

    const {
      currentDate
    } = state;

    if (currentDate) {
      const targetDate = props.type === 'single' ? currentDate : currentDate[0];
      scrollToDate(targetDate);
    } else {
      (0, _raf.raf)(onScroll);
    }
  };

  const init = () => {
    if (props.poppable && !props.visible) {
      return;
    }

    (0, _raf.raf)(() => {
      // add Math.floor to avoid decimal height issues
      // https://github.com/youzan/vant/issues/5640
      bodyHeightRef.current = Math.floor((0, _useRect.getRect)(bodyRef.current).height);
      scrollIntoView();
    });
  };

  const reset = (date = getInitialDate()) => {
    updateState({
      currentDate: date
    });
    scrollIntoView();
  };

  const checkRange = date => {
    var _a;

    const {
      maxRange,
      rangePrompt,
      showRangePrompt
    } = props;

    if (maxRange && (0, _utils2.calcDateNum)(date) > maxRange) {
      if (showRangePrompt) {
        _toast.default.info(rangePrompt || locale.vanCalendar.rangePrompt(+maxRange));
      }

      (_a = props.onOverRange) === null || _a === void 0 ? void 0 : _a.call(props);
      return false;
    }

    return true;
  };

  const onConfirm = () => {
    var _a;

    (_a = props.onConfirm) === null || _a === void 0 ? void 0 : _a.call(props, (0, _utils2.cloneDates)(state.currentDate));
  };

  const select = (date, complete) => {
    const setCurrentDate = current => {
      var _a;

      state.currentDate = current;
      updateState({
        currentDate: current
      });
      (_a = props.onSelect) === null || _a === void 0 ? void 0 : _a.call(props, (0, _utils2.cloneDates)(state.currentDate));
    };

    if (complete && props.type === 'range') {
      const valid = checkRange(date);

      if (!valid) {
        // auto selected to max range if showConfirm
        if (props.showConfirm) {
          setCurrentDate([date[0], (0, _utils2.getDayByOffset)(date[0], +props.maxRange - 1)]);
        } else {
          setCurrentDate(date);
        }

        return;
      }
    }

    setCurrentDate(date);

    if (complete && !props.showConfirm) {
      onConfirm();
    }
  };

  const onClickDay = item => {
    var _a;

    if (props.readonly || !item.date) {
      return;
    }

    const {
      date
    } = item;
    const {
      type
    } = props;
    const {
      currentDate
    } = state;

    if (type === 'range') {
      if (!currentDate) {
        select([date]);
        return;
      }

      const [startDay, endDay] = currentDate;

      if (startDay && !endDay) {
        const compareToStart = (0, _utils2.compareDay)(date, startDay);

        if (compareToStart === 1) {
          select([startDay, date], true);
        } else if (compareToStart === -1) {
          select([date]);
        } else if (props.allowSameDay) {
          select([date, date], true);
        }
      } else {
        select([date]);
      }
    } else if (type === 'multiple') {
      if (!currentDate) {
        select([date]);
        return;
      }

      let selectedIndex;
      const selected = state.currentDate.some((dateItem, index) => {
        const equal = (0, _utils2.compareDay)(dateItem, date) === 0;

        if (equal) {
          selectedIndex = index;
        }

        return equal;
      });

      if (selected) {
        const [unselectedDate] = currentDate.splice(selectedIndex, 1);
        (_a = props.onUnselect) === null || _a === void 0 ? void 0 : _a.call(props, (0, _utils2.cloneDate)(unselectedDate));
        select([...currentDate]);
      } else if (props.maxRange && currentDate.length >= props.maxRange) {
        (0, _toast.default)(props.rangePrompt || `选择天数不能超过 ${props.maxRange} 天`);
      } else {
        select([...currentDate, date]);
      }
    } else {
      select(date, true);
    }
  };

  const renderMonth = (date, index) => {
    const showMonthTitle = index !== 0 || !props.showSubtitle;
    return (0, _jsxRuntime().jsx)(_CalendarMonth.default, Object.assign({
      ref: setMonthRefs(index),
      date: date,
      currentDate: state.currentDate,
      showMonthTitle: showMonthTitle,
      firstDayOfWeek: dayOffset
    }, (0, _utils.pick)(props, ['type', 'color', 'minDate', 'maxDate', 'showMark', 'formatter', 'rowHeight', 'showSubtitle', 'lazyRender', 'allowSameDay', 'topInfoRender', 'bottomInfoRender', 'formatMonthTitle']), {
      onClick: onClickDay
    }), index);
  };

  const renderFooterButton = () => {
    if (props.footer) {
      return props.footer;
    }

    if (props.showConfirm) {
      const text = buttonDisabled ? props.confirmDisabledText : props.confirmText;
      return (0, _jsxRuntime().jsx)(_button.default, Object.assign({
        round: true,
        block: true,
        type: "danger",
        color: props.color,
        className: (0, _clsx().default)(bem('confirm')),
        disabled: buttonDisabled,
        nativeType: "button",
        onClick: onConfirm
      }, {
        children: text || locale.vanCalendar.confirm
      }));
    }

    return null;
  };

  const renderFooter = () => (0, _jsxRuntime().jsx)("div", Object.assign({
    className: (0, _clsx().default)(bem('footer'), {
      'rv-safe-area-bottom': props.safeAreaInsetBottom
    })
  }, {
    children: renderFooterButton()
  }));

  const renderCalendar = () => (0, _jsxRuntime().jsxs)("div", Object.assign({
    className: (0, _clsx().default)(className, bem()),
    style: style
  }, {
    children: [(0, _jsxRuntime().jsx)(_CalendarHeader.default, {
      weekdays: props.weekdays,
      title: props.title,
      subtitle: props.subtitle || state.subtitle,
      showTitle: props.showTitle,
      showSubtitle: props.showSubtitle,
      firstDayOfWeek: dayOffset,
      "onClick-subtitle": event => {
        var _a;

        (_a = props.onClickSubtitle) === null || _a === void 0 ? void 0 : _a.call(props, event);
      }
    }), (0, _jsxRuntime().jsx)("div", Object.assign({
      ref: bodyRef,
      className: (0, _clsx().default)(bem('body')),
      onScroll: onScroll
    }, {
      children: months.map(renderMonth)
    })), renderFooter()]
  }));

  (0, _react().useEffect)(() => {
    init();
  }, [props.visible]);
  (0, _useUpdateEffect.default)(() => {
    reset(getInitialDate(state.currentDate));
  }, [props.type, props.minDate, props.maxDate]);
  (0, _useUpdateEffect.default)(() => {
    updateState({
      currentDate: props.defaultDate
    });
    scrollIntoView();
  }, [props.defaultDate]);
  (0, _react().useImperativeHandle)(ref, () => ({
    reset,
    scrollToDate
  }));

  if (props.poppable) {
    return (0, _jsxRuntime().jsx)(_popup.default, Object.assign({
      visible: props.visible,
      className: (0, _clsx().default)(bem('popup')),
      round: props.round,
      position: props.position,
      closeable: props.showTitle || props.showSubtitle,
      closeOnPopstate: props.closeOnPopstate,
      closeOnClickOverlay: props.closeOnClickOverlay,
      onClose: props.onClose,
      onClosed: props.onClosed
    }, {
      children: renderCalendar()
    }));
  }

  return renderCalendar();
});
Calendar.defaultProps = {
  round: true,
  poppable: true,
  showMark: true,
  showTitle: true,
  showConfirm: true,
  showSubtitle: true,
  lazyRender: true,
  closeOnPopstate: true,
  closeOnClickOverlay: true,
  safeAreaInsetBottom: true,
  type: 'single',
  position: 'bottom',
  maxRange: null,
  minDate: (0, _utils2.getToday)(),
  maxDate: (() => {
    const now = (0, _utils2.getToday)();
    return new Date(now.getFullYear(), now.getMonth() + 6, now.getDate());
  })(),
  firstDayOfWeek: 0,
  showRangePrompt: true
};
var _default = Calendar;
exports.default = _default;