UNPKG

@vnmfify/core

Version:

```shell npm i @vnmfify/core -S ```

450 lines (379 loc) 13.6 kB
import _map from "lodash/map"; import _isArray from "lodash/isArray"; import _isDate from "lodash/isDate"; import _size from "lodash/size"; import _filter from "lodash/filter"; import _isFunction from "lodash/isFunction"; import _isBoolean from "lodash/isBoolean"; function asyncGeneratorStep(gen, resolve, reject, _next, _throw, key, arg) { try { var info = gen[key](arg); var value = info.value; } catch (error) { reject(error); return; } if (info.done) { resolve(value); } else { Promise.resolve(value).then(_next, _throw); } } function _asyncToGenerator(fn) { return function () { var self = this, args = arguments; return new Promise(function (resolve, reject) { var gen = fn.apply(self, args); function _next(value) { asyncGeneratorStep(gen, resolve, reject, _next, _throw, "next", value); } function _throw(err) { asyncGeneratorStep(gen, resolve, reject, _next, _throw, "throw", err); } _next(undefined); }); }; } import { useUncontrolled } from "@vnmfify/hooks"; import { ScrollView, View } from "@vnxjs/components"; import { nextTick } from "@vnxjs/vnmf"; import classNames from "classnames"; import * as React from "react"; import { Children, isValidElement, useCallback, useEffect, useMemo, useRef, useState } from "react"; import useMounted from "../hooks/use-mounted"; import { prefixClassname } from "../styles"; import { getRect } from "../utils/dom/rect"; import { getScrollTop } from "../utils/dom/scroll"; import { useRefs } from "../utils/state"; import CalendarFooter from "./calendar-footer"; import CalendarHeader from "./calendar-header"; import CalendarMonth from "./calendar-month"; import CalendarContext from "./calendar.context"; import { compareDate, compareYearMonth, createNextDay, createPreviousDay, createToday, MAX_DATE, MIN_DATE } from "./calendar.shared"; function defaultSubtitleRender(date) { return "".concat(date.getMonth() + 1, "/").concat(date.getFullYear()) } function useSubtitleRender(subtitle) { var renderRef = useRef(); var getRender = useCallback(() => { if (_isBoolean(subtitle) && subtitle) { return defaultSubtitleRender; } else if (_isBoolean(subtitle) && !subtitle) { return () => undefined; } else if (_isFunction(subtitle)) { return subtitle; } return () => subtitle; }, [subtitle]); useEffect(() => { renderRef.current = getRender(); }, [getRender, subtitle]); return useCallback(date => { var _renderRef$current; return (_renderRef$current = renderRef.current) === null || _renderRef$current === void 0 ? void 0 : _renderRef$current.call(renderRef, date); }, []); } function defaultFormatter(day) { return day; } function useCalendarChildren(children) { var __children__ = {}; Children.forEach(children, child => { if ( /*#__PURE__*/isValidElement(child)) { var element = child; var { type: elementType } = element; if (elementType === CalendarFooter) { __children__.footer = element; } } }); return __children__; } function Calendar(props) { var { className, style, title = true, subtitle: subtitleProp = true, type = "single", defaultValue, value: valueProp, min: minValue = MIN_DATE, max: maxValue = MAX_DATE, firstDayOfWeek, isVietNamese = false, readonly = false, watermark = true, formatter = defaultFormatter, children: childrenProp, onChange: onChangeProp, onConfirm } = props; var { value, setValue } = useUncontrolled({ defaultValue, value: valueProp, onChange: onChangeProp }); var { footer } = useCalendarChildren(childrenProp); var bodyRef = useRef(); var hasConfirmRef = useRef(false); var subtitleRender = useSubtitleRender(subtitleProp); var [subtitle, setSubtitle] = useState(); var changeValueRef = useRef(); var [bodyScrollTop, setBodyScrollTop] = useState(0); var bodyScrollTopRef = useRef(0); var { getRef: getMonthRef, getRefs: getMonthRefs, setRefs: setMonthRefs, clearRefs: clearMonthRefs } = useRefs(); var dayOffset = useMemo(() => firstDayOfWeek ? +firstDayOfWeek % 7 : 0, [firstDayOfWeek]); var months = useMemo(() => { var months = []; var cursor = new Date(minValue); cursor.setDate(1); do { months.push(new Date(cursor)); cursor.setMonth(cursor.getMonth() + 1); } while (compareYearMonth(cursor, maxValue) !== 1); return months; }, [maxValue, minValue]); function limitDateRange(date) { var minDate = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : minValue; var maxDate = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : maxValue; if (compareDate(date, minDate) === -1) { return minDate; } if (compareDate(date, maxDate) === 1) { return maxDate; } return date; } function getInitialDate(defaultDate) { if (defaultDate === null) { return defaultDate; } var now = createToday(); if (type === "range") { if (!Array.isArray(defaultDate)) { defaultDate = []; } var start = limitDateRange(defaultDate[0] || now, minValue, createPreviousDay(maxValue)); var end = limitDateRange(defaultDate[1] || now, createNextDay(minValue)); 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); } function getDisabledDate(disabledDays, startDay, date) { var _disabledDays$find; return (_disabledDays$find = disabledDays.find(day => compareDate(startDay, day.value) === -1 && compareDate(day.value, date) === -1)) === null || _disabledDays$find === void 0 ? void 0 : _disabledDays$find.value; } function getDisabledDays() { return getMonthRefs().reduce((arr, ref) => { var _ref$current$disabled, _ref$current; arr.push(...((_ref$current$disabled = (_ref$current = ref.current) === null || _ref$current === void 0 ? void 0 : _ref$current.disabledDays) !== null && _ref$current$disabled !== void 0 ? _ref$current$disabled : [])); return arr; }, []); } function change(dateValue, complete) { changeValueRef.current = dateValue; setValue === null || setValue === void 0 ? void 0 : setValue(dateValue); if (complete && !hasConfirmRef.current) { onConfirm === null || onConfirm === void 0 ? void 0 : onConfirm(dateValue); } } function onDayClick(day) { var { value: date } = day; if (readonly || !date) { return; } if (type === "range") { var disabledDays = getDisabledDays(); if (!value) { change([date]); return; } var [startDay, endDay] = value; if (startDay && !endDay) { var compareToStart = compareDate(date, startDay); if (compareToStart === 1) { var disabledDay = getDisabledDate(disabledDays, startDay, date); if (disabledDay) { change([startDay, createPreviousDay(disabledDay)]); } else { change([startDay, date], true); } } else if (compareToStart === -1) { change([date]); } else { change([date, date], true); } } else { change([date]); } } else if (type === "multiple") { if (!value) { change([date]); return; } var dates = value; var newDates = _filter(dates, dateItem => compareDate(dateItem, date) !== 0); if (_size(newDates) !== _size(dates)) { change(newDates); } else { change([...dates, date]); } } else { change(date, true); } } function _onScroll2() { return _onScroll.apply(this, arguments); } function _onScroll() { _onScroll = _asyncToGenerator(function* () { var top = yield getScrollTop(bodyRef); var bodyHeight = (yield getRect(bodyRef)).height; var bottom = top + bodyHeight; var heights = months.map((item, index) => getMonthRef(index).current.getHeight()); var heightSum = heights.reduce((a, b) => a + b, 0); if (bottom > heightSum && top > 0) { return; } var height = 0; var currentMonth; for (var i = 0; i < months.length; i++) { var month = getMonthRef(i); var visible = height <= bottom && height + heights[i] >= top; if (visible && !currentMonth) { currentMonth = month; break; } height += heights[i]; } if (currentMonth) { var _subtitle = subtitleRender(currentMonth.current.getValue()); setMonthSubtitle(currentMonth.current, _subtitle); } }); return _onScroll.apply(this, arguments); } function setMonthSubtitle(currentMonth, subtitle) { if (currentMonth) { setSubtitle(subtitle); } } function scrollToDate(_x) { return _scrollToDate.apply(this, arguments); } function _scrollToDate() { _scrollToDate = _asyncToGenerator(function* (targetDate) { months.some((month, index) => { if (compareYearMonth(month, targetDate) === 0) { var currentMonth = getMonthRef(index).current; var _subtitle2 = subtitleRender(currentMonth.getValue()); setMonthSubtitle(currentMonth, _subtitle2); nextTick(() => { if (bodyRef.current) { Promise.all([getRect(bodyRef), getScrollTop(bodyRef), currentMonth === null || currentMonth === void 0 ? void 0 : currentMonth.getScrollTop(_subtitle2)]).then(_ref2 => { var [{ top: bodyTop }, bodyScrollTop, monthScrollTop] = _ref2; var newBodyScrollTop = monthScrollTop - bodyTop + bodyScrollTop; if (bodyScrollTopRef.current !== newBodyScrollTop) { setBodyScrollTop(bodyScrollTopRef.current); setBodyScrollTop(newBodyScrollTop); } else { setBodyScrollTop(newBodyScrollTop); } }); } }); return true; } return false; }); }); return _scrollToDate.apply(this, arguments); } function scrollIntoView(_x2) { return _scrollIntoView.apply(this, arguments); } function _scrollIntoView() { _scrollIntoView = _asyncToGenerator(function* (newValue) { if (newValue) { var targetDate = (() => { if (type === "single" && _isDate(newValue)) { return newValue; } else if (_isArray(newValue)) { return newValue[0]; } })(); yield scrollToDate(targetDate); } else { yield _onScroll2(); } }); return _scrollIntoView.apply(this, arguments); } var reset = date => nextTick(() => scrollIntoView(date).then()); var init = () => reset(value); useEffect(() => { if (value !== changeValueRef.current) { reset(getInitialDate(value)); } }, [value]); useEffect(() => { reset(getInitialDate(value)); }, [type, subtitleRender, minValue, maxValue]); useMounted(init); var monthsRender = useMemo(() => { clearMonthRefs === null || clearMonthRefs === void 0 ? void 0 : clearMonthRefs(); return _map(months, (month, index) => /*#__PURE__*/React.createElement(CalendarMonth, { ref: setMonthRefs(index), key: month.getTime(), value: month, top: index === 0, watermark: watermark })); }, [clearMonthRefs, months, setMonthRefs, watermark]); function notifyConfirm(hasConfirm) { hasConfirmRef.current = hasConfirm; } function handleConfirm() { onConfirm === null || onConfirm === void 0 ? void 0 : onConfirm(value); } return /*#__PURE__*/React.createElement(CalendarContext.Provider, { value: { type, subtitle, firstDayOfWeek: dayOffset, min: minValue, max: maxValue, value, formatter, onDayClick, notifyConfirm, onConfirm: handleConfirm } }, /*#__PURE__*/React.createElement(View, { className: classNames(prefixClassname("calendar"), prefixClassname("calendar--".concat(type)), className), style: style }, (title || subtitle) && /*#__PURE__*/React.createElement(CalendarHeader, { isVietNamese: isVietNamese, title: title, subtitle: subtitle }), /*#__PURE__*/React.createElement(ScrollView, { ref: bodyRef, className: prefixClassname("calendar__body"), scrollY: true, scrollTop: bodyScrollTop, onScroll: function () { var _onScroll3 = _asyncToGenerator(function* (_ref) { var { detail } = _ref; bodyScrollTopRef.current = detail.scrollTop; yield _onScroll2(); }); function onScroll(_x3) { return _onScroll3.apply(this, arguments); } return onScroll; }() }, monthsRender), footer)); } export default Calendar; //# sourceMappingURL=calendar.js.map