|
@@ -1,12 +1,268 @@
|
|
|
import {
|
|
import {
|
|
|
|
|
+ forwardRef,
|
|
|
|
|
+ useEffect,
|
|
|
|
|
+ useImperativeHandle,
|
|
|
|
|
+ useState,
|
|
|
|
|
+ type Ref
|
|
|
|
|
+} from "react";
|
|
|
|
|
+import {
|
|
|
|
|
+ TouchableOpacity,
|
|
|
View
|
|
View
|
|
|
} from "react-native";
|
|
} from "react-native";
|
|
|
|
|
+import type {
|
|
|
|
|
+ CalendarDay,
|
|
|
|
|
+ IDateSelectorRef
|
|
|
|
|
+} from "./type";
|
|
|
|
|
+import type IDateSelectorProps from "./type";
|
|
|
|
|
+import stylesheet, {
|
|
|
|
|
+ useStyles
|
|
|
|
|
+} from "./stylesheet";
|
|
|
|
|
+import {
|
|
|
|
|
+ dateSelect
|
|
|
|
|
+} from "./util";
|
|
|
|
|
+import {
|
|
|
|
|
+ NCoreUIKitTheme
|
|
|
|
|
+} from "../../core/hooks";
|
|
|
|
|
+import moment from "moment";
|
|
|
|
|
+import Text from "../text";
|
|
|
|
|
+
|
|
|
|
|
+const DateSelector = ({
|
|
|
|
|
+ isShowTodayIndicator = true,
|
|
|
|
|
+ localeBasedFirstDayOfWeek,
|
|
|
|
|
+ dayOfWeekLength = "long",
|
|
|
|
|
+ viewDate: viewDateProp,
|
|
|
|
|
+ dateRange,
|
|
|
|
|
+ dateRule,
|
|
|
|
|
+ variant,
|
|
|
|
|
+ date
|
|
|
|
|
+}: IDateSelectorProps, ref: Ref<IDateSelectorRef>) => {
|
|
|
|
|
+ const {
|
|
|
|
|
+ radiuses,
|
|
|
|
|
+ borders,
|
|
|
|
|
+ spaces,
|
|
|
|
|
+ colors
|
|
|
|
|
+ } = NCoreUIKitTheme.useContext();
|
|
|
|
|
+
|
|
|
|
|
+ const selectDate = dateSelect({
|
|
|
|
|
+ dateRange,
|
|
|
|
|
+ dateRule,
|
|
|
|
|
+ variant,
|
|
|
|
|
+ date
|
|
|
|
|
+ });
|
|
|
|
|
+
|
|
|
|
|
+ const [
|
|
|
|
|
+ viewDate,
|
|
|
|
|
+ setViewDate
|
|
|
|
|
+ ] = useState(viewDateProp ? viewDateProp : selectDate);
|
|
|
|
|
+
|
|
|
|
|
+ const [
|
|
|
|
|
+ monthDays,
|
|
|
|
|
+ setMonthDays
|
|
|
|
|
+ ] = useState<Array<CalendarDay>>([]);
|
|
|
|
|
+
|
|
|
|
|
+ const {
|
|
|
|
|
+ todayIndicator: todayIndicatorDynamicStyle,
|
|
|
|
|
+ dayOfWeek: dayOfWeekDynamicStyle,
|
|
|
|
|
+ day: dayDynamicStyle
|
|
|
|
|
+ } = useStyles({
|
|
|
|
|
+ radiuses,
|
|
|
|
|
+ borders,
|
|
|
|
|
+ colors,
|
|
|
|
|
+ spaces
|
|
|
|
|
+ });
|
|
|
|
|
+
|
|
|
|
|
+ useEffect(() => {
|
|
|
|
|
+ const allDays = getDaysOfViewMonth({
|
|
|
|
|
+ tDate: viewDate
|
|
|
|
|
+ });
|
|
|
|
|
+
|
|
|
|
|
+ setMonthDays(allDays);
|
|
|
|
|
+ }, [viewDate]);
|
|
|
|
|
+
|
|
|
|
|
+ useEffect(() => {
|
|
|
|
|
+ if(viewDateProp) {
|
|
|
|
|
+ setViewDate(viewDateProp);
|
|
|
|
|
+ }
|
|
|
|
|
+ }, [viewDateProp]);
|
|
|
|
|
+
|
|
|
|
|
+ useImperativeHandle(
|
|
|
|
|
+ ref,
|
|
|
|
|
+ () => ({
|
|
|
|
|
+ changeCurrentMonth: (date: Date) => {
|
|
|
|
|
+ const allDays = getDaysOfViewMonth({
|
|
|
|
|
+ tDate: date
|
|
|
|
|
+ });
|
|
|
|
|
+
|
|
|
|
|
+ setMonthDays(allDays);
|
|
|
|
|
+ }
|
|
|
|
|
+ }),
|
|
|
|
|
+ []
|
|
|
|
|
+ );
|
|
|
|
|
+
|
|
|
|
|
+ const getDaysOfViewMonth = ({
|
|
|
|
|
+ tDate
|
|
|
|
|
+ }: {
|
|
|
|
|
+ tDate: Date
|
|
|
|
|
+ }): Array<CalendarDay> => {
|
|
|
|
|
+ const targetDate = moment(new Date(tDate));
|
|
|
|
|
+
|
|
|
|
|
+ const startOfCalendar = targetDate.clone().startOf("month").startOf("week");
|
|
|
|
|
+ const endOfCalendar = targetDate.clone().endOf("month").endOf("week");
|
|
|
|
|
+
|
|
|
|
|
+ const totalDays = endOfCalendar.diff(startOfCalendar, "days") + 1;
|
|
|
|
|
+
|
|
|
|
|
+ const allDays = Array.from({
|
|
|
|
|
+ length: totalDays
|
|
|
|
|
+ }).map((_, index) => {
|
|
|
|
|
+ const currentDay = startOfCalendar.clone().add(index, "days");
|
|
|
|
|
+
|
|
|
|
|
+ let isSelected = false;
|
|
|
|
|
+
|
|
|
|
|
+ if(variant === "single" && date) {
|
|
|
|
|
+ if(moment(currentDay).isSame(date, "day")) isSelected = true;
|
|
|
|
|
+ } else if(variant === "range" && dateRange && dateRange.start) {
|
|
|
|
|
+ if(moment(currentDay).isBetween(dateRange.start, dateRange.end, "day", "[]")) isSelected = true;
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ return {
|
|
|
|
|
+ isCurrentMonth: currentDay.isSame(targetDate, "month"),
|
|
|
|
|
+ isToday: currentDay.isSame(moment(), "day"),
|
|
|
|
|
+ dayOfWeek: currentDay.weekday(),
|
|
|
|
|
+ dayNumber: currentDay.date(),
|
|
|
|
|
+ date: currentDay,
|
|
|
|
|
+ isSelected
|
|
|
|
|
+ };
|
|
|
|
|
+ });
|
|
|
|
|
+
|
|
|
|
|
+ const weeks: CalendarDay[][] = [];
|
|
|
|
|
+
|
|
|
|
|
+ allDays.forEach((day, index) => {
|
|
|
|
|
+ const weekIndex = Math.floor(index / 7);
|
|
|
|
|
+ if (!weeks[weekIndex]) {
|
|
|
|
|
+ weeks[weekIndex] = [];
|
|
|
|
|
+ }
|
|
|
|
|
+ weeks[weekIndex].push(day);
|
|
|
|
|
+ });
|
|
|
|
|
+
|
|
|
|
|
+ return allDays;
|
|
|
|
|
+ };
|
|
|
|
|
+
|
|
|
|
|
+ const renderDay = ({
|
|
|
|
|
+ weekIndex,
|
|
|
|
|
+ nextItem,
|
|
|
|
|
+ prevItem,
|
|
|
|
|
+ dayIndex,
|
|
|
|
|
+ dayItem
|
|
|
|
|
+ }: {
|
|
|
|
|
+ nextItem?: CalendarDay;
|
|
|
|
|
+ prevItem?: CalendarDay;
|
|
|
|
|
+ dayItem: CalendarDay;
|
|
|
|
|
+ weekIndex: number;
|
|
|
|
|
+ dayIndex: number;
|
|
|
|
|
+ }) => {
|
|
|
|
|
+ const isNextItemSelected = nextItem && nextItem.isSelected && dayItem.isSelected;
|
|
|
|
|
+ const isPrevItemSelected = prevItem && prevItem.isSelected && dayItem.isSelected;
|
|
|
|
|
+
|
|
|
|
|
+ const selectionStyle = [
|
|
|
|
|
+ isNextItemSelected ? {
|
|
|
|
|
+ borderBottomRightRadius: 0,
|
|
|
|
|
+ borderTopRightRadius: 0
|
|
|
|
|
+ } : null,
|
|
|
|
|
+ isPrevItemSelected ? {
|
|
|
|
|
+ borderBottomLeftRadius: 0,
|
|
|
|
|
+ borderTopLeftRadius: 0
|
|
|
|
|
+ } : null,
|
|
|
|
|
+ isNextItemSelected && isPrevItemSelected ? {
|
|
|
|
|
+ borderBottomRightRadius: 0,
|
|
|
|
|
+ borderBottomLeftRadius: 0,
|
|
|
|
|
+ borderTopRightRadius: 0,
|
|
|
|
|
+ borderTopLeftRadius: 0
|
|
|
|
|
+ } : null
|
|
|
|
|
+ ];
|
|
|
|
|
+
|
|
|
|
|
+ return <TouchableOpacity
|
|
|
|
|
+ key={`day-${weekIndex}-${dayIndex}`}
|
|
|
|
|
+ style={[
|
|
|
|
|
+ stylesheet.day,
|
|
|
|
|
+ dayDynamicStyle,
|
|
|
|
|
+ dayItem.isSelected ? {
|
|
|
|
|
+ backgroundColor: colors.content.container.primary,
|
|
|
|
|
+ borderColor: colors.content.container.primary
|
|
|
|
|
+ } : null,
|
|
|
|
|
+ isNextItemSelected && isPrevItemSelected ? {
|
|
|
|
|
+ backgroundColor: colors.content.container.emphasized,
|
|
|
|
|
+ borderColor: colors.content.container.emphasized
|
|
|
|
|
+ } : null,
|
|
|
|
|
+ ...selectionStyle
|
|
|
|
|
+ ]}
|
|
|
|
|
+ >
|
|
|
|
|
+ <Text
|
|
|
|
|
+ color={isPrevItemSelected && isNextItemSelected ? "emphasized" : dayItem.isSelected ? "onPrimary" : dayItem.isCurrentMonth ? "mid" : "disabled"}
|
|
|
|
|
+ variant="labelLargeSize"
|
|
|
|
|
+ >
|
|
|
|
|
+ {dayItem.dayNumber}
|
|
|
|
|
+ </Text>
|
|
|
|
|
+ {isShowTodayIndicator && dayItem.isToday ? <View
|
|
|
|
|
+ style={[
|
|
|
|
|
+ stylesheet.todayIndicator,
|
|
|
|
|
+ selectionStyle,
|
|
|
|
|
+ todayIndicatorDynamicStyle
|
|
|
|
|
+ ]}
|
|
|
|
|
+ /> : null}
|
|
|
|
|
+ </TouchableOpacity>;
|
|
|
|
|
+ };
|
|
|
|
|
|
|
|
-const DateSelector = () => {
|
|
|
|
|
|
|
+ let weekDays = moment.weekdays(localeBasedFirstDayOfWeek ? true : false);
|
|
|
|
|
|
|
|
|
|
+ if(dayOfWeekLength === "short") {
|
|
|
|
|
+ weekDays = moment.weekdaysShort(localeBasedFirstDayOfWeek ? true : false);
|
|
|
|
|
+ } else if(dayOfWeekLength === "very-short") {
|
|
|
|
|
+ weekDays = moment.weekdaysMin(localeBasedFirstDayOfWeek ? true : false);
|
|
|
|
|
+ }
|
|
|
|
|
|
|
|
- return <View>
|
|
|
|
|
|
|
+ return <View
|
|
|
|
|
+ style={[
|
|
|
|
|
+ stylesheet.container
|
|
|
|
|
+ ]}
|
|
|
|
|
+ >
|
|
|
|
|
+ <View
|
|
|
|
|
+ style={[
|
|
|
|
|
+ stylesheet.contentContainer
|
|
|
|
|
+ ]}
|
|
|
|
|
+ >
|
|
|
|
|
+ {weekDays.map((weekItem, weekIndex) => {
|
|
|
|
|
+ const days = monthDays.map((day, dayI) => ({
|
|
|
|
|
+ ...day,
|
|
|
|
|
+ originalIndex: dayI
|
|
|
|
|
+ })).filter((dayItem) => dayItem.dayOfWeek === weekIndex);
|
|
|
|
|
|
|
|
|
|
+ return <View
|
|
|
|
|
+ key={`day-column-${weekIndex}`}
|
|
|
|
|
+ style={[
|
|
|
|
|
+ stylesheet.columnContainer
|
|
|
|
|
+ ]}
|
|
|
|
|
+ >
|
|
|
|
|
+ <Text
|
|
|
|
|
+ variant="labelMediumSize"
|
|
|
|
|
+ color="low"
|
|
|
|
|
+ style={{
|
|
|
|
|
+ ...dayOfWeekDynamicStyle
|
|
|
|
|
+ }}
|
|
|
|
|
+ >
|
|
|
|
|
+ {weekItem}
|
|
|
|
|
+ </Text>
|
|
|
|
|
+ {days.map((dayItem, dayIndex) => {
|
|
|
|
|
+ return renderDay({
|
|
|
|
|
+ nextItem: monthDays[dayItem.originalIndex + 1] ?? undefined,
|
|
|
|
|
+ prevItem: monthDays[dayItem.originalIndex - 1] ?? undefined,
|
|
|
|
|
+ weekIndex,
|
|
|
|
|
+ dayIndex,
|
|
|
|
|
+ dayItem
|
|
|
|
|
+ });
|
|
|
|
|
+ })}
|
|
|
|
|
+ </View>;
|
|
|
|
|
+ })}
|
|
|
|
|
+ </View>
|
|
|
</View>;
|
|
</View>;
|
|
|
};
|
|
};
|
|
|
-export default DateSelector;
|
|
|
|
|
|
|
+export default forwardRef(DateSelector);
|