Bläddra i källkod

Feature: NotificationIndicator component added.

lfabl 1 månad sedan
förälder
incheckning
dedeea9fc5

+ 18 - 1
example/src/pages/home/index.tsx

@@ -25,7 +25,8 @@ import {
     Button,
     Dialog,
     Switch,
-    Text
+    Text,
+    NotificationIndicator
 } from "ncore-ui-kit-mobile";
 import {
     useNavigation
@@ -235,6 +236,22 @@ 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}

+ 4 - 0
src/components/index.ts

@@ -85,3 +85,7 @@ export {
 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
+};

+ 1 - 0
src/index.tsx

@@ -14,6 +14,7 @@ export {
 } from "./core/hooks";
 
 export {
+    NotificationIndicator,
     PageContainer,
     TextAreaInput,
     BottomSheet,

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

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