Просмотр исходного кода

Merge branch 'release/1.0.0-pre-alpha.21'

lfabl 1 месяц назад
Родитель
Сommit
b003608718
35 измененных файлов с 2671 добавлено и 85 удалено
  1. 11 9
      example/package.json
  2. 4 0
      example/src/index.tsx
  3. 26 11
      example/src/pages/home/index.tsx
  4. 7 3
      package.json
  5. 68 36
      src/components/bottomSheet/index.tsx
  6. 3 1
      src/components/bottomSheet/type.ts
  7. 1 1
      src/components/button/type.ts
  8. 373 0
      src/components/dateSelector/index.tsx
  9. 92 0
      src/components/dateSelector/stylesheet.ts
  10. 60 0
      src/components/dateSelector/type.ts
  11. 26 0
      src/components/dateSelector/util.ts
  12. 897 0
      src/components/dateTimePicker/index.tsx
  13. 252 0
      src/components/dateTimePicker/stylesheet.ts
  14. 188 0
      src/components/dateTimePicker/type.ts
  15. 229 0
      src/components/dateTimeSheet/index.tsx
  16. 77 0
      src/components/dateTimeSheet/stylesheet.ts
  17. 91 0
      src/components/dateTimeSheet/type.ts
  18. 20 0
      src/components/index.ts
  19. 6 2
      src/components/markdownViewer/index.tsx
  20. 11 11
      src/components/selectBox/index.tsx
  21. 6 6
      src/components/selectBox/stylesheet.ts
  22. 0 3
      src/components/selectBox/type.ts
  23. 3 2
      src/components/selectSheet/index.tsx
  24. 1 0
      src/components/selectSheet/type.ts
  25. 0 0
      src/components/timeSelector/index.tsx
  26. 0 0
      src/components/timeSelector/stylesheet.ts
  27. 0 0
      src/components/timeSelector/type.ts
  28. 3 0
      src/context/localize.tsx
  29. 4 0
      src/helpers/index.ts
  30. 28 0
      src/helpers/locale/index.ts
  31. 16 0
      src/index.tsx
  32. 5 0
      src/types/index.ts
  33. 1 0
      src/types/locale.ts
  34. 140 0
      src/variants/locales/default.json
  35. 22 0
      yarn.lock

+ 11 - 9
example/package.json

