Quellcode durchsuchen

Feature: Menu Component start.

lfabl vor 6 Stunden
Ursprung
Commit
87a5ad8d96

+ 20 - 20
example/src/pages/home/index.tsx

@@ -1,4 +1,5 @@
 import {
+    useEffect,
     useLayoutEffect
 } from "react";
 import {
@@ -10,10 +11,12 @@ import {
     NCoreUIKitLocalize,
     NCoreUIKitTheme,
     PaletteSwitcher,
+    NCoreUIKitMenu,
     PageContainer,
     ThemeSwitcher,
     AvatarGroup,
     RowCard,
+    Button,
     Avatar,
     Text
 } from "ncore-ui-kit";
@@ -41,6 +44,15 @@ const Home = () => {
 
     const navigation = useNavigation<NativeStackNavigationProp<RootStackParamList>>();
 
+    useEffect(() => {
+        NCoreUIKitMenu.load({
+            buttons: [{
+                title: "Ana Sayfa"
+            }],
+            id: "test"
+        });
+    }, []);
+
     useLayoutEffect(() => {
         navigation.setOptions({
             headerShown: false
@@ -59,28 +71,16 @@ const Home = () => {
             }
         }}
     >
+        <Button
+            title="Open Menu"
+            onPress={() => {
+                NCoreUIKitMenu.open({
+                    id: "test"
+                });
+            }}
+        />
         <PaletteSwitcher/>
         <ThemeSwitcher/>
-        <Avatar
-            title="Furkan Atakan BOZKURT"
-            isStatusIndicator={true}
-            isWorkWithAction={true}
-            size="large"
-            // imageUrl="https://fotolifeakademi.com/uploads/2020/04/manzara-fotografi-cekmek-724x394.webp"
-        />
-        <AvatarGroup
-            isWorkWithAction={true}
-            avatars={[
-                {
-                    // imageUrl: "https://fotolifeakademi.com/uploads/2020/04/manzara-fotografi-cekmek-724x394.webp",
-                    title: "Furkan Atakan BOZKURT"
-                },
-                {
-                    imageUrl: "https://fotolifeakademi.com/uploads/2020/04/manzara-fotografi-cekmek-724x394.webp",
-                    title: "Mahmut PEKER"
-                }
-            ]}
-        />
         <View
             style={{
                 justifyContent: "center",

+ 257 - 0
src/components/menu/index.tsx

@@ -0,0 +1,257 @@
+import {
+    useImperativeHandle,
+    useLayoutEffect,
+    forwardRef,
+    useEffect,
+    useState,
+    useRef
+} from "react";
+import {
+    Animated,
+    Easing,
+    View
+} from "react-native";
+import type {
+    IMenuRef
+} from "./type";
+import type IMenuProps from "./type";
+import stylesheet, {
+    useStyles
+} from "./stylesheet";
+import {
+    NCoreUIKitTheme
+} from "../../core/hooks";
+import type {
+    RefForwardingComponent
+} from "../../types";
+import Modal from "../modal";
+import {
+    Portal
+} from "../../helpers/portalize";
+
+const Menu: RefForwardingComponent<IMenuRef, IMenuProps> = ({
+    portalName = "menu-system",
+    isWorkWithAnimation = true,
+    isWorkWithPortal = true,
+    isWorkWithModal = true,
+    customTheme,
+    modalProps,
+    isCollabs,
+    onClosed,
+    onOpened,
+    onClose,
+    onOpen,
+    style,
+    id,
+    ...props
+}, ref) => {
+    const {
+        colors,
+        spaces
+    } = NCoreUIKitTheme.useContext(customTheme);
+
+    const {
+        container: containerDynamicStyle
+    } = useStyles({
+        colors,
+        spaces
+    });
+
+    const [
+        isOpacityVisible,
+        setIsOpacityVisible
+    ] = useState(false);
+
+    const [
+        measures,
+        setMeasures
+    ] = useState<null | number>(null);
+
+    const [
+        isActive,
+        setIsActive
+    ] = useState(measures === null ? true : isCollabs);
+
+    const animatedX = useRef(new Animated.Value(0)).current;
+    console.log("aha da ranza:", id);
+    useImperativeHandle(
+        ref,
+        () => ({
+            close,
+            open
+        }),
+        []
+    );
+
+    useEffect(() => {
+        console.log("z", new Date().toISOString());
+        if(measures !== null) {
+            animatedX.setValue(measures * -1);
+
+            if(!isOpacityVisible) setIsOpacityVisible(true);
+            console.log("x", new Date().toISOString());
+            if(isActive) setIsActive(false);
+        }
+    }, [measures]);
+
+    useEffect(() => {
+        console.log("y", new Date().toISOString(), isCollabs);
+        setIsActive(isCollabs);
+    }, [isCollabs]);
+
+    useLayoutEffect(() => {
+        if(isActive && isOpacityVisible) {
+            if(isWorkWithAnimation) {
+                openAnimation();
+            } else {
+                if(onOpen) onOpen({
+                    id
+                });
+
+                animatedX.setValue(0);
+
+                if(onOpened) onOpened({
+                    id
+                });
+            }
+        } else {
+            if(!isWorkWithModal) {
+                if(isWorkWithAnimation) {
+                    closeAnimation();
+                } else {
+                    if(onClose) onClose({
+                        id
+                    });
+
+                    animatedX.setValue(measures! * -1);
+
+                    if(onClosed) onClosed({
+                        id
+                    });
+                }
+            }
+        }
+    }, [
+        isOpacityVisible,
+        isActive
+    ]);
+
+    const open = () => {
+        setIsActive(true);
+    };
+
+    const close = () => {
+        if(isWorkWithModal) {
+            if(isWorkWithAnimation) {
+                closeAnimation();
+            } else {
+                animatedX.setValue(measures! * -1);
+
+                if(onClosed) onClosed({
+                    id
+                });
+            }
+        } else {
+            setIsActive(false);
+        }
+    };
+
+    const openAnimation = () => {
+        if(onOpen) onOpen({
+            id
+        });
+
+        Animated.timing(animatedX, {
+            useNativeDriver: true,
+            easing: Easing.linear,
+            duration: 300,
+            toValue: 0
+        }).start(({
+            finished
+        }) => {
+            if(finished) {
+                if(onOpened) onOpened({
+                    id
+                });
+            }
+        });
+    };
+
+    const closeAnimation = () => {
+        if(onClose) onClose({
+            id
+        });
+
+        Animated.timing(animatedX, {
+            toValue: measures! * -1,
+            useNativeDriver: true,
+            easing: Easing.linear,
+            duration: 300
+        }).start(({
+            finished
+        }) => {
+            if(finished) {
+                if(onClosed) onClosed({
+                    id
+                });
+            }
+        });
+    };
+
+    const renderContent = () => {
+        return <Animated.View
+            {...props}
+            style={[
+                style,
+                stylesheet.container,
+                containerDynamicStyle,
+                {
+                    opacity: isOpacityVisible ? 1 : 0,
+                    transform: [{
+                        translateX: animatedX
+                    }]
+                }
+            ]}
+            onLayout={(event) => {
+                const width = event.nativeEvent.layout.width;
+
+                setMeasures(width);
+            }}
+        >
+            <View
+                style={{
+                    backgroundColor: "red",
+                    height: 300,
+                    width: 100
+                }}
+            />
+        </Animated.View>;
+    };
+
+    if(isWorkWithModal) {
+        return <Modal
+            {...modalProps}
+            isWorkWithPortal={isWorkWithPortal}
+            isOverlayVisible={isOpacityVisible}
+            onOverlayPress={() => {
+                close();
+            }}
+            portalName={portalName}
+            isActive={isActive}
+            id="menu-modal"
+        >
+            {renderContent()}
+        </Modal>;
+    }
+
+    if(isWorkWithPortal) {
+        return <Portal
+            name={portalName}
+        >
+            {renderContent()}
+        </Portal>;
+    }
+
+    return renderContent();
+};
+export default forwardRef(Menu);

+ 34 - 0
src/components/menu/stylesheet.ts

@@ -0,0 +1,34 @@
+import {
+    StyleSheet,
+    type ViewStyle
+} from "react-native";
+import type {
+    MenuDynamicStyle
+} from "./type";
+import type {
+    Mutable
+} from "../../types";
+
+const stylesheet = StyleSheet.create({
+    container: {
+        position: "absolute",
+        zIndex: 99997,
+        bottom: 0,
+        left: 0,
+        top: 0
+    }
+});
+
+export const useStyles = ({
+    colors,
+    spaces
+}: MenuDynamicStyle) => {
+    const styles = {
+        container: {
+        } as Mutable<ViewStyle>
+    };
+
+    return styles;
+};
+
+export default stylesheet;

+ 80 - 0
src/components/menu/type.ts

@@ -0,0 +1,80 @@
+import type {
+    ComponentType,
+    ReactElement
+} from "react";
+import type {
+    ImageProps,
+    StyleProp,
+    ViewStyle
+} from "react-native";
+import type {
+    NCoreUIKitIcon
+} from "../../types";
+import type IRowCardProps from "../rowCard/type";
+import type IModalProps from "../modal/type";
+
+export type IMenuRef = {
+    close: () => void;
+    open: () => void;
+};
+
+export type MenuDynamicStyle = {
+    spaces: NCoreUIKit.ActivePalette["spaces"];
+    colors: NCoreUIKit.ActivePalette["colors"];
+};
+
+export type MenuLogoType = {
+    image?: () => ComponentType<ImageProps>;
+    imageUrl?: string;
+    title?: string;
+};
+
+export type MenuButton = {
+    subButtons?: Array<MenuButton>;
+    isCollabsable?: boolean;
+    redirectMain?: string;
+    props?: IRowCardProps;
+    icon?: NCoreUIKitIcon;
+    redirectSub?: string;
+    isCollabs?: boolean;
+    title?: string;
+};
+
+interface IMenuProps {
+    style?: StyleProp<ViewStyle> | Array<StyleProp<ViewStyle>>;
+    customTheme?: {
+        gapPropagation?: keyof NCoreUIKit.GapPropagationKey;
+        sharpness?: keyof NCoreUIKit.SharpnessKey;
+        paletteKey?: keyof NCoreUIKit.PaletteKey;
+        themeKey?: keyof NCoreUIKit.ThemeKey;
+    };
+    renderFooter?: () => ReactElement;
+    isWorkWithAnimation?: boolean;
+    isShowCollabsButton?: boolean;
+    isWorkWithPortal?: boolean;
+    buttons: Array<MenuButton>;
+    isWorkWithModal?: boolean;
+    modalProps?: IModalProps;
+    isCollabsable?: boolean;
+    isAutoClosed?: boolean;
+    onOpened?: (props: {
+        id?: string
+    }) => void;
+    onClosed?: (props: {
+        id?: string
+    }) => void;
+    portalName?: string;
+    isCollabs?: boolean;
+    logo?: MenuLogoType;
+    onClose?: (props: {
+        id?: string
+    }) => void;
+    onOpen?: (props: {
+        id?: string
+    }) => void;
+    id?: string;
+};
+
+export type {
+    IMenuProps as default
+};

+ 16 - 3
src/context/index.tsx

@@ -2,12 +2,13 @@ import {
     type ReactNode
 } from "react";
 import NCoreUIKitBottomSheet from "./bottomSheet";
-import NCoreUIKitDialog from "./dialog";
 import NCoreUIKitLocalize from "./localize";
-import NCoreUIKitModal from "./modal";
 import NCoreUIKitSnackBar from "./snackBar";
+import NCoreUIKitDialog from "./dialog";
+import NCoreUIKitModal from "./modal";
 import NCoreUIKitTheme from "./theme";
 import NCoreUIKitToast from "./toast";
+import NCoreUIKitMenu from "./menu";
 import {
     type NCoreUIKitConfig
 } from "../types";
@@ -23,6 +24,7 @@ class CoreContext<T extends NCoreUIKitConfig> {
     NCoreUIKitDialog: NCoreUIKitDialog;
     NCoreUIKitModal: NCoreUIKitModal;
     NCoreUIKitToast: NCoreUIKitToast;
+    NCoreUIKitMenu: NCoreUIKitMenu;
 
     constructor(initialState: T) {
         this.NCoreUIKitTheme = new NCoreUIKitTheme({
@@ -57,6 +59,10 @@ class CoreContext<T extends NCoreUIKitConfig> {
         this.NCoreUIKitBottomSheet = new NCoreUIKitBottomSheet({
             data: []
         });
+
+        this.NCoreUIKitMenu = new NCoreUIKitMenu({
+            data: []
+        });
     }
 
     Provider = ({
@@ -71,6 +77,7 @@ class CoreContext<T extends NCoreUIKitConfig> {
         const ModalContext = this.NCoreUIKitModal;
         const ToastContext = this.NCoreUIKitToast;
         const ThemeContext = this.NCoreUIKitTheme;
+        const MenuContext = this.NCoreUIKitMenu;
 
         return <ThemeContext.Provider>
             <LocalizeContext.Provider>
@@ -88,7 +95,13 @@ class CoreContext<T extends NCoreUIKitConfig> {
                                                             <Host name="bottomSheet-system">
                                                                 <BottomSheetContext.Provider>
                                                                     <BottomSheetContext.Render>
-                                                                        {children}
+                                                                        <Host name="menu-system">
+                                                                            <MenuContext.Provider>
+                                                                                <MenuContext.Render>
+                                                                                    {children}
+                                                                                </MenuContext.Render>
+                                                                            </MenuContext.Provider>
+                                                                        </Host>
                                                                     </BottomSheetContext.Render>
                                                                 </BottomSheetContext.Provider>
                                                             </Host>

+ 186 - 0
src/context/menu.tsx

@@ -0,0 +1,186 @@
+import {
+    type ReactNode,
+    Fragment
+} from "react";
+import {
+    type MenuContextType,
+    type MenuDataType
+} from "../types/menu";
+import NCoreContext, {
+    type ConfigType
+} from "ncore-context";
+import Menu from "../components/menu";
+import {
+    uuid
+} from "../utils";
+
+class NCoreUIKitMenu extends NCoreContext<MenuContextType, ConfigType<MenuContextType>> {
+    constructor({
+        data = []
+    }: {
+        data?: Array<MenuDataType>
+    }) {
+        super({
+            unload: () => "",
+            close: () => {},
+            load: () => "",
+            open: () => {},
+            data: data
+        }, {
+            key: "NCoreUIKit-MenuContext"
+        });
+    };
+
+    load = (menuData: MenuDataType) => {
+        console.log("e geldi buu");
+        const currentData = JSON.parse(JSON.stringify(this.state.data));
+
+        const menuID = menuData.id ? menuData.id : uuid();
+
+        currentData.push({
+            ...menuData,
+            id: menuID
+        });
+
+        this.setState({
+            data: currentData
+        });
+
+        return menuID;
+    };
+
+    open = (props?: {
+        index?: number;
+        id?: string;
+    }) => {
+        const currentData = this.state.data;
+
+        if (props && props.id) {
+            const keyIndex = currentData.findIndex((menu) => menu.id === props.id);
+
+            if (keyIndex !== -1) {
+                currentData[keyIndex]!.isCollabs = true;
+
+                this.setState({
+                    data: currentData
+                });
+            }
+
+            return;
+        }
+
+        if (props && props.index !== undefined && currentData[props.index]) {
+            currentData[props.index!]!.isCollabs = true;
+
+            this.setState({
+                data: currentData
+            });
+
+            return;
+        }
+    };
+
+    close = (props?: {
+        index?: number;
+        id?: string;
+    }) => {
+        const currentData = this.state.data;
+
+        if (props && props.id) {
+            const keyIndex = currentData.findIndex((menu) => menu.id === props.id);
+
+            if (keyIndex !== -1) {
+                currentData[keyIndex]!.isCollabs = false;
+
+                this.setState({
+                    data: currentData
+                });
+            }
+
+            return;
+        }
+
+        if (props && props.index !== undefined && currentData[props.index]) {
+            currentData[props.index!]!.isCollabs = false;
+
+            this.setState({
+                data: currentData
+            });
+
+            return;
+        }
+    };
+
+    unload = (props?: {
+        index?: number;
+        id?: string;
+    }) => {
+        const currentData = this.state.data;
+
+        if (props && props.id) {
+            const keyIndex = currentData.findIndex((menu) => menu.id === props.id);
+
+            if (keyIndex !== -1) {
+                currentData.splice(keyIndex, 1);
+
+                this.setState({
+                    data: currentData
+                });
+            }
+
+            return;
+        }
+
+        if (props && props.index !== undefined) {
+            currentData.splice(props.index, 1);
+
+            this.setState({
+                data: currentData
+            });
+
+            return;
+        }
+
+        currentData.pop();
+
+        this.setState({
+            data: currentData
+        });
+    };
+
+    Render = ({
+        children
+    }: {
+        children: ReactNode;
+    }) => {
+        const {
+            data
+        } = this.useContext();
+        console.log("nasıl gelmek la bu ?", data);
+        return <Fragment>
+            {children}
+            {data.map((item: MenuDataType) => {
+                console.log("hii");
+                return <Menu
+                    key={`NCoreUIKit-Menu-${item.id}}`}
+                    id={item.id as string}
+                    onClosed={() => {
+                        if(item.onClosed) {
+                            item.onClosed({
+                                id: item.id as string
+                            });
+                        }
+
+                        if(item.isAutoClosed === undefined || item.isAutoClosed === true) {
+                            this.close({
+                                id: item.id
+                            });
+                        }
+                    }}
+                    {...item}
+                />;
+            })}
+        </Fragment>;
+    };
+}
+export default NCoreUIKitMenu;

+ 3 - 0
src/core/hooks.ts

@@ -13,6 +13,7 @@ import type NCoreUIKitDialogClass from "../context/dialog";
 import type NCoreUIKitModalClass from "../context/modal";
 import type NCoreUIKitToastClass from "../context/toast";
 import type NCoreUIKitThemeClass from "../context/theme";
+import type NCoreUIKitMenuClass from "../context/menu";
 
 export let NCoreUIKitBottomSheet: NCoreUIKitBottomSheetClass;
 export let NCoreUIKitLocalize: NCoreUIKitLocalizeClass<LocalizeType>;
@@ -21,6 +22,7 @@ export let NCoreUIKitSnackBar: NCoreUIKitSnackBarClass;
 export let NCoreUIKitDialog: NCoreUIKitDialogClass;
 export let NCoreUIKitModal: NCoreUIKitModalClass;
 export let NCoreUIKitToast: NCoreUIKitToastClass;
+export let NCoreUIKitMenu: NCoreUIKitMenuClass;
 
 export const initializeInstances = (NCoreUIKit: NCoreUIKitBase<NCoreUIKitConfig>) => {
     NCoreUIKitBottomSheet = NCoreUIKit.NCoreUIKitContext.NCoreUIKitBottomSheet;
@@ -30,4 +32,5 @@ export const initializeInstances = (NCoreUIKit: NCoreUIKitBase<NCoreUIKitConfig>
     NCoreUIKitTheme = NCoreUIKit.NCoreUIKitContext.NCoreUIKitTheme;
     NCoreUIKitModal = NCoreUIKit.NCoreUIKitContext.NCoreUIKitModal;
     NCoreUIKitToast = NCoreUIKit.NCoreUIKitContext.NCoreUIKitToast;
+    NCoreUIKitMenu = NCoreUIKit.NCoreUIKitContext.NCoreUIKitMenu;
 };

+ 1 - 1
src/helpers/portalize/Host.tsx

@@ -102,10 +102,10 @@ export const Host = ({
         name
     }}>
         <View
-            pointerEvents="box-none"
             collapsable={false}
             style={[
                 {
+                    pointerEvents: "box-none",
                     flex: 1
                 },
                 style

+ 6 - 2
src/helpers/portalize/Manager.tsx

@@ -65,8 +65,12 @@ export const Manager = forwardRef(({
         ) => (
             <View
                 key={`NCoreUIKit-Portal-${key}-${index}`}
-                style={StyleSheet.absoluteFill}
-                pointerEvents="box-none"
+                style={[
+                    StyleSheet.absoluteFill,
+                    {
+                        pointerEvents: "box-none"
+                    }
+                ]}
                 collapsable={false}
             >
                 {children}

+ 2 - 1
src/index.tsx

@@ -10,7 +10,8 @@ export {
     NCoreUIKitDialog,
     NCoreUIKitModal,
     NCoreUIKitTheme,
-    NCoreUIKitToast
+    NCoreUIKitToast,
+    NCoreUIKitMenu
 } from "./core/hooks";
 
 export {

+ 30 - 0
src/types/menu.ts

@@ -0,0 +1,30 @@
+import type IMenuProps from "../components/menu/type";
+
+export type MenuType = {
+    data?: Array<MenuDataType>;
+};
+
+export type MenuDataType = Omit<IMenuProps, "id"> & {
+    id?: string;
+};
+
+export type MenuContextType = {
+    load: (menuData: MenuDataType) => string;
+    data: Array<MenuDataType>;
+    unload: (props?: {
+        index?: number;
+        id?: string;
+    }) => string;
+    open: (props?: {
+        index?: number;
+        id?: string;
+    }) => void;
+    close: (props?: {
+        index?: number;
+        id?: string;
+    }) => void;
+};
+
+export type MenuStateContextType = {
+    data: Array<MenuDataType>;
+};