├── .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 | 
12 |
13 |
14 | ## Screenshots
15 |
16 | 
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 | You need to enable JavaScript to run this app.
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 |
7 |
10 |
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 |
--------------------------------------------------------------------------------