import React, {useState, useEffect, useMemo, useCallback} from 'react'; import {View, TouchableOpacity, StyleSheet, Platform} from 'react-native'; import {Calendar} from 'react-native-calendars'; import DateTimePicker, { DateTimePickerEvent, } from '@react-native-community/datetimepicker'; import moment from 'moment'; import PlayyText from '../PlayyText/PlayyText'; import {useTheme, ColorsScheme} from '../../theme/ThemeContext'; import {SPACING_8, SPACING_16} from '../Spacing/Spacing'; import {GLYPHS} from '../Glyphs'; import CardView from '../Card/CardView'; interface DateSelectorProps { selectedDate: Date; onDateChange: (date: Date) => void; minimumDate?: Date; maximumDate?: Date; showHeader?: boolean; headerTitle?: string; locale?: string; cardViewStyle?: object; dateFormat?: string; } const DateSelector: React.FC = ({ selectedDate, onDateChange, minimumDate = moment().subtract(100, 'years').toDate(), maximumDate = new Date(), showHeader = true, headerTitle, locale = 'en_US', cardViewStyle, dateFormat = 'MMMM YYYY', }) => { const {colors} = useTheme(); const styles = useMemo(() => themedStyle(colors || {}), [colors]); const [currentMonth, setCurrentMonth] = useState(selectedDate); const [showMonthPicker, setShowMonthPicker] = useState(false); // Update currentMonth when selectedDate changes externally useEffect(() => { setCurrentMonth(selectedDate); }, [selectedDate]); // Memoize formatted dates to avoid recalculations const formattedDates = useMemo( () => ({ current: moment(currentMonth).format('YYYY-MM-DD'), currentMonth: moment(currentMonth).format('YYYY-MM'), selected: moment(selectedDate).format('YYYY-MM-DD'), max: moment(maximumDate).format('YYYY-MM-DD'), min: moment(minimumDate).format('YYYY-MM-DD'), headerTitle: moment(currentMonth).format(dateFormat), }), [currentMonth, selectedDate, maximumDate, minimumDate, dateFormat], ); // Memoize calendar theme to prevent recreation const calendarTheme = useMemo( () => ({ calendarBackground: colors.background_grouped_secondary, textSectionTitleColor: colors.labels_tertiary, selectedDayTextColor: colors.blue, todayTextColor: colors.labels_primary, dayTextColor: colors.labels_primary, textDisabledColor: colors.labels_secondary, dotColor: colors.labels_primary, selectedDotColor: colors.red, arrowColor: colors.labels_primary, monthTextColor: colors.labels_primary, indicatorColor: colors.red, textDayFontFamily: 'Playy', textMonthFontFamily: 'Playy', textDayHeaderFontFamily: 'Playy', textDayFontWeight: '400' as const, textMonthFontWeight: '500' as const, textDayHeaderFontWeight: '400' as const, textDayFontSize: 16, textMonthFontSize: 16, textDayHeaderFontSize: 13, }), [colors], ); // Memoize marked dates const markedDates = useMemo( () => ({ [formattedDates.selected]: { selected: true, selectedColor: colors.blue_50, }, }), [formattedDates.selected, colors.blue_50], ); const onCalendarDateSelect = useCallback( (date: string) => { const selectedCalendarDate = new Date(date); onDateChange(selectedCalendarDate); }, [onDateChange], ); const handleMonthChange = useCallback( (event: DateTimePickerEvent, date?: Date) => { if (date) { setCurrentMonth(date); } }, [], ); const toggleMonthPicker = useCallback(() => { setShowMonthPicker(prev => !prev); }, []); const navigateToPreviousMonth = useCallback(() => { const prevMonth = moment(currentMonth).subtract(1, 'month'); const minDate = moment(minimumDate); if (prevMonth.isAfter(minDate) || prevMonth.isSame(minDate, 'month')) { setCurrentMonth(prevMonth.toDate()); } }, [currentMonth, minimumDate]); const navigateToNextMonth = useCallback(() => { const nextMonth = moment(currentMonth).add(1, 'month'); const maxDate = moment(maximumDate); if (nextMonth.isBefore(maxDate) || nextMonth.isSame(maxDate, 'month')) { setCurrentMonth(nextMonth.toDate()); } }, [currentMonth, maximumDate]); return ( {showHeader && ( {headerTitle || formattedDates.headerTitle} {showMonthPicker ? GLYPHS['chevron-down-strong'].char : GLYPHS['chevron-right-strong'].char} {GLYPHS['chevron-left-strong'].char} {GLYPHS['chevron-right-strong'].char} )} {!showMonthPicker ? ( onCalendarDateSelect(day.dateString) } maxDate={formattedDates.max} minDate={formattedDates.min} markedDates={markedDates} hideArrows={true} hideExtraDays={true} renderHeader={() => null} theme={calendarTheme} /> ) : ( )} ); }; const themedStyle = (colors: ColorsScheme) => StyleSheet.create({ customHeader: { flexDirection: 'row', justifyContent: 'space-between', alignItems: 'center', paddingVertical: 12, }, headerLeft: { flex: 1, flexDirection: 'row', gap: 4, alignItems: 'center', }, headerRight: { flexDirection: 'row', alignItems: 'center', gap: 28, }, arrowButton: { alignItems: 'center', justifyContent: 'center', minWidth: 44, minHeight: 44, }, dateTimePickerContainer: { paddingVertical: SPACING_16, alignItems: 'center', }, card: { paddingVertical: SPACING_8, paddingHorizontal: SPACING_16, }, }); export default DateSelector;