Parcourir la source

Feature: Month system created.

lfabl il y a 1 semaine
Parent
commit
b1c50f64f0

+ 4 - 4
example/src/pages/home/index.tsx

@@ -369,10 +369,10 @@ const Home = () => {
         <DateTimePicker
             minDate={moment().subtract(2, "months").toDate()}
             maxDate={moment().add(1, "months").toDate()}
-            multipleSelectMinimumRequireMS={709200000}
-            multipleSelectMinimumRequiredDayCount={3}
-            multipleSelectMaximumMS={745200000}
-            multipleSelectDayLimit={10}
+            // multipleSelectMinimumRequireMS={709200000}
+            // multipleSelectMinimumRequiredDayCount={3}
+            // multipleSelectMaximumMS={745200000}
+            // multipleSelectDayLimit={10}
             isWorkWithRealtime={false}
             isWorkWithSeconds={true}
             isCleanEnabled={true}

+ 2 - 2
src/components/dateSelector/stylesheet.ts

@@ -1,7 +1,7 @@
 import {
-    StyleSheet,
     type TextStyle,
-    type ViewStyle
+    type ViewStyle,
+    StyleSheet
 } from "react-native";
 import type {
     DateSelectorDynamicStyle

+ 48 - 19
src/components/dateTimePicker/index.tsx

@@ -70,6 +70,8 @@ const DateTimePicker = ({
     rightIcon: RightIconComponentProp,
     isEndDateSelectionRequired = true,
     localeBasedFirstDayOfWeek = true,
+    isShowCalenderChangerTool = true,
+    monthOfYearLengthType = "short",
     multipleSelectMinimumRequireMS,
     hintTextIcon: HintTextIconProp,
     spreadBehaviour = "baseline",
@@ -82,6 +84,7 @@ const DateTimePicker = ({
     dayOfWeekLength = "short",
     isWorkWithSeconds = false,
     customDateTimeSheetTheme,
+    yearLengthType = "short",
     pickerType = "date-time",
     multipleSelectMaximumMS,
     icon: IconComponentProp,
@@ -99,6 +102,7 @@ const DateTimePicker = ({
     dateTimeSheetProps,
     variant = "single",
     hintTextIconStyle,
+    dateCalendarType,
     initialDateRange,
     type = "default",
     selectValidation,
@@ -109,6 +113,7 @@ const DateTimePicker = ({
     isShowSubTitle,
     cleanIconStyle,
     maxChoice = -1,
+    returnFormat,
     contentStyle,
     initialDate,
     customTheme,
@@ -249,6 +254,7 @@ const DateTimePicker = ({
                     setTempDate(date);
                 }
             },
+            setIsLoading: setIsLoading,
             getValue: () => {
                 return {
                     dateRange,
@@ -287,11 +293,8 @@ const DateTimePicker = ({
 
     useEffect(() => {
         if(onChange) {
-            onChange({
-                dateRange,
-                dateRule,
-                date
-            });
+            const resp = getFormattedResponse();
+            onChange(resp);
         }
     }, [
         tempDateRange,
@@ -316,6 +319,37 @@ const DateTimePicker = ({
         iconProps.color = "disabled";
     }
 
+    const getFormattedResponse = () => {
+        if(returnFormat) {
+            const _date = date ? moment(date).format(returnFormat) : undefined;
+
+            const _dateRange: {
+                start?: Date | string;
+                end?: Date | string;
+            } | undefined = dateRange;
+
+            if(_dateRange && _dateRange.start) {
+                _dateRange.start = moment(_dateRange.start).format(returnFormat);
+            }
+
+            if(_dateRange && _dateRange.end) {
+                _dateRange.end = moment(_dateRange.end).format(returnFormat);
+            }
+
+            return {
+                dateRange: _dateRange,
+                date: _date,
+                dateRule
+            };
+        } else {
+            return {
+                dateRange,
+                dateRule,
+                date
+            };
+        }
+    };
+
     const blur = () => {
         setIsActive(false);
     };
@@ -337,11 +371,8 @@ const DateTimePicker = ({
             });
 
             if(onCancel) {
-                onCancel({
-                    dateRange,
-                    dateRule,
-                    date
-                });
+                const resp = getFormattedResponse();
+                onCancel(resp);
             }
         }
     };
@@ -389,11 +420,8 @@ const DateTimePicker = ({
             });
 
             if(onOk) {
-                onOk({
-                    dateRange,
-                    dateRule,
-                    date
-                });
+                const resp = getFormattedResponse();
+                onOk(resp);
             }
         }
     };
@@ -906,25 +934,26 @@ const DateTimePicker = ({
             multipleSelectMinimumRequiredDayCount={multipleSelectMinimumRequiredDayCount}
             multipleSelectMinimumRequireMS={multipleSelectMinimumRequireMS}
             localeBasedFirstDayOfWeek={localeBasedFirstDayOfWeek}
+            isShowCalenderChangerTool={isShowCalenderChangerTool}
             LoadingIconComponentProp={LoadingIconComponentProp}
             multipleSelectMaximumMS={multipleSelectMaximumMS}
             multipleSelectDayLimit={multipleSelectDayLimit}
             dateTimePickerKey={dateTimePickerKey.current}
+            monthOfYearLengthType={monthOfYearLengthType}
             customLocalize={customDateTimeSheetLocalize}
             selectMultipleObject={selectMultipleObject}
             isShowTodayIndicator={isShowTodayIndicator}
             isWorkWithRealtime={isWorkWithRealtime}
             customTheme={customDateTimeSheetTheme}
             isWorkWithSeconds={isWorkWithSeconds}
+            dateCalendarType={dateCalendarType}
             isShowTools={isShowDateTimeTools}
             dayOfWeekLength={dayOfWeekLength}
-            selectDateRule={selectDateRule}
+            yearLengthType={yearLengthType}
             bottomSheetRef={bottomSheetRef}
+            selectDateRule={selectDateRule}
             selectObject={selectObject}
-            setIsLoading={setIsLoading}
-            setDateRange={setDateRange}
             setIsActive={setIsActive}
-            setDateRule={setDateRule}
             bottomSheetProps={{
                 isAutoHeight: true,
                 ...dateTimeSheetProps

+ 65 - 46
src/components/dateTimePicker/type.ts

@@ -11,6 +11,12 @@ import type {
 import type {
     DayOfWeekLengthType
 } from "../dateSelector/type";
+import type {
+    MonthOfYearLengthType
+} from "../monthSelector/type";
+import type {
+    YearLengthType
+} from "../yearSelector/type";
 
 export type IDateTimePickerRef = {
     setDateRange: (dateRange: DateTimePickerDateRange) => void;
@@ -19,6 +25,7 @@ export type IDateTimePickerRef = {
         dateRule?: RRule;
         date?: Date;
     };
+    setIsLoading: (state: boolean) => void;
     setDateRule: (dateRule: RRule) => void;
     setDate: (date: Date) => void;
     cancel: () => void;
@@ -69,6 +76,13 @@ export type DateTimePickerDateRange = {
     end?: Date;
 };
 
+export type DateTimePickerDateRangeResponse = {
+    start?: Date | string;
+    end?: Date | string;
+};
+
+export type DateCalendarType = "day" | "month" | "year";
+
 export type YearToken = "YYYY" | "YY";
 
 export type MonthToken = "MMMM" | "MMM" | "MM" | "M";
@@ -86,38 +100,7 @@ export type TimeToken = "HH:mm" | "HH:mm:ss" | "hh:mm a";
 export type ValidTitleFormat = DateTokenCombination | `${DateTokenCombination} ${TimeToken}`;
 
 interface IDateTimePickerProps {
-    onChange?: (props: {
-        dateRange?: DateTimePickerDateRange,
-        dateRule?: RRule,
-        date?: Date
-    }) => void;
-    onOk?: (props: {
-        dateRange?: DateTimePickerDateRange,
-        dateRule?: RRule,
-        date?: Date
-    }) => void;
-    onCancel?: (props: {
-        dateRange?: DateTimePickerDateRange,
-        dateRule?: RRule,
-        date?: Date
-    }) => void;
-    removeSelectValidation?: (props: {
-        dateRange?: DateTimePickerDateRange,
-        dateRule?: RRule,
-        date?: Date
-    }) => boolean;
-    selectValidation?: (props: {
-        dateRange?: DateTimePickerDateRange,
-        dateRule?: RRule,
-        date?: Date
-    }) => boolean;
-    spreadBehaviour?: DateTimePickerSpreadBehaviour;
-    hintTextContainerStyle?: ViewStyle;
-    renderLoadingIcon?: NCoreUIKitIcon;
-    contentContainerStyle?: ViewStyle;
-    hintTextIconStyle?: ViewStyle;
-    hintTextIcon?: NCoreUIKitIcon;
-    isShowHintTextIcon?: boolean;
+    dateRangeTitle?: (dateRange: DateTimePickerDateRange | undefined) => string;
     customDateTimeSheetTheme?: {
         gapPropagation?: keyof NCoreUIKit.GapPropagationKey;
         sharpness?: keyof NCoreUIKit.SharpnessKey;
@@ -130,44 +113,80 @@ interface IDateTimePickerProps {
         paletteKey?: keyof NCoreUIKit.PaletteKey;
         themeKey?: keyof NCoreUIKit.ThemeKey;
     };
+    isTimeSelectWorksWithCenterWhenTimePicker?: boolean;
+    spreadBehaviour?: DateTimePickerSpreadBehaviour;
+    onChange?: (props: {
+        dateRange?: DateTimePickerDateRangeResponse,
+        date?: Date | string,
+        dateRule?: RRule
+    }) => void;
+    onOk?: (props: {
+        dateRange?: DateTimePickerDateRangeResponse,
+        date?: Date | string,
+        dateRule?: RRule
+    }) => void;
+    onCancel?: (props: {
+        dateRange?: DateTimePickerDateRangeResponse,
+        date?: Date | string,
+        dateRule?: RRule
+    }) => void;
+    multipleSelectMinimumRequiredDayCount?: number;
     customDateTimeSheetLocalize?: {
         activeLocale?: keyof NCoreUIKit.LocaleKey;
     };
     customLocalize?: {
         activeLocale?: keyof NCoreUIKit.LocaleKey;
     };
-    dateTimeSheetProps?: Omit<
-        IBottomSheetProps,
-        "key" |
-        "customKey" |
-        "ref" |
-        "isAutoHeight" |
-        "isActive" |
-        "renderBottom" |
-        "renderHeader" |
-        "onClose"
-    >;
-    dateRangeTitle?: (dateRange: DateTimePickerDateRange | undefined) => string;
-    isTimeSelectWorksWithCenterWhenTimePicker?: boolean;
-    multipleSelectMinimumRequiredDayCount?: number;
+    monthOfYearLengthType?: MonthOfYearLengthType;
     initialDateRange?: DateTimePickerDateRange;
     multipleSelectMinimumRequireMS?: number;
+    removeSelectValidation?: (props: {
+        dateRange?: DateTimePickerDateRange,
+        dateRule?: RRule,
+        date?: Date
+    }) => boolean;
+    selectValidation?: (props: {
+        dateRange?: DateTimePickerDateRange,
+        dateRule?: RRule,
+        date?: Date
+    }) => boolean;
     isStartDateSelectionRequired?: boolean;
     dayOfWeekLength?: DayOfWeekLengthType;
     pickerType?: DateTimePickerPickerType;
     isEndDateSelectionRequired?: boolean;
     localeBasedFirstDayOfWeek?: boolean;
+    isShowCalenderChangerTool?: boolean;
+    dateCalendarType?: DateCalendarType;
+    hintTextContainerStyle?: ViewStyle;
+    renderLoadingIcon?: NCoreUIKitIcon;
+    contentContainerStyle?: ViewStyle;
     multipleSelectMaximumMS?: number;
     multipleSelectDayLimit?: number;
+    yearLengthType?: YearLengthType;
+    returnFormat?: ValidTitleFormat;
     variant?: DateTimePickerVariant;
     isShowTodayIndicator?: boolean;
     titleFormat?: ValidTitleFormat;
     isShowDateTimeTools?: boolean;
+    hintTextIconStyle?: ViewStyle;
+    hintTextIcon?: NCoreUIKitIcon;
     isWorkWithRealtime?: boolean;
+    isShowHintTextIcon?: boolean;
     isWorkWithSeconds?: boolean;
     cleanIconStyle?: ViewStyle;
     rightIconStyle?: ViewStyle;
     rightIcon?: NCoreUIKitIcon;
+    dateTimeSheetProps?: Omit<
+        IBottomSheetProps,
+        "key" |
+        "customKey" |
+        "ref" |
+        "isAutoHeight" |
+        "isActive" |
+        "renderBottom" |
+        "renderHeader" |
+        "onClose"
+    >;
     type?: DateTimePickerType;
     isShowSubTitle?: boolean;
     isCleanEnabled?: boolean;

+ 155 - 19
src/components/dateTimeSheet/index.tsx

@@ -2,6 +2,7 @@ import {
     useState
 } from "react";
 import {
+    TouchableOpacity,
     View
 } from "react-native";
 import type IDateTimeSheetProps from "./type";
@@ -12,6 +13,8 @@ import {
     NCoreUIKitLocalize,
     NCoreUIKitTheme
 } from "../../core/hooks";
+import MonthSelector from "../monthSelector";
+import YearSelector from "../yearSelector";
 import DateSelector from "../dateSelector";
 import TimeSelector from "../timeSelector";
 import BottomSheet from "../bottomSheet";
@@ -22,20 +25,24 @@ import Text from "../text";
 
 const DateTimeSheet = ({
     isTimeSelectWorksWithCenterWhenTimePicker,
+    dateCalendarType: dateCalendarTypeProp,
     multipleSelectMinimumRequiredDayCount,
     localeBasedFirstDayOfWeek = true,
     multipleSelectMinimumRequireMS,
     isShowTodayIndicator = true,
+    isShowCalenderChangerTool,
     dayOfWeekLength = "short",
     LoadingIconComponentProp,
     multipleSelectMaximumMS,
     multipleSelectDayLimit,
+    monthOfYearLengthType,
     selectMultipleObject,
     isWorkWithRealtime,
     isShowTools = true,
     isWorkWithSeconds,
     dateTimePickerKey,
     bottomSheetProps,
+    yearLengthType,
     bottomSheetRef,
     customLocalize,
     selectObject,
@@ -58,7 +65,10 @@ const DateTimeSheet = ({
     ok
 }: IDateTimeSheetProps) => {
     const {
-        spaces
+        radiuses,
+        borders,
+        spaces,
+        colors
     } = NCoreUIKitTheme.useContext(customTheme);
 
     const {
@@ -70,7 +80,14 @@ const DateTimeSheet = ({
         setIsSheetContentReady
     ] = useState(false);
 
+    const [
+        dateCalendarType,
+        setDateCalendarType
+    ] = useState(dateCalendarTypeProp ?? "day");
+
     const {
+        calendarChangerToolContainer: calendarChangerToolContainerDynamicStyle,
+        calendarChangerToolButton: calendarChangerToolButtonDynamicStyle,
         loadingContainer: loadingContainerDynamicStyle,
         contentContainer: contentContainerDynamicStyle,
         headerContainer: headerContainerDynamicStyle,
@@ -80,9 +97,142 @@ const DateTimeSheet = ({
         headerTitle: headerTitleDynamicStyle,
         okButton: okButtonDynamicStyle
     } = useStyles({
-        spaces
+        radiuses,
+        borders,
+        spaces,
+        colors
     });
 
+    const renderCalenderChangerTool = () => {
+        if(!isShowCalenderChangerTool) {
+            return null;
+        }
+
+        if(dateCalendarTypeProp) {
+            return null;
+        }
+
+        return <View
+            style={[
+                stylesheet.calendarChangerToolContainer,
+                calendarChangerToolContainerDynamicStyle
+            ]}
+        >
+            <TouchableOpacity
+                style={[
+                    stylesheet.calendarChangerToolButton,
+                    calendarChangerToolButtonDynamicStyle,
+                    dateCalendarType === "day" ? {
+                        backgroundColor: colors.content.container.primary
+                    } : null
+                ]}
+                onPress={() => {
+                    setDateCalendarType("day");
+                }}
+            >
+                <Text
+                    color={dateCalendarType === "day" ? "onPrimary" : undefined}
+                >
+                    {localize("daily")}
+                </Text>
+            </TouchableOpacity>
+            <TouchableOpacity
+                style={[
+                    stylesheet.calendarChangerToolButton,
+                    calendarChangerToolButtonDynamicStyle,
+                    dateCalendarType === "month" ? {
+                        backgroundColor: colors.content.container.primary
+                    } : null
+                ]}
+                onPress={() => {
+                    setDateCalendarType("month");
+                }}
+            >
+                <Text
+                    color={dateCalendarType === "month" ? "onPrimary" : undefined}
+                >
+                    {localize("monthly")}
+                </Text>
+            </TouchableOpacity>
+            <TouchableOpacity
+                style={[
+                    stylesheet.calendarChangerToolButton,
+                    calendarChangerToolButtonDynamicStyle,
+                    dateCalendarType === "year" ? {
+                        backgroundColor: colors.content.container.primary
+                    } : null
+                ]}
+                onPress={() => {
+                    setDateCalendarType("year");
+                }}
+            >
+                <Text
+                    color={dateCalendarType === "year" ? "onPrimary" : undefined}
+                >
+                    {localize("yearly")}
+                </Text>
+            </TouchableOpacity>
+        </View>;
+    };
+
+    const renderDateCalendar = () => {
+        if(pickerType === "time") {
+            return null;
+        }
+
+        if(dateCalendarType === "month") {
+            return <MonthSelector
+                multipleSelectMinimumRequiredDayCount={multipleSelectMinimumRequiredDayCount}
+                localeBasedFirstDayOfWeek={localeBasedFirstDayOfWeek}
+                setIsSheetContentReady={setIsSheetContentReady}
+                multipleSelectDayLimit={multipleSelectDayLimit}
+                monthOfYearLengthType={monthOfYearLengthType}
+                selectMultipleObject={selectMultipleObject}
+                isShowTodayIndicator={isShowTodayIndicator}
+                selectObject={selectObject}
+                dateRange={dateRange}
+                maxDate={maxDate}
+                minDate={minDate}
+                variant={variant}
+                date={date}
+            />;
+        }
+
+        if(dateCalendarType === "year") {
+            return <YearSelector
+                multipleSelectMinimumRequiredDayCount={multipleSelectMinimumRequiredDayCount}
+                localeBasedFirstDayOfWeek={localeBasedFirstDayOfWeek}
+                setIsSheetContentReady={setIsSheetContentReady}
+                multipleSelectDayLimit={multipleSelectDayLimit}
+                selectMultipleObject={selectMultipleObject}
+                isShowTodayIndicator={isShowTodayIndicator}
+                yearLengthType={yearLengthType}
+                selectObject={selectObject}
+                dateRange={dateRange}
+                maxDate={maxDate}
+                minDate={minDate}
+                variant={variant}
+                date={date}
+            />;
+        }
+
+        return <DateSelector
+            multipleSelectMinimumRequiredDayCount={multipleSelectMinimumRequiredDayCount}
+            localeBasedFirstDayOfWeek={localeBasedFirstDayOfWeek}
+            setIsSheetContentReady={setIsSheetContentReady}
+            multipleSelectDayLimit={multipleSelectDayLimit}
+            selectMultipleObject={selectMultipleObject}
+            isShowTodayIndicator={isShowTodayIndicator}
+            dayOfWeekLength={dayOfWeekLength}
+            selectObject={selectObject}
+            dateRange={dateRange}
+            maxDate={maxDate}
+            minDate={minDate}
+            variant={variant}
+            date={date}
+        />;
+    };
+
     const renderBottomSheetContent = () => {
         if(isLoading) {
             return <View
@@ -100,26 +250,12 @@ const DateTimeSheet = ({
 
         return <View
             style={[
+                stylesheet.contentContainer,
                 contentContainerDynamicStyle
             ]}
         >
-            {
-                pickerType === "time" ? null : <DateSelector
-                    multipleSelectMinimumRequiredDayCount={multipleSelectMinimumRequiredDayCount}
-                    localeBasedFirstDayOfWeek={localeBasedFirstDayOfWeek}
-                    setIsSheetContentReady={setIsSheetContentReady}
-                    multipleSelectDayLimit={multipleSelectDayLimit}
-                    selectMultipleObject={selectMultipleObject}
-                    isShowTodayIndicator={isShowTodayIndicator}
-                    dayOfWeekLength={dayOfWeekLength}
-                    selectObject={selectObject}
-                    dateRange={dateRange}
-                    maxDate={maxDate}
-                    minDate={minDate}
-                    variant={variant}
-                    date={date}
-                />
-            }
+            {renderCalenderChangerTool()}
+            {renderDateCalendar()}
             {
                 pickerType === "date" ? null : <TimeSelector
                     isTimeSelectWorksWithCenterWhenTimePicker={isTimeSelectWorksWithCenterWhenTimePicker}

+ 24 - 2
src/components/dateTimeSheet/stylesheet.ts

@@ -10,6 +10,9 @@ import type {
 } from "../../types";
 
 const stylesheet = StyleSheet.create({
+    contentContainer: {
+        flexDirection: "column"
+    },
     headerContainer: {
         flexDirection: "column",
         width: "100%"
@@ -35,11 +38,20 @@ const stylesheet = StyleSheet.create({
         justifyContent: "center",
         alignItems: "center",
         width: "100%"
+    },
+    calendarChangerToolContainer: {
+        alignSelf: "baseline",
+        flexDirection: "row"
+    },
+    calendarChangerToolButton: {
     }
 });
 
 export const useStyles = ({
-    spaces
+    radiuses,
+    borders,
+    spaces,
+    colors
 }: DateTimeSheetDynamicStyleType) => {
     const styles = {
         contentContainer: {
@@ -49,7 +61,6 @@ export const useStyles = ({
             marginBottom: spaces.spacingSm
         } as Mutable<ViewStyle>,
         headerTitle: {
-
         } as Mutable<ViewStyle>,
         bottomContainer: {
             marginTop: spaces.spacingSm,
@@ -69,6 +80,17 @@ export const useStyles = ({
         } as Mutable<ViewStyle>,
         moreLoadingContainer: {
             padding: spaces.spacingSm
+        } as Mutable<ViewStyle>,
+        calendarChangerToolContainer: {
+            borderColor: colors.content.border.subtle,
+            marginBottom: spaces.spacingSm,
+            borderRadius: radiuses.actions,
+            borderWidth: borders.line
+        } as Mutable<ViewStyle>,
+        calendarChangerToolButton: {
+            paddingHorizontal: spaces.spacingSm,
+            paddingVertical: spaces.spacingSm,
+            borderRadius: radiuses.actions
         } as Mutable<ViewStyle>
     };
 

+ 15 - 7
src/components/dateTimeSheet/type.ts

@@ -16,19 +16,28 @@ import type {
 import type {
     DateTimePickerPickerType,
     DateTimePickerDateRange,
-    DateTimePickerVariant
+    DateTimePickerVariant,
+    DateCalendarType
 } from "../dateTimePicker/type";
 import type {
     DayOfWeekLengthType
 } from "../dateSelector/type";
+import type {
+    MonthOfYearLengthType
+} from "../monthSelector/type";
+import type {
+    YearLengthType
+} from "../yearSelector/type";
 
 export type DateTimeSheetDynamicStyleType = {
+    radiuses: NCoreUIKit.ActivePalette["radiuses"];
+    borders: NCoreUIKit.ActivePalette["borders"];
     spaces: NCoreUIKit.ActivePalette["spaces"];
+    colors: NCoreUIKit.ActivePalette["colors"];
 };
 
 interface IDateTimeSheetProps {
     selectMultipleObject:  (dateRange?: DateTimePickerDateRange) => void;
-    setDateRule: Dispatch<SetStateAction<RRule | undefined>>;
     customTheme?: {
         gapPropagation?: keyof NCoreUIKit.GapPropagationKey;
         sharpness?: keyof NCoreUIKit.SharpnessKey;
@@ -36,26 +45,25 @@ interface IDateTimeSheetProps {
         themeKey?: keyof NCoreUIKit.ThemeKey;
     };
     isTimeSelectWorksWithCenterWhenTimePicker?: boolean;
-    setIsLoading: Dispatch<SetStateAction<boolean>>;
     setIsActive: Dispatch<SetStateAction<boolean>>;
     multipleSelectMinimumRequiredDayCount?: number;
     customLocalize?: {
         activeLocale?: keyof NCoreUIKit.LocaleKey;
     };
+    monthOfYearLengthType?: MonthOfYearLengthType;
     bottomSheetRef: Ref<IBottomSheetRef> | null;
     selectDateRule: (dateRule?: RRule) => void;
     LoadingIconComponentProp?: NCoreUIKitIcon;
     multipleSelectMinimumRequireMS?: number;
-    setDateRange: Dispatch<SetStateAction<{
-        start?: Date;
-        end?: Date;
-    } | undefined>>;
     dayOfWeekLength?: DayOfWeekLengthType;
     pickerType: DateTimePickerPickerType;
     localeBasedFirstDayOfWeek?: boolean;
+    isShowCalenderChangerTool?: boolean;
     selectObject: (date?: Date) => void;
+    dateCalendarType?: DateCalendarType;
     multipleSelectMaximumMS?: number;
     multipleSelectDayLimit?: number;
+    yearLengthType?: YearLengthType;
     isShowTodayIndicator?: boolean;
     variant: DateTimePickerVariant;
     isWorkWithRealtime: boolean;

+ 442 - 0
src/components/monthSelector/index.tsx

@@ -0,0 +1,442 @@
+import {
+    useImperativeHandle,
+    useLayoutEffect,
+    forwardRef,
+    useEffect,
+    useState,
+    type Ref,
+    useRef
+} from "react";
+import {
+    TouchableOpacity,
+    type ViewStyle,
+    View
+} from "react-native";
+import type {
+    IMonthSelectorRef,
+    CalendarMonth
+} from "./type";
+import type IMonthSelectorProps from "./type";
+import stylesheet, {
+    useStyles
+} from "./stylesheet";
+import {
+    dateSelect
+} from "./util";
+import {
+    NCoreUIKitLocalize,
+    NCoreUIKitTheme,
+    NCoreUIKitToast
+} from "../../core/hooks";
+import type {
+    Mutable
+} from "../../types";
+import {
+    ChevronRight as ChevronRightIcon,
+    ChevronLeft as ChevronLeftIcon
+} from "lucide-react-native";
+import moment from "moment";
+import Text from "../text";
+
+const MONTH_LINES = [
+    [
+        0,
+        1,
+        2
+    ],
+    [
+        3,
+        4,
+        5
+    ],
+    [
+        6,
+        7,
+        8
+    ],
+    [
+        9,
+        10,
+        11
+    ]
+];
+
+const MonthSelector = ({
+    multipleSelectMinimumRequiredDayCount,
+    monthOfYearLengthType = "long",
+    isShowTodayIndicator = true,
+    viewDate: viewDateProp,
+    multipleSelectDayLimit,
+    setIsSheetContentReady,
+    selectMultipleObject,
+    selectObject,
+    dateRange,
+    maxDate,
+    minDate,
+    variant,
+    date
+}: IMonthSelectorProps, ref: Ref<IMonthSelectorRef>) => {
+    const {
+        radiuses,
+        borders,
+        spaces,
+        colors
+    } = NCoreUIKitTheme.useContext();
+
+    const {
+        localize
+    } = NCoreUIKitLocalize.useContext();
+
+    const selectDate = dateSelect({
+        dateRange,
+        variant,
+        date
+    });
+
+    const [
+        viewDate,
+        setViewDate
+    ] = useState(viewDateProp ? viewDateProp : selectDate);
+
+    const [
+        yearMonths,
+        setYearMonths
+    ] = useState<Array<CalendarMonth>>([]);
+
+    const allSelectedMonths = useRef<Array<Date>>([]);
+
+    const {
+        nextPrevToolChevronButton: nextPrevToolChevronButtonDynamicStyle,
+        nextPrevToolContainer: nextPrevToolContainerDynamicStyle,
+        todayIndicator: todayIndicatorDynamicStyle,
+        day: dayDynamicStyle
+    } = useStyles({
+        radiuses,
+        borders,
+        colors,
+        spaces
+    });
+
+    useEffect(() => {
+        const allMonths = getMonthsOfViewYear({
+            tDate: viewDate
+        });
+
+        setYearMonths(allMonths);
+    }, [
+        dateRange,
+        viewDate,
+        date
+    ]);
+
+    useLayoutEffect(() => {
+        if(yearMonths) setIsSheetContentReady(true);
+    }, [
+        yearMonths
+    ]);
+
+    useEffect(() => {
+        if(viewDateProp) {
+            setViewDate(viewDateProp);
+        }
+    }, [viewDateProp]);
+
+    useImperativeHandle(
+        ref,
+        () => ({
+            changeCurrentYear: (date: Date) => {
+                setViewDate(date);
+            }
+        }),
+        []
+    );
+
+    const getMonthsOfViewYear = ({
+        tDate
+    }: {
+        tDate: Date
+    }): Array<CalendarMonth> => {
+        allSelectedMonths.current = [];
+
+        const targetDate = moment(new Date(tDate));
+
+        const startOfCalendar = targetDate.clone().startOf("year");
+
+        const months = monthOfYearLengthType === "short" ? moment.monthsShort() : moment.months();
+
+        const totalMonths = months.length;
+
+        const allMonths = Array.from({
+            length: totalMonths
+        }).map((_, index) => {
+            const currentMonth = startOfCalendar.clone().add(index, "months");
+
+            let isSelected = false;
+
+            if(variant === "single" && date) {
+                allSelectedMonths.current = [date];
+
+                if(moment(currentMonth).isSame(date, "month")) isSelected = true;
+            } else if(variant === "range" && dateRange && dateRange.start) {
+                if(dateRange.end) {
+                    allSelectedMonths.current = Array.from({
+                        length: moment(dateRange.end).diff(dateRange.start, "months") + 1
+                    }, (_, i) =>
+                        moment(dateRange.start).clone().add(i, "months").toDate()
+                    );
+
+                    if(moment(currentMonth).isBetween(dateRange.start, dateRange.end, "month", "[]")) isSelected = true;
+                } else if(moment(currentMonth).isSame(dateRange.start)) {
+                    allSelectedMonths.current = [dateRange.start];
+
+                    isSelected = true;
+                }
+            }
+
+            let isDisabled = false;
+
+            if(minDate && moment(currentMonth).isBefore(moment(minDate).startOf("month"))) {
+                isDisabled = true;
+            }
+
+            if(maxDate && moment(currentMonth).isAfter(moment(maxDate).endOf("month"))) {
+                isDisabled = true;
+            }
+
+            const monthNumber = currentMonth.month();
+
+            return {
+                originalIndex: allSelectedMonths.current.findIndex((month) => moment(currentMonth).isSame(month, "month")),
+                isCurrentYear: currentMonth.isSame(targetDate, "year"),
+                isToday: currentMonth.isSame(moment(), "month"),
+                title: months[monthNumber] as string,
+                monthNumber: monthNumber + 1,
+                monthOfYear: monthNumber,
+                date: currentMonth,
+                isDisabled,
+                isSelected
+            };
+        });
+
+        return allMonths;
+    };
+
+    const renderDay = ({
+        monthIndex,
+        monthItem
+    }: {
+        monthItem: CalendarMonth;
+        monthIndex: number;
+    }) => {
+        const isLastItemSelected = monthItem.isSelected && monthItem.originalIndex === allSelectedMonths.current.length - 1;
+        const isFirstItemSelected = monthItem.isSelected && monthItem.originalIndex === 0;
+
+        const selectionStyle: Array<Mutable<ViewStyle> | null> = [
+            isFirstItemSelected && allSelectedMonths.current.length > 1 ? {
+                borderBottomRightRadius: 0,
+                borderTopRightRadius: 0
+            } : null,
+            isLastItemSelected && allSelectedMonths.current.length > 1 ? {
+                borderBottomLeftRadius: 0,
+                borderTopLeftRadius: 0
+            } : null
+        ];
+
+        let dayTitleColor: keyof NCoreUIKit.TextContentColors = "mid";
+
+        if(monthItem.isSelected && !isLastItemSelected && !isFirstItemSelected) {
+            dayTitleColor = "emphasized";
+
+            selectionStyle.push({
+                borderBottomRightRadius: 0,
+                borderBottomLeftRadius: 0,
+                borderTopRightRadius: 0,
+                borderTopLeftRadius: 0
+            });
+        } else if(monthItem.isSelected) {
+            dayTitleColor = "onPrimary";
+        }
+
+        if(!monthItem.isCurrentYear || monthItem.isDisabled) {
+            dayTitleColor = "disabled";
+        }
+
+        return <TouchableOpacity
+            disabled={!monthItem.isCurrentYear || monthItem.isDisabled}
+            key={`month-${monthIndex}`}
+            onPress={() => {
+                if(!monthItem.isCurrentYear) {
+                    return;
+                }
+
+                if(variant === "single") {
+                    const newDate = date as Date;
+
+                    newDate.setMonth(monthItem.date.toDate().getMonth());
+
+                    selectObject(newDate);
+                } else if(variant === "range") {
+                    if(!dateRange || !dateRange.start) {
+                        selectMultipleObject({
+                            start: new Date(monthItem.date.toDate().setHours(0, 0, 0)),
+                            end: undefined
+                        });
+                        return;
+                    }
+
+                    if(dateRange && moment(dateRange.start).isSame(monthItem.date, "month")) {
+                        selectMultipleObject({
+                            start: undefined,
+                            end: undefined
+                        });
+                        return;
+                    }
+
+                    if(dateRange && !dateRange.end) {
+                        if(multipleSelectDayLimit && moment(monthItem.date).diff(dateRange.start, "month") >= multipleSelectDayLimit) {
+                            NCoreUIKitToast.open({
+                                title: localize("maximum-selection-number-of-days-limit-exceeds")
+                            });
+                            return;
+                        }
+
+                        if(multipleSelectMinimumRequiredDayCount && moment(monthItem.date).diff(dateRange.start, "month") + 1 < multipleSelectMinimumRequiredDayCount) {
+                            NCoreUIKitToast.open({
+                                title: localize("minimum-selection-number-of-days-required-not-provided")
+                            });
+                            return;
+                        }
+
+                        if(moment(monthItem.date).isBefore(dateRange.start)) {
+                            selectMultipleObject({
+                                start: new Date(monthItem.date.toDate().setHours(0, 0, 0)),
+                                end: undefined
+                            });
+                        } else {
+                            selectMultipleObject({
+                                end: new Date(monthItem.date.toDate().setHours(23, 59, 59)),
+                                start: dateRange?.start
+                            });
+                        }
+                        return;
+                    }
+
+                    selectMultipleObject({
+                        start: monthItem.date.toDate(),
+                        end: undefined
+                    });
+                }
+            }}
+            style={[
+                stylesheet.day,
+                dayDynamicStyle,
+                monthItem.isSelected ? {
+                    backgroundColor: colors.content.container.primary,
+                    borderColor: colors.content.container.primary
+                } : null,
+                monthItem.isSelected && !isLastItemSelected && !isFirstItemSelected ? {
+                    backgroundColor: colors.content.container.emphasized,
+                    borderColor: colors.content.container.emphasized
+                } : null,
+                monthItem.isSelected && (monthItem.isDisabled || !monthItem.isCurrentYear) ? {
+                    opacity: 0.33
+                } : null,
+                ...selectionStyle
+            ]}
+        >
+            <Text
+                variant="labelLargeSize"
+                color={dayTitleColor}
+            >
+                {monthItem.title}
+            </Text>
+            {isShowTodayIndicator && monthItem.isToday ? <View
+                style={[
+                    stylesheet.todayIndicator,
+                    selectionStyle,
+                    todayIndicatorDynamicStyle,
+                    monthItem.isSelected ? {
+                        borderColor: colors.content.border.subtle
+                    } : null
+                ]}
+            /> : null}
+        </TouchableOpacity>;
+    };
+
+    const renderNextPrevTool = () => {
+        return <View
+            style={[
+                stylesheet.nextPrevToolContainer,
+                nextPrevToolContainerDynamicStyle
+            ]}
+        >
+            <TouchableOpacity
+                onPress={() => {
+                    const prevViewDate = moment(viewDate).clone().subtract(1, "years");
+                    setViewDate(prevViewDate.toDate());
+                }}
+                style={[
+                    nextPrevToolChevronButtonDynamicStyle
+                ]}
+            >
+                <ChevronLeftIcon
+                    color={colors.content.icon.default}
+                    size={26}
+                />
+            </TouchableOpacity>
+            <Text
+                variant="labelLargeSize"
+                color="high"
+            >
+                {moment(viewDate).format("YYYY")}
+            </Text>
+            <TouchableOpacity
+                onPress={() => {
+                    const nextViewDate = moment(viewDate).clone().add(1, "years");
+                    setViewDate(nextViewDate.toDate());
+                }}
+                style={[
+                    nextPrevToolChevronButtonDynamicStyle
+                ]}
+            >
+                <ChevronRightIcon
+                    color={colors.content.icon.default}
+                    size={26}
+                />
+            </TouchableOpacity>
+        </View>;
+    };
+
+    return <View
+        style={[
+            stylesheet.container
+        ]}
+    >
+        <View>
+            {renderNextPrevTool()}
+        </View>
+        <View
+            style={[
+                stylesheet.contentContainer
+            ]}
+        >
+            {yearMonths && yearMonths.length ? MONTH_LINES.map((mItem) => {
+                const currentMonths = mItem.map(mI => yearMonths[mI]);
+
+                return <View
+                    style={[
+                        stylesheet.rowContainer
+                    ]}
+                >
+                    {currentMonths.map((monthItem, monthIndex) => {
+                        return renderDay({
+                            monthItem: monthItem as CalendarMonth,
+                            monthIndex
+                        });
+                    })}
+                </View>;
+            }) : null}
+        </View>
+    </View>;
+};
+export default forwardRef(MonthSelector);

+ 89 - 0
src/components/monthSelector/stylesheet.ts

@@ -0,0 +1,89 @@
+import {
+    type ViewStyle,
+    StyleSheet
+} from "react-native";
+import type {
+    MonthSelectorDynamicStyle
+} from "./type";
+import type {
+    Mutable
+} from "../../types";
+
+const stylesheet = StyleSheet.create({
+    container: {
+        flex: 1
+    },
+    contentContainer: {
+        justifyContent: "space-between",
+        flexDirection: "column",
+        alignItems: "center"
+    },
+    columnContainer: {
+        justifyContent: "center",
+        flexDirection: "column",
+        alignItems: "center",
+        flexShrink: 1,
+        flex: 1
+    },
+    day: {
+        justifyContent: "center",
+        alignItems: "center",
+        position: "relative",
+        flexShrink: 1,
+        width: "100%",
+        flex: 1
+    },
+    todayIndicator: {
+        position: "absolute",
+        zIndex: 999
+    },
+    nextPrevToolContainer: {
+        justifyContent: "space-between",
+        flexDirection: "row",
+        alignItems: "center",
+        flex: 1
+    },
+    rowContainer: {
+        flexDirection: "row"
+    }
+});
+
+export const useStyles = ({
+    radiuses,
+    borders,
+    spaces,
+    colors
+}: MonthSelectorDynamicStyle) => {
+    const styles = {
+        day: {
+            borderBottomRightRadius: radiuses.full,
+            borderBottomLeftRadius: radiuses.full,
+            borderTopRightRadius: radiuses.full,
+            borderTopLeftRadius: radiuses.full,
+            borderColor: "transparent",
+            borderWidth: borders.line,
+            padding: spaces.spacingSm
+        } as Mutable<ViewStyle>,
+        todayIndicator: {
+            borderColor: colors.content.border.default,
+            borderBottomRightRadius: radiuses.full,
+            borderBottomLeftRadius: radiuses.full,
+            borderTopRightRadius: radiuses.full,
+            borderTopLeftRadius: radiuses.full,
+            borderWidth: borders.line,
+            bottom: spaces.spacingXs,
+            right: spaces.spacingXs,
+            left: spaces.spacingXs,
+            top: spaces.spacingXs
+        } as Mutable<ViewStyle>,
+        nextPrevToolContainer: {
+            marginBottom: spaces.spacingMd
+        } as Mutable<ViewStyle>,
+        nextPrevToolChevronButton: {
+            padding: spaces.spacingSm
+        } as Mutable<ViewStyle>
+    };
+
+    return styles;
+};
+export default stylesheet;

+ 59 - 0
src/components/monthSelector/type.ts

@@ -0,0 +1,59 @@
+import type {
+    SetStateAction,
+    Dispatch
+} from "react";
+import type {
+    DateTimePickerDateRange,
+    DateTimePickerVariant
+} from "../dateTimePicker/type";
+import type moment from "moment";
+
+export interface IMonthSelectorRef {
+    changeCurrentYear: (date: Date) => void;
+};
+
+export type MonthSelectorDynamicStyle = {
+    radiuses: NCoreUIKit.ActivePalette["radiuses"];
+    borders: NCoreUIKit.ActivePalette["borders"];
+    spaces: NCoreUIKit.ActivePalette["spaces"];
+    colors: NCoreUIKit.ActivePalette["colors"];
+};
+
+export interface CalendarMonth {
+    isCurrentYear: boolean;
+    originalIndex: number;
+    isDisabled: boolean;
+    isSelected: boolean;
+    date: moment.Moment;
+    monthNumber: number;
+    monthOfYear: number;
+    isToday: boolean;
+    title: string;
+};
+
+export type MonthSelectInitialGeneratorType = {
+    dateRange?: DateTimePickerDateRange;
+    variant: DateTimePickerVariant;
+    date?: Date;
+};
+
+export type MonthOfYearLengthType = "short" | "long";
+
+interface IMonthSelectorProps {
+    selectMultipleObject: (dateRange?: DateTimePickerDateRange) => void;
+    setIsSheetContentReady: Dispatch<SetStateAction<boolean>>;
+    multipleSelectMinimumRequiredDayCount?: number;
+    monthOfYearLengthType?: MonthOfYearLengthType;
+    selectObject: (date?: Date) => void;
+    dateRange?: DateTimePickerDateRange;
+    multipleSelectDayLimit?: number;
+    isShowTodayIndicator?: boolean;
+    variant: DateTimePickerVariant;
+    viewDate?: Date;
+    maxDate?: Date;
+    minDate?: Date;
+    date?: Date;
+};
+export type {
+    IMonthSelectorProps as default
+};

+ 20 - 0
src/components/monthSelector/util.ts

@@ -0,0 +1,20 @@
+import type {
+    MonthSelectInitialGeneratorType
+} from "./type";
+
+export const dateSelect = ({
+    dateRange,
+    variant,
+    date
+}: MonthSelectInitialGeneratorType): Date => {
+    if(variant === "single" && date) {
+        return date;
+    }
+
+    if(variant === "range" && dateRange && (dateRange.start || dateRange.end)) {
+        if(dateRange.start) return dateRange.start;
+        if(dateRange.end) return dateRange.end;
+    }
+
+    return new Date();
+};

+ 435 - 0
src/components/yearSelector/index.tsx

@@ -0,0 +1,435 @@
+import {
+    useImperativeHandle,
+    useLayoutEffect,
+    forwardRef,
+    useEffect,
+    useState,
+    type Ref,
+    useRef
+} from "react";
+import {
+    TouchableOpacity,
+    type ViewStyle,
+    View
+} from "react-native";
+import type {
+    IYearSelectorRef,
+    CalendarYear
+} from "./type";
+import type IYearSelectorProps from "./type";
+import stylesheet, {
+    useStyles
+} from "./stylesheet";
+import {
+    dateSelect
+} from "./util";
+import {
+    NCoreUIKitLocalize,
+    NCoreUIKitTheme,
+    NCoreUIKitToast
+} from "../../core/hooks";
+import type {
+    Mutable
+} from "../../types";
+import {
+    ChevronRight as ChevronRightIcon,
+    ChevronLeft as ChevronLeftIcon
+} from "lucide-react-native";
+import moment from "moment";
+import Text from "../text";
+
+const YearSelector = ({
+    multipleSelectMinimumRequiredDayCount,
+    isShowTodayIndicator = true,
+    localeBasedFirstDayOfWeek,
+    yearLengthType = "long",
+    viewDate: viewDateProp,
+    multipleSelectDayLimit,
+    setIsSheetContentReady,
+    selectMultipleObject,
+    selectObject,
+    dateRange,
+    maxDate,
+    minDate,
+    variant,
+    date
+}: IYearSelectorProps, ref: Ref<IYearSelectorRef>) => {
+    const {
+        radiuses,
+        borders,
+        spaces,
+        colors
+    } = NCoreUIKitTheme.useContext();
+
+    const {
+        localize
+    } = NCoreUIKitLocalize.useContext();
+
+    const selectDate = dateSelect({
+        dateRange,
+        variant,
+        date
+    });
+
+    const [
+        viewDate,
+        setViewDate
+    ] = useState(viewDateProp ? viewDateProp : selectDate);
+
+    const [
+        monthDays,
+        setMonthDays
+    ] = useState<Array<CalendarYear>>([]);
+
+    const allSelectedDays = useRef<Array<Date>>([]);
+
+    const {
+        nextPrevToolChevronButton: nextPrevToolChevronButtonDynamicStyle,
+        nextPrevToolContainer: nextPrevToolContainerDynamicStyle,
+        todayIndicator: todayIndicatorDynamicStyle,
+        dayOfWeek: dayOfWeekDynamicStyle,
+        day: dayDynamicStyle
+    } = useStyles({
+        radiuses,
+        borders,
+        colors,
+        spaces
+    });
+
+    useEffect(() => {
+        const allDays = getDaysOfViewMonth({
+            tDate: viewDate
+        });
+
+        setMonthDays(allDays);
+    }, [
+        dateRange,
+        viewDate,
+        date
+    ]);
+
+    useLayoutEffect(() => {
+        if(monthDays) setIsSheetContentReady(true);
+    }, [
+        monthDays
+    ]);
+
+    useEffect(() => {
+        if(viewDateProp) {
+            setViewDate(viewDateProp);
+        }
+    }, [viewDateProp]);
+
+    useImperativeHandle(
+        ref,
+        () => ({
+            changeCurrentView: (date: Date) => {
+                setViewDate(date);
+            }
+        }),
+        []
+    );
+
+    const getDaysOfViewMonth = ({
+        tDate
+    }: {
+        tDate: Date
+    }): Array<CalendarYear> => {
+        allSelectedDays.current = [];
+
+        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) {
+                allSelectedDays.current = [date];
+
+                if(moment(currentDay).isSame(date, "day")) isSelected = true;
+            } else if(variant === "range" && dateRange && dateRange.start) {
+                if(dateRange.end) {
+                    allSelectedDays.current = Array.from({
+                        length: moment(dateRange.end).diff(dateRange.start, "days") + 1
+                    }, (_, i) =>
+                        moment(dateRange.start).clone().add(i, "days").toDate()
+                    );
+
+                    if(moment(currentDay).isBetween(dateRange.start, dateRange.end, "day", "[]")) isSelected = true;
+                } else if(moment(currentDay).isSame(dateRange.start)) {
+                    allSelectedDays.current = [dateRange.start];
+
+                    isSelected = true;
+                }
+            }
+
+            let isDisabled = false;
+
+            if(minDate && moment(currentDay).isBefore(minDate)) {
+                isDisabled = true;
+            }
+
+            if(maxDate && moment(currentDay).isAfter(maxDate)) {
+                isDisabled = true;
+            }
+
+            return {
+                originalIndex: allSelectedDays.current.findIndex((day) => moment(currentDay).isSame(day, "day")),
+                isCurrentView: currentDay.isSame(targetDate, "month"),
+                isToday: currentDay.isSame(moment(), "day"),
+                dayOfWeek: currentDay.weekday(),
+                dayNumber: currentDay.date(),
+                date: currentDay,
+                isDisabled,
+                isSelected
+            };
+        });
+
+        return allDays;
+    };
+
+    const renderDay = ({
+        weekIndex,
+        dayIndex,
+        dayItem
+    }: {
+        dayItem: CalendarYear;
+        weekIndex: number;
+        dayIndex: number;
+    }) => {
+        const isLastItemSelected = dayItem.isSelected && dayItem.originalIndex === allSelectedDays.current.length - 1;
+        const isFirstItemSelected = dayItem.isSelected && dayItem.originalIndex === 0;
+
+        const selectionStyle: Array<Mutable<ViewStyle> | null> = [
+            isFirstItemSelected && allSelectedDays.current.length > 1 ? {
+                borderBottomRightRadius: 0,
+                borderTopRightRadius: 0
+            } : null,
+            isLastItemSelected && allSelectedDays.current.length > 1 ? {
+                borderBottomLeftRadius: 0,
+                borderTopLeftRadius: 0
+            } : null
+        ];
+
+        let dayTitleColor: keyof NCoreUIKit.TextContentColors = "mid";
+
+        if(dayItem.isSelected && !isLastItemSelected && !isFirstItemSelected) {
+            dayTitleColor = "emphasized";
+
+            selectionStyle.push({
+                borderBottomRightRadius: 0,
+                borderBottomLeftRadius: 0,
+                borderTopRightRadius: 0,
+                borderTopLeftRadius: 0
+            });
+        } else if(dayItem.isSelected) {
+            dayTitleColor = "onPrimary";
+        }
+
+        if(!dayItem.isCurrentView || dayItem.isDisabled) {
+            dayTitleColor = "disabled";
+        }
+
+        return <TouchableOpacity
+            disabled={!dayItem.isCurrentView || dayItem.isDisabled}
+            key={`day-${weekIndex}-${dayIndex}`}
+            onPress={() => {
+                if(!dayItem.isCurrentView) {
+                    return;
+                }
+
+                if(variant === "single") {
+                    selectObject(dayItem.date.toDate());
+                } else if(variant === "range") {
+                    if(!dateRange || !dateRange.start) {
+                        selectMultipleObject({
+                            start: new Date(dayItem.date.toDate().setHours(0, 0, 0)),
+                            end: undefined
+                        });
+                        return;
+                    }
+
+                    if(dateRange && moment(dateRange.start).isSame(dayItem.date, "day")) {
+                        selectMultipleObject({
+                            start: undefined,
+                            end: undefined
+                        });
+                        return;
+                    }
+
+                    if(dateRange && !dateRange.end) {
+                        if(multipleSelectDayLimit && moment(dayItem.date).diff(dateRange.start, "days") >= multipleSelectDayLimit) {
+                            NCoreUIKitToast.open({
+                                title: localize("maximum-selection-number-of-days-limit-exceeds")
+                            });
+                            return;
+                        }
+
+                        if(multipleSelectMinimumRequiredDayCount && moment(dayItem.date).diff(dateRange.start, "days") + 1 < multipleSelectMinimumRequiredDayCount) {
+                            NCoreUIKitToast.open({
+                                title: localize("minimum-selection-number-of-days-required-not-provided")
+                            });
+                            return;
+                        }
+
+                        if(moment(dayItem.date).isBefore(dateRange.start)) {
+                            selectMultipleObject({
+                                start: new Date(dayItem.date.toDate().setHours(0, 0, 0)),
+                                end: undefined
+                            });
+                        } else {
+                            selectMultipleObject({
+                                end: new Date(dayItem.date.toDate().setHours(23, 59, 59)),
+                                start: dateRange?.start
+                            });
+                        }
+                        return;
+                    }
+
+                    selectMultipleObject({
+                        start: dayItem.date.toDate(),
+                        end: undefined
+                    });
+                }
+            }}
+            style={[
+                stylesheet.day,
+                dayDynamicStyle,
+                dayItem.isSelected ? {
+                    backgroundColor: colors.content.container.primary,
+                    borderColor: colors.content.container.primary
+                } : null,
+                dayItem.isSelected && !isLastItemSelected && !isFirstItemSelected ? {
+                    backgroundColor: colors.content.container.emphasized,
+                    borderColor: colors.content.container.emphasized
+                } : null,
+                dayItem.isSelected && (dayItem.isDisabled || !dayItem.isCurrentView) ? {
+                    opacity: 0.33
+                } : null,
+                ...selectionStyle
+            ]}
+        >
+            <Text
+                variant="labelLargeSize"
+                color={dayTitleColor}
+            >
+                {dayItem.dayNumber}
+            </Text>
+            {isShowTodayIndicator && dayItem.isToday ? <View
+                style={[
+                    stylesheet.todayIndicator,
+                    selectionStyle,
+                    todayIndicatorDynamicStyle,
+                    dayItem.isSelected ? {
+                        borderColor: colors.content.border.subtle
+                    } : null
+                ]}
+            /> : null}
+        </TouchableOpacity>;
+    };
+
+    const renderNextPrevTool = () => {
+        return <View
+            style={[
+                stylesheet.nextPrevToolContainer,
+                nextPrevToolContainerDynamicStyle
+            ]}
+        >
+            <TouchableOpacity
+                onPress={() => {
+                    const prevViewDate = moment(viewDate).clone().subtract(1, "months");
+                    setViewDate(prevViewDate.toDate());
+                }}
+                style={[
+                    nextPrevToolChevronButtonDynamicStyle
+                ]}
+            >
+                <ChevronLeftIcon
+                    color={colors.content.icon.default}
+                    size={26}
+                />
+            </TouchableOpacity>
+            <Text
+                variant="labelLargeSize"
+                color="high"
+            >
+                {moment(viewDate).format("MMMM YYYY")}
+            </Text>
+            <TouchableOpacity
+                onPress={() => {
+                    const nextViewDate = moment(viewDate).clone().add(1, "months");
+                    setViewDate(nextViewDate.toDate());
+                }}
+                style={[
+                    nextPrevToolChevronButtonDynamicStyle
+                ]}
+            >
+                <ChevronRightIcon
+                    color={colors.content.icon.default}
+                    size={26}
+                />
+            </TouchableOpacity>
+        </View>;
+    };
+
+    let weekDays = moment.weekdays(localeBasedFirstDayOfWeek ? true : false);
+
+    if(yearLengthType === "short") {
+        weekDays = moment.weekdaysShort(localeBasedFirstDayOfWeek ? true : false);
+    } else if(yearLengthType === "very-short") {
+        weekDays = moment.weekdaysMin(localeBasedFirstDayOfWeek ? true : false);
+    }
+
+    return <View
+        style={[
+            stylesheet.container
+        ]}
+    >
+        <View>
+            {renderNextPrevTool()}
+        </View>
+        <View
+            style={[
+                stylesheet.contentContainer
+            ]}
+        >
+            {weekDays.map((weekItem, weekIndex) => {
+                const days = monthDays.filter((dayItem) => dayItem.dayOfWeek === weekIndex);
+
+                return <View
+                    key={`day-column-${weekIndex}`}
+                    style={[
+                        stylesheet.columnContainer
+                    ]}
+                >
+                    <Text
+                        variant="labelMediumSize"
+                        numberOfLines={1}
+                        color="low"
+                        style={{
+                            ...dayOfWeekDynamicStyle
+                        }}
+                    >
+                        {weekItem}
+                    </Text>
+                    {days.map((dayItem, dayIndex) => {
+                        return renderDay({
+                            weekIndex,
+                            dayIndex,
+                            dayItem
+                        });
+                    })}
+                </View>;
+            })}
+        </View>
+    </View>;
+};
+export default forwardRef(YearSelector);

+ 92 - 0
src/components/yearSelector/stylesheet.ts

@@ -0,0 +1,92 @@
+import {
+    type TextStyle,
+    type ViewStyle,
+    StyleSheet
+} from "react-native";
+import type {
+    MonthSelectorDynamicStyle
+} from "./type";
+import type {
+    Mutable
+} from "../../types";
+
+const stylesheet = StyleSheet.create({
+    container: {
+        flex: 1
+    },
+    contentContainer: {
+        justifyContent: "space-between",
+        flexDirection: "row",
+        alignItems: "center",
+        flexShrink: 1,
+        flex: 1
+    },
+    columnContainer: {
+        justifyContent: "center",
+        flexDirection: "column",
+        alignItems: "center",
+        flexShrink: 1,
+        flex: 1
+    },
+    day: {
+        justifyContent: "center",
+        alignItems: "center",
+        position: "relative",
+        flexShrink: 1,
+        width: "100%",
+        flex: 1
+    },
+    todayIndicator: {
+        position: "absolute",
+        zIndex: 999
+    },
+    nextPrevToolContainer: {
+        justifyContent: "space-between",
+        flexDirection: "row",
+        alignItems: "center",
+        flex: 1
+    }
+});
+
+export const useStyles = ({
+    radiuses,
+    borders,
+    spaces,
+    colors
+}: MonthSelectorDynamicStyle) => {
+    const styles = {
+        dayOfWeek: {
+            marginBottom: spaces.spacingMd
+        } as Mutable<TextStyle>,
+        day: {
+            borderBottomRightRadius: radiuses.full,
+            borderBottomLeftRadius: radiuses.full,
+            borderTopRightRadius: radiuses.full,
+            borderTopLeftRadius: radiuses.full,
+            borderColor: "transparent",
+            borderWidth: borders.line,
+            padding: spaces.spacingSm
+        } as Mutable<ViewStyle>,
+        todayIndicator: {
+            borderColor: colors.content.border.default,
+            borderBottomRightRadius: radiuses.full,
+            borderBottomLeftRadius: radiuses.full,
+            borderTopRightRadius: radiuses.full,
+            borderTopLeftRadius: radiuses.full,
+            borderWidth: borders.line,
+            bottom: spaces.spacingXs,
+            right: spaces.spacingXs,
+            left: spaces.spacingXs,
+            top: spaces.spacingXs
+        } as Mutable<ViewStyle>,
+        nextPrevToolContainer: {
+            marginBottom: spaces.spacingMd
+        } as Mutable<ViewStyle>,
+        nextPrevToolChevronButton: {
+            padding: spaces.spacingSm
+        } as Mutable<ViewStyle>
+    };
+
+    return styles;
+};
+export default stylesheet;

+ 59 - 0
src/components/yearSelector/type.ts

@@ -0,0 +1,59 @@
+import type {
+    SetStateAction,
+    Dispatch
+} from "react";
+import type {
+    DateTimePickerDateRange,
+    DateTimePickerVariant
+} from "../dateTimePicker/type";
+import type moment from "moment";
+
+export interface IYearSelectorRef {
+    changeCurrentView: (date: Date) => void;
+};
+
+export type YearSelectorDynamicStyle = {
+    radiuses: NCoreUIKit.ActivePalette["radiuses"];
+    borders: NCoreUIKit.ActivePalette["borders"];
+    spaces: NCoreUIKit.ActivePalette["spaces"];
+    colors: NCoreUIKit.ActivePalette["colors"];
+};
+
+export interface CalendarYear {
+    isCurrentView: boolean;
+    originalIndex: number;
+    isDisabled: boolean;
+    isSelected: boolean;
+    date: moment.Moment;
+    dayNumber: number;
+    dayOfWeek: number;
+    isToday: boolean;
+};
+
+export type YearSelectInitialGeneratorType = {
+    dateRange?: DateTimePickerDateRange;
+    variant: DateTimePickerVariant;
+    date?: Date;
+};
+
+export type YearLengthType = "short" | "very-short" | "long";
+
+interface IYearSelectorProps {
+    selectMultipleObject: (dateRange?: DateTimePickerDateRange) => void;
+    setIsSheetContentReady: Dispatch<SetStateAction<boolean>>;
+    multipleSelectMinimumRequiredDayCount?: number;
+    selectObject: (date?: Date) => void;
+    localeBasedFirstDayOfWeek?: boolean;
+    dateRange?: DateTimePickerDateRange;
+    multipleSelectDayLimit?: number;
+    yearLengthType?: YearLengthType;
+    isShowTodayIndicator?: boolean;
+    variant: DateTimePickerVariant;
+    viewDate?: Date;
+    maxDate?: Date;
+    minDate?: Date;
+    date?: Date;
+};
+export type {
+    IYearSelectorProps as default
+};

+ 20 - 0
src/components/yearSelector/util.ts

@@ -0,0 +1,20 @@
+import type {
+    YearSelectInitialGeneratorType
+} from "./type";
+
+export const dateSelect = ({
+    dateRange,
+    variant,
+    date
+}: YearSelectInitialGeneratorType): Date => {
+    if(variant === "single" && date) {
+        return date;
+    }
+
+    if(variant === "range" && dateRange && (dateRange.start || dateRange.end)) {
+        if(dateRange.start) return dateRange.start;
+        if(dateRange.end) return dateRange.end;
+    }
+
+    return new Date();
+};

+ 6 - 0
src/variants/locales/default.json

@@ -24,7 +24,10 @@
             "is-optional": "Opsiyonel",
             "select-all": "Tümünü Seç",
             "end-time": "Bitiş Zamanı",
+            "monthly": "Aylık",
+            "yearly": "Yıllık",
             "cancel": "İptal",
+            "daily": "Günlük",
             "search": "Ara",
             "ok": "Tamam"
         },
@@ -123,8 +126,11 @@
             "is-optional": "Optional",
             "clean-all": "Clean All",
             "end-time": "End Time",
+            "monthly": "Monthly",
             "cancel": "Cancel",
             "search": "Search",
+            "yearly": "Yearly",
+            "daily": "Daily",
             "ok": "Okey"
         },
         "rruleConfig": {