Преглед изворни кода

Feature: Embedded Menu start.

lfabl пре 1 месец
родитељ
комит
24de2a080a

+ 101 - 0
README.md

@@ -1,2 +1,103 @@
 # NİBGAT® | NCore - Mobile
 The NCore, NİBGAT® UI - Kit. The NİBGAT®'s UI / Component Library.
+
+```typescript
+useEffect(() => {
+    NCoreUIKitMenu.load({
+        siteLogoProps: {
+            imageUrl: "https://www.nibgat.com/assets/images/logo.png",
+            onPress: () => {
+                navigation.navigate("Home");
+            },
+            imageProps: {
+                resizeMode: "stretch"
+            },
+            imageSpace: "spacingLg",
+            isWorkWithAction: true,
+            subTitle: "Core Tech",
+            title: "NİBGAT®"
+        },
+        renderFooter: () => {
+            return <View
+                style={{
+                    padding: spaces.spacingMd,
+                    flexDirection: "row",
+                    alignItems: "center"
+                }}
+            >
+                <View
+                    style={{
+                        marginRight: spaces.spacingSm,
+                        justifyContent: "center",
+                        flexDirection: "row",
+                        alignItems: "center"
+                    }}
+                >
+                    <Text
+                        variant="labelLargeSize"
+                    >
+                        Palet:
+                    </Text>
+                    <PaletteSwitcher/>
+                </View>
+                <View
+                    style={{
+                        justifyContent: "center",
+                        flexDirection: "row",
+                        alignItems: "center"
+                    }}
+                >
+                    <Text
+                        variant="labelLargeSize"
+                    >
+                        Tema:
+                    </Text>
+                    <ThemeSwitcher/>
+                </View>
+            </View>;
+        },
+        isAutoClosed: true,
+        buttons: [
+            {
+                redirectMain: "Home",
+                isCollabsable: true,
+                title: "Ana Sayfa",
+                icon: ({
+                    color
+                }) => <HomeIcon
+                    color={colors.content.icon[color]}
+                />,
+                subButtons: [
+                    {
+                        title: "Text",
+                        redirectMain: "TextPage"
+                    }
+                ]
+            },
+            {
+                redirectMain: "Text",
+                isCollabsable: true,
+                title: "Deneme",
+                icon: ({
+                    color
+                }) => <HomeIcon
+                    color={colors.content.icon[color]}
+                />,
+                subButtons: [
+                    {
+                        title: "Noliii",
+                        redirectMain: "Home"
+                    }
+                ]
+            },
+            {
+                title: "Text",
+                redirectMain: "TextPage"
+            }
+        ],
+        navigation: navigation as unknown as NCoreUIKit.Navigation,
+        id: "test"
+    });
+}, []);
+
+```

+ 102 - 11
example/src/navigation/index.tsx

@@ -1,27 +1,118 @@
 import {
-    NavigationContainer
+    View
+} from "react-native";
+import type RootStackParamList from "./type";
+import stylesheet from "./stylesheet";
+import {
+    NCoreUIKitTheme,
+    EmbeddedMenu
+} from "ncore-ui-kit";
+import {
+    NavigationContainer,
+    useNavigation
 } from "@react-navigation/native";
 import {
+    type NativeStackNavigationProp,
     createNativeStackNavigator
 } from "@react-navigation/native-stack";
