Prechádzať zdrojové kódy

Feature: Date Time Picker is continue.

lfabl 1 mesiac pred
rodič
commit
1e1ccd9bfd

+ 6 - 2
package.json

@@ -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"
     }
 }

+ 1 - 1
src/components/bottomSheet/index.tsx

@@ -149,7 +149,7 @@ const BottomSheet: RefForwardingComponent<IBottomSheetRef, IBottomSheetProps> =
     useImperativeHandle(
         ref,
         () => ({
-            close: (onClosed) => {
+            close: (onClosed?) => {
                 closeAnimation(undefined, onClosed);
             },
             open: () => {

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

@@ -9,7 +9,7 @@ import type {
 import type IModalProps from "../modal/type";
 
 export interface IBottomSheetRef {
-    close: (onClosed: (props: {
+    close: (onClosed?: (props: {
         id: string;
     }) => void) => void;
     open: () => void;

+ 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>;

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

@@ -0,0 +1,792 @@
+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,
+    hintTextIcon: HintTextIconProp,
+    spreadBehaviour = "baseline",
+    customDateTimeSheetLocalize,
+    titleFormat = "DD MMM YYYY",
+    isShowDateTimeTools = true,
+    isShowHintTextIcon = false,
+    isWorkWithRealtime = true,
+    customDateTimeSheetTheme,
+    isMultipleSelect = false,
+    isWorkWithRRule = false,
+    icon: IconComponentProp,
+    hintTextContainerStyle,
+    removeSelectValidation,
+    isCleanEnabled = false,
+    contentContainerStyle,
+    subTitle = "Optional",
+    isSearchable = false,
+    onFocus: onFocusProp,
+    isRequired = false,
+    isDisabled = false,
+    onBlur: onBlurProp,
+    dateTimeSheetProps,
+    hintTextIconStyle,
+    initialDateRange,
+    type = "default",
+    selectValidation,
+    initialDateRule,
+    dateRangeTitle,
+    customLocalize,
+    rightIconStyle,
+    isShowSubTitle,
+    cleanIconStyle,
+    maxChoice = -1,
+    initialDate,
+    contentStyle,
+    customTheme,
+    placeholder,
+    isOptional,
+    customKey,
+    minChoice,
+    iconStyle,
+    onChange,
+    hintText,
+    onCancel,
+    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 selectObject = (selectedDate: Date) => {
+        if(isWorkWithRRule) {
+            // TODO: date rule.
+            console.log(0);
+        } else {
+            if(isMultipleSelect) {
+                // TODO: dateRange.
+                console.log(1);
+            } else {
+                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
+            LoadingIconComponentProp={LoadingIconComponentProp}
+            dateTimePickerKey={dateTimePickerKey.current}
+            customLocalize={customDateTimeSheetLocalize}
+            isWorkWithRealtime={isWorkWithRealtime}
+            customTheme={customDateTimeSheetTheme}
+            bottomSheetProps={dateTimeSheetProps}
+            isMultipleSelect={isMultipleSelect}
+            isShowTools={isShowDateTimeTools}
+            bottomSheetRef={bottomSheetRef}
+            selectObject={selectObject}
+            setIsLoading={setIsLoading}
+            setIsActive={setIsActive}
+            maxChoice={maxChoice}
+            minChoice={minChoice}
+            isLoading={isLoading}
+            isActive={isActive}
+            cancel={cancel}
+            clean={clean}
+            title={title}
+            ok={ok}
+        />
+    </View>;
+};
+export default forwardRef(DateTimePicker);

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

@@ -0,0 +1,250 @@
+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: {
+
+    },
+    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;

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

@@ -0,0 +1,174 @@
+import {
+    type ViewStyle
+} from "react-native";
+import {
+    type NCoreUIKitIcon
+} from "../../types";
+import type IBottomSheetProps from "../bottomSheet/type";
+import type {
+    RRule
+} from "rrule";
+
+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 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;
+    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;
+    title?: string;
+    id?: string;
+};
+export type {
+    IDateTimePickerProps as default
+};

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

@@ -0,0 +1,296 @@
+import {
+    TouchableOpacity,
+    View
+} from "react-native";
+import type IDateTimeSheetProps from "./type";
+import stylesheet, {
+    useStyles
+} from "./stylesheet";
+import {
+    NCoreUIKitLocalize,
+    NCoreUIKitTheme
+} from "../../core/hooks";
+import {
+    CheckIcon
+} from "lucide-react-native";
+import BottomSheet from "../bottomSheet";
+import CheckBox from "../checkBox";
+import Loading from "../loading";
+import Button from "../button";
+import Text from "../text";
+
+const DateTimeSheet = ({
+    LoadingIconComponentProp,
+    isWorkWithRealtime,
+    isShowTools = true,
+    dateTimePickerKey,
+    isMultipleSelect,
+    bottomSheetProps,
+    bottomSheetRef,
+    customLocalize,
+    setIsLoading,
+    selectObject,
+    customTheme,
+    setIsActive,
+    maxChoice,
+    minChoice,
+    isLoading,
+    isActive,
+    cancel,
+    clean,
+    title,
+    ok
+}: IDateTimeSheetProps) => {
+    const {
+        radiuses,
+        colors,
+        spaces
+    } = NCoreUIKitTheme.useContext(customTheme);
+
+    const {
+        localize
+    } = NCoreUIKitLocalize.useContext(customLocalize);
+
+    const {
+        checkIconContainer: checkIconContainerDynamicStyle,
+        loadingContainer: loadingContainerDynamicStyle,
+        contentContainer: contentContainerDynamicStyle,
+        headerContainer: headerContainerDynamicStyle,
+        bottomContainer: bottomContainerDynamicStyle,
+        toolsContainer: toolsContainerDynamicStyle,
+        itemContainer: itemContainerDynamicStyle,
+        cancelButton: cancelButtonDynamicStyle,
+        headerTitle: headerTitleDynamicStyle,
+        okButton: okButtonDynamicStyle
+    } = useStyles({
+        radiuses,
+        spaces,
+        colors
+    });
+
+    const renderBottomSheetContent = () => {
+        if(isLoading) {
+            return <View
+                style={[
+                    stylesheet.loadingContainer,
+                    loadingContainerDynamicStyle
+                ]}
+            >
+                {LoadingIconComponentProp ? <LoadingIconComponentProp
+                    color="emphasized"
+                    size={20}
+                /> : <Loading/>}
+            </View>;
+        }
+
+        const currentData = searchText && searchText.length ? searchedData : data;
+
+        return <View
+            style={[
+                contentContainerDynamicStyle
+            ]}
+        >
+            {
+                currentData.map((item: T & SelectedItem | SelectedItem, index: number) => {
+                    const isSelected = mainSelectedItems.findIndex((sI: SelectedItem) => {
+                        return sI.__key === item.__key;
+                    }) !== -1;
+
+                    if(renderItem) {
+                        return renderItem({
+                            key: keyExtractor(item as T & SelectedItem, index),
+                            onSelect: (sItem) => {
+                                selectObject(sItem);
+                            },
+                            selectedItems: mainSelectedItems,
+                            setIsMoreLoading,
+                            setIsLoading,
+                            isSelected,
+                            searchText,
+                            index,
+                            item,
+                            data
+                        });
+                    }
+
+                    let isDisabled = false;
+
+                    if(isMultipleSelect && !isSelected && maxChoice !== -1) {
+                        if(!maxChoice) {
+                            throw new Error("If you want to use the \"isMultipleSelect\" prop, you must be provide \"maxChoice\" prop.");
+                        }
+
+                        if(mainSelectedItems.length >= maxChoice) {
+                            isDisabled = true;
+                        }
+                    }
+
+                    return <TouchableOpacity
+                        key={keyExtractor(item as T & SelectedItem, index)}
+                        disabled={isDisabled}
+                        style={[
+                            stylesheet.itemContainer,
+                            itemContainerDynamicStyle,
+                            {
+                                marginTop: index === 0 ? 0 : spaces.spacingSm,
+                                position: "relative"
+                            }
+                        ]}
+                        onPress={() => {
+                            if(isDisabled) {
+                                return;
+                            }
+
+                            selectObject(item);
+                        }}
+                    >
+                        <View
+                            style={[
+                                stylesheet.itemContentContainer,
+                                {
+                                    zIndex: 99999
+                                }
+                            ]}
+                        >
+                            <Text
+                                color={isSelected ? "emphasized" : undefined}
+                            >{item.__title}</Text>
+                        </View>
+                        <View
+                            style={[
+                                stylesheet.checkIconContainer,
+                                checkIconContainerDynamicStyle
+                            ]}
+                        >
+                            {isSelected ?
+                                <CheckIcon
+                                    color={colors.content.icon.emphasized}
+                                    size={24}
+                                /> : null}
+                        </View>
+                        {isDisabled ? <View
+                            style={[
+                                {
+                                    backgroundColor: colors.system.disabled.content,
+                                    borderRadius: radiuses.actions,
+                                    position: "absolute",
+                                    zIndex: 99998,
+                                    bottom: 0,
+                                    right: 0,
+                                    left: 0,
+                                    top: 0
+                                }
+                            ]}
+                        /> : null}
+                    </TouchableOpacity>;
+                })
+            }
+        </View>;
+    };
+
+    const renderTools = () => {
+        if(!isShowTools) {
+            return null;
+        }
+
+        if(!isMultipleSelect) {
+            return null;
+        }
+
+        if(minChoice && maxChoice !== -1) {
+            return null;
+        }
+
+        return <View
+            style={[
+                stylesheet.toolsContainer,
+                toolsContainerDynamicStyle
+            ]}
+        >
+            {!minChoice ? <CheckBox
+                isChecked={mainSelectedItems.length === 0 ? "checked" : null}
+                title={localize("clean-all")}
+                spreadBehaviour="free"
+                onPress={() => {
+                    clean();
+                }}
+            /> : null}
+        </View>;
+    };
+
+    const renderBottomSheetHeader = () => {
+        return <View
+            style={[
+                stylesheet.headerContainer,
+                headerContainerDynamicStyle
+            ]}
+        >
+            <Text
+                variant="titleMediumSize"
+                style={[
+                    headerTitleDynamicStyle
+                ]}
+            >
+                {title}
+            </Text>
+            {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`}
+        customKey={dateTimePickerKey}
+        ref={bottomSheetRef}
+        isAutoHeight={true}
+        isActive={isActive}
+        renderBottom={() => {
+            return renderBottomSheetBottom();
+        }}
+        renderHeader={() => {
+            return renderBottomSheetHeader();
+        }}
+        onClose={() => {
+            setIsActive(false);
+        }}
+    >
+        {renderBottomSheetContent()}
+    </BottomSheet>;
+};
+export default DateTimeSheet;

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

@@ -0,0 +1,97 @@
+import {
+    type ViewStyle,
+    StyleSheet
+} from "react-native";
+import {
+    type SelectSheetDynamicStyleType
+} 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
+    },
+    itemContainer: {
+        flexDirection: "row",
+        alignItems: "center"
+    },
+    itemContentContainer: {
+        flex: 1
+    },
+    checkIconContainer: {
+        justifyContent: "center",
+        alignItems: "center"
+    },
+    toolsContainer: {
+        justifyContent: "space-between",
+        flexDirection: "row",
+        alignItems: "center"
+    },
+    moreLoadingContainer: {
+        justifyContent: "center",
+        alignItems: "center",
+        width: "100%"
+    }
+});
+
+export const useStyles = ({
+    radiuses,
+    spaces,
+    colors
+}: SelectSheetDynamicStyleType) => {
+    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>,
+        itemContainer: {
+            backgroundColor: colors.content.container.mid,
+            paddingHorizontal: spaces.spacingMd,
+            paddingVertical: spaces.spacingSm,
+            borderRadius: radiuses.form
+        } as Mutable<ViewStyle>,
+        checkIconContainer: {
+            height: 24 + spaces.spacingSm,
+            width: 24 + 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;

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

@@ -0,0 +1,61 @@
+import type {
+    SetStateAction,
+    Dispatch,
+    Ref
+} from "react";
+import {
+    type NCoreUIKitIcon
+} from "../../types";
+import type {
+    IBottomSheetRef
+} from "../bottomSheet/type";
+import type IBottomSheetProps from "../bottomSheet/type";
+
+export type SelectSheetDynamicStyleType = {
+    radiuses: NCoreUIKit.ActivePalette["radiuses"];
+    spaces: NCoreUIKit.ActivePalette["spaces"];
+    colors: NCoreUIKit.ActivePalette["colors"];
+};
+
+interface IDateTimeSheetProps {
+    selectObject: (date: Date) => void;
+    customTheme?: {
+        gapPropagation?: keyof NCoreUIKit.GapPropagationKey;
+        sharpness?: keyof NCoreUIKit.SharpnessKey;
+        paletteKey?: keyof NCoreUIKit.PaletteKey;
+        themeKey?: keyof NCoreUIKit.ThemeKey;
+    };
+    customLocalize?: {
+        activeLocale?: keyof NCoreUIKit.LocaleKey;
+    };
+    setIsLoading: Dispatch<SetStateAction<boolean>>;
+    setIsActive: Dispatch<SetStateAction<boolean>>;
+    bottomSheetRef: Ref<IBottomSheetRef> | null;
+    LoadingIconComponentProp?: NCoreUIKitIcon;
+    dateTimePickerKey: string;
+    bottomSheetProps?: Omit<
+        IBottomSheetProps,
+        "key" |
+        "customKey" |
+        "ref" |
+        "isAutoHeight" |
+        "isActive" |
+        "renderBottom" |
+        "renderHeader" |
+        "onClose"
+    >;
+    isWorkWithRealtime: boolean;
+    isMultipleSelect?: boolean;
+    isShowTools?: boolean;
+    maxChoice?: number;
+    minChoice?: number;
+    isLoading: boolean;
+    cancel: () => void;
+    isActive: boolean;
+    clean: () => void;
+    ok: () => void;
+    title?: string;
+};
+export type {
+    IDateTimeSheetProps as default
+};

+ 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

+ 1 - 6
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;
         }
@@ -609,7 +604,7 @@ function SelectBox<T>({
             return null;
         }
 
-        if(isCleanEnabled && selectedItems.length > 0 && variant === "text") {
+        if(isCleanEnabled && selectedItems.length > 0) {
             return null;
         }
 

+ 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 - 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
+    };
+};

+ 12 - 0
src/index.tsx

@@ -64,6 +64,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"
+            }
         }
     }
 ]

+ 20 - 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"
@@ -13249,6 +13256,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 +13264,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 +15511,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"