From 282d132b4b0a5ff7c0c2f36ceeda8bc020f53b30 Mon Sep 17 00:00:00 2001 From: mystica2000 Date: Thu, 6 Feb 2025 22:28:05 +0530 Subject: [PATCH] feat: isYearDisabled, isMonthDisabled --- .../components/examples/single-datepicker.tsx | 2 + src/components/months.tsx | 19 ++++- src/components/years.tsx | 16 +++- src/theme.ts | 12 +-- src/types.ts | 6 +- src/ui.ts | 9 ++- src/utils.ts | 78 +++++++++++++++---- 7 files changed, 111 insertions(+), 31 deletions(-) diff --git a/demo/components/examples/single-datepicker.tsx b/demo/components/examples/single-datepicker.tsx index 8ef6129..27b84fc 100644 --- a/demo/components/examples/single-datepicker.tsx +++ b/demo/components/examples/single-datepicker.tsx @@ -14,6 +14,8 @@ export default function SingleDatePicker() { date={date} onChange={({ date }) => setDate(date)} timePicker + // minDate={new Date()} + // maxDate={new Date(new Date().getFullYear(), 11, 31)} // end of the year /> { @@ -13,6 +13,8 @@ const Months = () => { components = {}, containerHeight = CONTAINER_HEIGHT, monthsFormat = 'full', + minDate, + maxDate } = useCalendarContext(); const style = useMemo( @@ -33,31 +35,41 @@ const Months = () => { {getMonthsArray()?.map((item, index) => { const isSelected = index === month; + const isDisabled = isMonthDisabled(index, currentDate, { + minDate, + maxDate, + }); + const itemStyle = StyleSheet.flatten([ defaultStyles.month, styles.month, isSelected && styles.selected_month, + isDisabled && styles.disabled ]); const textStyle = StyleSheet.flatten([ styles.month_label, isSelected && styles.selected_month_label, + isDisabled && styles.disabled_label ]); const containerClassName = cn( classNames.month, - isSelected && classNames.selected_month + isSelected && classNames.selected_month, + isDisabled && classNames.disabled ); const textClassName = cn( classNames.month_label, - isSelected && classNames.selected_month_label + isSelected && classNames.selected_month_label, + isDisabled && classNames.disabled_label ); return ( {components.Month ? ( onSelectMonth(index)} accessibilityRole="button" accessibilityLabel={item.name.full} @@ -67,6 +79,7 @@ const Months = () => { ) : ( onSelectMonth(index)} accessibilityRole="button" accessibilityLabel={item.name.full} diff --git a/src/components/years.tsx b/src/components/years.tsx index 48affdb..edd13ed 100644 --- a/src/components/years.tsx +++ b/src/components/years.tsx @@ -1,7 +1,7 @@ import React, { useCallback, useMemo } from 'react'; import { View, StyleSheet, Pressable, Text } from 'react-native'; import { useCalendarContext } from '../calendar-context'; -import { cn, formatNumber, getDateYear, getYearRange } from '../utils'; +import { cn, formatNumber, getDateYear, getYearRange, isYearDisabled } from '../utils'; import { CONTAINER_HEIGHT } from '../enums'; const Years = () => { @@ -16,6 +16,8 @@ const Years = () => { classNames = {}, components = {}, containerHeight = CONTAINER_HEIGHT, + minDate, + maxDate } = useCalendarContext(); const style = useMemo( @@ -32,35 +34,42 @@ const Years = () => { const isSelected = year === selectedYear; const isActivated = year === activeYear; + const isDisabled = isYearDisabled(year, { minDate, maxDate }); + const containerStyle = StyleSheet.flatten([ defaultStyles.year, styles.year, isActivated && styles.active_year, isSelected && styles.selected_year, + isDisabled && styles.disabled ]); const textStyle = StyleSheet.flatten([ styles.year_label, isActivated && styles.active_year_label, isSelected && styles.selected_year_label, + isDisabled && styles.disabled_label ]); const containerClassName = cn( classNames.year, isActivated && classNames.active_year, - isSelected && classNames.selected_year + isSelected && classNames.selected_year, + isDisabled && classNames.disabled ); const textClassName = cn( classNames.year_label, isActivated && classNames.active_year_label, - isSelected && classNames.selected_year_label + isSelected && classNames.selected_year_label, + isDisabled && classNames.disabled_label ); return ( {components.Year ? ( onSelectYear(year)} accessibilityRole="button" accessibilityLabel={year.toString()} @@ -75,6 +84,7 @@ const Years = () => { ) : ( onSelectYear(year)} accessibilityRole="button" accessibilityLabel={year.toString()} diff --git a/src/theme.ts b/src/theme.ts index 796c14e..712eff3 100644 --- a/src/theme.ts +++ b/src/theme.ts @@ -1,6 +1,6 @@ import { useColorScheme } from 'react-native'; import { ClassNames, Styles } from './types'; -import { UI, SelectionState, DayFlag, MonthState, YearState } from './ui'; +import { UI, SelectionState, DayFlag, MonthState, YearState, CalenderFlag } from './ui'; export function getDefaultClassNames(): ClassNames { const classNames: ClassNames = { @@ -44,8 +44,9 @@ export function getDefaultClassNames(): ClassNames { 'group bg-primary web:hover:bg-primary web:hover:opacity-90 active:opacity-90', [SelectionState.selected_label]: 'text-primary-foreground', - [DayFlag.disabled]: '', - [DayFlag.disabled_label]: 'text-muted-foreground opacity-50', + [CalenderFlag.disabled]: '', + [CalenderFlag.disabled_label]: 'text-muted-foreground opacity-50', + [DayFlag.hidden]: '', [DayFlag.outside]: '', [DayFlag.outside_label]: 'text-muted-foreground', @@ -149,11 +150,12 @@ export function getDefaultStyles(): Styles { color: COLORS[theme].primaryForeground, }, - [DayFlag.disabled]: {}, - [DayFlag.disabled_label]: { + [CalenderFlag.disabled]: {}, + [CalenderFlag.disabled_label]: { color: COLORS[theme].mutedForeground, opacity: 0.5, }, + [DayFlag.hidden]: {}, [DayFlag.outside]: {}, [DayFlag.outside_label]: { color: COLORS[theme].mutedForeground }, diff --git a/src/types.ts b/src/types.ts index 94dc1b6..bb6bdbc 100644 --- a/src/types.ts +++ b/src/types.ts @@ -1,7 +1,7 @@ import type { Dayjs } from 'dayjs'; import type { CalendarActionKind, CalendarViews } from './enums'; import type { TextStyle, ViewStyle } from 'react-native'; -import { UI, SelectionState, DayFlag, MonthState, YearState } from './ui'; +import { UI, SelectionState, DayFlag, MonthState, YearState, CalenderFlag } from './ui'; export type DateType = string | number | Dayjs | Date | null | undefined; @@ -87,11 +87,11 @@ export type MultiChange = (params: { }) => void; export type ClassNames = Partial<{ - [key in UI | SelectionState | DayFlag | MonthState | YearState]: string; + [key in UI | SelectionState | DayFlag | MonthState | YearState | CalenderFlag]: string; }>; export type Styles = Partial<{ - [key in UI | SelectionState | DayFlag | MonthState | YearState]: + [key in UI | SelectionState | DayFlag | MonthState | YearState | CalenderFlag]: | ViewStyle | TextStyle; }>; diff --git a/src/ui.ts b/src/ui.ts index 7bf9ac7..f1e34e7 100644 --- a/src/ui.ts +++ b/src/ui.ts @@ -74,11 +74,14 @@ export enum SelectionState { selected_label = 'selected_label', } -export enum DayFlag { - /** The day is disabled. */ +export enum CalenderFlag { + /** The day/month/year is disabled. */ disabled = 'disabled', - /** The label of the disabled day. */ + /** The label of the disabled day/month/year. */ disabled_label = 'disabled_label', +} + +export enum DayFlag { /** The day is hidden. */ hidden = 'hidden', /** The day is outside the current month. */ diff --git a/src/utils.ts b/src/utils.ts index 462d32f..e98bfea 100644 --- a/src/utils.ts +++ b/src/utils.ts @@ -168,6 +168,56 @@ export function isDateDisabled( return false; } +/** + * Check if year is disabled + * + * @param year - year to check + * @param options - options + * + * @returns true if year is disabled, false otherwise + */ +export function isYearDisabled( + year: number, + { + minDate, + maxDate, + }: { + minDate?: DateType; + maxDate?: DateType; + } +): boolean { + if (minDate && year < getDateYear(minDate)) return true; + if (maxDate && year > getDateYear(maxDate)) return true; + + return false; +} + +/** + * Check if month is disabled + * + * @param month - month to check + * @param date - date to check + * @param options - options + * + * @returns true if month is disabled, false otherwise + */ +export function isMonthDisabled( + month: number, + date: DateType, + { + minDate, + maxDate, + }: { + minDate?: DateType; + maxDate?: DateType; + } +): boolean { + if (minDate && month < getDateMonth(minDate) && getDateYear(date) === getDateYear(minDate)) return true; + if (maxDate && month > getDateMonth(maxDate) && getDateYear(date) === getDateYear(maxDate)) return true; + + return false; +} + /** * Get formated date * @@ -356,20 +406,20 @@ export const getMonthDays = ( const prevDays = showOutsideDays ? Array.from({ length: prevMonthOffset }, (_, index) => { - const number = index + (prevMonthDays - prevMonthOffset + 1); - const thisDay = date.add(-1, 'month').date(number); - return generateCalendarDay( - number, - thisDay, - minDate, - maxDate, - disabledDates, - false, - index + 1, - firstDayOfWeek, - numerals - ); - }) + const number = index + (prevMonthDays - prevMonthOffset + 1); + const thisDay = date.add(-1, 'month').date(number); + return generateCalendarDay( + number, + thisDay, + minDate, + maxDate, + disabledDates, + false, + index + 1, + firstDayOfWeek, + numerals + ); + }) : Array(prevMonthOffset).fill(null); const currentDays = Array.from({ length: daysInCurrentMonth }, (_, index) => {