@@ -11,21 +11,23 @@
     },
     "dependencies": {
         "@expo/metro-runtime": "~55.0.6",
-        "@react-navigation/elements": "^2.9.14",
-        "@react-navigation/native": "^7.2.2",
-        "@react-navigation/native-stack": "^7.14.11",
+        "@react-navigation/elements": "2.9.14",
+        "@react-navigation/native": "7.2.2",
+        "@react-navigation/native-stack": "7.14.11",
         "expo": "~55.0.4",
         "expo-font": "~55.0.4",
         "expo-status-bar": "~55.0.4",
-        "lucide-react-native": "^1.8.0",
-        "ncore-context": "^1.0.5",
+        "lucide-react-native": "1.8.0",
+        "moment": "https://git.nibgat.space/nibgat-community/moment.git",
+        "ncore-context": "1.0.5",
         "react": "19.2.6",
         "react-dom": "19.2.6",
         "react-native": "0.83.2",
-        "react-native-safe-area-context": "^5.7.0",
-        "react-native-screens": "^4.24.0",
-        "react-native-svg": "^15.15.3",
-        "react-native-web": "~0.21.0"
+        "react-native-safe-area-context": "5.7.0",
+        "react-native-screens": "4.24.0",
+        "react-native-svg": "15.15.3",
+        "react-native-web": "~0.21.0",
+        "rrule": "https://git.nibgat.space/nibgat-community/rrule.git"
     },
     "private": true,
     "devDependencies": {

+ 4 - 0
example/src/index.tsx

@@ -2,15 +2,19 @@ import Navigation from "./navigation";
 import {
     useFonts
 } from "expo-font";
+import moment from "moment";
 import {
     setupNCoreUIKit
 } from "ncore-ui-kit-mobile";
+import "moment/locale/tr";
 
 const NCoreUIKitBase = setupNCoreUIKit({
     initialSelectedGapPropagation: "spacious",
     initialSelectedTheme: "light"
 });
 
+moment.locale("tr");
+
 const App = () => {
     return <Navigation/>;
 };

+ 26 - 11
example/src/pages/home/index.tsx

@@ -16,6 +16,8 @@ import {
     NCoreUIKitDialog,
     NCoreUIKitToast,
     NCoreUIKitTheme,
+    DateTimePicker,
+    MarkdownViewer,
     PageContainer,
     TextAreaInput,
     BottomSheet,
@@ -27,8 +29,7 @@ import {
     Button,
     Dialog,
     Switch,
-    Text,
-    MarkdownViewer
+    Text
 } from "ncore-ui-kit-mobile";
 import {
     useNavigation
@@ -41,6 +42,7 @@ import {
     ChevronRight as ChevronRightIcon,
     HomeIcon
 } from "lucide-react-native";
+import moment from "moment";
 
 const X = [
     {
@@ -364,13 +366,24 @@ const Home = () => {
             </Fragment>;
         }}
     >
+        <DateTimePicker
+            minDate={moment().subtract(2, "months").toDate()}
+            maxDate={moment().add(1, "months").toDate()}
+            isCleanEnabled={true}
+            variant="single"
+            initialDateRange={{
+                start: moment().subtract(2, "days").startOf("day").toDate(),
+                end: moment().add(6, "days").endOf("day").toDate()
+            }}
+            initialDate={new Date()}
+        />
         <MarkdownViewer
             content={`
 # Merhaba Furkan!.
 
 Nabersin ?
 
-<right>
+<center>
 <link href="https://www.nibgat.com">
 ## Deneme **title** 2
 </link>
@@ -378,7 +391,7 @@ Nabersin ?
 \`\`\`
 safasfasfasf
 \`\`\`
-</right>
+</center>
 iyisindir **umarım.**
 
 \`\`\`
@@ -471,18 +484,20 @@ Burası alıntı metnidir.
         <SelectBox
             keyExtractor={(data, index) => `${data.t}-${index}`}
             titleExtractor={(data) => data.t}
+            initialSelectedItems={[{
+                __title: X[0]?.t as string,
+                __key: `${X[0]?.t}-0`
+            }]}
             subTitle="Deneme Subtitle"
             // isWorkWithRealtime={false}
-            hintText="Test deneme"
-            isShowSubTitle={true}
-            isMultipleSelect={true}
             isWorkWithRealtime={false}
+            spreadBehaviour="baseline"
+            isMultipleSelect={true}
+            isShowSubTitle={true}
+            hintText="Test deneme"
+            isCleanEnabled={true}
             maxChoice={-1}
             minChoice={0}
-            initialSelectedItems={[{
-                __title: X[0]?.t as string,
-                __key: `${X[0]?.t}-0`
-            }]}
             isSearchable={true}
             title="Deneme Box"
             isRequired={true}

+ 7 - 3
package.json

@@ -1,6 +1,6 @@
 {
     "name": "ncore-ui-kit-mobile",
-    "version": "1.0.0-pre-alpha.20",
+    "version": "1.0.0-pre-alpha.21",
     "description": "NİBGAT® | NCore - UI Kit for React-Native Mobile Apps.",
     "main": "./lib/module/index.js",
     "types": "./lib/typescript/src/index.d.ts",
@@ -99,11 +99,13 @@
     },
     "peerDependencies": {
         "lucide-react-native": ">= 0.577.0",
+        "moment": "*",
         "ncore-context": ">= 1.0.5",
         "react": "*",
         "react-native": "*",
         "react-native-safe-area-context": ">= 5.7.0",
-        "react-native-svg": ">= 15.15.3"
+        "react-native-svg": ">= 15.15.3",
+        "rrule": "*"
     },
     "workspaces": [
         "example"
@@ -156,8 +158,10 @@
     },
     "dependencies": {
         "lucide-react-native": "0.577.0",
+        "moment": "https://git.nibgat.space/nibgat-community/moment.git",
         "ncore-context": "1.0.5",
         "react-native-safe-area-context": "5.7.0",
-        "react-native-svg": "15.15.3"
+        "react-native-svg": "15.15.3",
+        "rrule": "https://git.nibgat.space/nibgat-community/rrule.git"
     }
 }

+ 68 - 36
src/components/bottomSheet/index.tsx

@@ -38,6 +38,7 @@ import {
 import Modal from "../modal";
 
 const BottomSheet: RefForwardingComponent<IBottomSheetRef, IBottomSheetProps> = ({
+    isContentReady: isContentReadyProp = true,
     renderHeader: RenderHeaderComponent,
     renderBottom: RenderBottomComponent,
     isForceFullScreenOnSwipe = false,
@@ -76,6 +77,7 @@ const BottomSheet: RefForwardingComponent<IBottomSheetRef, IBottomSheetProps> =
     ...props
 }, ref) => {
     const {
+        radiuses,
         colors,
         spaces
     } = NCoreUIKitTheme.useContext(customTheme);
@@ -142,6 +144,8 @@ const BottomSheet: RefForwardingComponent<IBottomSheetRef, IBottomSheetProps> =
     const isSwipeCloseRef = useRef(isSwipeClose);
     const isCanSwipeRef = useRef(isCanSwipe);
 
+    const isContentReadyRef = useRef(isContentReadyProp);
+
     if(!isWorkAsFullScreen && !isCanFullScreenOnSwipe && snapPoint) {
         maxHeight.current = snapPoint;
     }
@@ -149,11 +153,14 @@ const BottomSheet: RefForwardingComponent<IBottomSheetRef, IBottomSheetProps> =
     useImperativeHandle(
         ref,
         () => ({
-            close: (onClosed) => {
+            close: (onClosed?) => {
                 closeAnimation(undefined, onClosed);
             },
             open: () => {
                 setIsActive(true);
+            },
+            setIsContentReady: (status) => {
+                isContentReadyRef.current = status;
             }
         }),
         []
@@ -176,26 +183,20 @@ const BottomSheet: RefForwardingComponent<IBottomSheetRef, IBottomSheetProps> =
     }, [isWorkAsFullScreen]);
 
     useEffect(() => {
-        if(isMeasured && contentHeight.current !== -1) {
-            if(isCanFullScreenOnSwipe && !isWorkAsFullScreen) {
-                maxHeight.current = containerHeightRef.current - (isForceFullScreenOnSwipe ? 0 : topSafeArea);
-            } else if(isAutoHeight || !snapPoint) {
-                maxHeight.current = contentHeight.current;
-            }
-        }
-    }, [isMeasured]);
+        isContentReadyRef.current = isContentReadyProp;
+    }, [isContentReadyProp]);
 
     useEffect(() => {
-        if(isActive && isMeasured) {
-            if(!isWorkAsFullScreen && !isCanFullScreenOnSwipe && snapPoint) {
-                maxHeight.current = snapPoint;
-            } else if(isWorkAsFullScreen) {
-                maxHeight.current = containerHeightRef.current - (isWrapSafeAreaContext ? topSafeArea : 0);
-            } else {
-                maxHeight.current = containerHeightRef.current - (isForceFullScreenOnSwipe ? 0 : isWrapSafeAreaContext ? topSafeArea : 0);
-            }
+        if (!isMeasured || contentHeight.current === -1) return;
 
-            openAnimation();
+        if (isCanFullScreenOnSwipe && !isWorkAsFullScreen) {
+            maxHeight.current = containerHeightRef.current - (isForceFullScreenOnSwipe ? 0 : topSafeArea);
+        } else if (isAutoHeight || !snapPoint) {
+            maxHeight.current = contentHeight.current;
+        }
+
+        if (isActive) {
+            open();
         }
     }, [
         isActive,
@@ -205,7 +206,9 @@ const BottomSheet: RefForwardingComponent<IBottomSheetRef, IBottomSheetProps> =
     if(isActiveProp !== undefined) {
         useEffect(() => {
             setIsActive(isActiveProp);
-        }, [isActiveProp]);
+        }, [
+            isActiveProp
+        ]);
     }
 
     useEffect(() => {
@@ -288,22 +291,46 @@ const BottomSheet: RefForwardingComponent<IBottomSheetRef, IBottomSheetProps> =
         }
     };
 
-    const openAnimation = () => {
+    const openAnimation = (runIndex?: number) => {
+        if(isContentReadyRef.current) {
+            Animated.timing(animatedTranslateY, {
+                useNativeDriver: false,
+                duration: 300,
+                toValue: 0
+            }).start(({
+                finished
+            }) => {
+                if(finished) {
+                    if(onOpened) onOpened();
+                }
+            });
+        } else {
+            if(!runIndex || runIndex < 10) {
+                setTimeout(() => {
+                    openAnimation(runIndex ? runIndex + 1 : 1);
+                }, 100);
+            }
+        }
+    };
+
+    const open = () => {
+        if (translateYValue.current < 0) {
+            const safeValue = snapPoint ?? contentHeight.current;
+            animatedTranslateY.setValue(safeValue);
+            translateYValue.current = safeValue;
+        }
+
         resetState();
 
+        if (isAutoHeight || !snapPoint) {
+            animatedHeight.setValue(contentHeight.current);
+        }
+
         if(onOpen) onOpen();
 
-        Animated.timing(animatedTranslateY, {
-            useNativeDriver: false,
-            duration: 300,
-            toValue: 0
-        }).start(({
-            finished
-        }) => {
-            if(finished) {
-                if(onOpened) onOpened();
-            }
-        });
+        setTimeout(() => {
+            openAnimation();
+        }, 100);
     };
 
     const closeAnimation = (toValue?: number, _onClosed?: (props: {
@@ -341,17 +368,18 @@ const BottomSheet: RefForwardingComponent<IBottomSheetRef, IBottomSheetProps> =
     };
 
     const onLayout = (event: LayoutChangeEvent) => {
-        if (isMeasured) return;
-
         const {
             height
         } = event.nativeEvent.layout;
 
-        animatedHeight.setValue(height);
+        if (height === contentHeight.current) return;
 
         contentHeight.current = height;
 
-        setIsMeasured(true);
+        if(!isMeasured) {
+            animatedHeight.setValue(height);
+            setIsMeasured(true);
+        }
     };
 
     const resetState = () => {
@@ -363,7 +391,7 @@ const BottomSheet: RefForwardingComponent<IBottomSheetRef, IBottomSheetProps> =
 
         const pivot = isWorkAsFullScreen
             ? containerHeightRef.current
-            : (snapPoint ?? contentHeight.current);
+            : (isAutoHeight ? 0 : (snapPoint ?? contentHeight.current));
 
         initialHeight.current = pivot;
         initialTranslateY.current = 0;
@@ -718,6 +746,10 @@ const BottomSheet: RefForwardingComponent<IBottomSheetRef, IBottomSheetProps> =
             {...panResponder.panHandlers}
             onLayout={onLayout}
             style={[
+                {
+                    borderTopStartRadius: radiuses.lg,
+                    borderTopEndRadius: radiuses.lg
+                },
                 style,
                 {
                     height: (isAutoHeight || !snapPoint) && !isMeasured ? "auto" : animatedHeight,

+ 3 - 1
src/components/bottomSheet/type.ts

@@ -9,7 +9,8 @@ import type {
 import type IModalProps from "../modal/type";
 
 export interface IBottomSheetRef {
-    close: (onClosed: (props: {
+    setIsContentReady: (status: boolean) => void;
+    close: (onClosed?: (props: {
         id: string;
     }) => void) => void;
     open: () => void;
@@ -32,6 +33,7 @@ interface IBottomSheetProps {
     scrollEndThreshold?: number;
     isCloseOnOverlay?: boolean;
     isWorkWithPortal?: boolean;
+    isContentReady?: boolean;
     onScrollEnd?: () => void;
     modalProps?: IModalProps;
     isSwipeClose?: boolean;

+ 1 - 1
src/components/button/type.ts

@@ -80,7 +80,7 @@ export type ButtonVariant = "filled" | "outline" | "ghost";
 
 export type ButtonSize = "small" | "medium" | "large";
 
-interface IButtonProps extends Omit<ButtonProps, "title"> {
+interface IButtonProps extends Omit<ButtonProps, "title" | "disabled"> {
     displayBehaviourWhileLoading?: ButtonDisplayBehaviourWhileLoading;
     titleStyle?: StyleProp<TextStyle>[] | StyleProp<TextStyle>;
     style?: StyleProp<ViewStyle>[] | StyleProp<ViewStyle>;

+ 373 - 0
src/components/dateSelector/index.tsx

@@ -0,0 +1,373 @@
+import {
+    useImperativeHandle,
+    useLayoutEffect,
+    forwardRef,
+    useEffect,
+    useState,
+    type Ref
+} from "react";
+import {
+    TouchableOpacity,
+    View
+} 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 {
+    ChevronRight as ChevronRightIcon,
+    ChevronLeft as ChevronLeftIcon
+} from "lucide-react-native";
+import moment from "moment";
+import Text from "../text";
+
+const DateSelector = ({
+    isShowTodayIndicator = true,
+    localeBasedFirstDayOfWeek,
+    dayOfWeekLength = "long",
+    viewDate: viewDateProp,
+    setIsSheetContentReady,
+    dateRange,
+    dateRule,
+    maxDate,
+    minDate,
+    setDate,
+    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 {
+        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,
+        dateRule,
+        date
+    ]);
+
+    useLayoutEffect(() => {
+        if(monthDays) setIsSheetContentReady(true);
+    }, [
+        monthDays
+    ]);
+
+    useEffect(() => {
+        if(viewDateProp) {
+            setViewDate(viewDateProp);
+        }
+    }, [viewDateProp]);
+
+    useImperativeHandle(
+        ref,
+        () => ({
+            changeCurrentMonth: (date: Date) => {
+                setViewDate(date);
+            }
+        }),
+        []
+    );
+
+    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;
+            }
+
+            let isDisabled = false;
+
+            if(minDate && moment(currentDay).isBefore(minDate)) {
+                isDisabled = true;
+            }
+
+            if(maxDate && moment(currentDay).isAfter(maxDate)) {
+                isDisabled = true;
+            }
+
+            return {
+                isCurrentMonth: currentDay.isSame(targetDate, "month"),
+                isToday: currentDay.isSame(moment(), "day"),
+                dayOfWeek: currentDay.weekday(),
+                dayNumber: currentDay.date(),
+                date: currentDay,
+                isDisabled,
+                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
+        ];
+
+        let dayTitleColor: keyof NCoreUIKit.TextContentColors = "mid";
+
+        if(isPrevItemSelected && isNextItemSelected) {
+            dayTitleColor = "emphasized";
+        } else if(dayItem.isSelected) {
+            dayTitleColor = "onPrimary";
+        }
+
+        if(!dayItem.isCurrentMonth || dayItem.isDisabled) {
+            dayTitleColor = "disabled";
+        }
+
+        return <TouchableOpacity
+            disabled={!dayItem.isCurrentMonth || dayItem.isDisabled}
+            key={`day-${weekIndex}-${dayIndex}`}
+            onPress={() => {
+                if(!dayItem.isCurrentMonth) {
+                    return;
+                }
+
+                if(variant === "single") {
+                    setDate(dayItem.date.toDate());
+                } else if(variant === "range") {
+                    console.log("range");
+                } else {
+                    console.log("rrule");
+                }
+            }}
+            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
+                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(dayOfWeekLength === "short") {
+        weekDays = moment.weekdaysShort(localeBasedFirstDayOfWeek ? true : false);
+    } else if(dayOfWeekLength === "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.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>;
+};
+export default forwardRef(DateSelector);

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

@@ -0,0 +1,92 @@
+import {
+    StyleSheet,
+    type TextStyle,
+    type ViewStyle
+} from "react-native";
+import type {
+    DateSelectorDynamicStyle
+} 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
+}: DateSelectorDynamicStyle) => {
+    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;

+ 60 - 0
src/components/dateSelector/type.ts

@@ -0,0 +1,60 @@
+import type {
+    SetStateAction,
+    Dispatch
+} from "react";
+import type {
+    DateTimePickerDateRange,
+    DateTimePickerVariant
+} from "../dateTimePicker/type";
+import type moment from "moment";
+import type {
+    RRule
+} from "rrule";
+
+export interface IDateSelectorRef {
+    changeCurrentMonth: (date: Date) => void;
+};
+
+export type DateSelectorDynamicStyle = {
+    radiuses: NCoreUIKit.ActivePalette["radiuses"];
+    borders: NCoreUIKit.ActivePalette["borders"];
+    spaces: NCoreUIKit.ActivePalette["spaces"];
+    colors: NCoreUIKit.ActivePalette["colors"];
+};
+
+export interface CalendarDay {
+    isCurrentMonth: boolean;
+    isDisabled: boolean;
+    isSelected: boolean;
+    date: moment.Moment;
+    dayNumber: number;
+    dayOfWeek: number;
+    isToday: boolean;
+};
+
+export type DateSelectInitialGeneratorType = {
+    dateRange?: DateTimePickerDateRange;
+    variant: DateTimePickerVariant;
+    dateRule?: RRule;
+    date?: Date;
+};
+
+export type DayOfWeekLengthType = "short" | "very-short" | "long";
+
+interface IDateSelectorProps {
+    setIsSheetContentReady: Dispatch<SetStateAction<boolean>>;
+    setDate: Dispatch<SetStateAction<Date | undefined>>;
+    dayOfWeekLength?: DayOfWeekLengthType;
+    localeBasedFirstDayOfWeek?: boolean;
+    dateRange?: DateTimePickerDateRange;
+    isShowTodayIndicator?: boolean;
+    variant: DateTimePickerVariant;
+    dateRule?: RRule;
+    viewDate?: Date;
+    maxDate?: Date;
+    minDate?: Date;
+    date?: Date;
+};
+export type {
+    IDateSelectorProps as default
+};

+ 26 - 0
src/components/dateSelector/util.ts

@@ -0,0 +1,26 @@
+import type {
+    DateSelectInitialGeneratorType
+} from "./type";
+
+export const dateSelect = ({
+    dateRange,
+    dateRule,
+    variant,
+    date
+}: DateSelectInitialGeneratorType): 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;
+    }
+
+    if(dateRule) {
+        const rrule = dateRule.all();
+        if(rrule[0]) return new Date(rrule[0]);
+    }
+
+    return new Date();
+};

+ 897 - 0
src/components/dateTimePicker/index.tsx

@@ -0,0 +1,897 @@
+import {
+    useImperativeHandle,
+    forwardRef,
+    useEffect,
+    useState,
+    type Ref,
+    useRef
+} from "react";
+import {
+    TouchableOpacity,
+    View
+} from "react-native";
+import type IDateTimePickerProps from "./type";
+import type {
+    DateTimePickerDateRange,
+    DateTimePickerType,
+    IDateTimePickerRef
+} from "./type";
+import stylesheet, {
+    getDateTimePickerType,
+    useStyles
+} from "./stylesheet";
+import {
+    NCoreUIKitLocalize,
+    NCoreUIKitTheme
+} from "../../core/hooks";
+import {
+    type NCoreUIKitIcon
+} from "../../types";
+import type {
+    IBottomSheetRef
+} from "../bottomSheet/type";
+import type ITextProps from "../text/type";
+import {
+    type RRule as RRuleType,
+    RRule
+} from "rrule";
+import moment from "moment";
+import {
+    BadgeQuestionMarkIcon,
+    BadgeSuccessIcon,
+    BadgeDangerIcon,
+    BadgeAlertIcon,
+    BadgeInfoIcon,
+    CleanIcon
+} from "../../assets/svg";
+import {
+    uuid
+} from "../../utils";
+import Text from "../text";
+import DateTimeSheet from "../dateTimeSheet";
+import {
+    parseRRuleConfig
+} from "../../helpers";
+
+const DateTimePickerTypeIcon: Record<Exclude<DateTimePickerType, "default">, NCoreUIKitIcon> = {
+    "question": BadgeQuestionMarkIcon,
+    "success": BadgeSuccessIcon,
+    "warning": BadgeAlertIcon,
+    "danger": BadgeDangerIcon,
+    "info": BadgeInfoIcon
+};
+
+const DateTimePicker = ({
+    renderLoadingIcon : LoadingIconComponentProp,
+    rightIcon: RightIconComponentProp,
+    localeBasedFirstDayOfWeek = true,
+    hintTextIcon: HintTextIconProp,
+    spreadBehaviour = "baseline",
+    customDateTimeSheetLocalize,
+    isShowTodayIndicator = true,
+    titleFormat = "DD MMM YYYY",
+    isShowDateTimeTools = true,
+    isShowHintTextIcon = false,
+    isWorkWithRealtime = true,
+    dayOfWeekLength = "short",
+    customDateTimeSheetTheme,
+    isMultipleSelect = false,
+    pickerType = "date-time",
+    isWorkWithRRule = false,
+    icon: IconComponentProp,
+    hintTextContainerStyle,
+    removeSelectValidation,
+    isCleanEnabled = false,
+    contentContainerStyle,
+    subTitle = "Optional",
+    isSearchable = false,
+    onFocus: onFocusProp,
+    isRequired = false,
+    isDisabled = false,
+    onBlur: onBlurProp,
+    dateTimeSheetProps,
+    variant = "single",
+    hintTextIconStyle,
+    initialDateRange,
+    type = "default",
+    selectValidation,
+    initialDateRule,
+    dateRangeTitle,
+    customLocalize,
+    rightIconStyle,
+    isShowSubTitle,
+    cleanIconStyle,
+    maxChoice = -1,
+    initialDate,
+    contentStyle,
+    customTheme,
+    placeholder,
+    isOptional,
+    customKey,
+    minChoice,
+    iconStyle,
+    onChange,
+    hintText,
+    onCancel,
+    maxDate,
+    minDate,
+    style,
+    title,
+    onOk
+}: IDateTimePickerProps, ref: Ref<IDateTimePickerRef>) => {
+    const {
+        inlineSpaces,
+        typography,
+        radiuses,
+        borders,
+        spaces,
+        colors
+    } = NCoreUIKitTheme.useContext(customTheme);
+
+    const {
+        rruleConfig,
+        localize
+    } = NCoreUIKitLocalize.useContext(customLocalize);
+
+    const bottomSheetRef = useRef<IBottomSheetRef>(null);
+
+    const dateTimePickerKey = useRef(customKey ? customKey : uuid());
+
+    const currentType = getDateTimePickerType({
+        type
+    });
+
+    const styleType = type === "default" ? "neutral" : type === "question" ? "neutral" : type === "danger" ? "error" : type;
+
+    const [
+        isActive,
+        setIsActive
+    ] = useState(false);
+
+    const [
+        isLoading,
+        setIsLoading
+    ] = useState(false);
+
+    const [
+        tempDate,
+        setTempDate
+    ] = useState<Date | undefined>(initialDate);
+
+    const [
+        date,
+        setDate
+    ] = useState<Date | undefined>(initialDate);
+
+    const [
+        tempDateRule,
+        setTempDateRule
+    ] = useState<RRuleType | undefined>(initialDateRule);
+
+    const [
+        dateRule,
+        setDateRule
+    ] = useState<RRuleType | undefined>(initialDateRule);
+
+    const [
+        dateRange,
+        setDateRange
+    ] = useState<DateTimePickerDateRange | undefined>(initialDateRange);
+
+    const [
+        tempDateRange,
+        setTempDateRange
+    ] = useState<DateTimePickerDateRange | undefined>(initialDateRange);
+
+    const mainDateRange = isWorkWithRealtime ? dateRange : tempDateRange;
+    const mainDateRule = isWorkWithRealtime ? dateRule : tempDateRule;
+    const mainDate = isWorkWithRealtime ? date : tempDate;
+
+    const {
+        contentContainer: contentContainerDynamicStyle,
+        titleContainer: titleContainerDynamicStyle,
+        hintTextIcon: hintTextIconDynamicStyle,
+        contentText: contentTextDynamicStyle,
+        cleanButton: cleanButtonDynamicStyle,
+        rightIcon: rightIconDynamicStyle,
+        container: containerDynamicStyle,
+        hintText: hintTextDynamicStyle,
+        required: requiredDynamicStyle,
+        subTitle: subTitleDynamicStyle,
+        overlay: overlayDynamicStyle,
+        content: contentDynamicStyle,
+        title: titleDynamicStyle,
+        icon: iconDynamicStyle
+    } = useStyles({
+        icon: IconComponentProp ? true : false,
+        spreadBehaviour,
+        inlineSpaces,
+        isSearchable,
+        currentType,
+        isDisabled,
+        isActive,
+        radiuses,
+        borders,
+        spaces,
+        colors,
+        title,
+        type
+    });
+
+    useImperativeHandle(
+        ref,
+        () => ({
+            setDateRange: (dateRange) => {
+                if(isWorkWithRealtime) {
+                    setDateRange(dateRange);
+                } else {
+                    setTempDateRange(dateRange);
+                }
+            },
+            setDateRule: (dateRule) => {
+                if(isWorkWithRealtime) {
+                    setDateRule(dateRule);
+                } else {
+                    setTempDateRule(dateRule);
+                }
+            },
+            setDate: (date) => {
+                if(isWorkWithRealtime) {
+                    setDate(date);
+                } else {
+                    setTempDate(date);
+                }
+            },
+            cancel,
+            clean,
+            focus,
+            blur,
+            ok
+        }),
+        []
+    );
+
+    useEffect(() => {
+        if(isActive) {
+            if(onFocus) onFocus();
+        } else {
+            if(!isWorkWithRealtime) {
+                setTempDateRange(undefined);
+                setTempDateRule(undefined);
+                setTempDate(undefined);
+            }
+
+            if(onBlur) onBlur();
+        }
+    }, [isActive]);
+
+    useEffect(() => {
+        if(onChange) {
+            onChange({
+                dateRange,
+                dateRule,
+                date
+            });
+        }
+    }, [
+        tempDateRange,
+        tempDateRule,
+        dateRange,
+        dateRule,
+        tempDate,
+        date
+    ]);
+
+    const titleProps: ITextProps = {
+        color: currentType.titleColor,
+        variant: "bodyLargeSize"
+    };
+
+    const iconProps: NCoreUIKit.IconCallbackProps = {
+        size: Number(typography.labelLargeSize.fontSize) + 6,
+        color: currentType.iconColor
+    };
+
+    if(isDisabled) {
+        iconProps.color = "disabled";
+    }
+
+    const blur = () => {
+        setIsActive(false);
+    };
+
+    const focus = () => {
+        if(!isWorkWithRealtime) {
+            setTempDateRange(dateRange);
+            setTempDateRule(dateRule);
+            setTempDate(date);
+        }
+
+        setIsActive(true);
+    };
+
+    const cancel = () => {
+        if(!isWorkWithRealtime) {
+            bottomSheetRef.current?.close(() => {
+                blur();
+            });
+
+            if(onCancel) {
+                onCancel({
+                    dateRange,
+                    dateRule,
+                    date
+                });
+            }
+        }
+    };
+
+    const ok = () => {
+        if(!isWorkWithRealtime) {
+            setDateRange(tempDateRange);
+            setDateRule(tempDateRule);
+            setDate(tempDate);
+
+            bottomSheetRef.current?.close(() => {
+                blur();
+            });
+
+            if(onOk) {
+                onOk({
+                    dateRange,
+                    dateRule,
+                    date
+                });
+            }
+        }
+    };
+
+    const clean = () => {
+        setDateRange(undefined);
+        setDateRule(undefined);
+        setDate(undefined);
+
+        if(!isWorkWithRealtime) {
+            setTempDateRange(undefined);
+            setTempDateRule(undefined);
+            setTempDate(undefined);
+        }
+    };
+
+    const selectDateRule = (selectedDateRule?: RRule) => {
+        if(isWorkWithRRule) {
+            if(selectedDateRule && selectedDateRule !== dateRule) {
+                if(selectValidation) {
+                    if(selectValidation({
+                        dateRule: selectedDateRule
+                    })) {
+                        if(isWorkWithRealtime) {
+                            setDateRule(selectedDateRule);
+                        } else {
+                            setTempDateRule(selectedDateRule);
+                        }
+                    }
+                } else {
+                    if(isWorkWithRealtime) {
+                        setDateRule(selectedDateRule);
+                    } else {
+                        setTempDateRule(selectedDateRule);
+                    }
+                }
+            } else {
+                if(removeSelectValidation) {
+                    if(removeSelectValidation({
+                        dateRule
+                    })) {
+                        setTempDateRule(undefined);
+                        setDateRule(undefined);
+                    }
+                } else {
+                    setTempDateRule(undefined);
+                    setDateRule(undefined);
+                }
+            }
+        }
+    };
+
+    const selectMultipleObject = (selectedDateRange?: {
+        start?: Date;
+        end?: Date;
+    }) => {
+        if(!isWorkWithRRule && isMultipleSelect) {
+            if(selectedDateRange) {
+                if(selectValidation) {
+                    if(selectValidation({
+                        dateRule
+                    })) {
+                        if(isWorkWithRealtime) {
+                            setDateRange({
+                                start: selectedDateRange.start,
+                                end: selectedDateRange.end
+                            });
+                        } else {
+                            setTempDateRange({
+                                start: selectedDateRange.start,
+                                end: selectedDateRange.end
+                            });
+                        }
+                    }
+                } else {
+                    if(isWorkWithRealtime) {
+                        setDateRange({
+                            start: selectedDateRange.start,
+                            end: selectedDateRange.end
+                        });
+                    } else {
+                        setTempDateRange({
+                            start: selectedDateRange.start,
+                            end: selectedDateRange.end
+                        });
+                    }
+                }
+            } else {
+                if(removeSelectValidation) {
+                    if(removeSelectValidation({
+                        dateRange: selectedDateRange
+                    })) {
+                        setTempDateRange(undefined);
+                        setDateRange(undefined);
+                    }
+                } else {
+                    setTempDateRange(undefined);
+                    setDateRange(undefined);
+                }
+            }
+        }
+    };
+
+    const selectObject = (selectedDate?: Date) => {
+        if(!isWorkWithRRule && !isMultipleSelect) {
+            if(selectedDate === date) {
+                if(removeSelectValidation) {
+                    if(removeSelectValidation({
+                        date
+                    })) {
+                        if(isWorkWithRealtime) {
+                            setDate(undefined);
+                        } else {
+                            setTempDate(undefined);
+                        }
+                    }
+                } else {
+                    if(isWorkWithRealtime) {
+                        setDate(undefined);
+                    } else {
+                        setTempDate(undefined);
+                    }
+                }
+            } else {
+                if(selectValidation) {
+                    if(selectValidation({
+                        date
+                    })) {
+                        if(isWorkWithRealtime) {
+                            setDate(selectedDate);
+                        } else {
+                            setTempDate(selectedDate);
+                        }
+                    }
+                } else {
+                    if(isWorkWithRealtime) {
+                        setDate(selectedDate);
+                    } else {
+                        setTempDate(selectedDate);
+                    }
+                }
+            }
+        }
+    };
+
+    const onFocus = () => {
+        if(onFocusProp) onFocusProp();
+    };
+
+    const onBlur = () => {
+        if(onBlurProp) onBlurProp();
+    };
+
+    const renderCleanButton = () => {
+        if(isDisabled) {
+            return null;
+        }
+
+        if(!isCleanEnabled) {
+            return null;
+        }
+
+        if(isWorkWithRRule) {
+            if(!mainDateRule) return null;
+        } else {
+            if(isMultipleSelect) {
+                if(!mainDateRange) return null;
+            } else {
+                if(!mainDate) return null;
+            }
+        }
+
+        return <TouchableOpacity
+            style={[
+                cleanIconStyle,
+                stylesheet.cleanButton,
+                cleanButtonDynamicStyle
+            ]}
+            onPress={() => {
+                if(isWorkWithRealtime) {
+                    setDateRange(undefined);
+                    setDateRule(undefined);
+                    setDate(undefined);
+                } else {
+                    setTempDateRange(undefined);
+                    setTempDateRule(undefined);
+                    setTempDate(undefined);
+                }
+            }}
+        >
+            <CleanIcon
+                color="mid"
+                size={20}
+            />
+        </TouchableOpacity>;
+    };
+
+    const renderIcon = () => {
+        if (!IconComponentProp) {
+            return null;
+        }
+
+        return <View
+            style={[
+                iconStyle,
+                stylesheet.icon,
+                iconDynamicStyle
+            ]}
+        >
+            <IconComponentProp
+                color={iconProps.color}
+                size={iconProps.size}
+            />
+        </View>;
+    };
+
+    const renderRightIcon = () => {
+        if (!RightIconComponentProp) {
+            return null;
+        }
+
+        if(isCleanEnabled) {
+            return null;
+        }
+
+        if(isWorkWithRRule) {
+            if(!mainDateRule) return null;
+        } else {
+            if(isMultipleSelect) {
+                if(!mainDateRange) return null;
+            } else {
+                if(!mainDate) return null;
+            }
+        }
+
+        return <View
+            style={[
+                rightIconStyle,
+                stylesheet.rightIcon,
+                rightIconDynamicStyle
+            ]}
+        >
+            <RightIconComponentProp
+                color={iconProps.color}
+                size={iconProps.size}
+            />
+        </View>;
+    };
+
+    const renderHintIcon = () => {
+        if(!isShowHintTextIcon) {
+            return null;
+        }
+
+        if(HintTextIconProp) {
+            return <HintTextIconProp
+                color={isDisabled ? "disabled" : currentType.hintTextIconColor}
+                size={20}
+                style={[
+                    hintTextIconStyle,
+                    stylesheet.hintTextIcon,
+                    hintTextIconDynamicStyle
+                ]}
+            />;
+        }
+
+        const CurrentHintIcon = DateTimePickerTypeIcon[type === "default" ? "question" : type];
+
+        return <CurrentHintIcon
+            customColor={isDisabled ? colors.system.state.content.disabled[styleType] : undefined}
+            color={currentType.hintTextIconColor}
+            size={20}
+            style={[
+                hintTextIconStyle,
+                stylesheet.hintTextIcon,
+                hintTextIconDynamicStyle
+            ]}
+        />;
+    };
+
+    const renderHintText = () => {
+        if (!hintText) {
+            return null;
+        }
+
+        return <View
+            style={[
+                hintTextContainerStyle,
+                stylesheet.hintText,
+                hintTextDynamicStyle
+            ]}
+        >
+            {renderHintIcon()}
+            <Text
+                customColor={isDisabled ? colors.system.state.content.disabled[styleType] : undefined}
+                color={currentType.hintTextColor}
+                variant="labelSmallSize"
+            >
+                {hintText}
+            </Text>
+        </View>;
+    };
+
+    const renderRequired = () => {
+        if(!isRequired) {
+            return null;
+        }
+
+        return <Text
+            color="danger"
+            style={[
+                stylesheet.required,
+                requiredDynamicStyle
+            ]}
+        >*</Text>;
+    };
+
+    const renderSubtitle = () => {
+        if(!isShowSubTitle && !isOptional) {
+            return null;
+        }
+
+        return <Text
+            variant="labelLargeSize"
+            color={titleProps.color}
+            style={[
+                stylesheet.subTitle,
+                subTitleDynamicStyle
+            ]}
+        >
+            ( {isOptional ? localize("is-optional") : subTitle} )
+        </Text>;
+    };
+
+    const renderTitle = () => {
+        if (!title) {
+            return null;
+        }
+
+        return <TouchableOpacity
+            style={[
+                stylesheet.titleContainer,
+                titleContainerDynamicStyle
+            ]}
+            onPress={() => {
+                if(!isDisabled) {
+                    focus();
+                }
+            }}
+        >
+            {renderRequired()}
+            <Text
+                {...titleProps}
+                variant={titleProps.variant}
+                color={titleProps.color}
+                style={[
+                    stylesheet.title,
+                    titleDynamicStyle
+                ]}
+            >
+                {title}
+            </Text>
+            {renderSubtitle()}
+        </TouchableOpacity>;
+    };
+
+    const renderDateValue = () => {
+        if(!mainDate) {
+            return <Text
+                variant="labelLargeSize"
+                style={[
+                    stylesheet.contentText,
+                    contentTextDynamicStyle
+                ]}
+            >
+                {placeholder ? placeholder : localize("select-any-date")}
+            </Text>;
+        }
+
+        return <Text
+            variant="labelLargeSize"
+            style={[
+                stylesheet.contentText,
+                contentTextDynamicStyle
+            ]}
+        >
+            {moment(mainDate).format(titleFormat)}
+        </Text>;
+    };
+
+    const renderDateRangeValue = () => {
+        if(!mainDateRange) {
+            return <Text
+                variant="labelLargeSize"
+                style={[
+                    stylesheet.contentText,
+                    contentTextDynamicStyle
+                ]}
+            >
+                {placeholder ? placeholder : localize("select-any-date")}
+            </Text>;
+        }
+
+        if(dateRangeTitle) {
+            return dateRangeTitle(dateRange);
+        }
+
+        return <Text
+            variant="labelLargeSize"
+            style={[
+                stylesheet.contentText,
+                contentTextDynamicStyle
+            ]}
+        >
+            {moment(mainDateRange.start).format(titleFormat)} - {moment(mainDateRange.end).format(titleFormat)}
+        </Text>;
+    };
+
+    const renderDateRuleValue = () => {
+        if(!mainDateRule) {
+            return <Text
+                variant="labelLargeSize"
+                style={[
+                    stylesheet.contentText,
+                    contentTextDynamicStyle
+                ]}
+            >
+                {placeholder ? placeholder : localize("select-any-date")}
+            </Text>;
+        }
+
+        return <Text
+            variant="labelLargeSize"
+            style={[
+                stylesheet.contentText,
+                contentTextDynamicStyle
+            ]}
+        >
+            {new RRule().toText(undefined, parseRRuleConfig(rruleConfig))}
+        </Text>;
+    };
+
+    const renderValue = () => {
+        if(isWorkWithRRule) {
+            return renderDateRuleValue();
+        }
+
+        if(isMultipleSelect) {
+            return renderDateRangeValue();
+        }
+
+        return renderDateValue();
+    };
+
+    const renderContent = () => {
+        return <View
+            style={[
+                contentStyle,
+                stylesheet.content,
+                contentDynamicStyle
+            ]}
+        >
+            {renderValue()}
+        </View>;
+    };
+
+    const renderOverlay = () => {
+        return <View
+            style={[
+                stylesheet.overlay,
+                overlayDynamicStyle
+            ]}
+        />;
+    };
+
+    return <View
+        style={[
+            style,
+            stylesheet.container,
+            containerDynamicStyle
+        ]}
+    >
+        {renderTitle()}
+
+        <TouchableOpacity
+            disabled={isDisabled}
+            style={[
+                contentContainerStyle,
+                stylesheet.contentContainer,
+                contentContainerDynamicStyle
+            ]}
+            onPress={() => {
+                if(!isDisabled) {
+                    focus();
+                }
+            }}
+        >
+            {renderOverlay()}
+
+            {renderIcon()}
+
+            {renderContent()}
+
+            {renderCleanButton()}
+            {renderRightIcon()}
+        </TouchableOpacity>
+
+        {renderHintText()}
+
+        <DateTimeSheet
+            localeBasedFirstDayOfWeek={localeBasedFirstDayOfWeek}
+            LoadingIconComponentProp={LoadingIconComponentProp}
+            dateTimePickerKey={dateTimePickerKey.current}
+            customLocalize={customDateTimeSheetLocalize}
+            selectMultipleObject={selectMultipleObject}
+            isShowTodayIndicator={isShowTodayIndicator}
+            isWorkWithRealtime={isWorkWithRealtime}
+            customTheme={customDateTimeSheetTheme}
+            isMultipleSelect={isMultipleSelect}
+            isShowTools={isShowDateTimeTools}
+            dayOfWeekLength={dayOfWeekLength}
+            isWorkWithRRule={isWorkWithRRule}
+            selectDateRule={selectDateRule}
+            bottomSheetRef={bottomSheetRef}
+            selectObject={selectObject}
+            setIsLoading={setIsLoading}
+            setDateRange={setDateRange}
+            setIsActive={setIsActive}
+            setDateRule={setDateRule}
+            bottomSheetProps={{
+                isAutoHeight: true,
+                ...dateTimeSheetProps
+            }}
+            pickerType={pickerType}
+            maxChoice={maxChoice}
+            minChoice={minChoice}
+            isLoading={isLoading}
+            dateRange={dateRange}
+            dateRule={dateRule}
+            isActive={isActive}
+            variant={variant}
+            setDate={setDate}
+            maxDate={maxDate}
+            minDate={minDate}
+            cancel={cancel}
+            clean={clean}
+            title={title}
+            date={date}
+            ok={ok}
+        />
+    </View>;
+};
+export default forwardRef(DateTimePicker);

+ 252 - 0
src/components/dateTimePicker/stylesheet.ts

@@ -0,0 +1,252 @@
+import {
+    type ViewStyle,
+    type TextStyle,
+    StyleSheet
+} from "react-native";
+import {
+    type DateTimePickerDynamicStyleType,
+    type DateTimePickerTypes,
+    type DateTimePickerType
+} from "./type";
+import type {
+    Mutable
+} from "../../types";
+
+export const DATE_TIME_PICKER_TYPE_STYLES: Record<
+    DateTimePickerType,
+    DateTimePickerTypes
+> = {
+    default: {
+        focusBorderColor: "emphasized",
+        borderColor: "emphasized",
+        hintTextIconColor: "mid",
+        placeholderColor: "low",
+        containerColor: "mid",
+        hintTextColor: "mid",
+        titleColor: "high",
+        inputColor: "mid",
+        iconColor: "mid"
+    },
+    danger: {
+        placeholderColor: "dangerLow",
+        hintTextIconColor: "danger",
+        focusBorderColor: "danger",
+        containerColor: "danger",
+        hintTextColor: "danger",
+        borderColor: "danger",
+        titleColor: "danger",
+        inputColor: "danger",
+        iconColor: "danger"
+    },
+    success: {
+        placeholderColor: "successLow",
+        hintTextIconColor: "success",
+        focusBorderColor: "success",
+        containerColor: "success",
+        hintTextColor: "success",
+        borderColor: "success",
+        titleColor: "success",
+        inputColor: "success",
+        iconColor: "success"
+    },
+    warning: {
+        placeholderColor: "warningLow",
+        hintTextIconColor: "warning",
+        focusBorderColor: "warning",
+        containerColor: "warning",
+        hintTextColor: "warning",
+        borderColor: "warning",
+        titleColor: "warning",
+        inputColor: "warning",
+        iconColor: "warning"
+    },
+    info: {
+        placeholderColor: "infoLow",
+        hintTextIconColor: "info",
+        focusBorderColor: "info",
+        containerColor: "info",
+        hintTextColor: "info",
+        borderColor: "info",
+        titleColor: "info",
+        inputColor: "info",
+        iconColor: "info"
+    },
+    question: {
+        focusBorderColor: "emphasized",
+        borderColor: "emphasized",
+        hintTextIconColor: "mid",
+        placeholderColor: "low",
+        containerColor: "mid",
+        hintTextColor: "mid",
+        titleColor: "mid",
+        inputColor: "mid",
+        iconColor: "mid"
+    }
+};
+
+export const getDateTimePickerType = ({
+    type
+}: {
+    type: DateTimePickerType;
+}) => {
+    const currentType = DATE_TIME_PICKER_TYPE_STYLES[type];
+
+    return currentType;
+};
+
+const stylesheet = StyleSheet.create({
+    container: {
+        flexDirection: "column",
+        boxSizing: "border-box"
+    },
+    contentContainer: {
+        boxSizing: "border-box",
+        flexDirection: "row",
+        borderStyle: "solid",
+        alignItems: "center",
+        position: "relative",
+        minWidth: 250
+    },
+    content: {
+        flexShrink: 1,
+        width: "100%",
+        flex: 1
+    },
+    contentText: {
+        minHeight: 20
+    },
+    cleanButton: {
+        justifyContent: "center",
+        alignItems: "center",
+        alignSelf: "center",
+        zIndex: 99
+    },
+    titleContainer: {
+        flexDirection: "row",
+        alignItems: "center"
+    },
+    title: {
+
+    },
+    icon: {
+        justifyContent: "center",
+        alignContent: "center",
+        alignItems: "center",
+        zIndex: 99
+    },
+    required: {
+    },
+    hintTextIcon: {
+    },
+    hintText: {
+        flexDirection: "row",
+        alignItems: "center"
+    },
+    subTitle: {
+
+    },
+    overlay: {
+        position: "absolute",
+        zIndex: 98,
+        bottom: 0,
+        right: 0,
+        left: 0,
+        top: 0
+    },
+    rightIcon: {
+        justifyContent: "center",
+        alignItems: "center",
+        alignSelf: "center",
+        zIndex: 99
+    }
+});
+
+export const useStyles = ({
+    spreadBehaviour,
+    inlineSpaces,
+    currentType,
+    isDisabled,
+    radiuses,
+    isActive,
+    borders,
+    colors,
+    spaces,
+    type
+}: DateTimePickerDynamicStyleType) => {
+    const styleType = type === "danger" ? "error" : type === "question" ? "neutral" : type;
+
+    const styles = {
+        container: {
+        } as Mutable<ViewStyle>,
+        contentContainer: {
+            backgroundColor: colors.content.container[currentType.containerColor],
+            borderColor: colors.content.container[currentType.containerColor],
+            paddingBottom: spaces.spacingMd,
+            paddingRight: spaces.spacingMd,
+            borderRadius: radiuses.actions,
+            paddingLeft: spaces.spacingMd,
+            paddingTop: spaces.spacingMd,
+            borderWidth: borders.line
+        } as Mutable<ViewStyle>,
+        content: {
+
+        } as Mutable<ViewStyle>,
+        contentText: {
+
+        } as Mutable<TextStyle>,
+        cleanButton: {
+            marginLeft: spaces.spacingSm
+        } as Mutable<ViewStyle>,
+        titleContainer: {
+            marginBottom: spaces.spacingSm
+        } as Mutable<ViewStyle>,
+        title: {
+
+        } as Mutable<TextStyle>,
+        icon: {
+            marginRight: spaces.spacingSm
+        } as Mutable<ViewStyle>,
+        required: {
+            marginRight: inlineSpaces.required
+        } as Mutable<ViewStyle>,
+        hintTextIcon: {
+            marginRight: spaces.spacingXs
+        } as Mutable<ViewStyle>,
+        hintText: {
+            marginTop: spaces.spacingSm
+        } as Mutable<TextStyle>,
+        subTitle: {
+            marginLeft: inlineSpaces.subTitle
+        } as Mutable<TextStyle>,
+        overlay: {
+            borderRadius: radiuses.actions - 1
+        } as Mutable<ViewStyle>,
+        rightIcon: {
+            marginLeft: spaces.spacingSm
+        } as Mutable<ViewStyle>
+    };
+
+    if(isDisabled) {
+        const disableStyleType = styleType === "default" ? "neutral" : styleType;
+
+        styles.overlay.backgroundColor = colors.system.state.overlay.disabled[disableStyleType];
+        styles.container.borderColor = colors.system.state.overlay.disabled[disableStyleType];
+    }
+
+    if (spreadBehaviour === "baseline") {
+        styles.container.alignSelf = spreadBehaviour;
+        styles.container.width = "auto";
+    }
+
+    if (spreadBehaviour === "stretch") {
+        styles.container.justifyContent = "center";
+        styles.container.width = "100%";
+    }
+
+    if(isActive) {
+        styles.contentContainer.borderColor = colors.content.border.emphasized;
+    }
+
+    return styles;
+};
+export default stylesheet;

+ 188 - 0
src/components/dateTimePicker/type.ts

@@ -0,0 +1,188 @@
+import {
+    type ViewStyle
+} from "react-native";
+import {
+    type NCoreUIKitIcon
+} from "../../types";
+import type IBottomSheetProps from "../bottomSheet/type";
+import type {
+    RRule
+} from "rrule";
+import type {
+    DayOfWeekLengthType
+} from "../dateSelector/type";
+
+export type IDateTimePickerRef = {
+    setDateRange: (dateRange: DateTimePickerDateRange) => void;
+    setDateRule: (dateRule: RRule) => void;
+    setDate: (date: Date) => void;
+    cancel: () => void;
+    clean: () => void;
+    focus: () => void;
+    blur: () => void;
+    ok: () => void;
+};
+
+export type DateTimePickerDynamicStyleType = {
+    inlineSpaces: NCoreUIKit.ActivePalette["inlineSpaces"];
+    spreadBehaviour?: DateTimePickerSpreadBehaviour;
+    radiuses: NCoreUIKit.ActivePalette["radiuses"];
+    spaces: NCoreUIKit.ActivePalette["spaces"];
+    colors: NCoreUIKit.ActivePalette["colors"];
+    currentType: DateTimePickerTypes;
+    borders: NCoreUIKit.Borders;
+    type: DateTimePickerType;
+    isSearchable?: boolean;
+    isDisabled?: boolean;
+    isActive: boolean;
+    title?: string;
+    icon?: boolean;
+};
+
+export type DateTimePickerTypes = {
+    containerColor: keyof NCoreUIKit.ContainerContentColors;
+    focusBorderColor: keyof NCoreUIKit.BorderContentColors;
+    hintTextIconColor: keyof NCoreUIKit.IconContentColors;
+    placeholderColor: keyof NCoreUIKit.TextContentColors;
+    hintTextColor: keyof NCoreUIKit.TextContentColors;
+    borderColor: keyof NCoreUIKit.BorderContentColors;
+    inputColor: keyof NCoreUIKit.TextContentColors;
+    titleColor: keyof NCoreUIKit.TextContentColors;
+    iconColor: keyof NCoreUIKit.IconContentColors;
+};
+
+export type DateTimePickerType = "default" | "danger" | "warning" | "question" | "success" | "info";
+
+export type DateTimePickerSpreadBehaviour = "baseline" | "stretch" | "free";
+
+export type DateTimePickerPickerType = "date" | "time" | "date-time";
+
+export type DateTimePickerVariant = "single" | "range" | "rrule";
+
+export type DateTimePickerDateRange = {
+    start?: Date;
+    end?: Date;
+};
+
+export type YearToken = "YYYY" | "YY";
+
+export type MonthToken = "MMMM" | "MMM" | "MM" | "M";
+
+export type DayToken = "dddd" | "ddd" | "DD" | "D";
+
+export type DateTokenCombination = `${YearToken}${Separator}${MonthToken}${Separator}${DayToken}` |
+    `${DayToken}${Separator}${MonthToken}${Separator}${YearToken}` |
+    `${MonthToken}${Separator}${DayToken}${Separator}${YearToken}`;
+
+export type Separator = "-" | "." | "/" | " ";
+
+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;
+    customDateTimeSheetTheme?: {
+        gapPropagation?: keyof NCoreUIKit.GapPropagationKey;
+        sharpness?: keyof NCoreUIKit.SharpnessKey;
+        paletteKey?: keyof NCoreUIKit.PaletteKey;
+        themeKey?: keyof NCoreUIKit.ThemeKey;
+    };
+    customTheme?: {
+        gapPropagation?: keyof NCoreUIKit.GapPropagationKey;
+        sharpness?: keyof NCoreUIKit.SharpnessKey;
+        paletteKey?: keyof NCoreUIKit.PaletteKey;
+        themeKey?: keyof NCoreUIKit.ThemeKey;
+    };
+    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;
+    initialDateRange?: DateTimePickerDateRange;
+    dayOfWeekLength?: DayOfWeekLengthType;
+    pickerType?: DateTimePickerPickerType;
+    localeBasedFirstDayOfWeek?: boolean;
+    variant?: DateTimePickerVariant;
+    isShowTodayIndicator?: boolean;
+    titleFormat?: ValidTitleFormat;
+    isShowDateTimeTools?: boolean;
+    isWorkWithRealtime?: boolean;
+    isMultipleSelect?: boolean;
+    cleanIconStyle?: ViewStyle;
+    rightIconStyle?: ViewStyle;
+    rightIcon?: NCoreUIKitIcon;
+    isWorkWithRRule?: boolean;
+    type?: DateTimePickerType;
+    isShowSubTitle?: boolean;
+    isCleanEnabled?: boolean;
+    contentStyle?: ViewStyle;
+    initialDateRule?: RRule;
+    isSearchable?: boolean;
+    iconStyle?: ViewStyle;
+    icon?: NCoreUIKitIcon;
+    placeholder?: string;
+    isRequired?: boolean;
+    isDisabled?: boolean;
+    isOptional?: boolean;
+    onFocus?: () => void;
+    onBlur?: () => void;
+    initialDate?: Date;
+    customKey?: string;
+    maxChoice?: number;
+    minChoice?: number;
+    hintText?: string;
+    subTitle?: string;
+    style?: ViewStyle;
+    maxDate?: Date;
+    minDate?: Date;
+    title?: string;
+    id?: string;
+};
+export type {
+    IDateTimePickerProps as default
+};

+ 229 - 0
src/components/dateTimeSheet/index.tsx

@@ -0,0 +1,229 @@
+import {
+    useState
+} from "react";
+import {
+    View
+} from "react-native";
+import type IDateTimeSheetProps from "./type";
+import stylesheet, {
+    useStyles
+} from "./stylesheet";
+import {
+    NCoreUIKitLocalize,
+    NCoreUIKitTheme
+} from "../../core/hooks";
+import DateSelector from "../dateSelector";
+import BottomSheet from "../bottomSheet";
+import CheckBox from "../checkBox";
+import Loading from "../loading";
+import Button from "../button";
+import Text from "../text";
+
+const DateTimeSheet = ({
+    localeBasedFirstDayOfWeek = true,
+    isShowTodayIndicator = true,
+    dayOfWeekLength = "short",
+    LoadingIconComponentProp,
+    isWorkWithRealtime,
+    isShowTools = true,
+    dateTimePickerKey,
+    bottomSheetProps,
+    bottomSheetRef,
+    customLocalize,
+    customTheme,
+    setIsActive,
+    dateRange,
+    maxChoice,
+    minChoice,
+    isLoading,
+    isActive,
+    dateRule,
+    setDate,
+    variant,
+    maxDate,
+    minDate,
+    cancel,
+    clean,
+    title,
+    date,
+    ok
+}: IDateTimeSheetProps) => {
+    const {
+        spaces
+    } = NCoreUIKitTheme.useContext(customTheme);
+
+    const {
+        localize
+    } = NCoreUIKitLocalize.useContext(customLocalize);
+
+    const [
+        isSheetContentReady,
+        setIsSheetContentReady
+    ] = useState(false);
+
+    const {
+        loadingContainer: loadingContainerDynamicStyle,
+        contentContainer: contentContainerDynamicStyle,
+        headerContainer: headerContainerDynamicStyle,
+        bottomContainer: bottomContainerDynamicStyle,
+        toolsContainer: toolsContainerDynamicStyle,
+        cancelButton: cancelButtonDynamicStyle,
+        headerTitle: headerTitleDynamicStyle,
+        okButton: okButtonDynamicStyle
+    } = useStyles({
+        spaces
+    });
+
+    const renderBottomSheetContent = () => {
+        if(isLoading) {
+            return <View
+                style={[
+                    stylesheet.loadingContainer,
+                    loadingContainerDynamicStyle
+                ]}
+            >
+                {LoadingIconComponentProp ? <LoadingIconComponentProp
+                    color="emphasized"
+                    size={20}
+                /> : <Loading/>}
+            </View>;
+        }
+
+        return <View
+            style={[
+                contentContainerDynamicStyle
+            ]}
+        >
+            <DateSelector
+                localeBasedFirstDayOfWeek={localeBasedFirstDayOfWeek}
+                setIsSheetContentReady={setIsSheetContentReady}
+                isShowTodayIndicator={isShowTodayIndicator}
+                dayOfWeekLength={dayOfWeekLength}
+                dateRange={dateRange}
+                dateRule={dateRule}
+                maxDate={maxDate}
+                minDate={minDate}
+                setDate={setDate}
+                variant={variant}
+                date={date}
+            />
+        </View>;
+    };
+
+    const renderTools = () => {
+        if(!isShowTools) {
+            return null;
+        }
+
+        if(minChoice && maxChoice !== -1) {
+            return null;
+        }
+
+        let isAnySelected = false;
+
+        if(variant === "single" && date) {
+            isAnySelected = true;
+        }
+
+        if(variant === "range" && dateRange && (dateRange.start || dateRange.end)) {
+            isAnySelected = true;
+        }
+
+        if(variant === "rrule" && dateRule) {
+            isAnySelected = true;
+        }
+
+        return <View
+            style={[
+                stylesheet.toolsContainer,
+                toolsContainerDynamicStyle
+            ]}
+        >
+            {!minChoice ? <CheckBox
+                isChecked={isAnySelected ? "checked" : null}
+                title={localize("clean-all")}
+                spreadBehaviour="free"
+                onPress={() => {
+                    clean();
+                }}
+            /> : null}
+        </View>;
+    };
+
+    const renderBottomSheetHeader = () => {
+        return <View
+            style={[
+                stylesheet.headerContainer,
+                headerContainerDynamicStyle
+            ]}
+        >
+            {title ? <Text
+                variant="titleMediumSize"
+                style={[
+                    headerTitleDynamicStyle
+                ]}
+            >
+                {title}
+            </Text> : null}
+            {renderTools()}
+        </View>;
+    };
+
+    const renderBottomSheetBottom = () => {
+        if(isWorkWithRealtime) {
+            return null;
+        }
+
+        return <View
+            style={[
+                stylesheet.bottomContainer,
+                bottomContainerDynamicStyle
+            ]}
+        >
+            <Button
+                title={localize("cancel")}
+                spreadBehaviour="stretch"
+                variant="outline"
+                type="neutral"
+                onPress={() => {
+                    cancel();
+                }}
+                style={{
+                    ...cancelButtonDynamicStyle
+                }}
+            />
+            <Button
+                spreadBehaviour="stretch"
+                title={localize("ok")}
+                onPress={() => {
+                    ok();
+                }}
+                style={{
+                    ...okButtonDynamicStyle
+                }}
+            />
+        </View>;
+    };
+
+    return <BottomSheet
+        {...bottomSheetProps}
+        key={`${dateTimePickerKey}-bottomsheet`}
+        isContentReady={isSheetContentReady}
+        customKey={dateTimePickerKey}
+        ref={bottomSheetRef}
+        isAutoHeight={true}
+        isActive={isActive}
+        renderBottom={() => {
+            return renderBottomSheetBottom();
+        }}
+        renderHeader={() => {
+            return renderBottomSheetHeader();
+        }}
+        onClosed={() => {
+            setIsActive(false);
+        }}
+    >
+        {renderBottomSheetContent()}
+    </BottomSheet>;
+};
+export default DateTimeSheet;

+ 77 - 0
src/components/dateTimeSheet/stylesheet.ts

@@ -0,0 +1,77 @@
+import {
+    type ViewStyle,
+    StyleSheet
+} from "react-native";
+import {
+    type DateTimeSheetDynamicStyleType
+} from "./type";
+import type {
+    Mutable
+} from "../../types";
+
+const stylesheet = StyleSheet.create({
+    headerContainer: {
+        flexDirection: "column",
+        width: "100%"
+    },
+    bottomContainer: {
+        flexDirection: "row",
+        alignItems: "center"
+    },
+    loadingContainer: {
+        justifyContent: "center",
+        alignItems: "center",
+        flex: 1
+    },
+    itemContentContainer: {
+        flex: 1
+    },
+    toolsContainer: {
+        justifyContent: "space-between",
+        flexDirection: "row",
+        alignItems: "center"
+    },
+    moreLoadingContainer: {
+        justifyContent: "center",
+        alignItems: "center",
+        width: "100%"
+    }
+});
+
+export const useStyles = ({
+    spaces
+}: DateTimeSheetDynamicStyleType) => {
+    const styles = {
+        contentContainer: {
+            paddingVertical: spaces.spacingSm
+        } as Mutable<ViewStyle>,
+        headerContainer: {
+            marginBottom: spaces.spacingSm
+        } as Mutable<ViewStyle>,
+        headerTitle: {
+
+        } as Mutable<ViewStyle>,
+        bottomContainer: {
+            marginTop: spaces.spacingSm,
+            padding: spaces.spacingSm
+        } as Mutable<ViewStyle>,
+        cancelButton: {
+            marginRight: spaces.spacingSm
+        } as Mutable<ViewStyle>,
+        okButton: {
+            marginLeft: spaces.spacingSm
+        } as Mutable<ViewStyle>,
+        loadingContainer: {
+            padding: spaces.spacingMd
+        } as Mutable<ViewStyle>,
+        toolsContainer: {
+            marginTop: spaces.spacingSm
+        } as Mutable<ViewStyle>,
+        moreLoadingContainer: {
+            padding: spaces.spacingSm
+        } as Mutable<ViewStyle>
+    };
+
+    return styles;
+};
+export default stylesheet;

+ 91 - 0
src/components/dateTimeSheet/type.ts

@@ -0,0 +1,91 @@
+import type {
+    SetStateAction,
+    Dispatch,
+    Ref
+} from "react";
+import {
+    type NCoreUIKitIcon
+} from "../../types";
+import type {
+    IBottomSheetRef
+} from "../bottomSheet/type";
+import type IBottomSheetProps from "../bottomSheet/type";
+import type {
+    RRule
+} from "rrule";
+import type {
+    DateTimePickerPickerType,
+    DateTimePickerDateRange,
+    DateTimePickerVariant
+} from "../dateTimePicker/type";
+import type {
+    DayOfWeekLengthType
+} from "../dateSelector/type";
+
+export type DateTimeSheetDynamicStyleType = {
+    spaces: NCoreUIKit.ActivePalette["spaces"];
+};
+
+interface IDateTimeSheetProps {
+    selectMultipleObject:  (dateRange?: DateTimePickerDateRange) => void;
+    setDateRule: Dispatch<SetStateAction<RRule | undefined>>;
+    customTheme?: {
+        gapPropagation?: keyof NCoreUIKit.GapPropagationKey;
+        sharpness?: keyof NCoreUIKit.SharpnessKey;
+        paletteKey?: keyof NCoreUIKit.PaletteKey;
+        themeKey?: keyof NCoreUIKit.ThemeKey;
+    };
+    setDate: Dispatch<SetStateAction<Date | undefined>>;
+    setIsLoading: Dispatch<SetStateAction<boolean>>;
+    setIsActive: Dispatch<SetStateAction<boolean>>;
+    customLocalize?: {
+        activeLocale?: keyof NCoreUIKit.LocaleKey;
+    };
+    bottomSheetRef: Ref<IBottomSheetRef> | null;
+    selectDateRule: (dateRule?: RRule) => void;
+    LoadingIconComponentProp?: NCoreUIKitIcon;
+    setDateRange: Dispatch<SetStateAction<{
+        start?: Date;
+        end?: Date;
+    } | undefined>>;
+    dayOfWeekLength?: DayOfWeekLengthType;
+    pickerType: DateTimePickerPickerType;
+    localeBasedFirstDayOfWeek?: boolean;
+    selectObject: (date?: Date) => void;
+    isShowTodayIndicator?: boolean;
+    variant: DateTimePickerVariant;
+    isWorkWithRealtime: boolean;
+    isMultipleSelect?: boolean;
+    dateTimePickerKey: string;
+    isWorkWithRRule: boolean;
+    bottomSheetProps?: Omit<
+        IBottomSheetProps,
+        "key" |
+        "customKey" |
+        "ref" |
+        "isActive" |
+        "renderBottom" |
+        "renderHeader" |
+        "onClose"
+    >;
+    isShowTools?: boolean;
+    cancel: () => void;
+    maxChoice?: number;
+    minChoice?: number;
+    isLoading: boolean;
+    clean: () => void;
+    isActive: boolean;
+    dateRule?: RRule;
+    maxDate?: Date;
+    minDate?: Date;
+    dateRange?: {
+        start?: Date;
+        end?: Date;
+    };
+    ok: () => void;
+    title?: string;
+    date?: Date;
+};
+export type {
+    IDateTimeSheetProps as default
+};

+ 20 - 0
src/components/index.ts

@@ -90,6 +90,26 @@ export {
     default as NotificationIndicator
 } from "./notificationIndicator";
 
+export {
+    default as DateTimePicker
+} from "./dateTimePicker";
+
+export {
+    default as DateTimeSheet
+} from "./dateTimeSheet";
+
+export type {
+    IDateTimePickerRef
+} from "./dateTimePicker/type";
+
+export {
+    default as DateSelector
+} from "./dateSelector";
+
+export type {
+    IDateSelectorRef
+} from "./dateSelector/type";
+
 export {
     default as MarkdownViewer
 } from "./markdownViewer";

+ 6 - 2
src/components/markdownViewer/index.tsx

@@ -351,8 +351,12 @@ const MarkdownViewer: FC<IMarkdownViewerProps> = ({
         return <TouchableOpacity
             {...linkContainerProps}
             key={node.key}
-            onPress={() => {
-                Linking.canOpenURL(node.url as string);
+            onPress={async () => {
+                const isCanOpen = await Linking.canOpenURL(node.url as string);
+
+                if(isCanOpen) {
+                    Linking.openURL(node.url as string);
+                }
             }}
         >
             <MarkdownViewer

+ 11 - 11
src/components/selectBox/index.tsx

@@ -82,7 +82,6 @@ function SelectBox<T>({
     hintTextIconStyle,
     data: initialData,
     selectSheetProps,
-    variant = "text",
     type = "default",
     rightIconOnPress,
     renderOptionIcon,
@@ -555,10 +554,6 @@ function SelectBox<T>({
             return null;
         }
 
-        if(variant !== "text") {
-            return null;
-        }
-
         if(!isCleanEnabled || !selectedItems.length) {
             return null;
         }
@@ -570,11 +565,8 @@ function SelectBox<T>({
                 cleanButtonDynamicStyle
             ]}
             onPress={() => {
-                if(isWorkWithRealtime) {
-                    setSelectedItems([]);
-                } else {
-                    setTempSelectedItems([]);
-                }
+                setSelectedItems([]);
+                setTempSelectedItems([]);
             }}
         >
             <CleanIcon
@@ -609,7 +601,7 @@ function SelectBox<T>({
             return null;
         }
 
-        if(isCleanEnabled && selectedItems.length > 0 && variant === "text") {
+        if(isCleanEnabled && selectedItems.length > 0) {
             return null;
         }
 
@@ -704,6 +696,8 @@ function SelectBox<T>({
         return <Text
             variant="labelLargeSize"
             color={titleProps.color}
+            ellipsizeMode="tail"
+            numberOfLines={1}
             style={[
                 stylesheet.subTitle,
                 subTitleDynamicStyle
@@ -734,6 +728,8 @@ function SelectBox<T>({
                 {...titleProps}
                 variant={titleProps.variant}
                 color={titleProps.color}
+                ellipsizeMode="tail"
+                numberOfLines={1}
                 style={[
                     stylesheet.title,
                     titleDynamicStyle
@@ -749,6 +745,8 @@ function SelectBox<T>({
         if(!selectedItems.length) {
             return <Text
                 variant="labelLargeSize"
+                ellipsizeMode="tail"
+                numberOfLines={1}
                 style={[
                     stylesheet.contentText,
                     contentTextDynamicStyle
@@ -760,6 +758,8 @@ function SelectBox<T>({
 
         return <Text
             variant="labelLargeSize"
+            ellipsizeMode="tail"
+            numberOfLines={1}
             style={[
                 stylesheet.contentText,
                 contentTextDynamicStyle

+ 6 - 6
src/components/selectBox/stylesheet.ts

@@ -108,7 +108,9 @@ const stylesheet = StyleSheet.create({
         minWidth: 250
     },
     content: {
-
+        flexShrink: 1,
+        width: "100%",
+        flex: 1
     },
     contentText: {
         minHeight: 20
@@ -124,7 +126,6 @@ const stylesheet = StyleSheet.create({
         alignItems: "center"
     },
     title: {
-
     },
     icon: {
         justifyContent: "center",
@@ -141,10 +142,10 @@ const stylesheet = StyleSheet.create({
         alignItems: "center"
     },
     subTitle: {
-
     },
     overlay: {
         position: "absolute",
+        display: "none",
         zIndex: 98,
         bottom: 0,
         right: 0,
@@ -187,10 +188,8 @@ export const useStyles = ({
             borderWidth: borders.line
         } as Mutable<ViewStyle>,
         content: {
-
         } as Mutable<ViewStyle>,
         contentText: {
-
         } as Mutable<TextStyle>,
         cleanButton: {
             marginLeft: spaces.spacingSm
@@ -199,7 +198,6 @@ export const useStyles = ({
             marginBottom: spaces.spacingSm
         } as Mutable<ViewStyle>,
         title: {
-
         } as Mutable<TextStyle>,
         icon: {
             marginRight: spaces.spacingSm
@@ -237,7 +235,9 @@ export const useStyles = ({
     }
 
     if (spreadBehaviour === "stretch") {
+        styles.container.alignSelf = spreadBehaviour;
         styles.container.justifyContent = "center";
+        styles.container.flexShrink = 1;
         styles.container.width = "100%";
     }
 

+ 0 - 3
src/components/selectBox/type.ts

@@ -57,8 +57,6 @@ export type SelectBoxType = "default" | "danger" | "warning" | "question" | "suc
 
 export type SelectBoxSpreadBehaviour = "baseline" | "stretch" | "free";
 
-export type SelectBoxVariant = "text" | "hidden";
-
 export type SelectBoxComponent = <T>(
   props: ISelectBoxProps<T> & { ref?: Ref<ISelectBoxRef<T>> }
 ) => ReactNode;
@@ -166,7 +164,6 @@ interface ISelectBoxProps<T> {
     cleanIconStyle?: ViewStyle;
     rightIconStyle?: ViewStyle;
     rightIcon?: NCoreUIKitIcon;
-    variant?: SelectBoxVariant;
     isShowSubTitle?: boolean;
     isCleanEnabled?: boolean;
     iconOnPress?: () => void;

+ 3 - 2
src/components/selectSheet/index.tsx

@@ -52,6 +52,7 @@ function SelectSheet<T>({
     minChoice,
     isLoading,
     selectAll,
+    customKey,
     cleanAll,
     isActive,
     cancel,
@@ -340,8 +341,8 @@ function SelectSheet<T>({
 
     return <BottomSheet
         {...bottomSheetProps}
+        key={customKey ? customKey : `${selectBoxKey}-bottomsheet`}
         scrollEndThreshold={moreLoadThreshold}
-        key={`${selectBoxKey}-bottomsheet`}
         customKey={selectBoxKey}
         ref={bottomSheetRef}
         isAutoHeight={true}
@@ -361,7 +362,7 @@ function SelectSheet<T>({
         renderHeader={() => {
             return renderBottomSheetHeader();
         }}
-        onClose={() => {
+        onClosed={() => {
             setIsActive(false);
         }}
     >

+ 1 - 0
src/components/selectSheet/type.ts

@@ -91,6 +91,7 @@ interface ISelectSheetProps<T> {
     maxChoice?: number;
     minChoice?: number;
     searchText: string;
+    customKey?: string;
     isLoading: boolean;
     cancel: () => void;
     isActive: boolean;

+ 0 - 0
src/components/timeSelector/index.tsx


+ 0 - 0
src/components/timeSelector/stylesheet.ts


+ 0 - 0
src/components/timeSelector/type.ts


+ 3 - 0
src/context/localize.tsx

@@ -65,6 +65,7 @@ class NCoreUIKitLocalize<T extends LocalizeType> extends NCoreContext<LocalizeCo
             activeLocale: initialSelectedLocale ?? "tr-TR",
             localize: () => "Localization is loading...",
             translations: initialLanguage.translations,
+            rruleConfig: initialLanguage.rruleConfig,
             isRTL: initialLanguage.isRTL
         }, {
             key: "NCoreUIKitLocalize"
@@ -90,6 +91,7 @@ class NCoreUIKitLocalize<T extends LocalizeType> extends NCoreContext<LocalizeCo
             activeLocale: defaultLocale.locale as keyof NCoreUIKit.LocaleKey,
             // localizeWithObject: this.localizeWithObject,
             translations: defaultLocale.translations,
+            rruleConfig: defaultLocale.rruleConfig,
             isRTL: defaultLocale.isRTL
             // localize: this.localize
         };
@@ -107,6 +109,7 @@ class NCoreUIKitLocalize<T extends LocalizeType> extends NCoreContext<LocalizeCo
         const newState: LocalizeContextStateType = {
             activeLocale: currentProjectLocale.locale as keyof NCoreUIKit.LocaleKey,
             // localizeWithObject: this.localizeWithObject,
+            rruleConfig: currentProjectLocale.rruleConfig,
             isRTL: currentProjectLocale.isRTL,
             // localize: this.localize,
             translations: {

+ 4 - 0
src/helpers/index.ts

@@ -14,3 +14,7 @@ export {
     mergePalettes,
     mergeTheme
 } from "./theme/palette";
+
+export {
+    parseRRuleConfig
+} from "./locale";

+ 28 - 0
src/helpers/locale/index.ts

@@ -0,0 +1,28 @@
+export const parseRRuleConfig = (rawConfig: NCoreUIKit.GeneratedRRuleConfigs) => {
+    const tokens: {
+        [key: string]: RegExp;
+    } = {
+    };
+
+    Object.keys(rawConfig.tokens).forEach((key) => {
+        let pattern = rawConfig.tokens[key as keyof typeof rawConfig.tokens];
+        let flags = "";
+
+        if (pattern.endsWith("/i")) {
+            pattern = pattern.slice(0, -2).replace(/^\^?/, "^");
+            flags = "i";
+        }
+
+        if (pattern.startsWith("/") && pattern.endsWith("/")) {
+            pattern = pattern.slice(1, -1);
+        }
+
+        tokens[key] = new RegExp(pattern, flags);
+    });
+
+    return {
+        monthNames: rawConfig.monthNames,
+        dayNames: rawConfig.dayNames,
+        tokens: tokens
+    };
+};

+ 16 - 0
src/index.tsx

@@ -16,8 +16,10 @@ export {
 export {
     NotificationIndicator,
     MarkdownViewer,
+    DateTimePicker,
     PageContainer,
     TextAreaInput,
+    DateSelector,
     BottomSheet,
     SelectSheet,
     RadioButton,
@@ -36,10 +38,12 @@ export {
 } from "./components";
 
 export type {
+    IDateTimePickerRef,
     EnterMarkdownTypes,
     ITextAreaInputRef,
     CodeMarkdownTypes,
     TextMarkdownTypes,
+    IDateSelectorRef,
     IBottomSheetRef,
     BlockquoteTypes,
     MarkdownObject,
@@ -64,6 +68,18 @@ export {
     textMarkdown
 } from "./components/markdownViewer/util";
 
+export {
+    mergeTypographyTokens,
+    parseRRuleConfig,
+    mergeTypography,
+    mergeRadiuses,
+    mergePalettes,
+    mergeBorders,
+    mergeSpaces,
+    mergeShapes,
+    mergeTheme
+} from "./helpers";
+
 export {
     ChevronRightIcon,
     LoadingIcon,

+ 5 - 0
src/types/index.ts

@@ -177,6 +177,7 @@ declare global {
         type Locales = Array<Locale>;
 
         interface Locale {
+            rruleConfig: GeneratedRRuleConfigs;
             translations: Translation;
             locale: string;
             isRTL: boolean;
@@ -184,6 +185,10 @@ declare global {
 
         type DefaultLocalization = typeof defaultLocaleJSON[0];
 
+        type GeneratedRRuleConfigs = {
+            [K in keyof DefaultLocalization["rruleConfig"]]: DefaultLocalization["rruleConfig"][K];
+        };
+
         type GeneratedTranslations = {
             [K in keyof DefaultLocalization["translations"]]: DefaultLocalization["translations"][K];
         };

+ 1 - 0
src/types/locale.ts

@@ -14,6 +14,7 @@ export type LocaleType = "en-US" | "tr-TR";
 export type LocalizeContextStateType = Partial<LocalizeContextType>;
 
 export type LocalizeContextType = {
+    rruleConfig: NCoreUIKit.GeneratedRRuleConfigs;
     translations: NCoreUIKit.Translation;
     localizeWithObject: (
         translation: keyof NCoreUIKit.Translation,

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

@@ -6,6 +6,7 @@
             "maximum-selection-limit-has-been-reached": "Maksimum seçim limitine ulaşıldı.",
             "minimum-a-item-selection-required": "Minimum 1 öğe seçimi gereklidir.",
             "selected-options-with-count": "{{0}} seçim yapıldı",
+            "select-any-date": "Bir tarih seçin",
             "select-an-option": "Seçim yapın",
             "clean-all": "Tümünü Temizle",
             "select-all": "Tümünü Seç",
@@ -13,6 +14,75 @@
             "cancel": "İptal",
             "search": "Ara",
             "ok": "Tamam"
+        },
+        "rruleConfig": {
+            "dayNames": [
+                "Pazar",
+                "Pazartesi",
+                "Salı",
+                "Çarşamba",
+                "Perşembe",
+                "Cuma",
+                "Cumartesi"
+            ],
+            "monthNames": [
+                "Ocak",
+                "Şubat",
+                "Mart",
+                "Nisan",
+                "Mayıs",
+                "Haziran",
+                "Temmuz",
+                "Ağustos",
+                "Eylül",
+                "Ekim",
+                "Kasım",
+                "Aralık"
+            ],
+            "tokens": {
+                "SKIP": "^[ \\r\\n\\t]+|^\\.$",
+                "number": "^[1-9][0-9]*",
+                "numberAsText": "^(bir|iki|üç)/i",
+                "every": "^her/i",
+                "day(s)": "^gün(ler)?/i",
+                "weekday(s)": "^hafta içi/i",
+                "week(s)": "^hafta(lar)?/i",
+                "hour(s)": "^saat(ler)?/i",
+                "minute(s)": "^dakika(lar)?/i",
+                "month(s)": "^ay(lar)?/i",
+                "year(s)": "^yıl(lar)?|^sene(ler)?/i",
+                "on": "^(günü|günde|ayında|yılında)/i",
+                "at": "^(saatinde|saat)/i",
+                "the": "^o/i",
+                "first": "^ilk|^birinci/i",
+                "second": "^ikinci/i",
+                "third": "^üçüncü/i",
+                "nth": "^([1-9][0-9]*)(\\.|ıncı|inci|uncu|üncü|ncı|nci|ncu|ncü)/i",
+                "last": "^son/i",
+                "for": "^için/i",
+                "time(s)": "^kez|^defa|^sefer/i",
+                "until": "^e kadar|^kadar/i",
+                "monday": "^pazartesi|^pzt/i",
+                "tuesday": "^salı|^sal/i",
+                "wednesday": "^çarşamba|^çar/i",
+                "thursday": "^perşembe|^per/i",
+                "friday": "^cuma|^cum/i",
+                "saturday": "^cumartesi|^cts/i",
+                "sunday": "^pazar|^paz/i",
+                "january": "^ocak|^oca/i",
+                "february": "^şubat|^şub/i",
+                "march": "^mart|^mar/i",
+                "april": "^nisan|^nis/i",
+                "may": "^mayıs|^may/i",
+                "june": "^haziran|^haz/i",
+                "july": "^temmuz|^tem/i",
+                "august": "^ağustos|^ağu/i",
+                "september": "^eylül|^eyl/i",
+                "october": "^ekim|^eki/i",
+                "november": "^kasım|^kas/i",
+                "december": "^aralık|^ara/i",
+                "comma": "^(,\\s*|(ve|veya)\\s*)+/i"
+            }
         }
     },
     {
@@ -23,12 +93,82 @@
             "minimum-a-item-selection-required": "Minimum 1 item selection required.",
             "selected-options-with-count": "{{0}} items selected",
             "select-an-option": "Select an option",
+            "select-any-date": "Select any date",
             "select-all": "Select All",
             "is-optional": "Optional",
             "clean-all": "Clean All",
             "cancel": "Cancel",
             "search": "Search",
             "ok": "Okey"
+        },
+        "rruleConfig": {
+            "dayNames": [
+                "Sunday",
+                "Monday",
+                "Tuesday",
+                "Wednesday",
+                "Thursday",
+                "Friday",
+                "Saturday"
+            ],
+            "monthNames": [
+                "January",
+                "February",
+                "March",
+                "April",
+                "May",
+                "June",
+                "July",
+                "August",
+                "September",
+                "October",
+                "November",
+                "December"
+            ],
+            "tokens": {
+                "SKIP": "^[ \\r\\n\\t]+|^\\.$",
+                "number": "^[1-9][0-9]*",
+                "numberAsText": "^(one|two|three)/i",
+                "every": "^every/i",
+                "day(s)": "^days?/i",
+                "weekday(s)": "^weekdays?/i",
+                "week(s)": "^weeks?/i",
+                "hour(s)": "^hours?/i",
+                "minute(s)": "^minutes?/i",
+                "month(s)": "^months?/i",
+                "year(s)": "^years?/i",
+                "on": "^(on|in)/i",
+                "at": "^(at)/i",
+                "the": "^the/i",
+                "first": "^first/i",
+                "second": "^second/i",
+                "third": "^third/i",
+                "nth": "^([1-9][0-9]*)(\\.|th|nd|rd|st)/i",
+                "last": "^last/i",
+                "for": "^for/i",
+                "time(s)": "^times?/i",
+                "until": "^(un)?til/i",
+                "monday": "^mo(n(day)?)?/i",
+                "tuesday": "^tu(e(s(day)?)?)?/i",
+                "wednesday": "^we(d(n(esday)?)?)?/i",
+                "thursday": "^th(u(r(sday)?)?)?/i",
+                "friday": "^fr(i(day)?)?/i",
+                "saturday": "^sa(t(urday)?)?/i",
+                "sunday": "^su(n(day)?)?/i",
+                "january": "^jan(uary)?/i",
+                "february": "^feb(ruary)?/i",
+                "march": "^mar(ch)?/i",
+                "april": "^apr(il)?/i",
+                "may": "^may/i",
+                "june": "^june?/i",
+                "july": "^july?/i",
+                "august": "^aug(ust)?/i",
+                "september": "^sep(t(ember)?)?/i",
+                "october": "^oct(ober)?/i",
+                "november": "^nov(ember)?/i",
+                "december": "^dec(ember)?/i",
+                "comma": "^(,\\s*|(and|or)\\s*)+/i"
+            }
         }
     }
 ]

+ 22 - 0
yarn.lock

@@ -13090,6 +13090,13 @@ __metadata:
   languageName: node
   linkType: hard
 
+"moment@https://git.nibgat.space/nibgat-community/moment.git":
+  version: 2.30.1
+  resolution: "moment@https://git.nibgat.space/nibgat-community/moment.git#commit=d0fddc23327d0d76fd88b340b9e58b418a7ade85"
+  checksum: 10c0/15ccd7171618ecce2b91ec230e104314c0262c38261d313c2a1899a4f0339f95ef98133c0d467c5c82912b495bc196ceeae588649559518326d674e833c8eca0
+  languageName: node
+  linkType: hard
+
 "move-concurrently@npm:^1.0.1":
   version: 1.0.1
   resolution: "move-concurrently@npm:1.0.1"
@@ -13211,6 +13218,7 @@ __metadata:
     expo-font: "npm:~55.0.4"
     expo-status-bar: "npm:~55.0.4"
     lucide-react-native: "npm:^1.8.0"
+    moment: "https://git.nibgat.space/nibgat-community/moment.git"
     ncore-context: "npm:^1.0.5"
     react: "npm:19.2.6"
     react-dom: "npm:19.2.6"
@@ -13221,6 +13229,7 @@ __metadata:
     react-native-screens: "npm:^4.24.0"
     react-native-svg: "npm:^15.15.3"
     react-native-web: "npm:~0.21.0"
+    rrule: "https://git.nibgat.space/nibgat-community/rrule.git"
   languageName: unknown
   linkType: soft
 
@@ -13249,6 +13258,7 @@ __metadata:
     globals: "npm:17.4.0"
     jsonc-eslint-parser: "npm:2.4.1"
     lucide-react-native: "npm:0.577.0"
+    moment: "https://git.nibgat.space/nibgat-community/moment.git"
     ncore-context: "npm:1.0.5"
     react: "npm:19.2.6"
     react-native: "npm:0.83.2"
@@ -13256,16 +13266,19 @@ __metadata:
     react-native-safe-area-context: "npm:5.7.0"
     react-native-svg: "npm:15.15.3"
     release-it: "npm:19.0.4"
+    rrule: "https://git.nibgat.space/nibgat-community/rrule.git"
     turbo: "npm:2.5.6"
     typescript: "npm:5.9.3"
     typescript-eslint: "npm:8.46.1"
   peerDependencies:
     lucide-react-native: ">= 0.577.0"
+    moment: "*"
     ncore-context: ">= 1.0.5"
     react: "*"
     react-native: "*"
     react-native-safe-area-context: ">= 5.7.0"
     react-native-svg: ">= 15.15.3"
+    rrule: "*"
   languageName: unknown
   linkType: soft
 
@@ -15500,6 +15513,15 @@ __metadata:
   languageName: node
   linkType: hard
 
+"rrule@https://git.nibgat.space/nibgat-community/rrule.git":
+  version: 2.8.0
+  resolution: "rrule@https://git.nibgat.space/nibgat-community/rrule.git#commit=2bf2e09ab11a7be8ec36aeebe711c00aa2899f70"
+  dependencies:
+    tslib: "npm:^2.4.0"
+  checksum: 10c0/b95d0925685cd72a9d34de51e65579dc12e15dc439f75cf1e61954acfbe5a24924829d9c443aa20db02d0d84bf041d0de6cd7f158d9dec6ac0a4847bb57f6441
+  languageName: node
+  linkType: hard
+
 "rsvp@npm:^4.8.4":
   version: 4.8.5
   resolution: "rsvp@npm:4.8.5"