├── .gitignore ├── LICENSE.md ├── README.md ├── demo.gif ├── drop.png ├── package-lock.json ├── package.json ├── public ├── favicon.ico ├── index.html ├── logo192.png ├── logo512.png ├── manifest.json └── robots.txt └── src ├── App.js ├── components ├── Dropdown │ ├── DropdownItem.css │ ├── DropdownItem.jsx │ ├── DropdownMenu.css │ └── DropdownMenu.jsx ├── NavIteam │ ├── NavItem.jsx │ └── navItem.css └── Navbar │ ├── Navbar.jsx │ └── navbar.css ├── index.css └── index.js /.gitignore: -------------------------------------------------------------------------------- 1 | # See https://help.github.com/articles/ignoring-files/ for more about ignoring files. 2 | 3 | # dependencies 4 | /node_modules 5 | /.pnp 6 | .pnp.js 7 | 8 | # testing 9 | /coverage 10 | 11 | # production 12 | /build 13 | 14 | # misc 15 | .DS_Store 16 | .env.local 17 | .env.development.local 18 | .env.test.local 19 | .env.production.local 20 | 21 | npm-debug.log* 22 | yarn-debug.log* 23 | yarn-error.log* 24 | -------------------------------------------------------------------------------- /LICENSE.md: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2022 AhriMain 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | 2 | # Mini Project : Multi-level Dropdown level like Facebook with Animations. 3 | 4 | Used "react-transition-group" package for animations 5 | 6 | 7 | ## Demo 8 | 9 | Live - https://chethanby.github.io/AhriMain.github.io-dropdown/ 10 | 11 | ![Alt Text](./demo.gif) 12 | 13 | 14 | ## Screenshots 15 | 16 | ![App Screenshot](./drop.png) 17 | 18 | 19 | -------------------------------------------------------------------------------- /demo.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/chethanBy/AhriMain.github.io-dropdown/57ff2bec1317abb255dd86b7c3af4e02ee0ab657/demo.gif -------------------------------------------------------------------------------- /drop.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/chethanBy/AhriMain.github.io-dropdown/57ff2bec1317abb255dd86b7c3af4e02ee0ab657/drop.png -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "multi_level_dropdown_fb", 3 | "homepage": "https://ahrimain.github.io/AhriMain.github.io-dropdown", 4 | "version": "0.1.0", 5 | "private": true, 6 | "dependencies": { 7 | "@testing-library/jest-dom": "^5.16.5", 8 | "@testing-library/react": "^13.4.0", 9 | "@testing-library/user-event": "^13.5.0", 10 | "gh-pages": "^4.0.0", 11 | "react": "^18.2.0", 12 | "react-dom": "^18.2.0", 13 | "react-icons": "^4.4.0", 14 | "react-scripts": "5.0.1", 15 | "react-transition-group": "^4.4.5", 16 | "web-vitals": "^2.1.4" 17 | }, 18 | "scripts": { 19 | "predeploy": "npm run build", 20 | "deploy": "gh-pages -d build", 21 | "start": "react-scripts start", 22 | "build": "react-scripts build", 23 | "test": "react-scripts test", 24 | "eject": "react-scripts eject" 25 | }, 26 | "eslintConfig": { 27 | "extends": [ 28 | "react-app", 29 | "react-app/jest" 30 | ] 31 | }, 32 | "browserslist": { 33 | "production": [ 34 | ">0.2%", 35 | "not dead", 36 | "not op_mini all" 37 | ], 38 | "development": [ 39 | "last 1 chrome version", 40 | "last 1 firefox version", 41 | "last 1 safari version" 42 | ] 43 | } 44 | } 45 | -------------------------------------------------------------------------------- /public/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/chethanBy/AhriMain.github.io-dropdown/57ff2bec1317abb255dd86b7c3af4e02ee0ab657/public/favicon.ico -------------------------------------------------------------------------------- /public/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 12 | 13 | 17 | 18 | 27 | React App 28 | 29 | 30 | 31 |
32 | 42 | 43 | 44 | -------------------------------------------------------------------------------- /public/logo192.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/chethanBy/AhriMain.github.io-dropdown/57ff2bec1317abb255dd86b7c3af4e02ee0ab657/public/logo192.png -------------------------------------------------------------------------------- /public/logo512.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/chethanBy/AhriMain.github.io-dropdown/57ff2bec1317abb255dd86b7c3af4e02ee0ab657/public/logo512.png -------------------------------------------------------------------------------- /public/manifest.json: -------------------------------------------------------------------------------- 1 | { 2 | "short_name": "React App", 3 | "name": "Create React App Sample", 4 | "icons": [ 5 | { 6 | "src": "favicon.ico", 7 | "sizes": "64x64 32x32 24x24 16x16", 8 | "type": "image/x-icon" 9 | }, 10 | { 11 | "src": "logo192.png", 12 | "type": "image/png", 13 | "sizes": "192x192" 14 | }, 15 | { 16 | "src": "logo512.png", 17 | "type": "image/png", 18 | "sizes": "512x512" 19 | } 20 | ], 21 | "start_url": ".", 22 | "display": "standalone", 23 | "theme_color": "#000000", 24 | "background_color": "#ffffff" 25 | } 26 | -------------------------------------------------------------------------------- /public/robots.txt: -------------------------------------------------------------------------------- 1 | # https://www.robotstxt.org/robotstxt.html 2 | User-agent: * 3 | Disallow: 4 | -------------------------------------------------------------------------------- /src/App.js: -------------------------------------------------------------------------------- 1 | import React from "react"; 2 | import Navbar from "./components/Navbar/Navbar"; 3 | import NavItem from "./components/NavIteam/NavItem"; 4 | import DropdownMenu from "./components/Dropdown/DropdownMenu"; 5 | import { BiBellMinus } from "react-icons/bi"; 6 | import { AiOutlinePlusCircle, AiOutlineCaretDown } from "react-icons/ai"; 7 | import { TbBrandMessenger } from "react-icons/tb"; 8 | 9 | function App() { 10 | return ( 11 |
12 | 13 | } /> 14 | } /> 15 | } /> 16 | }> 17 | 18 | 19 | 20 |
21 | ); 22 | } 23 | 24 | export default App; 25 | -------------------------------------------------------------------------------- /src/components/Dropdown/DropdownItem.css: -------------------------------------------------------------------------------- 1 | .menu-item { 2 | height: 50px; 3 | display: flex; 4 | align-items: center; 5 | border-radius: var(--border-radius); 6 | transition: background 200; 7 | padding: 0.5rem; 8 | } 9 | 10 | .menu-item:hover { 11 | background-color: #525357; 12 | } 13 | -------------------------------------------------------------------------------- /src/components/Dropdown/DropdownItem.jsx: -------------------------------------------------------------------------------- 1 | import React from "react"; 2 | import "./DropdownItem.css"; 3 | 4 | const DropdownItem = (props) => { 5 | return ( 6 | <> 7 | props.goToMenu && props.setActive(props.goToMenu)} 10 | > 11 | {props.leftIcon} 12 | {props.children} 13 | 14 | 15 | ); 16 | }; 17 | 18 | export default DropdownItem; 19 | -------------------------------------------------------------------------------- /src/components/Dropdown/DropdownMenu.css: -------------------------------------------------------------------------------- 1 | .dropdown { 2 | position: absolute; 3 | top: 58px; 4 | width: 300px; 5 | transform: translateX(-45%); 6 | background-color: var(--bg); 7 | border: var(--border); 8 | border-radius: var(--border-radius); 9 | padding: 1rem; 10 | overflow: hidden; 11 | transition: height var(--speed) ease; 12 | } 13 | 14 | .menu-primary-enter { 15 | position: absolute; 16 | transform: translateX(-110%); 17 | } 18 | .menu-primary-enter-active { 19 | transform: translateX(0%); 20 | transition: all var(--speed) ease; 21 | } 22 | .menu-primary-exit { 23 | position: absolute; 24 | } 25 | .menu-primary-exit-active { 26 | transform: translateX(-110%); 27 | transition: all var(--speed) ease; 28 | } 29 | 30 | .menu-secondary-enter { 31 | transform: translateX(110%); 32 | } 33 | .menu-secondary-enter-active { 34 | transform: translateX(0%); 35 | transition: all var(--speed) ease; 36 | } 37 | .menu-secondary-exit { 38 | } 39 | .menu-secondary-exit-active { 40 | transform: translateX(110%); 41 | transition: all var(--speed) ease; 42 | } 43 | -------------------------------------------------------------------------------- /src/components/Dropdown/DropdownMenu.jsx: -------------------------------------------------------------------------------- 1 | import React, { useState } from "react"; 2 | import DropdownItem from "./DropdownItem"; 3 | import { HiOutlineCog } from "react-icons/hi"; 4 | import { AiFillCaretLeft, AiOutlineThunderbolt } from "react-icons/ai"; 5 | import { FaChevronRight } from "react-icons/fa"; 6 | import { CSSTransition } from "react-transition-group"; 7 | import "./DropdownMenu.css"; 8 | 9 | const DropdownMenu = () => { 10 | // state for csstransition 11 | const [active, setActive] = useState("main"); 12 | const [menuHeight, setMenuHeight] = useState(null); 13 | 14 | { 15 | /*we use this function as a callback in CSSTransition onEnter prop which runs this callback when it is 16 | mounted to DOM 17 | */ 18 | } 19 | function calcHeight(el) { 20 | // el.offsetHeight is height in pixels of that component. we use this in dropdown menu style height to set height 21 | const height = el.offsetHeight; 22 | console.log(height); 23 | setMenuHeight(height); 24 | } 25 | 26 | return ( 27 |
28 | {/* 29 | There are two dropdown containers for csstransitions component main and secondary. 30 | we always go back to main conyainer and we use secondary as name for more container because we can style easy 31 | */} 32 | 40 | {/* CSSTransition component check for next element and adds transitions to that element by adding classNames we specified 41 | in this component props to next element and we add css to animate 42 | */} 43 |
44 | My Profile 45 | {/* if this item is clicked then only CSSTransition component will be triggered if active === settings as given in in prop boolean */} 46 | } 48 | goToMenu={"settings"} 49 | setActive={setActive} 50 | > 51 | Settings 52 | 53 | 54 | Animals 55 | 56 |
57 |
58 | 65 |
66 | } 68 | goToMenu={"main"} 69 | setActive={setActive} 70 | /> 71 | 72 | }>HTML 73 | }>CSS 74 | }> 75 | JavaScript 76 | 77 | }> 78 | Awesome! 79 | 80 |
81 |
82 | 89 |
90 | } 93 | setActive={setActive} 94 | > 95 |

