Przeglądaj źródła

Feature: theme switcher continue.

lfabl 3 dni temu
rodzic
commit
d130ae50c9

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

@@ -10,6 +10,7 @@ import {
     NCoreUIKitLocalize,
     NCoreUIKitTheme,
     PageContainer,
+    ThemeSwitcher,
     RowCard,
     Text
 } from "ncore-ui-kit";
@@ -55,6 +56,7 @@ const Home = () => {
             }
         }}
     >
+        <ThemeSwitcher/>
         <View
             style={{
                 justifyContent: "center",

+ 22 - 0
src/components/button/index.tsx

@@ -32,9 +32,13 @@ const Button: FC<IButtonProps> = ({
     iconDirection = "left",
     variant = "filled",
     isDisabled = false,
+    customBorderColor,
     type = "primary",
+    customIconColor,
+    customTextColor,
     size = "medium",
     renderLoading,
+    customColor,
     customTheme,
     titleStyle,
     isLoading,
@@ -72,12 +76,14 @@ const Button: FC<IButtonProps> = ({
     } = useStyles({
         displayBehaviourWhileLoading,
         icon: IconComponentProp,
+        customBorderColor,
         isCustomPadding,
         spreadBehaviour,
         currentVariant,
         iconDirection,
         currentType,
         currentSize,
+        customColor,
         isDisabled,
         isLoading,
         radiuses,
@@ -124,6 +130,16 @@ const Button: FC<IButtonProps> = ({
     }
 
     const renderIcon = () => {
+        if(!isDisabled && !isLoading) {
+            if(customTextColor) {
+                iconProps.customColor = customTextColor;
+            }
+
+            if(customIconColor) {
+                iconProps.customColor = customIconColor;
+            }
+        }
+
         if (isLoading) {
             if(renderLoading) {
                 return renderLoading();
@@ -149,6 +165,12 @@ const Button: FC<IButtonProps> = ({
             return null;
         }
 
+        if(!isDisabled && !isLoading) {
+            if(customTextColor) {
+                titleProps.customColor = customTextColor;
+            }
+        }
+
         return <Text
             variant={currentSize.fontSize}
             style={[

+ 10 - 0
src/components/button/stylesheet.ts

@@ -170,12 +170,14 @@ const stylesheet = StyleSheet.create({
 
 export const useStyles = ({
     displayBehaviourWhileLoading,
+    customBorderColor,
     spreadBehaviour,
     isCustomPadding,
     currentVariant,
     iconDirection,
     currentType,
     currentSize,
+    customColor,
     isDisabled,
     isLoading,
     radiuses,
@@ -289,6 +291,14 @@ export const useStyles = ({
         delete styles.container.paddingTop;
     }
 
+    if(customColor) {
+        styles.container.backgroundColor = customColor;
+    }
+
+    if(customBorderColor) {
+        styles.container.borderColor = customBorderColor;
+    }
+
     return styles;
 };
 export default stylesheet;

+ 6 - 0
src/components/button/type.ts

@@ -21,11 +21,13 @@ export type ButtonDynamicStyleType = {
     currentVariant: ButtonVariants;
     borders: NCoreUIKit.Borders;
     currentSize: ButtonMeasures;
+    customBorderColor?: string;
     isCustomPadding?: boolean;
     currentType: ButtonTypes;
     variant: ButtonVariant;
     icon?: NCoreUIKitIcon;
     isDisabled?: boolean;
+    customColor?: string;
     isLoading?: boolean;
     type: ButtonType;
     title?: string;
@@ -93,9 +95,13 @@ interface IButtonProps extends Omit<ButtonProps, "title" | "disabled"> {
     spreadBehaviour?: ButtonSpreadBehaviour;
     iconDirection?: "left" | "right";
     renderLoading?: () => ReactNode;
+    customBorderColor?: string;
     isCustomPadding?: boolean;
+    customIconColor?: string;
+    customTextColor?: string;
     variant?: ButtonVariant;
     icon?: NCoreUIKitIcon;
+    customColor?: string;
     isDisabled?: boolean;
     onPress: () => void;
     isLoading?: boolean;

+ 4 - 0
src/components/index.ts

@@ -142,6 +142,10 @@ export {
     default as Header
 } from "./header";
 
+export {
+    default as ThemeSwitcher
+} from "./themeSwitcher";
+
 export type {
     EnterMarkdownTypes,
     CodeMarkdownTypes,

+ 8 - 10
src/components/text/index.tsx

@@ -24,19 +24,17 @@ const Text: FC<ITextProps> = ({
         colors
     } = NCoreUIKitTheme.useContext(customTheme);
 
-    return (
-        <NativeText
-            {...props}
-            style={[
-                style,
+    return <NativeText
+        {...props}
+        style={[
+            style,
                 {
                     ...typography[variant],
                     color: customColor ? customColor : color ? colors.content.text[color] : colors.content.text.mid
                 } as TextStyle
-            ]}
-        >
-            {children}
-        </NativeText>
-    );
+        ]}
+    >
+        {children}
+    </NativeText>;
 };
 export default Text;

+ 118 - 0
src/components/themeSwitcher/index.tsx

@@ -0,0 +1,118 @@
+import {
+    useEffect,
+    useState,
+    type FC
+} from "react";
+import type IThemeSwitcherProps from "./type";
+import type {
+    ThemeSwitchVariants
+} from "./type";
+import {
+    NCoreUIKitTheme
+} from "../../core/hooks";
+import type {
+    NCoreUIKitIcon
+} from "../../types";
+import {
+    Moon as MoonIcon,
+    Sun as SunIcon
+} from "lucide-react-native";
+import Button from "../button";
+
+const ThemeSwitcher: FC<IThemeSwitcherProps> = ({
+    variants: variantsProps = [],
+    customTheme,
+    color,
+    style,
+    ...props
+}) => {
+    const {
+        activeTheme,
+        themeKeys,
+        colors
+    } = NCoreUIKitTheme.useContext(customTheme);
+
+    const [
+        currentIndex,
+        setCurrentIndex
+    ] = useState(themeKeys.findIndex(t => t === activeTheme));
+
+    useEffect(() => {
+        const currentTheme = themeKeys[currentIndex];
+
+        NCoreUIKitTheme.switch({
+            themeKey: currentTheme
+        });
+    }, [currentIndex]);
+
+    const variants: ThemeSwitchVariants = variantsProps;
+
+    const isHaveLight = variantsProps.findIndex(iV => iV.themeKey === "light") !== -1;
+    const isHaveDark = variantsProps.findIndex(iV => iV.themeKey === "dark") !== -1;
+
+    if(!isHaveDark) {
+        variants.unshift({
+            borderColor: "transparent",
+            backgroundColor: "subtle",
+            iconColor: "info",
+            themeKey: "dark",
+            icon: ({
+                color,
+                size
+            }) => <MoonIcon
+                color={colors.content.icon[color]}
+                size={size + 5}
+            />
+        });
+    }
+
+    if(!isHaveLight) {
+        variants.unshift({
+            borderColor: "transparent",
+            backgroundColor: "subtle",
+            iconColor: "warning",
+            themeKey: "light",
+            icon: ({
+                color,
+                size
+            }) => <SunIcon
+                color={colors.content.icon[color]}
+                size={size + 5}
+            />
+        });
+    }
+
+    const currentVariant = variants.find(e => e.themeKey === activeTheme);
+
+    if(!currentVariant) {
+        return null;
+    }
+
+    return <Button
+        {...props}
+        customBorderColor={currentVariant && currentVariant.backgroundColor ?
+            currentVariant.backgroundColor
+            :
+            color
+        }
+        customColor={currentVariant && currentVariant.backgroundColor ? currentVariant.backgroundColor : color}
+        icon={({
+            color,
+            size
+        }) => {
+            const Icon = currentVariant?.icon as NCoreUIKitIcon;
+
+            return <Icon
+                color={currentVariant && currentVariant.iconColor ? currentVariant.iconColor : color}
+                size={size}
+            />;
+        }}
+        onPress={() => {
+            setCurrentIndex(currentIndex + 1 > themeKeys.length - 1 ? 0 : currentIndex + 1);
+        }}
+        style={[
+            style
+        ]}
+    />;
+};
+export default ThemeSwitcher;

+ 33 - 0
src/components/themeSwitcher/type.ts

@@ -0,0 +1,33 @@
+import {
+    type TextStyle,
+    type StyleProp
+} from "react-native";
+import type {
+    NCoreUIKitIcon
+} from "../../types";
+import type IButtonProps from "../button/type";
+
+export type ThemeSwitchVariant = {
+    backgroundColor: keyof NCoreUIKit.ContainerContentColors | "transparent";
+    borderColor: keyof NCoreUIKit.BorderContentColors | "transparent";
+    iconColor: keyof NCoreUIKit.IconContentColors;
+    themeKey: keyof NCoreUIKit.ThemeKey;
+    icon: NCoreUIKitIcon;
+};
+
+export type ThemeSwitchVariants = Array<ThemeSwitchVariant>;
+
+interface IThemeSwitcherProps extends Omit<IButtonProps, "onPress"> {
+    color?: keyof NCoreUIKit.ContainerContentColors | "transparent";
+    customTheme?: {
+        gapPropagation?: keyof NCoreUIKit.GapPropagationKey;
+        sharpness?: keyof NCoreUIKit.SharpnessKey;
+        paletteKey?: keyof NCoreUIKit.PaletteKey;
+        themeKey?: keyof NCoreUIKit.ThemeKey;
+    };
+    style?: StyleProp<TextStyle>[] | StyleProp<TextStyle>;
+    variants?: ThemeSwitchVariants;
+}
+export type {
+    IThemeSwitcherProps as default
+};

+ 10 - 2
src/context/theme.tsx

@@ -70,15 +70,16 @@ class NCoreUIKitTheme<T extends ThemesType> extends NCoreContext<ThemeContextTyp
             initialSelectedTheme
         } = initialState;
 
-        const initialPalette = defaultPaletteData.palettes.find(p => p.name === "nibgat");
+        const initialPalette = defaultPaletteData.palettes.find(p => p.name === (initialSelectedPalette ?? "nibgat"));
 
         if(!initialPalette) {
             throw new Error("Initial Theme error!.");
         }
 
-        const initialTheme = initialPalette.themes[initialSelectedTheme as keyof typeof initialPalette.themes];
+        const initialTheme = initialPalette.themes[initialSelectedTheme ?? "light"];
 
         super({
+            themeKeys: Object.keys(initialPalette.themes) as Array<keyof NCoreUIKit.ThemeKey>,
             activeGapPropagation: initialSelectedGapPropagation ?? "compact",
             activeSharpness: initialSelectedActiveSharpness ?? "soft",
             inlineSpaces: defaultPaletteData.shapes.inlineSpaces,
@@ -120,6 +121,7 @@ class NCoreUIKitTheme<T extends ThemesType> extends NCoreContext<ThemeContextTyp
         const defaultTheme = defaultPalette.themes[theme as keyof typeof defaultPalette.themes] ?? defaultPalette.themes.dark;
 
         const defaultState: ThemeContextStateType = {
+            themeKeys: Object.keys(defaultPalette.themes) as Array<keyof NCoreUIKit.ThemeKey>,
             typography: defaultPaletteData.typography[gapPropagation],
             spaces: defaultPaletteData.shapes.spaces[gapPropagation],
             radiuses: defaultPaletteData.shapes.radiuses[sharpness],
@@ -165,7 +167,13 @@ class NCoreUIKitTheme<T extends ThemesType> extends NCoreContext<ThemeContextTyp
             theme
         );
 
+        const dPKeys = [
+            ...Object.keys(defaultPaletteData.palettes[0]?.themes ?? currentProjectPalette.themes),
+            ...Object.keys(this.projectThemes.palettes[0]?.themes ?? currentProjectPalette.themes)
+        ];
+
         const newState: ThemeContextStateType = {
+            themeKeys: dPKeys as Array<keyof NCoreUIKit.ThemeKey>,
             typography: typography[gapPropagation],
             spaces: shapes.spaces[gapPropagation],
             activeGapPropagation: gapPropagation,

+ 1 - 0
src/index.tsx

@@ -21,6 +21,7 @@ export {
     DateTimeSheet,
     TextAreaInput,
     MonthSelector,
+    ThemeSwitcher,
     NumericInput,
     TimeSelector,
     DateSelector,

+ 1 - 0
src/types/theme.ts

@@ -17,6 +17,7 @@ export type ThemeType = "light" | "dark";
 export type ThemeContextStateType = Partial<ThemeContextType>;
 
 export type ThemeContextType = {
+    themeKeys: Array<keyof NCoreUIKit.ThemeKey>;
     activePalette: keyof NCoreUIKit.PaletteKey;
     activeGapPropagation: GapPropagationType;
     activeTheme: keyof NCoreUIKit.ThemeKey;