├── .gitignore ├── README.md ├── app.json ├── app ├── _layout.tsx └── index.tsx ├── assets ├── fonts │ └── SpaceMono-Regular.ttf └── images │ ├── adaptive-icon.png │ ├── favicon.png │ ├── icon.png │ ├── partial-react-logo.png │ ├── react-logo.png │ ├── react-logo@2x.png │ ├── react-logo@3x.png │ └── splash.png ├── babel.config.js ├── components ├── Button.tsx ├── CustomBlurView.tsx ├── FloatingMenu.tsx └── MenuItem.tsx ├── package-lock.json ├── package.json ├── tsconfig.json └── types └── index.ts /.gitignore: -------------------------------------------------------------------------------- 1 | node_modules/ 2 | .expo/ 3 | dist/ 4 | npm-debug.* 5 | *.jks 6 | *.p8 7 | *.p12 8 | *.key 9 | *.mobileprovision 10 | *.orig.* 11 | web-build/ 12 | 13 | # macOS 14 | .DS_Store 15 | 16 | # @generated expo-cli sync-2b81b286409207a5da26e14c78851eb30d8ccbdb 17 | # The following patterns were generated by expo-cli 18 | 19 | expo-env.d.ts 20 | # @end expo-cli -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Welcome to your Expo app 👋 2 | 3 | This is an [Expo](https://expo.dev) project created with [`create-expo-app`](https://www.npmjs.com/package/create-expo-app). 4 | 5 | ## Get started 6 | 7 | 1. Install dependencies 8 | 9 | ```bash 10 | npm install 11 | ``` 12 | 13 | 2. Start the app 14 | 15 | ```bash 16 | npx expo start 17 | ``` 18 | 19 | In the output, you'll find options to open the app in a 20 | 21 | - [development build](https://docs.expo.dev/develop/development-builds/introduction/) 22 | - [Android emulator](https://docs.expo.dev/workflow/android-studio-emulator/) 23 | - [iOS simulator](https://docs.expo.dev/workflow/ios-simulator/) 24 | - [Expo Go](https://expo.dev/go), a limited sandbox for trying out app development with Expo 25 | 26 | You can start developing by editing the files inside the **app** directory. This project uses [file-based routing](https://docs.expo.dev/router/introduction). 27 | 28 | ## Get a fresh project 29 | 30 | When you're ready, run: 31 | 32 | ```bash 33 | npm run reset-project 34 | ``` 35 | 36 | This command will move the starter code to the **app-example** directory and create a blank **app** directory where you can start developing. 37 | 38 | ## Learn more 39 | 40 | To learn more about developing your project with Expo, look at the following resources: 41 | 42 | - [Expo documentation](https://docs.expo.dev/): Learn fundamentals, or go into advanced topics with our [guides](https://docs.expo.dev/guides). 43 | - [Learn Expo tutorial](https://docs.expo.dev/tutorial/introduction/): Follow a step-by-step tutorial where you'll create a project that runs on Android, iOS, and the web. 44 | 45 | ## Join the community 46 | 47 | Join our community of developers creating universal apps. 48 | 49 | - [Expo on GitHub](https://github.com/expo/expo): View our open source platform and contribute. 50 | - [Discord community](https://chat.expo.dev): Chat with Expo users and ask questions. 51 | -------------------------------------------------------------------------------- /app.json: -------------------------------------------------------------------------------- 1 | { 2 | "expo": { 3 | "name": "rn-accordion", 4 | "slug": "rn-accordion", 5 | "version": "1.0.0", 6 | "orientation": "default", 7 | "icon": "./assets/images/icon.png", 8 | "scheme": "myapp", 9 | "userInterfaceStyle": "automatic", 10 | "splash": { 11 | "image": "./assets/images/splash.png", 12 | "resizeMode": "contain", 13 | "backgroundColor": "#ffffff" 14 | }, 15 | "ios": { 16 | "supportsTablet": true 17 | }, 18 | "android": { 19 | "adaptiveIcon": { 20 | "foregroundImage": "./assets/images/adaptive-icon.png", 21 | "backgroundColor": "#ffffff" 22 | } 23 | }, 24 | "web": { 25 | "bundler": "metro", 26 | "output": "static", 27 | "favicon": "./assets/images/favicon.png" 28 | }, 29 | "plugins": ["expo-router"], 30 | "experiments": { 31 | "typedRoutes": true 32 | } 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /app/_layout.tsx: -------------------------------------------------------------------------------- 1 | import React from "react"; 2 | import { Stack } from "expo-router"; 3 | import { 4 | DarkTheme, 5 | DefaultTheme, 6 | ThemeProvider, 7 | } from "@react-navigation/native"; 8 | import "react-native-reanimated"; 9 | import { useColorScheme } from "react-native"; 10 | 11 | export default function RootLayout() { 12 | const colorScheme = useColorScheme(); 13 | return ( 14 | 15 | 16 | 17 | ); 18 | } 19 | -------------------------------------------------------------------------------- /app/index.tsx: -------------------------------------------------------------------------------- 1 | import React from "react"; 2 | import { ImageBackground, StyleSheet, View } from "react-native"; 3 | import { useTheme } from "@react-navigation/native"; 4 | import { Ionicons } from "@expo/vector-icons"; 5 | import FloatingMenu from "@/components/FloatingMenu"; 6 | import MenuItem from "@/components/MenuItem"; 7 | 8 | const Main = () => { 9 | const theme = useTheme(); 10 | return ( 11 | 17 | 18 | 19 | 20 | 27 | } 28 | title={"Media"} 29 | /> 30 | 37 | } 38 | title={"Template"} 39 | /> 40 | 47 | } 48 | title={"Event"} 49 | /> 50 | 51 | 52 | 55 | } 56 | title={"Celebrate"} 57 | /> 58 | 65 | } 66 | title={"Job"} 67 | /> 68 | 75 | } 76 | title={"Poll"} 77 | /> 78 | 79 | 80 | 87 | } 88 | title={"Document"} 89 | /> 90 | } 92 | title={"Services"} 93 | /> 94 | 95 | 96 | 97 | 98 | 99 | ); 100 | }; 101 | 102 | export default Main; 103 | 104 | const styles = StyleSheet.create({ 105 | container: { 106 | gap: 20, 107 | }, 108 | rows: { 109 | flexDirection: "row", 110 | gap: 20, 111 | }, 112 | }); 113 | -------------------------------------------------------------------------------- /assets/fonts/SpaceMono-Regular.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/arunabhverma/rn-floating-menu/e973888a9a369c1405b0d045e1d969b9988b251c/assets/fonts/SpaceMono-Regular.ttf -------------------------------------------------------------------------------- /assets/images/adaptive-icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/arunabhverma/rn-floating-menu/e973888a9a369c1405b0d045e1d969b9988b251c/assets/images/adaptive-icon.png -------------------------------------------------------------------------------- /assets/images/favicon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/arunabhverma/rn-floating-menu/e973888a9a369c1405b0d045e1d969b9988b251c/assets/images/favicon.png -------------------------------------------------------------------------------- /assets/images/icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/arunabhverma/rn-floating-menu/e973888a9a369c1405b0d045e1d969b9988b251c/assets/images/icon.png -------------------------------------------------------------------------------- /assets/images/partial-react-logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/arunabhverma/rn-floating-menu/e973888a9a369c1405b0d045e1d969b9988b251c/assets/images/partial-react-logo.png -------------------------------------------------------------------------------- /assets/images/react-logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/arunabhverma/rn-floating-menu/e973888a9a369c1405b0d045e1d969b9988b251c/assets/images/react-logo.png -------------------------------------------------------------------------------- /assets/images/react-logo@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/arunabhverma/rn-floating-menu/e973888a9a369c1405b0d045e1d969b9988b251c/assets/images/react-logo@2x.png -------------------------------------------------------------------------------- /assets/images/react-logo@3x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/arunabhverma/rn-floating-menu/e973888a9a369c1405b0d045e1d969b9988b251c/assets/images/react-logo@3x.png -------------------------------------------------------------------------------- /assets/images/splash.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/arunabhverma/rn-floating-menu/e973888a9a369c1405b0d045e1d969b9988b251c/assets/images/splash.png -------------------------------------------------------------------------------- /babel.config.js: -------------------------------------------------------------------------------- 1 | module.exports = function (api) { 2 | api.cache(true); 3 | return { 4 | presets: ['babel-preset-expo'], 5 | }; 6 | }; 7 | -------------------------------------------------------------------------------- /components/Button.tsx: -------------------------------------------------------------------------------- 1 | import { 2 | Pressable, 3 | PressableProps, 4 | StyleSheet, 5 | Text, 6 | View, 7 | } from "react-native"; 8 | import React, { useState } from "react"; 9 | import Animated, { 10 | useAnimatedStyle, 11 | withTiming, 12 | } from "react-native-reanimated"; 13 | 14 | const Button = (props: PressableProps) => { 15 | const [isPressed, setIsPressed] = useState(false); 16 | 17 | const animatedStyle = useAnimatedStyle(() => { 18 | return { 19 | transform: [ 20 | { 21 | scale: isPressed 22 | ? withTiming(0.5, { duration: 200 }) 23 | : withTiming(1, { duration: 200 }), 24 | }, 25 | ], 26 | }; 27 | }); 28 | 29 | return ( 30 | setIsPressed(true)} 33 | onTouchEnd={() => setIsPressed(false)} 34 | > 35 | 36 | {props.children} 37 | 38 | 39 | ); 40 | }; 41 | 42 | export default Button; 43 | -------------------------------------------------------------------------------- /components/CustomBlurView.tsx: -------------------------------------------------------------------------------- 1 | import { 2 | Platform, 3 | StyleSheet, 4 | useColorScheme, 5 | useWindowDimensions, 6 | } from "react-native"; 7 | import React from "react"; 8 | import { BlurView } from "expo-blur"; 9 | import { useTheme } from "@react-navigation/native"; 10 | 11 | const CustomBlurView = ({ bgColor }: { bgColor?: string }) => { 12 | const tint = useColorScheme(); 13 | const theme = useTheme(); 14 | 15 | const { width, height } = useWindowDimensions(); 16 | return ( 17 | 30 | ); 31 | }; 32 | 33 | export default CustomBlurView; 34 | -------------------------------------------------------------------------------- /components/FloatingMenu.tsx: -------------------------------------------------------------------------------- 1 | import React, { ReactNode, useState } from "react"; 2 | import { StyleSheet, useWindowDimensions } from "react-native"; 3 | import Animated, { 4 | FadeIn, 5 | FadeOut, 6 | LinearTransition, 7 | useAnimatedStyle, 8 | withSpring, 9 | withTiming, 10 | } from "react-native-reanimated"; 11 | import { useSafeAreaInsets } from "react-native-safe-area-context"; 12 | import { useTheme } from "@react-navigation/native"; 13 | import { Ionicons } from "@expo/vector-icons"; 14 | import CustomBlurView from "./CustomBlurView"; 15 | 16 | const FloatingMenu = ({ children }: { children: ReactNode }) => { 17 | const { right, bottom } = useSafeAreaInsets(); 18 | const { width, height } = useWindowDimensions(); 19 | const theme = useTheme(); 20 | const [isOpen, setIsOpen] = useState(false); 21 | 22 | const animatedStyle = useAnimatedStyle(() => { 23 | return { 24 | borderRadius: isOpen ? withTiming(20) : withTiming(50), 25 | padding: isOpen ? 15 : 0, 26 | }; 27 | }, [isOpen]); 28 | 29 | const iconRotation = useAnimatedStyle(() => { 30 | return { 31 | transform: [ 32 | { rotate: isOpen ? withSpring("-45deg") : withSpring("0deg") }, 33 | ], 34 | }; 35 | }); 36 | 37 | return ( 38 | 50 | 54 | 55 | {isOpen && ( 56 | 62 | {children} 63 | 64 | )} 65 | setIsOpen((prev) => !prev)} 73 | > 74 | 75 | 76 | 77 | 78 | 79 | ); 80 | }; 81 | 82 | export default FloatingMenu; 83 | 84 | const styles = StyleSheet.create({ 85 | iconWrapper: { 86 | width: 60, 87 | aspectRatio: 1, 88 | borderRadius: 30, 89 | justifyContent: "center", 90 | alignItems: "center", 91 | overflow: "hidden", 92 | }, 93 | childrenWrapper: { 94 | paddingBottom: 30, 95 | flex: 1, 96 | }, 97 | containerWrapper: { 98 | justifyContent: "center", 99 | alignItems: "flex-end", 100 | overflow: "hidden", 101 | }, 102 | mainContainerWrapper: { 103 | position: "absolute", 104 | }, 105 | }); 106 | -------------------------------------------------------------------------------- /components/MenuItem.tsx: -------------------------------------------------------------------------------- 1 | import React from "react"; 2 | import { StyleSheet, Text, View } from "react-native"; 3 | import { useTheme } from "@react-navigation/native"; 4 | import { MenuItemsType } from "@/types"; 5 | import Button from "./Button"; 6 | 7 | const MenuItem = ({ icon, title }: MenuItemsType) => { 8 | const theme = useTheme(); 9 | return ( 10 | 11 | 12 | 13 | {title} 14 | 15 | 16 | ); 17 | }; 18 | 19 | export default MenuItem; 20 | 21 | const styles = StyleSheet.create({ 22 | container: { 23 | alignItems: "center", 24 | width: 80, 25 | gap: 5, 26 | }, 27 | iconContainer: { 28 | width: 60, 29 | backgroundColor: "rgba(0,0,0,0.1)", 30 | aspectRatio: 1, 31 | justifyContent: "center", 32 | alignItems: "center", 33 | borderRadius: 30, 34 | }, 35 | textStyle: { 36 | fontSize: 13, 37 | fontWeight: "500", 38 | }, 39 | }); 40 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "rn-accordion", 3 | "main": "expo-router/entry", 4 | "version": "1.0.0", 5 | "scripts": { 6 | "start": "expo start", 7 | "reset-project": "node ./scripts/reset-project.js", 8 | "android": "expo start --android", 9 | "ios": "expo start --ios", 10 | "web": "expo start --web", 11 | "test": "jest --watchAll", 12 | "lint": "expo lint" 13 | }, 14 | "jest": { 15 | "preset": "jest-expo" 16 | }, 17 | "dependencies": { 18 | "@expo/vector-icons": "^14.0.2", 19 | "@react-navigation/native": "^6.0.2", 20 | "expo": "~51.0.18", 21 | "expo-constants": "~16.0.2", 22 | "expo-font": "~12.0.7", 23 | "expo-linking": "~6.3.1", 24 | "expo-router": "~3.5.17", 25 | "expo-splash-screen": "~0.27.5", 26 | "expo-status-bar": "~1.12.1", 27 | "expo-system-ui": "~3.0.7", 28 | "expo-web-browser": "~13.0.3", 29 | "react": "18.2.0", 30 | "react-dom": "18.2.0", 31 | "react-native": "0.74.3", 32 | "react-native-gesture-handler": "~2.16.1", 33 | "react-native-reanimated": "~3.10.1", 34 | "react-native-safe-area-context": "4.10.1", 35 | "react-native-screens": "3.31.1", 36 | "react-native-web": "~0.19.10", 37 | "expo-blur": "~13.0.2" 38 | }, 39 | "devDependencies": { 40 | "@babel/core": "^7.20.0", 41 | "@types/jest": "^29.5.12", 42 | "@types/react": "~18.2.45", 43 | "@types/react-test-renderer": "^18.0.7", 44 | "jest": "^29.2.1", 45 | "jest-expo": "~51.0.3", 46 | "react-test-renderer": "18.2.0", 47 | "typescript": "~5.3.3" 48 | }, 49 | "private": true 50 | } 51 | -------------------------------------------------------------------------------- /tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "expo/tsconfig.base", 3 | "compilerOptions": { 4 | "strict": true, 5 | "paths": { 6 | "@/*": [ 7 | "./*" 8 | ] 9 | } 10 | }, 11 | "include": [ 12 | "**/*.ts", 13 | "**/*.tsx", 14 | ".expo/types/**/*.ts", 15 | "expo-env.d.ts" 16 | ] 17 | } 18 | -------------------------------------------------------------------------------- /types/index.ts: -------------------------------------------------------------------------------- 1 | import { IconProps } from "@expo/vector-icons/build/createIconSet"; 2 | 3 | export type MenuItemsType = { 4 | icon: IconProps; 5 | title: string; 6 | }; 7 | --------------------------------------------------------------------------------