Merge pull request #3 from kfnawaz/sidebar-nav
Feat - Sidebar navigation
This commit is contained in:
@@ -24,7 +24,8 @@
|
||||
"start": "react-scripts start",
|
||||
"build": "react-scripts build",
|
||||
"test": "react-scripts test",
|
||||
"eject": "react-scripts eject"
|
||||
"eject": "react-scripts eject",
|
||||
"deploy": "scp -Cvpr -i <keyfile> build/* ubuntu@ec2-13-59-140-102.us-east-2.compute.amazonaws.com:/home/ubuntu/web-wms/"
|
||||
},
|
||||
"eslintConfig": {
|
||||
"extends": [
|
||||
|
||||
19
src/assets/icons/Hamburger-icon.js
Normal file
19
src/assets/icons/Hamburger-icon.js
Normal file
@@ -0,0 +1,19 @@
|
||||
const HamburgerIcon = ({ width = 24, height = 24, color, ...props }) => (
|
||||
<svg
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
width={width}
|
||||
height={height}
|
||||
viewBox="0 0 24 24"
|
||||
stroke={color}
|
||||
strokeWidth="2"
|
||||
strokeLinecap="round"
|
||||
strokeLinejoin="round"
|
||||
{...props}
|
||||
>
|
||||
<line x1="3" y1="12" x2="21" y2="12"></line>
|
||||
<line x1="3" y1="6" x2="21" y2="6"></line>
|
||||
<line x1="3" y1="18" x2="21" y2="18"></line>
|
||||
</svg>
|
||||
);
|
||||
|
||||
export default HamburgerIcon;
|
||||
14
src/assets/icons/Home-icon.js
Normal file
14
src/assets/icons/Home-icon.js
Normal file
@@ -0,0 +1,14 @@
|
||||
const HomeIcon = ({ width = 24, height = 24, color = 'white', ...props }) => (
|
||||
<svg width={width} height={height} viewBox="0 0 24 24" fill="none" {...props} xmlns="http://www.w3.org/2000/svg">
|
||||
<path
|
||||
d="M17.79 22.75H6.21C3.47 22.75 1.25 20.52 1.25 17.78V10.37C1.25 9.01 2.09 7.3 3.17 6.46L8.56 2.26C10.18 1 12.77 0.940005 14.45 2.12L20.63 6.45C21.82 7.28 22.75 9.06001 22.75 10.51V17.79C22.75 20.52 20.53 22.75 17.79 22.75ZM9.48 3.44L4.09 7.64C3.38 8.2 2.75 9.47001 2.75 10.37V17.78C2.75 19.69 4.3 21.25 6.21 21.25H17.79C19.7 21.25 21.25 19.7 21.25 17.79V10.51C21.25 9.55 20.56 8.22 19.77 7.68L13.59 3.35C12.45 2.55 10.57 2.59 9.48 3.44Z"
|
||||
fill={color}
|
||||
/>
|
||||
<path
|
||||
d="M12 18.75C11.59 18.75 11.25 18.41 11.25 18V15C11.25 14.59 11.59 14.25 12 14.25C12.41 14.25 12.75 14.59 12.75 15V18C12.75 18.41 12.41 18.75 12 18.75Z"
|
||||
fill={color}
|
||||
/>
|
||||
</svg>
|
||||
);
|
||||
|
||||
export default HomeIcon;
|
||||
10
src/assets/icons/SidebarDown-icon.js
Normal file
10
src/assets/icons/SidebarDown-icon.js
Normal file
@@ -0,0 +1,10 @@
|
||||
const SidebarDownIcon = ({ width = 14, height = 8, color = 'white', ...props }) => (
|
||||
<svg width={width} height={height} viewBox="0 0 15 8" fill="none" {...props} xmlns="http://www.w3.org/2000/svg">
|
||||
<path
|
||||
d="M8.51 7.19999L10.48 5.22999L13.69 2.01999C14.36 1.33999 13.88 0.179993 12.92 0.179993L6.69 0.179993L1.08 0.179993C0.120001 0.179993 -0.36 1.33999 0.320001 2.01999L5.5 7.19999C6.32 8.02999 7.68 8.02999 8.51 7.19999Z"
|
||||
fill={color}
|
||||
/>
|
||||
</svg>
|
||||
);
|
||||
|
||||
export default SidebarDownIcon;
|
||||
19
src/assets/icons/X-close-icon.js
Normal file
19
src/assets/icons/X-close-icon.js
Normal file
@@ -0,0 +1,19 @@
|
||||
const XIcon = ({ width = 18, height = 18, color = '#ffffff', ...props }) => (
|
||||
<svg
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
width={width}
|
||||
height={height}
|
||||
viewBox="0 0 14 14"
|
||||
fill="none"
|
||||
stroke={color}
|
||||
strokeWidth="2"
|
||||
strokeLinecap="round"
|
||||
strokeLinejoin="round"
|
||||
{...props}
|
||||
>
|
||||
<line x1="13" y1="1" x2="1" y2="13"></line>
|
||||
<line x1="1" y1="1" x2="13" y2="13"></line>
|
||||
</svg>
|
||||
);
|
||||
|
||||
export default XIcon;
|
||||
@@ -1,9 +1,81 @@
|
||||
import { NavLink } from 'react-router-dom';
|
||||
import SidebarDownIcon from '../../assets/icons/SidebarDown-icon';
|
||||
import HomeIcon from '../../assets/icons/Home-icon';
|
||||
import XIcon from '../../assets/icons/X-close-icon';
|
||||
import combineClasses from '../../utils/combineCssClasses';
|
||||
import styles from './sidebar.module.css';
|
||||
|
||||
export default function Sidebar() {
|
||||
const items = {
|
||||
title: {
|
||||
icon: '',
|
||||
text: 'Main link',
|
||||
link: '/',
|
||||
},
|
||||
subs: [
|
||||
{
|
||||
text: 'Sub links',
|
||||
link: '/login',
|
||||
},
|
||||
{
|
||||
text: 'Sub links',
|
||||
link: '/login',
|
||||
},
|
||||
{
|
||||
text: 'Sub links',
|
||||
link: '/login',
|
||||
},
|
||||
{
|
||||
text: 'Sub links',
|
||||
link: '/',
|
||||
},
|
||||
{
|
||||
text: 'Sub links',
|
||||
link: '/login',
|
||||
},
|
||||
],
|
||||
};
|
||||
|
||||
const NavGroupItems = ({ list }) => {
|
||||
return (
|
||||
<div className={styles.navSidebar}>
|
||||
<div className={styles.sidebarBranding}>Plaidware</div>
|
||||
<div className={styles.sidebarGroup}>
|
||||
<div className={combineClasses(styles.sidebarMainItem)}>
|
||||
<NavLink className={combineClasses(styles.alink, styles.mainItemContent)} to={list.title.link}>
|
||||
<HomeIcon className={styles.mainItemIcon} />
|
||||
<div>{list.title.text}</div>
|
||||
</NavLink>
|
||||
<SidebarDownIcon className={styles.dropdownIcon} />
|
||||
</div>
|
||||
{list.subs.map((subLink, idx) => (
|
||||
<NavLink key={idx} className={styles.alink} to={subLink.link}>
|
||||
{({ isActive }) => (
|
||||
<div className={combineClasses(styles.sidebarSubItem, [isActive, styles.subActive])}>{subLink.text}</div>
|
||||
)}
|
||||
</NavLink>
|
||||
))}
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export default function Sidebar({ isMobileScreen, isOpen, setOpen }) {
|
||||
if (!isMobileScreen && isOpen) {
|
||||
return (
|
||||
<div className={styles.navSidebar}>
|
||||
<div className={styles.sidebarBrandingHeader}>
|
||||
<div className={styles.sidebarBranding}>Plaidware</div>
|
||||
<div
|
||||
onClick={() => {
|
||||
setOpen(false);
|
||||
}}
|
||||
>
|
||||
<XIcon />
|
||||
</div>
|
||||
</div>
|
||||
<div className={styles.sidebarList}>
|
||||
<NavGroupItems list={items} />
|
||||
<NavGroupItems list={items} />
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
@@ -2,7 +2,18 @@
|
||||
grid-column: 1;
|
||||
background: #2d373c;
|
||||
min-height: 100vh;
|
||||
padding: 40px;
|
||||
min-width: 350px;
|
||||
}
|
||||
|
||||
/* @media only screen and (max-width: 800px) {
|
||||
|
||||
} */
|
||||
|
||||
.sidebarBrandingHeader {
|
||||
display: grid;
|
||||
padding: 40px 0px 30px 40px;
|
||||
grid-template-columns: auto 40px;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.sidebarBranding {
|
||||
@@ -12,4 +23,60 @@
|
||||
line-height: 44px;
|
||||
|
||||
color: #ffffff;
|
||||
}
|
||||
}
|
||||
|
||||
.sidebarList {
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.sidebarGroup {
|
||||
}
|
||||
|
||||
.sidebarMainItem {
|
||||
width: 100%;
|
||||
display: grid;
|
||||
align-items: center;
|
||||
grid-template-columns: auto 55px;
|
||||
height: 48px;
|
||||
|
||||
/* background: #007aff; */
|
||||
}
|
||||
|
||||
.mainItemContent {
|
||||
display: grid;
|
||||
align-items: center;
|
||||
grid-template-columns: 80px auto;
|
||||
height: 100%;
|
||||
|
||||
color: white;
|
||||
}
|
||||
|
||||
.mainItemIcon {
|
||||
padding-left: 40px;
|
||||
width: auto;
|
||||
}
|
||||
|
||||
.dropdownIcon {
|
||||
padding-left: 20px;
|
||||
width: auto;
|
||||
}
|
||||
|
||||
.sidebarSubItem {
|
||||
width: 100%;
|
||||
height: 46px;
|
||||
display: grid;
|
||||
align-items: center;
|
||||
padding-left: 80px;
|
||||
|
||||
color: white;
|
||||
}
|
||||
|
||||
.alink:visited,
|
||||
.alink:link {
|
||||
text-decoration: none;
|
||||
}
|
||||
|
||||
.subActive {
|
||||
background: #1f282d;
|
||||
color: #007aff !important;
|
||||
}
|
||||
|
||||
@@ -1,8 +1,18 @@
|
||||
import React from 'react'
|
||||
import styles from './topbar.module.css'
|
||||
import React from 'react';
|
||||
import HamburgerIcon from '../../assets/icons/Hamburger-icon';
|
||||
import styles from './topbar.module.css';
|
||||
|
||||
export default function TopBar() {
|
||||
export default function TopBar({ isMobileScreen, isSidebarOpen, setSidebarOpen }) {
|
||||
return (
|
||||
<div className={styles.topBar}>search comes here</div>
|
||||
)
|
||||
<div className={styles.topBar}>
|
||||
<div
|
||||
onClick={() => {
|
||||
setSidebarOpen(true);
|
||||
}}
|
||||
>
|
||||
{isSidebarOpen ? null : <HamburgerIcon color="#000" />}
|
||||
</div>
|
||||
<div>Right side icons here</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -1,5 +1,10 @@
|
||||
.topBar {
|
||||
grid-row: 1;
|
||||
|
||||
padding: 24px 48px;
|
||||
box-shadow: 0px 1px 4px rgba(0, 0, 0, 0.08);
|
||||
}
|
||||
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
}
|
||||
|
||||
25
src/hooks/useWindowDimensions.js
Normal file
25
src/hooks/useWindowDimensions.js
Normal file
@@ -0,0 +1,25 @@
|
||||
import { useState, useEffect } from 'react';
|
||||
|
||||
function getWindowDimensions() {
|
||||
const { innerWidth: width, innerHeight: height } = window;
|
||||
return {
|
||||
width,
|
||||
height,
|
||||
isMobileScreen: width <= 800,
|
||||
};
|
||||
}
|
||||
|
||||
export default function useWindowDimensions() {
|
||||
const [windowDimensions, setWindowDimensions] = useState(getWindowDimensions());
|
||||
|
||||
useEffect(() => {
|
||||
function handleResize() {
|
||||
setWindowDimensions(getWindowDimensions());
|
||||
}
|
||||
|
||||
window.addEventListener('resize', handleResize);
|
||||
return () => window.removeEventListener('resize', handleResize);
|
||||
}, []);
|
||||
|
||||
return windowDimensions;
|
||||
}
|
||||
@@ -1,11 +1,13 @@
|
||||
html,
|
||||
body {
|
||||
margin: 0;
|
||||
-webkit-font-smoothing: antialiased;
|
||||
-moz-osx-font-smoothing: grayscale;
|
||||
}
|
||||
|
||||
* {
|
||||
font-family: Averta;
|
||||
-webkit-font-smoothing: antialiased;
|
||||
-moz-osx-font-smoothing: grayscale;
|
||||
box-sizing: border-box;
|
||||
}
|
||||
|
||||
code {
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
.dashboardGrid {
|
||||
width: 100vw;
|
||||
display: grid;
|
||||
grid-template-columns: 300px auto;
|
||||
grid-template-columns: max-content auto;
|
||||
}
|
||||
|
||||
.mainContent {
|
||||
@@ -10,7 +10,19 @@
|
||||
grid-template-rows: 108px 72px auto;
|
||||
}
|
||||
|
||||
@media only screen and (max-width: 800px) {
|
||||
.dashboardGrid {
|
||||
grid-template-columns: auto;
|
||||
}
|
||||
|
||||
.mainContent {
|
||||
grid-column: 1;
|
||||
grid-template-rows: 50px 50px auto;
|
||||
}
|
||||
}
|
||||
|
||||
.content {
|
||||
grid-row: 3;
|
||||
padding: 24px 48px;
|
||||
min-height: 100%;
|
||||
}
|
||||
|
||||
@@ -1,15 +1,21 @@
|
||||
import { useState } from 'react';
|
||||
import { Outlet } from 'react-router-dom';
|
||||
|
||||
import Breadcrumbs from '../../components/breadcrumbs';
|
||||
import Sidebar from '../../components/Sidebar';
|
||||
import TopBar from '../../components/topbar';
|
||||
import useWindowDimensions from '../../hooks/useWindowDimensions';
|
||||
import styles from './dashboard.module.css';
|
||||
|
||||
export default function Dashboard() {
|
||||
const [sidebarOpen, setSidebarOpen] = useState(true);
|
||||
const { isMobileScreen } = useWindowDimensions();
|
||||
|
||||
return (
|
||||
<div className={styles.dashboardGrid}>
|
||||
<Sidebar />
|
||||
<Sidebar isMobileScreen={isMobileScreen} isOpen={sidebarOpen} setOpen={setSidebarOpen} />
|
||||
<div className={styles.mainContent}>
|
||||
<TopBar />
|
||||
<TopBar isMobileScreen={isMobileScreen} isSidebarOpen={sidebarOpen} setSidebarOpen={setSidebarOpen} />
|
||||
<Breadcrumbs />
|
||||
<div className={styles.content}>
|
||||
<Outlet />
|
||||
|
||||
15
src/utils/combineCssClasses.js
Normal file
15
src/utils/combineCssClasses.js
Normal file
@@ -0,0 +1,15 @@
|
||||
export const combineClasses = (...classes) => {
|
||||
return classes.reduce((classNames, currentClassName) => {
|
||||
if (Array.isArray(currentClassName)) {
|
||||
return currentClassName[0] ? classNames + ' ' + currentClassName[1] : classNames;
|
||||
} else if (currentClassName) {
|
||||
return classNames + ' ' + currentClassName;
|
||||
} else {
|
||||
return classNames;
|
||||
}
|
||||
}, '');
|
||||
};
|
||||
|
||||
export default combineClasses;
|
||||
|
||||
// Ref: https://www.npmjs.com/package/combine-classes
|
||||
Reference in New Issue
Block a user