Animals

96 |
97 | Kangaroo 98 | Frog 99 | Horse? 100 | Hedgehog 101 |
102 |
103 |
104 | ); 105 | }; 106 | 107 | export default DropdownMenu; 108 | -------------------------------------------------------------------------------- /src/components/NavIteam/NavItem.jsx: -------------------------------------------------------------------------------- 1 | import React, { useState } from "react"; 2 | import "./navItem.css"; 3 | 4 | const NavItem = (props) => { 5 | const [open, setOpen] = useState(false); 6 | 7 | return ( 8 |
  • 9 | setOpen(!open)}> 10 | {props.icon} 11 | 12 | {open && props.children} 13 |
  • 14 | ); 15 | }; 16 | 17 | export default NavItem; 18 | -------------------------------------------------------------------------------- /src/components/NavIteam/navItem.css: -------------------------------------------------------------------------------- 1 | .nav-item { 2 | width: calc(var(--nav-size) * 0.8); 3 | display: flex; 4 | justify-content: center; 5 | align-items: center; 6 | transition: all; 7 | } 8 | 9 | /* Icon Button */ 10 | .icon-button { 11 | --button--size: calc(var(--nav-size) * 0.5); 12 | width: var(--button--size); 13 | height: var(--button--size); 14 | background-color: #484a4d; 15 | border-radius: 50%; 16 | padding: 5px; 17 | margin: 2px 6px; 18 | display: flex; 19 | align-items: center; 20 | justify-content: center; 21 | transition: filter 0.2s; 22 | } 23 | 24 | .icon-button:hover { 25 | filter: brightness(1.2); 26 | } 27 | 28 | .icon-button svg { 29 | fill: var(--text-color); 30 | width: 20px; 31 | height: 20px; 32 | } 33 | -------------------------------------------------------------------------------- /src/components/Navbar/Navbar.jsx: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | import './navbar.css' 3 | 4 | const Navbar = (props) => { 5 | return ( 6 | 11 | ) 12 | } 13 | 14 | export default Navbar -------------------------------------------------------------------------------- /src/components/Navbar/navbar.css: -------------------------------------------------------------------------------- 1 | .navbar { 2 | height: var(--nav-size); 3 | background-color: var(--bg); 4 | padding: 0 1rem; 5 | border-radius: var(--border); 6 | } 7 | 8 | .navbar-nav { 9 | max-width: 100%; 10 | height: 100%; 11 | display: flex; 12 | justify-content: flex-end; 13 | } 14 | -------------------------------------------------------------------------------- /src/index.css: -------------------------------------------------------------------------------- 1 | body { 2 | margin: 0; 3 | font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", "Roboto", "Oxygen", 4 | "Ubuntu", "Cantarell", "Fira Sans", "Droid Sans", "Helvetica Neue", 5 | sans-serif; 6 | -webkit-font-smoothing: antialiased; 7 | -moz-osx-font-smoothing: grayscale; 8 | } 9 | 10 | code { 11 | font-family: source-code-pro, Menlo, Monaco, Consolas, "Courier New", 12 | monospace; 13 | } 14 | 15 | /* CSS Variables */ 16 | :root { 17 | --bg: #242526; 18 | --bg-accent: #484a4d; 19 | --text-color: #dadce1; 20 | --nav-size: 60px; 21 | --border: 1px solid #474a4d; 22 | --border-radius: 8px; 23 | --speed: 500ms; 24 | } 25 | 26 | body { 27 | background-color: var(--text-color); 28 | } 29 | /* over-riding default styling */ 30 | ul { 31 | list-style: none; 32 | margin: 0; 33 | padding: 0; 34 | } 35 | a { 36 | color: var(--text-color); 37 | text-decoration: none; 38 | } 39 | -------------------------------------------------------------------------------- /src/index.js: -------------------------------------------------------------------------------- 1 | import React from "react"; 2 | import ReactDOM from "react-dom/client"; 3 | import "./index.css"; 4 | import App from "./App"; 5 | 6 | const root = ReactDOM.createRoot(document.getElementById("root")); 7 | root.render( 8 | 9 | 10 | 11 | ); 12 | --------------------------------------------------------------------------------