react-native-dates-picker
Version:
Customizable date picker for React Native
333 lines • 10.9 kB
JavaScript
import React, { memo, useCallback, useEffect, useMemo, useReducer, useRef } from 'react';
import dayjs from 'dayjs';
import utc from 'dayjs/plugin/utc';
import localeData from 'dayjs/plugin/localeData';
import relativeTime from 'dayjs/plugin/relativeTime';
import localizedFormat from 'dayjs/plugin/localizedFormat';
import 'dayjs/locale/zh-cn';
import { getFormatted, dateToUnix, getEndOfDay, getStartOfDay, areDatesOnSameDay } from './utils';
import CalendarContext from './CalendarContext';
import { CalendarActionKind } from './enums';
import Calendar from './components/Calendar';
import DatePicker from './components/DatePicker';
dayjs.extend(utc);
dayjs.extend(localeData);
dayjs.extend(relativeTime);
dayjs.extend(localizedFormat);
const DateTimePicker = props => {
const {
mode = 'single',
locale = 'zh-cn',
displayFullDays = false,
timePicker = false,
firstDayOfWeek,
buttonPrevIcon,
buttonNextIcon,
minDate,
maxDate,
date,
startDate,
endDate,
dates,
onChange,
initialView = 'day',
height,
containerStyle,
columns = ['year', 'month', 'day'],
...rest
} = props;
dayjs.locale(locale);
const initialCalendarView = useMemo(() => mode !== 'single' && initialView === 'time' ? 'day' : initialView, [mode, initialView]);
const firstDay = useMemo(() => firstDayOfWeek && firstDayOfWeek > 0 && firstDayOfWeek <= 6 ? firstDayOfWeek : 0, [firstDayOfWeek]);
const initialState = useMemo(() => {
let initialDate = dayjs();
if (mode === 'single' && date) {
initialDate = dayjs(date);
}
if (mode === 'range' && startDate) {
initialDate = dayjs(startDate);
}
if (mode === 'multiple' && dates && dates.length > 0) {
initialDate = dayjs(dates[0]);
}
if (minDate && initialDate.isBefore(minDate)) {
initialDate = dayjs(minDate);
}
let _date = date ? dayjs(date) : date;
if (_date && maxDate && dayjs(_date).isAfter(maxDate)) {
_date = dayjs(maxDate);
}
if (_date && minDate && dayjs(_date).isBefore(minDate)) {
_date = dayjs(minDate);
}
let start = startDate ? dayjs(startDate) : startDate;
if (start && maxDate && dayjs(start).isAfter(maxDate)) {
start = dayjs(maxDate);
}
if (start && minDate && dayjs(start).isBefore(minDate)) {
start = dayjs(minDate);
}
let end = endDate ? dayjs(endDate) : endDate;
if (end && maxDate && dayjs(end).isAfter(maxDate)) {
end = dayjs(maxDate);
}
if (end && minDate && dayjs(end).isBefore(minDate)) {
end = dayjs(minDate);
}
return {
date: _date,
startDate: start,
endDate: end,
dates,
calendarView: initialCalendarView,
currentDate: initialDate,
currentYear: initialDate.year()
};
}, [mode, date, startDate, endDate, dates, minDate, maxDate, initialCalendarView]);
const [state, dispatch] = useReducer((prevState, action) => {
switch (action.type) {
case CalendarActionKind.SET_CALENDAR_VIEW:
return {
...prevState,
calendarView: action.payload
};
case CalendarActionKind.CHANGE_CURRENT_DATE:
return {
...prevState,
currentDate: action.payload
};
case CalendarActionKind.CHANGE_CURRENT_YEAR:
return {
...prevState,
currentYear: action.payload
};
case CalendarActionKind.CHANGE_SELECTED_DATE:
const {
date: selectDate
} = action.payload;
return {
...prevState,
date: selectDate,
currentDate: selectDate
};
case CalendarActionKind.CHANGE_SELECTED_RANGE:
const {
startDate: start,
endDate: end
} = action.payload;
return {
...prevState,
startDate: start,
endDate: end
};
case CalendarActionKind.CHANGE_SELECTED_MULTIPLE:
const {
dates: selectedDates
} = action.payload;
return {
...prevState,
dates: selectedDates
};
default:
return prevState;
}
}, initialState);
const stateRef = useRef(state);
stateRef.current = state;
useEffect(() => {
if (mode === 'single') {
let newDate = (date && (timePicker ? date : getStartOfDay(date))) ?? date;
if (newDate && maxDate && dayjs(newDate).isAfter(maxDate)) {
newDate = dayjs(maxDate);
}
if (newDate && minDate && dayjs(newDate).isBefore(minDate)) {
newDate = dayjs(minDate);
}
dispatch({
type: CalendarActionKind.CHANGE_SELECTED_DATE,
payload: {
date: newDate
}
});
} else if (mode === 'range') {
let start = startDate ? dayjs(startDate) : startDate;
if (start && maxDate && dayjs(start).isAfter(maxDate)) {
start = dayjs(maxDate);
}
if (start && minDate && dayjs(start).isBefore(minDate)) {
start = dayjs(minDate);
}
let end = endDate ? dayjs(endDate) : endDate;
if (end && maxDate && dayjs(end).isAfter(maxDate)) {
end = dayjs(maxDate);
}
if (end && minDate && dayjs(end).isBefore(minDate)) {
end = dayjs(minDate);
}
dispatch({
type: CalendarActionKind.CHANGE_SELECTED_RANGE,
payload: {
startDate: start,
endDate: end
}
});
} else if (mode === 'multiple') dispatch({
type: CalendarActionKind.CHANGE_SELECTED_MULTIPLE,
payload: {
dates
}
});else if (mode === 'wheel') dispatch({
type: CalendarActionKind.CHANGE_SELECTED_DATE,
payload: {
date
}
});
}, [mode, date, startDate, endDate, dates, minDate, timePicker, maxDate]);
const setCalendarView = useCallback(view => {
dispatch({
type: CalendarActionKind.SET_CALENDAR_VIEW,
payload: view
});
}, []);
const onSelectDate = useCallback(datetime => {
if (mode === 'single') {
const newDate = timePicker ? datetime : getStartOfDay(datetime);
dispatch({
type: CalendarActionKind.CHANGE_CURRENT_DATE,
payload: newDate
});
onChange === null || onChange === void 0 || onChange({
date: newDate
});
} else if (mode === 'range') {
const sd = stateRef.current.startDate;
const ed = stateRef.current.endDate;
let newDateRang = {
startDate: getStartOfDay(datetime),
endDate: undefined
};
if (sd && !ed) {
if (dateToUnix(datetime) >= dateToUnix(sd)) {
newDateRang = {
startDate: sd,
endDate: getEndOfDay(datetime)
};
} else {
newDateRang = {
startDate: getStartOfDay(datetime),
endDate: sd
};
}
}
dispatch({
type: CalendarActionKind.CHANGE_SELECTED_RANGE,
payload: newDateRang
});
onChange === null || onChange === void 0 || onChange(newDateRang);
} else if (mode === 'multiple') {
const safeDates = stateRef.current.dates || [];
const newDate = getStartOfDay(datetime);
const exists = safeDates.some(ed => areDatesOnSameDay(ed, newDate));
const newDates = exists ? safeDates.filter(ed => !areDatesOnSameDay(ed, newDate)) : [...safeDates, newDate];
newDates.sort((a, b) => dayjs(a).isAfter(dayjs(b)) ? 1 : -1);
const newDatesObj = {
dates: newDates,
datePressed: newDate,
change: exists ? 'removed' : 'added'
};
dispatch({
type: CalendarActionKind.CHANGE_SELECTED_MULTIPLE,
payload: newDatesObj
});
onChange === null || onChange === void 0 || onChange(newDatesObj);
} else if (mode === 'wheel') {
dispatch({
type: CalendarActionKind.CHANGE_CURRENT_DATE,
payload: datetime
});
onChange === null || onChange === void 0 || onChange({
date: datetime
});
}
}, [onChange, mode, timePicker]);
const onSelectMonth = useCallback(month => {
let newDate = dayjs(stateRef.current.currentDate).month(month);
if (maxDate && newDate.isAfter(maxDate)) newDate = dayjs(maxDate);
if (minDate && newDate.isBefore(minDate)) newDate = dayjs(minDate);
dispatch({
type: CalendarActionKind.CHANGE_CURRENT_DATE,
payload: newDate
});
if (mode !== 'wheel') setCalendarView('day');
if (mode === 'single' || mode === 'wheel') onChange === null || onChange === void 0 || onChange({
date: getFormatted(newDate)
});
}, [maxDate, minDate, mode, onChange, setCalendarView]);
const onSelectYear = useCallback(year => {
let newDate = dayjs(stateRef.current.currentDate).year(year);
if (maxDate && newDate.isAfter(maxDate)) newDate = dayjs(maxDate);
if (minDate && newDate.isBefore(minDate)) newDate = dayjs(minDate);
dispatch({
type: CalendarActionKind.CHANGE_CURRENT_DATE,
payload: newDate
});
if (mode !== 'wheel') setCalendarView('day');
if (mode === 'single' || mode === 'wheel') onChange === null || onChange === void 0 || onChange({
date: getFormatted(newDate)
});
}, [maxDate, minDate, mode, setCalendarView, onChange]);
const onChangeMonth = useCallback(month => {
const newDate = dayjs(stateRef.current.currentDate).add(month, 'month');
dispatch({
type: CalendarActionKind.CHANGE_CURRENT_DATE,
payload: dayjs(newDate)
});
}, []);
const onChangeYear = useCallback(year => {
dispatch({
type: CalendarActionKind.CHANGE_CURRENT_YEAR,
payload: year
});
}, []);
const baseContextValue = useMemo(() => ({
columns,
mode,
locale,
timePicker,
minDate,
maxDate,
startDate,
endDate,
dates,
displayFullDays,
firstDayOfWeek: firstDay,
height,
theme: rest
}), [columns, mode, locale, timePicker, minDate, maxDate, startDate, endDate, dates, displayFullDays, firstDay, height, rest]);
const handlerContextValue = useMemo(() => ({
setCalendarView,
onSelectDate,
onSelectMonth,
onSelectYear,
onChangeMonth,
onChangeYear
}), [setCalendarView, onSelectDate, onSelectMonth, onSelectYear, onChangeMonth, onChangeYear]);
const memoizedValue = useMemo(() => ({
...state,
...baseContextValue,
...handlerContextValue
}), [state, baseContextValue, handlerContextValue]);
return /*#__PURE__*/React.createElement(CalendarContext.Provider, {
value: memoizedValue
}, mode !== 'wheel' ? /*#__PURE__*/React.createElement(Calendar, {
buttonPrevIcon: buttonPrevIcon,
buttonNextIcon: buttonNextIcon,
height: height,
containerStyle: containerStyle
}) : /*#__PURE__*/React.createElement(DatePicker, {
height: height,
containerStyle: containerStyle
}));
};
export default /*#__PURE__*/memo(DateTimePicker);
//# sourceMappingURL=DateTimePicker.js.map