+import {
+    HomeIcon
+} from "lucide-react-native";
 import TextPage from "../pages/text";
 import Home from "../pages/home";
 
 const RootStack = createNativeStackNavigator();
 
 const RootNav = () => {
-    return <RootStack.Navigator
-        initialRouteName="Home"
+    const {
+        colors
+    } = NCoreUIKitTheme.useContext();
+
+    const navigation = useNavigation<NativeStackNavigationProp<RootStackParamList>>();
+
+    return <View
+        style={[
+            stylesheet.container
+        ]}
     >
-        <RootStack.Screen
-            name="Home"
-            component={Home}
-        />
-        <RootStack.Screen
-            name="TextPage"
-            component={TextPage}
+        <EmbeddedMenu
+            navigation={navigation as unknown as NCoreUIKit.Navigation}
+            siteLogoProps={{
+                imageUrl: "https://www.nibgat.com/assets/images/logo.png",
+                onPress: () => {
+                    navigation.navigate("Home");
+                },
+                imageProps: {
+                    resizeMode: "stretch"
+                },
+                imageSpace: "spacingLg",
+                isWorkWithAction: true,
+                subTitle: "Core Tech",
+                title: "NİBGAT® Deneme Anonim Ticaret Limited Şti."
+            }}
+            buttons={[
+                {
+                    redirectMain: "Home",
+                    isCollabsable: true,
+                    title: "Ana Sayfa",
+                    icon: ({
+                        color
+                    }) => <HomeIcon
+                        color={colors.content.icon[color]}
+                    />
+                },
+                {
+                    redirectMain: "Home",
+                    isCollabsable: true,
+                    title: "Ana Sayfa",
+                    icon: ({
+                        color
+                    }) => <HomeIcon
+                        color={colors.content.icon[color]}
+                    />,
+                    subButtons: [
+                        {
+                            title: "Text",
+                            redirectMain: "TextPage"
+                        }
+                    ]
+                },
+                {
+                    redirectMain: "Text",
+                    isCollabsable: true,
+                    title: "Deneme",
+                    icon: ({
+                        color
+                    }) => <HomeIcon
+                        color={colors.content.icon[color]}
+                    />,
+                    subButtons: [
+                        {
+                            title: "Noliii",
+                            redirectMain: "Home"
+                        }
+                    ]
+                },
+                {
+                    title: "Text",
+                    redirectMain: "TextPage"
+                }
+            ]}
+            id="main-menu"
         />
-    </RootStack.Navigator>;
+        <RootStack.Navigator
+            initialRouteName="Home"
+        >
+            <RootStack.Screen
+                name="Home"
+                component={Home}
+            />
+            <RootStack.Screen
+                name="TextPage"
+                component={TextPage}
+            />
+        </RootStack.Navigator>
+    </View>;
 };
 
 const Navigation = () => {

+ 11 - 0
example/src/navigation/stylesheet.ts

@@ -0,0 +1,11 @@
+import {
+    StyleSheet
+} from "react-native";
+
+const stylesheet = StyleSheet.create({
+    container: {
+        flexDirection: "row",
+        flex: 1
+    }
+});
+export default stylesheet;

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

@@ -1,6 +1,5 @@
 import {
-    useLayoutEffect,
-    useEffect
+    useLayoutEffect
 } from "react";
 import {
     Image,
@@ -10,11 +9,7 @@ import stylesheet from "./stylesheet";
 import {
     NCoreUIKitLocalize,
     NCoreUIKitTheme,
-    PaletteSwitcher,
-    NCoreUIKitMenu,
     PageContainer,
-    ThemeSwitcher,
-    Button,
     Text
 } from "ncore-ui-kit";
 import {
@@ -24,9 +19,6 @@ import type RootStackParamList from "../../navigation/type";
 import type {
     NativeStackNavigationProp
 } from "@react-navigation/native-stack";
-import {
-    HomeIcon
-} from "lucide-react-native";
 import packageJSON from "../../../../package.json";
 
 const Home = () => {
@@ -41,104 +33,6 @@ const Home = () => {
 
     const navigation = useNavigation<NativeStackNavigationProp<RootStackParamList>>();
 
-    useEffect(() => {
-        NCoreUIKitMenu.load({
-            siteLogoProps: {
-                imageUrl: "https://www.nibgat.com/assets/images/logo.png",
-                onPress: () => {
-                    navigation.navigate("Home");
-                },
-                imageProps: {
-                    resizeMode: "stretch"
-                },
-                imageSpace: "spacingLg",
-                isWorkWithAction: true,
-                subTitle: "Core Tech",
-                title: "NİBGAT®"
-            },
-            renderFooter: () => {
-                return <View
-                    style={{
-                        padding: spaces.spacingMd,
-                        flexDirection: "row",
-                        alignItems: "center"
-                    }}
-                >
-                    <View
-                        style={{
-                            marginRight: spaces.spacingSm,
-                            justifyContent: "center",
-                            flexDirection: "row",
-                            alignItems: "center"
-                        }}
-                    >
-                        <Text
-                            variant="labelLargeSize"
-                        >
-                            Palet:
-                        </Text>
-                        <PaletteSwitcher/>
-                    </View>
-                    <View
-                        style={{
-                            justifyContent: "center",
-                            flexDirection: "row",
-                            alignItems: "center"
-                        }}
-                    >
-                        <Text
-                            variant="labelLargeSize"
-                        >
-                            Tema:
-                        </Text>
-                        <ThemeSwitcher/>
-                    </View>
-                </View>;
-            },
-            isAutoClosed: true,
-            buttons: [
-                {
-                    redirectMain: "Home",
-                    isCollabsable: true,
-                    title: "Ana Sayfa",
-                    icon: ({
-                        color
-                    }) => <HomeIcon
-                        color={colors.content.icon[color]}
-                    />,
-                    subButtons: [
-                        {
-                            title: "Text",
-                            redirectMain: "TextPage"
-                        }
-                    ]
-                },
-                {
-                    redirectMain: "Text",
-                    isCollabsable: true,
-                    title: "Deneme",
-                    icon: ({
-                        color
-                    }) => <HomeIcon
-                        color={colors.content.icon[color]}
-                    />,
-                    subButtons: [
-                        {
-                            title: "Noliii",
-                            redirectMain: "Home"
-                        }
-                    ]
-                },
-                {
-                    title: "Text",
-                    redirectMain: "TextPage"
-                }
-            ],
-            navigation: navigation as unknown as NCoreUIKit.Navigation,
-            id: "test"
-        });
-    }, []);
-
     useLayoutEffect(() => {
         navigation.setOptions({
             headerShown: false
@@ -157,14 +51,6 @@ const Home = () => {
             }
         }}
     >
-        <Button
-            title="Open Menu"
-            onPress={() => {
-                NCoreUIKitMenu.open({
-                    id: "test"
-                });
-            }}
-        />
         <View
             style={{
                 justifyContent: "center",

+ 19 - 8
src/components/embeddedMenu/components/menuButton/index.tsx

@@ -12,9 +12,9 @@ import {
     View
 } from "react-native";
 import type {
-    MenuButton as MenuButtonType
+    EmbeddedMenuButton as EmbeddedMenuButtonType
 } from "./type";
-import type IMenuButton from "./type";
+import type IEmbeddedMenuButton from "./type";
 import {
     NCoreUIKitTheme
 } from "../../../../core/hooks";
@@ -25,7 +25,7 @@ import {
 } from "lucide-react-native";
 import RowCard from "../../../rowCard";
 
-const isPageActive = ((buttonItem: MenuButtonType, navigation: NCoreUIKit.Navigation) => {
+const isPageActive = ((buttonItem: EmbeddedMenuButtonType, navigation: NCoreUIKit.Navigation) => {
     const state = navigation.getState();
     const currentRoute = state?.routes?.[state.index];
 
@@ -41,8 +41,9 @@ const isPageActive = ((buttonItem: MenuButtonType, navigation: NCoreUIKit.Naviga
     return currentRoute.name === (buttonItem.redirectSub ?? buttonItem.redirectMain);
 });
 
-const MenuButton: FC<IMenuButton> = ({
+const EmbeddedMenuButton: FC<IEmbeddedMenuButton> = ({
     updateMenuButtonCollabs,
+    isMenuCollabs,
     closeAction,
     buttonIndex,
     buttonItem,
@@ -110,13 +111,21 @@ const MenuButton: FC<IMenuButton> = ({
 
     const isPActive = isPageActive(buttonItem, navigation);
 
+    if(!isMenuCollabs && buttonItem.subButtons && buttonItem.subButtons.length) {
+        return null;
+    }
+
+    if(!isMenuCollabs && !buttonItem.icon) {
+        return null;
+    }
+
     return <View
         key={`menu-button-${id}-${buttonIndex}`}
     >
         <RowCard
             {...buttonItem.props}
             rightIcon={
-                buttonItem.isCollabsable || buttonItem.isShowRedirectIcon ?
+                isMenuCollabs && (buttonItem.isCollabsable || buttonItem.isShowRedirectIcon) ?
                     () => {
                         if(buttonItem.isCollabsable) {
                             if(buttonItem.isCollabs) {
@@ -143,7 +152,7 @@ const MenuButton: FC<IMenuButton> = ({
             backgroundColor={isPActive ? "primary" : undefined}
             titleColor={isPActive ? "onPrimary" : undefined}
             iconColor={isPActive ? "onPrimary" : undefined}
-            title={buttonItem.title}
+            title={isMenuCollabs ? buttonItem.title : ""}
             icon={buttonItem.icon}
             onPress={() => {
                 if(buttonItem.isCollabsable) {
@@ -185,8 +194,10 @@ const MenuButton: FC<IMenuButton> = ({
                     ]}
                 >
                     {buttonItem.subButtons.map((subButtonItem, subButtonIndex) => {
-                        return <MenuButton
+                        return <EmbeddedMenuButton
+                            key={`embedded-menu-sub-button-${id}-${subButtonIndex}`}
                             updateMenuButtonCollabs={updateMenuButtonCollabs}
+                            isMenuCollabs={isMenuCollabs}
                             buttonIndex={subButtonIndex}
                             buttonItem={subButtonItem}
                             closeAction={closeAction}
@@ -204,4 +215,4 @@ const MenuButton: FC<IMenuButton> = ({
         }
     </View>;
 };
-export default MenuButton;
+export default EmbeddedMenuButton;

+ 7 - 6
src/components/embeddedMenu/components/menuButton/type.ts

@@ -3,9 +3,9 @@ import type {
 } from "../../../../types";
 import type IRowCardProps from "../../../rowCard/type";
 
-export type MenuButton = {
+export type EmbeddedMenuButton = {
+    subButtons?: Array<EmbeddedMenuButton>;
     isShowRedirectIcon?: NCoreUIKitIcon;
-    subButtons?: Array<MenuButton>;
     isCollabsable?: boolean;
     redirectMain?: string;
     props?: IRowCardProps;
@@ -15,19 +15,20 @@ export type MenuButton = {
     title: string;
 };
 
-interface IMenuButton {
+interface IEmbeddedMenuButton {
     updateMenuButtonCollabs: (props: {
+        buttonItem: EmbeddedMenuButton;
         depthMap: Array<number>;
-        buttonItem: MenuButton;
         status: boolean;
     }) => void;
     navigation: NCoreUIKit.Navigation;
+    buttonItem: EmbeddedMenuButton;
     closeAction?: () => void;
     depthMap: Array<number>;
-    buttonItem: MenuButton;
+    isMenuCollabs: boolean;
     buttonIndex: number;
     id: string;
 };
 export type {
-    IMenuButton as default
+    IEmbeddedMenuButton as default
 };

+ 137 - 112
src/components/embeddedMenu/index.tsx

@@ -14,12 +14,12 @@ import {
     View
 } from "react-native";
 import {
-    type MenuButton as MenuButtonType
+    type EmbeddedMenuButton as EmbeddedMenuButtonType
 } from "./components/menuButton/type";
 import {
-    type IMenuRef
+    type IEmbeddedMenuRef
 } from "./type";
-import type IMenuProps from "./type";
+import type IEmbeddedMenuProps from "./type";
 import stylesheet, {
     useStyles
 } from "./stylesheet";
@@ -31,29 +31,28 @@ import {
 import type {
     RefForwardingComponent
 } from "../../types";
+import {
+    PanelLeftClose,
+    PanelLeftOpen
+} from "lucide-react-native";
 import {
     SafeAreaView
 } from "react-native-safe-area-context";
-import {
-    Portal
-} from "../../helpers/portalize";
-import Modal from "../modal";
+import Button from "../button";
 import Seperator from "../seperator";
 import SiteLogo from "../siteLogo";
 
-const Menu: RefForwardingComponent<IMenuRef, IMenuProps> = ({
+const Menu: RefForwardingComponent<IEmbeddedMenuRef, IEmbeddedMenuProps> = ({
     isWorkWithSafeAreaView = true,
     isWorkWithAnimation = true,
     renderHeader: RenderHeader,
     renderFooter: RenderFooter,
-    portalName = "menu-system",
-    isWorkWithPortal = true,
-    isWorkWithModal = true,
     close: closeAction,
+    maxWidth = 300,
+    minWidth = 75,
     siteLogoProps,
     customTheme,
     navigation,
-    modalProps,
     isCollabs,
     onClosed,
     onOpened,
@@ -69,31 +68,25 @@ const Menu: RefForwardingComponent<IMenuRef, IMenuProps> = ({
         spaces
     } = NCoreUIKitTheme.useContext(customTheme);
 
+    const [
+        isActive,
+        setIsActive
+    ] = useState(false);
+
     const {
+        headerContentContainer: headerContentContainerDynamicStyle,
         headerContainer: headerContainerDynamicStyle,
+        collapseButton: collapseButtonDynamicStyle,
         seperator: seperatorDynamicStyle,
-        container: containerDynamicStyle
+        container: containerDynamicStyle,
+        siteLogo: siteLogoDynamicStyle
     } = useStyles({
+        isCollapse: isActive,
         colors,
         spaces
     });
 
-    const [
-        isOpacityVisible,
-        setIsOpacityVisible
-    ] = useState(false);
-
-    const [
-        measures,
-        setMeasures
-    ] = useState<null | number>(null);
-
-    const [
-        isActive,
-        setIsActive
-    ] = useState(false);
-
-    const animatedX = useRef(new Animated.Value(0)).current;
+    const animatedX = useRef(new Animated.Value(!isCollabs ? minWidth : maxWidth)).current;
 
     useImperativeHandle(
         ref,
@@ -104,22 +97,12 @@ const Menu: RefForwardingComponent<IMenuRef, IMenuProps> = ({
         []
     );
 
-    useEffect(() => {
-        if(measures !== null) {
-            animatedX.setValue(measures * -1);
-
-            if(!isOpacityVisible) setIsOpacityVisible(true);
-
-            if(isActive) setIsActive(false);
-        }
-    }, [measures]);
-
     useEffect(() => {
         if(isCollabs !== undefined) setIsActive(isCollabs);
     }, [isCollabs]);
 
     useLayoutEffect(() => {
-        if(isActive && isOpacityVisible) {
+        if(isActive) {
             if(isWorkWithAnimation) {
                 openAnimation();
             } else {
@@ -127,31 +110,28 @@ const Menu: RefForwardingComponent<IMenuRef, IMenuProps> = ({
                     id
                 });
 
-                animatedX.setValue(0);
+                animatedX.setValue(maxWidth);
 
                 if(onOpened) onOpened({
                     id
                 });
             }
         } else {
-            if(!isWorkWithModal) {
-                if(isWorkWithAnimation) {
-                    closeAnimation();
-                } else {
-                    if(onClose) onClose({
-                        id
-                    });
-
-                    animatedX.setValue(measures! * -1);
-
-                    if(onClosed) onClosed({
-                        id
-                    });
-                }
+            if(isWorkWithAnimation) {
+                closeAnimation();
+            } else {
+                if(onClose) onClose({
+                    id
+                });
+
+                animatedX.setValue(minWidth);
+
+                if(onClosed) onClosed({
+                    id
+                });
             }
         }
     }, [
-        isOpacityVisible,
         isActive
     ]);
 
@@ -160,19 +140,7 @@ const Menu: RefForwardingComponent<IMenuRef, IMenuProps> = ({
     };
 
     const close = () => {
-        if(isWorkWithModal) {
-            if(isWorkWithAnimation) {
-                closeAnimation();
-            } else {
-                animatedX.setValue(measures! * -1);
-
-                if(onClosed) onClosed({
-                    id
-                });
-            }
-        } else {
-            setIsActive(false);
-        }
+        setIsActive(false);
     };
 
     const openAnimation = () => {
@@ -183,8 +151,8 @@ const Menu: RefForwardingComponent<IMenuRef, IMenuProps> = ({
         Animated.timing(animatedX, {
             useNativeDriver: Platform.OS !== "web",
             easing: Easing.linear,
-            duration: 300,
-            toValue: 0
+            toValue: maxWidth,
+            duration: 300
         }).start(({
             finished
         }) => {
@@ -203,8 +171,8 @@ const Menu: RefForwardingComponent<IMenuRef, IMenuProps> = ({
 
         Animated.timing(animatedX, {
             useNativeDriver: Platform.OS !== "web",
-            toValue: measures! * -1,
             easing: Easing.linear,
+            toValue: minWidth,
             duration: 300
         }).start(({
             finished
@@ -232,7 +200,7 @@ const Menu: RefForwardingComponent<IMenuRef, IMenuProps> = ({
             return;
         }
 
-        const recursivelyCatch = (item: Array<MenuButtonType>, index = 0): Array<MenuButtonType> => {
+        const recursivelyCatch = (item: Array<EmbeddedMenuButtonType>, index = 0): Array<EmbeddedMenuButtonType> => {
             const tIndex = depthMap[index];
 
             return item.map((c_item, c_index) => {
@@ -249,7 +217,7 @@ const Menu: RefForwardingComponent<IMenuRef, IMenuProps> = ({
 
                 return {
                     ...c_item,
-                    subButtons: recursivelyCatch(c_item.subButtons as Array<MenuButtonType>, index + 1)
+                    subButtons: recursivelyCatch(c_item.subButtons as Array<EmbeddedMenuButtonType>, index + 1)
                 };
             });
         };
@@ -264,9 +232,69 @@ const Menu: RefForwardingComponent<IMenuRef, IMenuProps> = ({
         });
     };
 
+    const renderToggleCollapseForCollabsed = () => {
+        if(isActive) {
+            return null;
+        }
+
+        return <Button
+            isCustomPadding={true}
+            spreadBehaviour="free"
+            type="neutral"
+            size="medium"
+            icon={({
+                color,
+                size
+            }) => {
+                return <PanelLeftOpen
+                    color={colors.content.icon[color]}
+                    size={size * 2}
+                />;
+            }}
+            style={{
+                ...collapseButtonDynamicStyle
+            }}
+            onPress={() => {
+                setIsActive(!isActive);
+            }}
+        />;
+    };
+
+    const renderToggleCollapseForExpended = () => {
+        if(!isActive) {
+            return null;
+        }
+
+        return <Button
+            isCustomPadding={true}
+            spreadBehaviour="free"
+            type="neutral"
+            size="medium"
+            icon={({
+                color,
+                size
+            }) => {
+                return <PanelLeftClose
+                    color={colors.content.icon[color]}
+                    size={size * 2}
+                />;
+            }}
+            style={{
+                ...collapseButtonDynamicStyle
+            }}
+            onPress={() => {
+                setIsActive(!isActive);
+            }}
+        />;
+    };
+
     const renderHeader = () => {
         if(RenderHeader) {
-            return <RenderHeader/>;
+            return <RenderHeader
+                isCollapse={isActive}
+                maxWidth={maxWidth}
+                minWidth={minWidth}
+            />;
         }
 
         return <View
@@ -275,9 +303,24 @@ const Menu: RefForwardingComponent<IMenuRef, IMenuProps> = ({
                 headerContainerDynamicStyle
             ]}
         >
-            <SiteLogo
-                {...siteLogoProps}
-            />
+            {renderToggleCollapseForCollabsed()}
+            <View
+                style={[
+                    stylesheet.headerContentContainer,
+                    headerContentContainerDynamicStyle
+                ]}
+            >
+                <SiteLogo
+                    {...siteLogoProps}
+                    title={isActive ? siteLogoProps?.title : undefined}
+                    size={isActive ? siteLogoProps?.size : "small"}
+                    style={[
+                        siteLogoProps?.style,
+                        siteLogoDynamicStyle
+                    ]}
+                />
+                {renderToggleCollapseForExpended()}
+            </View>
             <Seperator
                 style={[
                     stylesheet.seperator,
@@ -294,9 +337,11 @@ const Menu: RefForwardingComponent<IMenuRef, IMenuProps> = ({
 
         return buttons.map((buttonItem, buttonIndex) => {
             return <MenuButton
+                key={`embedded-menu-button-${id}-${buttonIndex}`}
                 updateMenuButtonCollabs={updateMenuButtonCollabs}
                 closeAction={closeAction}
                 buttonIndex={buttonIndex}
+                isMenuCollabs={isActive}
                 depthMap={[buttonIndex]}
                 buttonItem={buttonItem}
                 navigation={navigation}
@@ -310,13 +355,25 @@ const Menu: RefForwardingComponent<IMenuRef, IMenuProps> = ({
             return null;
         }
 
-        return <RenderFooter/>;
+        return <RenderFooter
+            isCollapse={isActive}
+            maxWidth={maxWidth}
+            minWidth={minWidth}
+        />;
     };
 
     const renderSafeAreaContext = () => {
         if(!isWorkWithSafeAreaView) {
             return <Fragment>
                 {renderHeader()}
+                <View
+                    style={[
+                        stylesheet.content
+                    ]}
+                >
+                    {renderButtons()}
+                </View>
+                {renderFooter()}
             </Fragment>;
         }
 
@@ -345,46 +402,14 @@ const Menu: RefForwardingComponent<IMenuRef, IMenuProps> = ({
                 stylesheet.container,
                 containerDynamicStyle,
                 {
-                    opacity: isOpacityVisible ? 1 : 0,
-                    transform: [{
-                        translateX: animatedX
-                    }]
+                    width: animatedX
                 }
             ]}
-            onLayout={(event) => {
-                const width = event.nativeEvent.layout.width;
-
-                setMeasures(width);
-            }}
         >
             {renderSafeAreaContext()}
         </Animated.View>;
     };
 
-    if(isWorkWithModal && measures !== null) {
-        return <Modal
-            {...modalProps}
-            isWorkWithPortal={isWorkWithPortal}
-            isOverlayVisible={isOpacityVisible}
-            onOverlayPress={() => {
-                close();
-            }}
-            portalName={portalName}
-            isActive={isActive}
-            id="menu-modal"
-        >
-            {renderContent()}
-        </Modal>;
-    }
-
-    if(isWorkWithPortal && measures !== null) {
-        return <Portal
-            name={portalName}
-        >
-            {renderContent()}
-        </Portal>;
-    }
-
     return renderContent();
 };
 export default forwardRef(Menu);

+ 25 - 20
src/components/embeddedMenu/stylesheet.ts

@@ -1,10 +1,9 @@
 import {
     type ViewStyle,
-    StyleSheet,
-    Platform
+    StyleSheet
 } from "react-native";
 import type {
-    MenuDynamicStyle
+    EmbeddedMenuDynamicStyle
 } from "./type";
 import type {
     Mutable
@@ -13,23 +12,8 @@ import type {
 const stylesheet = StyleSheet.create({
     container: {
         flexDirection: "column",
-        ...Platform.select({
-            web: {
-                minWidth: 280,
-                maxWidth: 340,
-                width: "auto"
-            },
-            default: {
-                width: "82%"
-            }
-        }),
-        position: "absolute",
         display: "flex",
-        height: "100%",
-        zIndex: 99997,
-        bottom: 0,
-        left: 0,
-        top: 0
+        height: "100%"
     },
     content: {
         justifyContent: "flex-start",
@@ -46,13 +30,19 @@ const stylesheet = StyleSheet.create({
         width: "100%"
     },
     seperator: {
+    },
+    headerContentContainer: {
+        flexDirection: "row",
+        alignItems: "center",
+        width: "100%"
     }
 });
 
 export const useStyles = ({
+    isCollapse,
     colors,
     spaces
-}: MenuDynamicStyle) => {
+}: EmbeddedMenuDynamicStyle) => {
     const styles = {
         container: {
             backgroundColor: colors.content.container.default
@@ -63,9 +53,24 @@ export const useStyles = ({
         } as Mutable<ViewStyle>,
         seperator: {
             marginVertical: spaces.spacingMd
+        } as Mutable<ViewStyle>,
+        collapseButton: {
+            marginBottom: spaces.spacingSm,
+            padding: spaces.spacingSm
+        } as Mutable<ViewStyle>,
+        headerContentContainer: {
+        } as Mutable<ViewStyle>,
+        siteLogo: {
         } as Mutable<ViewStyle>
     };
 
+    if(isCollapse) {
+        styles.siteLogo.marginRight = spaces.spacingMd;
+    } else {
+        styles.headerContainer.justifyContent = "center";
+        styles.headerContainer.alignItems = "center";
+    }
+
     return styles;
 };
 

+ 22 - 16
src/components/embeddedMenu/type.ts

@@ -12,27 +12,27 @@ import type {
 } from "../../types";
 import type ISiteLogoProps from "../siteLogo/type";
 import type IRowCardProps from "../rowCard/type";
-import type IModalProps from "../modal/type";
 
-export type IMenuRef = {
+export type IEmbeddedMenuRef = {
     close: () => void;
     open: () => void;
 };
 
-export type MenuDynamicStyle = {
+export type EmbeddedMenuDynamicStyle = {
     spaces: NCoreUIKit.ActivePalette["spaces"];
     colors: NCoreUIKit.ActivePalette["colors"];
+    isCollapse: boolean;
 };
 
-export type MenuLogoType = {
+export type EmbeddedMenuLogoType = {
     image?: () => ComponentType<ImageProps>;
     imageUrl?: string;
     title?: string;
 };
 
-export type MenuButton = {
+export type EmbeddedMenuButton = {
+    subButtons?: Array<EmbeddedMenuButton>;
     isShowRedirectIcon?: NCoreUIKitIcon;
-    subButtons?: Array<MenuButton>;
     isCollabsable?: boolean;
     redirectMain?: string;
     props?: IRowCardProps;
@@ -42,7 +42,7 @@ export type MenuButton = {
     title: string;
 };
 
-interface IMenuProps {
+interface IEmbeddedMenuProps {
     style?: StyleProp<ViewStyle> | Array<StyleProp<ViewStyle>>;
     customTheme?: {
         gapPropagation?: keyof NCoreUIKit.GapPropagationKey;
@@ -50,18 +50,24 @@ interface IMenuProps {
         paletteKey?: keyof NCoreUIKit.PaletteKey;
         themeKey?: keyof NCoreUIKit.ThemeKey;
     };
+    buttons: Array<EmbeddedMenuButton>;
     navigation: NCoreUIKit.Navigation;
     isWorkWithSafeAreaView?: boolean;
     isAutoClosedOnClosed?: boolean;
     siteLogoProps?: ISiteLogoProps;
-    renderHeader?: () => ReactNode;
-    renderFooter?: () => ReactNode;
     isWorkWithAnimation?: boolean;
     isShowCollabsButton?: boolean;
-    isWorkWithPortal?: boolean;
-    buttons: Array<MenuButton>;
-    isWorkWithModal?: boolean;
-    modalProps?: IModalProps;
+    logo?: EmbeddedMenuLogoType;
+    renderHeader?: (props: {
+        isCollapse: boolean;
+        maxWidth: number;
+        minWidth: number;
+    }) => ReactNode;
+    renderFooter?: (props: {
+        isCollapse: boolean;
+        maxWidth: number;
+        minWidth: number;
+    }) => ReactNode;
     isCollabsable?: boolean;
     isAutoClosed?: boolean;
     onOpened?: (props: {
@@ -70,9 +76,7 @@ interface IMenuProps {
     onClosed?: (props: {
         id?: string
     }) => void;
-    portalName?: string;
     isCollabs?: boolean;
-    logo?: MenuLogoType;
     onClose?: (props: {
         id?: string
     }) => void;
@@ -80,9 +84,11 @@ interface IMenuProps {
     onOpen?: (props: {
         id?: string
     }) => void;
+    maxWidth?: number;
+    minWidth?: number;
     id: string;
 };
 
 export type {
-    IMenuProps as default
+    IEmbeddedMenuProps as default
 };

+ 4 - 0
src/components/index.ts

@@ -174,6 +174,10 @@ export {
     default as Seperator
 } from "./seperator";
 
+export {
+    default as EmbeddedMenu
+} from "./embeddedMenu";
+
 export type {
     EnterMarkdownTypes,
     CodeMarkdownTypes,

+ 1 - 0
src/components/menu/components/menuButton/index.tsx

@@ -187,6 +187,7 @@ const MenuButton: FC<IMenuButton> = ({
                     {buttonItem.subButtons.map((subButtonItem, subButtonIndex) => {
                         return <MenuButton
                             updateMenuButtonCollabs={updateMenuButtonCollabs}
+                            key={`menu-sub-button-${id}-${subButtonIndex}`}
                             buttonIndex={subButtonIndex}
                             buttonItem={subButtonItem}
                             closeAction={closeAction}

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

@@ -295,6 +295,7 @@ const Menu: RefForwardingComponent<IMenuRef, IMenuProps> = ({
         return buttons.map((buttonItem, buttonIndex) => {
             return <MenuButton
                 updateMenuButtonCollabs={updateMenuButtonCollabs}
+                key={`menu-button-${id}-${buttonIndex}`}
                 closeAction={closeAction}
                 buttonIndex={buttonIndex}
                 depthMap={[buttonIndex]}

+ 8 - 0
src/components/siteLogo/index.tsx

@@ -186,8 +186,12 @@ const SiteLogo = ({
             <Text
                 variant={titleVariant ? titleVariant : currentSize.titleSize}
                 customColor={titleColor}
+                ellipsizeMode="tail"
                 numberOfLines={1}
                 color="high"
+                style={{
+                    width: "100%"
+                }}
             >
                 {title}
             </Text>
@@ -195,8 +199,12 @@ const SiteLogo = ({
                 subTitle ?
                     <Text
                         variant={subTitleVariant ? subTitleVariant : currentSize.subTitleSize}
+                        ellipsizeMode="tail"
                         numberOfLines={1}
                         color="low"
+                        style={{
+                            width: "100%"
+                        }}
                     >
                         {subTitle}
                     </Text>

+ 5 - 2
src/components/siteLogo/stylesheet.ts

@@ -85,7 +85,8 @@ const stylesheet = StyleSheet.create({
         alignItems: "center",
         position: "relative",
         overflow: "visible",
-        display: "flex"
+        display: "flex",
+        flex: 1
     },
     image: {
         objectFit: "cover"
@@ -103,7 +104,9 @@ const stylesheet = StyleSheet.create({
     titlesContainer: {
         justifyContent: "center",
         flexDirection: "column",
-        display: "flex"
+        display: "flex",
+        minWidth: 0,
+        flex: 1
     }
 });
 

+ 3 - 3
src/components/siteLogo/type.ts

@@ -4,8 +4,8 @@ import type {
 import {
     type ImageURISource,
     type ImageProps,
-    type TextStyle,
-    type StyleProp
+    type StyleProp,
+    type ViewStyle
 } from "react-native";
 import type {
     NCoreUIKitIcon
@@ -60,7 +60,7 @@ interface ISiteLogoProps {
         paletteKey?: keyof NCoreUIKit.PaletteKey;
         themeKey?: keyof NCoreUIKit.ThemeKey;
     };
-    style?: StyleProp<TextStyle>[] | StyleProp<TextStyle>;
+    style?: StyleProp<ViewStyle>[] | StyleProp<ViewStyle>;
     imageSpace?: keyof NCoreUIKit.ActivePalette["spaces"];
     titleColor?: keyof NCoreUIKit.TextContentColors;
     subTitleVariant?: keyof NCoreUIKit.Typography;

+ 1 - 0
src/index.tsx

@@ -26,6 +26,7 @@ export {
     ThemeSwitcher,
     NumericInput,
     TimeSelector,
+    EmbeddedMenu,
     DateSelector,
     BottomSheet,
     AvatarGroup,