فهرست منبع

Merge branch 'develop' of https://git.nibgat.space/nibgat-community/ncore-ui-kit-mobile

lfabl 1 ماه پیش
والد
کامیت
efeccce56e

+ 35 - 2
example/src/pages/home/index.tsx

@@ -24,7 +24,9 @@ import {
     RowCard,
     Button,
     Dialog,
-    Text
+    Switch,
+    Text,
+    NotificationIndicator
 } from "ncore-ui-kit-mobile";
 import {
     useNavigation
@@ -220,6 +222,11 @@ const Home = () => {
         setIsRadioActive
     ] = useState(false);
 
+    const [
+        isSwitchActive,
+        setIsSwitchActive
+    ] = useState(false);
+
     return <PageContainer
         isScrollable={true}
         scrollViewStyle={stylesheet.container}
@@ -229,6 +236,32 @@ const Home = () => {
             }
         }}
     >
+        <NotificationIndicator
+            type="danger"
+            title={153}
+        >
+            <Button
+                size="small"
+                icon={({
+                    color
+                }) => <HomeIcon
+                    color={colors.content.icon[color]}
+                />}
+                onPress={() => {
+
+                }}
+            />
+        </NotificationIndicator>
+        <Switch
+            spreadBehaviour="stretch"
+            isActive={isSwitchActive}
+            title="dsgojksdgpojk"
+            subTitle="SJFAJdfa"
+            isDisabled={false}
+            onPress={() => {
+                setIsSwitchActive(!isSwitchActive);
+            }}
+        />
         <RowCard
             title="Merhaba"
             icon={({
@@ -293,7 +326,7 @@ const Home = () => {
             isRequired={true}
             data={X}
             onMoreLoad={(props) => {
-                console.log("GELMİŞEEEE:", props);
+                console.log("More Loaded:", props);
             }}
         />
         <TextInput

+ 8 - 0
src/components/index.ts

@@ -81,3 +81,11 @@ export {
 export {
     default as RowCard
 } from "./rowCard";
+
+export {
+    default as Switch
+} from "./switch";
+
+export {
+    default as NotificationIndicator
+} from "./notificationIndicator";

+ 162 - 0
src/components/notificationIndicator/index.tsx

@@ -0,0 +1,162 @@
+import {
+    type FC
+} from "react";
+import {
+    View
+} from "react-native";
+import type INotificationIndicatorProps from "./type";
+import stylesheet, {
+    getNotificationIndicatorType,
+    useStyles
+} from "./stylesheet";
+import {
+    NCoreUIKitTheme
+} from "../../core/hooks";
+import type ITextProps from "../text/type";
+import Loading from "../loading";
+import Text from "../text";
+
+const NotificationIndicator: FC<INotificationIndicatorProps> = ({
+    floorBackgroundColor = "default",
+    titleVariant = "labelSmallSize",
+    isDisabled = false,
+    type = "neutral",
+    spreadBehaviour,
+    customTheme,
+    titleStyle,
+    isLoading,
+    location,
+    children,
+    title,
+    style,
+    ...props
+}) => {
+    const {
+        typography,
+        radiuses,
+        borders,
+        colors,
+        spaces
+    } = NCoreUIKitTheme.useContext(customTheme);
+
+    const currentType = getNotificationIndicatorType({
+        type
+    });
+
+    const {
+        indicatorContainer: indicatorContainerDynamicStyle,
+        titleContainer: titleContainerDynamicStyle,
+        container: containerDynamicStyle,
+        loading: loadingDynamicStyle,
+        overlay: overlayDynamicStyle,
+        title: titleDynamicStyle
+    } = useStyles({
+        floorBackgroundColor,
+        spreadBehaviour,
+        currentType,
+        isDisabled,
+        isLoading,
+        radiuses,
+        borders,
+        colors,
+        spaces,
+        type
+    });
+
+    const titleProps: ITextProps = {
+        color: currentType.titleColor,
+        variant: titleVariant
+    };
+
+    const fontSize = typography[titleProps.variant as keyof NCoreUIKit.Typography].fontSize;
+
+    let baseLocation = location ? location : {
+        bottom: undefined,
+        left: undefined,
+        right: 0,
+        top: 2
+    };
+
+    if(!location && title) {
+        baseLocation = {
+            right: -fontSize / String(title).length,
+            top: -fontSize / 2,
+            bottom: undefined,
+            left: undefined
+        };
+    }
+
+    if (isDisabled || isLoading) {
+        const stateType = type === "danger" ? "error" : type;
+
+        titleProps.customColor = colors.system.state.content.disabled[stateType];
+    }
+
+    const renderTitle = () => {
+        if (!title) {
+            return null;
+        }
+
+        return <View
+            style={[
+                stylesheet.titleContainer,
+                titleContainerDynamicStyle
+            ]}
+        >
+            <Text
+                variant="bodyMediumSize"
+                style={[
+                    titleStyle,
+                    stylesheet.title,
+                    titleDynamicStyle
+                ]}
+                {...titleProps}
+            >
+                {title}
+            </Text>
+            {renderOverlay()}
+        </View>;
+    };
+
+    const renderIndicatorContainer = () => {
+        if (isLoading) {
+            return <Loading
+                style={[
+                    loadingDynamicStyle
+                ]}
+            />;
+        }
+
+        return <View
+            style={[
+                stylesheet.indicatorContainer,
+                indicatorContainerDynamicStyle,
+                baseLocation
+            ]}
+        >
+            {renderTitle()}
+        </View>;
+    };
+
+    const renderOverlay = () => {
+        return <View
+            style={[
+                stylesheet.overlay,
+                overlayDynamicStyle
+            ]}
+        />;
+    };
+
+    return <View
+        {...props}
+        style={[
+            style,
+            stylesheet.container,
+            containerDynamicStyle
+        ]}
+    >
+        {children}
+        {renderIndicatorContainer()}
+    </View>;
+};
+export default NotificationIndicator;

+ 155 - 0
src/components/notificationIndicator/stylesheet.ts

@@ -0,0 +1,155 @@
+import {
+    type TextStyle,
+    type ViewStyle,
+    StyleSheet
+} from "react-native";
+import {
+    type NotificationIndicatorDynamicStyleType,
+    type NotificationIndicatorTypeConstantType,
+    type NotificationIndicatorTypes,
+    type NotificationIndicatorType
+} from "./type";
+import type {
+    Mutable
+} from "../../types";
+
+export const NOTIFICATION_INDICATOR_TYPE_STYLES: Record<
+    NotificationIndicatorType,
+    {
+        containerColor: keyof NCoreUIKit.ContainerContentColors;
+        subTitleColor: keyof NCoreUIKit.TextContentColors;
+        titleColor: keyof NCoreUIKit.TextContentColors;
+    }
+> = {
+    primary: {
+        containerColor: "emphasized",
+        titleColor: "emphasized",
+        subTitleColor: "low"
+    },
+    danger: {
+        subTitleColor: "dangerLow",
+        containerColor: "danger",
+        titleColor: "danger"
+    },
+    success: {
+        subTitleColor: "successLow",
+        containerColor: "success",
+        titleColor: "success"
+    },
+    warning: {
+        subTitleColor: "warningLow",
+        containerColor: "warning",
+        titleColor: "warning"
+    },
+    info: {
+        subTitleColor: "infoLow",
+        containerColor: "info",
+        titleColor: "info"
+    },
+    neutral: {
+        containerColor: "mid",
+        subTitleColor: "low",
+        titleColor: "mid"
+    }
+};
+
+export const getNotificationIndicatorType = ({
+    type
+}: NotificationIndicatorTypeConstantType): NotificationIndicatorTypes => {
+    const currentType = NOTIFICATION_INDICATOR_TYPE_STYLES[type];
+
+    return currentType;
+};
+
+const stylesheet = StyleSheet.create({
+    container: {
+        backgroundColor: "transparent",
+        borderColor: "transparent",
+        alignSelf: "baseline",
+        position: "relative"
+    },
+    indicatorContainer: {
+        borderStyle: "solid",
+        position: "absolute",
+        minHeight: 18,
+        minWidth: 18
+    },
+    titleContainer: {
+        position: "relative"
+    },
+    title: {
+    },
+    loading: {
+    },
+    overlay: {
+        position: "absolute",
+        display: "none",
+        zIndex: 999,
+        bottom: 0,
+        right: 0,
+        left: 0,
+        top: 0
+    }
+});
+
+export const useStyles = ({
+    floorBackgroundColor,
+    spreadBehaviour,
+    currentType,
+    isDisabled,
+    isLoading,
+    radiuses,
+    borders,
+    colors,
+    spaces,
+    type
+}: NotificationIndicatorDynamicStyleType) => {
+    const styleType = type === "danger" ? "error" : type;
+
+    const styles = {
+        container: {
+            padding: spaces.spacingSm
+        } as Mutable<ViewStyle>,
+        indicatorContainer: {
+            backgroundColor: colors.content.container[currentType.containerColor],
+            borderColor: colors.content.container[floorBackgroundColor],
+            paddingHorizontal: spaces.spacingSm,
+            paddingVertical: spaces.spacingXs,
+            borderWidth: borders.subtract,
+            borderRadius: radiuses.full
+        } as Mutable<ViewStyle>,
+        titleContainer: {
+        } as Mutable<ViewStyle>,
+        title: {
+
+        } as Mutable<TextStyle>,
+        loading: {
+        } as Mutable<ViewStyle>,
+        overlay: {
+
+        } as Mutable<ViewStyle>
+    };
+
+    if (spreadBehaviour === "baseline") {
+        styles.container.alignSelf = spreadBehaviour;
+        styles.container.width = "auto";
+    } else if (spreadBehaviour === "stretch") {
+        styles.container.alignSelf = spreadBehaviour;
+        styles.container.justifyContent = "center";
+        styles.container.flexShrink = 1;
+        styles.container.width = "100%";
+    }
+
+    if (isLoading) {
+        styles.overlay.backgroundColor = colors.system.state.overlay.disabled[styleType];
+        styles.overlay.display = "flex";
+    }
+
+    if (isDisabled) {
+        styles.overlay.backgroundColor = colors.system.state.overlay.disabled[styleType];
+        styles.overlay.display = "flex";
+    }
+
+    return styles;
+};
+export default stylesheet;

+ 63 - 0
src/components/notificationIndicator/type.ts

@@ -0,0 +1,63 @@
+import type {
+    ReactNode
+} from "react";
+import {
+    type ViewStyle,
+    type StyleProp,
+    type TextStyle
+} from "react-native";
+
+export type NotificationIndicatorDynamicStyleType = {
+    floorBackgroundColor: keyof NCoreUIKit.ContainerContentColors;
+    radiuses: NCoreUIKit.ActivePalette["radiuses"];
+    borders: NCoreUIKit.ActivePalette["borders"];
+    spaces: NCoreUIKit.ActivePalette["spaces"];
+    colors: NCoreUIKit.ActivePalette["colors"];
+    spreadBehaviour?: SwitchSpreadBehaviour;
+    currentType: NotificationIndicatorTypes;
+    type: NotificationIndicatorType;
+    isDisabled?: boolean;
+    isLoading?: boolean;
+};
+
+export type NotificationIndicatorTypes = {
+    containerColor: keyof NCoreUIKit.ContainerContentColors;
+    subTitleColor: keyof NCoreUIKit.TextContentColors;
+    titleColor: keyof NCoreUIKit.TextContentColors;
+};
+
+export type NotificationIndicatorTypeConstantType = {
+    type: NotificationIndicatorType;
+};
+
+export type NotificationIndicatorType = "primary" | "danger" | "warning" | "neutral" | "success" | "info";
+
+export type SwitchSpreadBehaviour = "baseline" | "stretch" | "free";
+
+interface INotificationIndicatorProps {
+    floorBackgroundColor?: keyof NCoreUIKit.ContainerContentColors;
+    titleStyle?: StyleProp<TextStyle>[] | StyleProp<TextStyle>;
+    style?: StyleProp<ViewStyle>[] | StyleProp<ViewStyle>;
+    customTheme?: {
+        gapPropagation?: NCoreUIKit.GapPropagationKey;
+        sharpness?: NCoreUIKit.SharpnessKey;
+        paletteKey?: NCoreUIKit.PaletteKey;
+        themeKey?: NCoreUIKit.ThemeKey;
+    };
+    titleVariant?: keyof NCoreUIKit.Typography;
+    spreadBehaviour?: SwitchSpreadBehaviour;
+    type?: NotificationIndicatorType;
+    isDisabled?: boolean;
+    isLoading?: boolean;
+    children: ReactNode;
+    title?: number;
+    location?: {
+        bottom?: number;
+        right?: number;
+        left?: number;
+        top?: number;
+    };
+}
+export type {
+    INotificationIndicatorProps as default
+};

+ 31 - 30
src/components/selectBox/index.tsx

@@ -131,36 +131,6 @@ function SelectBox<T>({
         type
     });
 
-    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,
-        radiuses,
-        borders,
-        spaces,
-        colors,
-        title,
-        type
-    });
-
     const styleType = type === "default" ? "neutral" : type === "question" ? "neutral" : type === "danger" ? "error" : type;
 
     const [
@@ -205,6 +175,37 @@ function SelectBox<T>({
 
     const mainSelectedItems = isWorkWithRealtime ? selectedItems : tempSelectedItems;
 
+    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,
         () => ({

+ 5 - 0
src/components/selectBox/stylesheet.ts

@@ -165,6 +165,7 @@ export const useStyles = ({
     currentType,
     isDisabled,
     radiuses,
+    isActive,
     borders,
     colors,
     spaces,
@@ -240,6 +241,10 @@ export const useStyles = ({
         styles.container.width = "100%";
     }
 
+    if(isActive) {
+        styles.contentContainer.borderColor = colors.content.border.emphasized;
+    }
+
     return styles;
 };
 export default stylesheet;

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

@@ -36,6 +36,7 @@ export type SelectBoxDynamicStyleType = {
     isSearchable?: boolean;
     isDisabled?: boolean;
     type: SelectBoxType;
+    isActive: boolean;
     title?: string;
     icon?: boolean;
 };

+ 275 - 0
src/components/switch/index.tsx

@@ -0,0 +1,275 @@
+import {
+    type FC,
+    useEffect,
+    useRef
+} from "react";
+import {
+    TouchableOpacity,
+    Animated,
+    Easing,
+    View
+} from "react-native";
+import type ISwitchProps from "./type";
+import stylesheet, {
+    getSwitchType,
+    useStyles
+} from "./stylesheet";
+import {
+    NCoreUIKitLocalize,
+    NCoreUIKitTheme
+} from "../../core/hooks";
+import type ITextProps from "../text/type";
+import Loading from "../loading";
+import Text from "../text";
+
+const Switch: FC<ISwitchProps> = ({
+    displayBehaviourWhileLoading = "disabled",
+    spreadBehaviour = "baseline",
+    animationDuration = 100,
+    isOptional = false,
+    isDisabled = false,
+    type = "neutral",
+    isFlip = false,
+    customLocalize,
+    subTitleStyle,
+    optionalText,
+    customTheme,
+    titleStyle,
+    isLoading,
+    isActive,
+    subTitle,
+    onPress,
+    title,
+    style,
+    ...props
+}) => {
+    const {
+        inlineSpaces,
+        colors,
+        spaces
+    } = NCoreUIKitTheme.useContext(customTheme);
+
+    const {
+        localize
+    } = NCoreUIKitLocalize.useContext(customLocalize);
+
+    const indicatorAnim = useRef(new Animated.Value(0)).current;
+
+    const currentType = getSwitchType({
+        type
+    });
+
+    const SWITCH_INDICATOR_SIZE = 20;
+
+    const {
+        indicatorContainer: indicatorContainerDynamicStyle,
+        contentContainer: contentContainerDynamicStyle,
+        titleContainer: titleContainerDynamicStyle,
+        optionalText: optionalTextDynamicStyle,
+        container: containerDynamicStyle,
+        indicator: indicatorDynamicStyle,
+        subTitle: subTitleDynamicStyle,
+        loading: loadingDynamicStyle,
+        overlay: overlayDynamicStyle,
+        title: titleDynamicStyle
+    } = useStyles({
+        displayBehaviourWhileLoading,
+        SWITCH_INDICATOR_SIZE,
+        spreadBehaviour,
+        inlineSpaces,
+        currentType,
+        isDisabled,
+        isLoading,
+        isActive,
+        isFlip,
+        colors,
+        spaces,
+        type
+    });
+
+    useEffect(() => {
+        if(isActive) {
+            Animated.timing(indicatorAnim, {
+                toValue: (SWITCH_INDICATOR_SIZE / 2) + spaces.spacingSm,
+                duration: animationDuration,
+                useNativeDriver: true,
+                easing: Easing.linear
+            }).start();
+        } else {
+            Animated.timing(indicatorAnim, {
+                duration: animationDuration,
+                useNativeDriver: true,
+                easing: Easing.linear,
+                toValue: 0
+            }).start();
+        }
+    }, [isActive]);
+
+    const titleProps: ITextProps = {
+        color: currentType.titleColor,
+    };
+
+    const subTitleProps: ITextProps = {
+        color: currentType.subTitleColor,
+    };
+
+    const indicatorIconProps: {
+        color: keyof NCoreUIKit.ContainerContentColors;
+        customColor?: string;
+    } = {
+        color: currentType.indicatorColor
+    };
+
+    if (isDisabled || isLoading) {
+        const stateType = type === "danger" ? "error" : type;
+
+        subTitleProps.customColor = colors.system.state.content.disabled[stateType];
+        titleProps.customColor = colors.system.state.content.disabled[stateType];
+
+        if(isActive && stateType === "neutral") {
+            indicatorIconProps.customColor = colors.system.state.border.disabled.primary;
+        } else {
+            indicatorIconProps.customColor = colors.system.state.border.disabled[stateType];
+        }
+    }
+
+    const renderOptionalText = () => {
+        if(!isOptional && !optionalText) {
+            return null;
+        }
+
+        return <Text
+            variant="labelLargeSize"
+            color={titleProps.color}
+            style={[
+                optionalTextDynamicStyle
+            ]}
+            {...titleProps}
+        >
+            ( {isOptional ? localize("is-optional") : optionalText} )
+        </Text>;
+    };
+
+    const renderTitle = () => {
+        if (!title) {
+            return null;
+        }
+
+        return <View
+            style={[
+                stylesheet.titleContainer,
+                titleContainerDynamicStyle
+            ]}
+        >
+            <Text
+                variant="bodyMediumSize"
+                style={[
+                    titleStyle,
+                    stylesheet.title,
+                    titleDynamicStyle
+                ]}
+                {...titleProps}
+            >
+                {title}
+            </Text>
+            {renderOptionalText()}
+        </View>;
+    };
+
+    const renderIndicator = () => {
+        return <Animated.View
+            style={[
+                stylesheet.indicator,
+                indicatorDynamicStyle,
+                {
+                    transform: [{
+                        translateX: indicatorAnim
+                    }]
+                }
+            ]}
+        />;
+    };
+
+    const renderIndicatorContainer = () => {
+        if (isLoading) {
+            return <Loading
+                style={[
+                    loadingDynamicStyle
+                ]}
+            />;
+        }
+
+        return <View
+            style={[
+                stylesheet.indicatorContainer,
+                indicatorContainerDynamicStyle
+            ]}
+        >
+            {renderIndicator()}
+
+            {renderOverlay()}
+        </View>;
+    };
+
+    const renderOverlay = () => {
+        return <View
+            style={[
+                stylesheet.overlay,
+                overlayDynamicStyle
+            ]}
+        />;
+    };
+
+    const renderSubtitle = () => {
+        if(!subTitle) {
+            return null;
+        }
+
+        return <Text
+            {...subTitleProps}
+            style={[
+                subTitleStyle,
+                stylesheet.subTitle,
+                subTitleDynamicStyle
+            ]}
+        >
+            {subTitle}
+        </Text>;
+    };
+
+    const renderContent = () => {
+        if(!title) {
+            return null;
+        }
+
+        return <View
+            style={[
+                stylesheet.contentContainer,
+                contentContainerDynamicStyle
+            ]}
+        >
+            {renderTitle()}
+            {renderSubtitle()}
+        </View>;
+    };
+
+    return (
+        <TouchableOpacity
+            {...props}
+            onPress={isDisabled || isLoading ? () => null : onPress ? onPress : undefined}
+            disabled={isDisabled || isLoading || !onPress}
+            style={[
+                style,
+                stylesheet.container,
+                containerDynamicStyle
+            ]}
+        >
+            {isFlip ? null : renderIndicatorContainer()}
+
+            {renderContent()}
+
+            {isFlip ? renderIndicatorContainer() : null}
+        </TouchableOpacity>
+    );
+};
+export default Switch;

+ 214 - 0
src/components/switch/stylesheet.ts

@@ -0,0 +1,214 @@
+import {
+    type TextStyle,
+    type ViewStyle,
+    StyleSheet
+} from "react-native";
+import {
+    type SwitchDynamicStyleType,
+    type SwitchTypeConstantType,
+    type SwitchTypes,
+    type SwitchType
+} from "./type";
+import type {
+    Mutable
+} from "../../types";
+
+export const SWITCH_TYPE_STYLES: Record<
+    SwitchType,
+    {
+        containerColor: keyof NCoreUIKit.ContainerContentColors;
+        indicatorColor: keyof NCoreUIKit.IconContentColors;
+        subTitleColor: keyof NCoreUIKit.TextContentColors;
+        titleColor: keyof NCoreUIKit.TextContentColors;
+    }
+> = {
+    primary: {
+        indicatorColor: "default",
+        containerColor: "mid",
+        subTitleColor: "low",
+        titleColor: "mid"
+    },
+    danger: {
+        subTitleColor: "dangerLow",
+        indicatorColor: "danger",
+        containerColor: "danger",
+        titleColor: "danger"
+    },
+    success: {
+        subTitleColor: "successLow",
+        indicatorColor: "success",
+        containerColor: "success",
+        titleColor: "success"
+    },
+    warning: {
+        subTitleColor: "warningLow",
+        indicatorColor: "warning",
+        containerColor: "warning",
+        titleColor: "warning"
+    },
+    info: {
+        subTitleColor: "infoLow",
+        indicatorColor: "info",
+        containerColor: "info",
+        titleColor: "info"
+    },
+    neutral: {
+        indicatorColor: "default",
+        containerColor: "mid",
+        subTitleColor: "low",
+        titleColor: "mid"
+    }
+};
+
+export const getSwitchType = ({
+    type
+}: SwitchTypeConstantType): SwitchTypes => {
+    const currentType = SWITCH_TYPE_STYLES[type];
+
+    return currentType;
+};
+
+const stylesheet = StyleSheet.create({
+    container: {
+        backgroundColor: "transparent",
+        borderColor: "transparent",
+        flexDirection: "row",
+        borderStyle: "solid",
+        alignItems: "center",
+        display: "flex"
+    },
+    indicatorContainer: {
+        position: "relative"
+    },
+    indicator: {
+
+    },
+    contentContainer: {
+        justifyContent: "center",
+        flexDirection: "column"
+    },
+    titleContainer: {
+        justifyContent: "flex-start",
+        flexDirection: "row",
+        alignItems: "center"
+    },
+    title: {
+        textAlign: "left",
+        margin: 0
+    },
+    subTitle: {
+        textAlign: "left"
+    },
+    loading: {},
+    overlay: {
+        position: "absolute",
+        display: "none",
+        zIndex: 999,
+        bottom: 0,
+        right: 0,
+        left: 0,
+        top: 0
+    }
+});
+
+export const useStyles = ({
+    displayBehaviourWhileLoading,
+    SWITCH_INDICATOR_SIZE,
+    spreadBehaviour,
+    inlineSpaces,
+    currentType,
+    isDisabled,
+    isLoading,
+    isActive,
+    colors,
+    isFlip,
+    spaces,
+    type
+}: SwitchDynamicStyleType) => {
+    const styleType = type === "danger" ? "error" : type;
+
+    const styles = {
+        container: {
+            padding: spaces.spacingSm
+        } as Mutable<ViewStyle>,
+        indicatorContainer: {
+            backgroundColor: colors.content.container[currentType.containerColor],
+            borderRadius: (SWITCH_INDICATOR_SIZE + (spaces.spacingSm * 2)) / 2,
+            width: (SWITCH_INDICATOR_SIZE * 1.5) + (spaces.spacingSm * 2),
+            padding: spaces.spacingXs
+        } as Mutable<ViewStyle>,
+        indicator: {
+            backgroundColor: colors.content.icon[currentType.indicatorColor],
+            borderRadius: SWITCH_INDICATOR_SIZE / 2,
+            height: SWITCH_INDICATOR_SIZE,
+            width: SWITCH_INDICATOR_SIZE
+        } as Mutable<ViewStyle>,
+        contentContainer: {
+            marginLeft: spaces.spacingSm
+        } as Mutable<ViewStyle>,
+        titleContainer: {
+
+        } as Mutable<ViewStyle>,
+        title: {
+
+        } as Mutable<TextStyle>,
+        subTitle: {
+
+        } as Mutable<TextStyle>,
+        loading: {
+        } as Mutable<ViewStyle>,
+        overlay: {
+            borderRadius: (SWITCH_INDICATOR_SIZE + (spaces.spacingSm * 2)) / 2
+        } as Mutable<ViewStyle>,
+        optionalText: {
+            marginLeft: inlineSpaces.subTitle
+        } as Mutable<TextStyle>
+    };
+
+    if (isLoading) {
+        if (displayBehaviourWhileLoading === "disabled") {
+            styles.overlay.backgroundColor = colors.system.state.overlay.disabled[styleType];
+            styles.overlay.display = "flex";
+        }
+    }
+
+    if (isFlip) {
+        styles.contentContainer.marginRight = spaces.spacingSm;
+        styles.titleContainer.justifyContent = "flex-end";
+        styles.contentContainer.marginLeft = 0;
+        styles.subTitle.textAlign = "right";
+        styles.title.textAlign = "right";
+    }
+
+    if (spreadBehaviour === "baseline") {
+        styles.container.alignSelf = spreadBehaviour;
+        styles.container.width = "auto";
+    } else if (spreadBehaviour === "stretch") {
+        styles.contentContainer.alignSelf = spreadBehaviour;
+        styles.container.alignSelf = spreadBehaviour;
+        styles.container.justifyContent = "center";
+        styles.contentContainer.flexShrink = 1;
+        styles.contentContainer.width = "100%";
+        styles.container.flexShrink = 1;
+        styles.container.width = "100%";
+    }
+
+    if(isActive) {
+        if(styleType === "neutral") {
+            styles.indicatorContainer.backgroundColor = colors.content.container.emphasized;
+            styles.indicator.backgroundColor = colors.content.icon.emphasized;
+        }
+    }
+
+    if (isDisabled) {
+        if(styleType === "neutral" && isActive) {
+            styles.indicatorContainer.backgroundColor = colors.system.state.border.disabled.primary;
+        }
+
+        styles.overlay.backgroundColor = colors.system.state.overlay.disabled[styleType];
+        styles.overlay.display = "flex";
+    }
+
+    return styles;
+};
+export default stylesheet;

+ 68 - 0
src/components/switch/type.ts

@@ -0,0 +1,68 @@
+import {
+    type ViewStyle,
+    type StyleProp,
+    type TextStyle
+} from "react-native";
+
+export type SwitchDynamicStyleType = {
+    displayBehaviourWhileLoading?: SwitchDisplayBehaviourWhileLoading;
+    inlineSpaces: NCoreUIKit.ActivePalette["inlineSpaces"];
+    spaces: NCoreUIKit.ActivePalette["spaces"];
+    colors: NCoreUIKit.ActivePalette["colors"];
+    spreadBehaviour?: SwitchSpreadBehaviour;
+    SWITCH_INDICATOR_SIZE: number;
+    currentType: SwitchTypes;
+    isDisabled?: boolean;
+    isLoading?: boolean;
+    isActive?: boolean;
+    isFlip?: boolean;
+    type: SwitchType;
+};
+
+export type SwitchTypes = {
+    containerColor: keyof NCoreUIKit.ContainerContentColors;
+    indicatorColor: keyof NCoreUIKit.IconContentColors;
+    subTitleColor: keyof NCoreUIKit.TextContentColors;
+    titleColor: keyof NCoreUIKit.TextContentColors;
+};
+
+export type SwitchTypeConstantType = {
+    type: SwitchType;
+};
+
+export type SwitchType = "primary" | "danger" | "warning" | "neutral" | "success" | "info";
+
+export type SwitchDisplayBehaviourWhileLoading = "none" | "disabled";
+
+export type SwitchSpreadBehaviour = "baseline" | "stretch" | "free";
+
+interface ISwitchProps {
+    displayBehaviourWhileLoading?: SwitchDisplayBehaviourWhileLoading;
+    subTitleStyle?: StyleProp<TextStyle>[] | StyleProp<TextStyle>;
+    titleStyle?: StyleProp<TextStyle>[] | StyleProp<TextStyle>;
+    style?: StyleProp<ViewStyle>[] | StyleProp<ViewStyle>;
+    customTheme?: {
+        gapPropagation?: NCoreUIKit.GapPropagationKey;
+        sharpness?: NCoreUIKit.SharpnessKey;
+        paletteKey?: NCoreUIKit.PaletteKey;
+        themeKey?: NCoreUIKit.ThemeKey;
+    };
+    spreadBehaviour?: SwitchSpreadBehaviour;
+    customLocalize?: {
+        activeLocale?: NCoreUIKit.LocaleKey;
+    };
+    animationDuration?: number;
+    optionalText?: string;
+    isDisabled?: boolean;
+    isOptional?: boolean;
+    onPress?: () => void;
+    isLoading?: boolean;
+    isActive?: boolean;
+    subTitle?: string;
+    type?: SwitchType;
+    isFlip?: boolean;
+    title?: string;
+}
+export type {
+    ISwitchProps as default
+};

+ 2 - 0
src/index.tsx

@@ -14,6 +14,7 @@ export {
 } from "./core/hooks";
 
 export {
+    NotificationIndicator,
     PageContainer,
     TextAreaInput,
     BottomSheet,
@@ -26,6 +27,7 @@ export {
     RowCard,
     Loading,
     Dialog,
+    Switch,
     Button,
     Modal,
     Toast,

+ 1 - 0
src/variants/themes/default.json

@@ -47,6 +47,7 @@
             "required": 4
         },
         "borders": {
+            "subtract": 3,
             "line": 2
         },
         "spaces": {