Skip to content

Commit

Permalink
Adding navbar.
Browse files Browse the repository at this point in the history
  • Loading branch information
dereckmezquita committed Jun 17, 2024
1 parent 08f7645 commit 98db80e
Show file tree
Hide file tree
Showing 4 changed files with 304 additions and 5 deletions.
1 change: 1 addition & 0 deletions client/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@
"next-mdx-remote": "^5.0.0",
"react": "^18",
"react-dom": "^18",
"react-icons": "^5.2.1",
"rehype-external-links": "^3.0.0",
"rehype-katex": "^7.0.0",
"rehype-mathjax": "^6.0.0",
Expand Down
12 changes: 7 additions & 5 deletions client/src/components/ui/Footer.tsx
Original file line number Diff line number Diff line change
@@ -1,5 +1,8 @@
import styled from 'styled-components';

const minWidthSnapUp = (props: any) =>
props.theme.container.widths.min_width_snap_up;

const FooterContainer = styled.footer`
margin: 0px auto;
margin-top: 30px;
Expand All @@ -9,11 +12,10 @@ const FooterContainer = styled.footer`
`1px solid ${props.theme.text.colour.light_grey()}`};
width: 50%;
${(props) => `
@media (max-width: ${props.theme.container.widths.min_width_snap_up}) {
width: 85%;
}
`}
@media (max-width: ${minWidthSnapUp}) {
width: 85%;
}
`;

const FooterText = styled.p`
Expand Down
291 changes: 291 additions & 0 deletions client/src/components/ui/Navbar.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,291 @@
import Link from 'next/link';
import { useState, useEffect } from 'react';
import styled from 'styled-components';
import { FaBars, FaFilter, FaUser } from 'react-icons/fa';

// TODO: create a type for theme; so we can have intellisense
const minWidthMobile = (props: any) =>
props.theme.container.widths.min_width_mobile;

const HamburgerIcon = styled.div`
display: none;
float: right;
cursor: pointer;
padding: 14px 13px;
@media screen and (max-width: ${minWidthMobile}) {
display: block;
}
`;

const ResponsiveMenu = styled.div<{ open: boolean }>`
display: ${(props) => (props.open ? 'block' : 'none')};
@media screen and (max-width: ${minWidthMobile}) {
display: ${(props) => (props.open ? 'block' : 'none')};
}
@media screen and (min-width: ${minWidthMobile}) {
display: block;
}
`;

const minWidthSnapUp = (props: any) =>
props.theme.container.widths.min_width_snap_up;

const NavContainer = styled.nav`
background-color: ${(props) =>
props.theme.container.background.colour.primary()};
overflow: hidden;
margin: 20px auto;
width: 90%;
color: ${(props) => props.theme.theme_colours[5]()};
border: 1px solid #ccc;
border-radius: 5px;
box-shadow:
1px 1px 20px rgba(153, 153, 153, 0.5),
0 0 20px rgba(100, 100, 40, 0.2) inset;
&:hover {
box-shadow: 1px 1px 20px rgba(153, 153, 153, 0.5);
}
@media screen and (max-width: ${minWidthSnapUp}) {
width: 95%;
}
`;

const CommonNavItem = styled.div`
cursor: pointer;
display: block;
color: inherit;
text-align: center;
padding: 14px 13px;
text-decoration: none;
font-size: 17px;
@media screen and (max-width: ${minWidthMobile}) {
width: 100%;
float: none;
text-align: left;
}
`;

// allows for argument to determine if align left or right
// inherit from Link component allows for linking to other pages
const NavLeftItem = styled(CommonNavItem).attrs({ as: Link })<{
rightmost?: boolean;
}>`
float: left;
&:hover {
color: ${(props) => props.theme.text.colour.white()};
background-color: ${(props) => props.theme.theme_colours[5]()};
}
@media screen and (max-width: ${minWidthMobile}) {
float: none;
}
`;

// prettier-ignore
const NavRightItemLink = styled(CommonNavItem).attrs({ as: Link })<{ rightmost?: boolean; }>`
float: right;
`;

const NavRightItem = styled(CommonNavItem)<{ rightmost?: boolean }>`
float: right;
&:hover {
color: ${(props) => props.theme.text.colour.white()};
background-color: ${(props) => props.theme.theme_colours[5]()};
}
@media screen and (max-width: ${minWidthMobile}) {
float: none;
}
`;

const DropDownContainer = styled.div`
float: left;
overflow: hidden;
@media screen and (max-width: ${minWidthMobile}) {
width: 100%;
float: none;
text-align: left;
}
`;

// the same as NavItem but no link
const DropDownLabel = styled(CommonNavItem)<{ rightmost?: boolean }>`
&:hover {
color: ${(props) => props.theme.text.colour.white()};
background-color: ${(props) => props.theme.theme_colours[5]()};
}
`;

const DropDownContent = styled.div`
display: none;
position: absolute;
min-width: 160px;
z-index: 1;
border: 1px solid #ccc;
box-shadow: 1px 1px 10px #ccc;
background-color: ${(props) =>
props.theme.container.background.colour.primary()};
${DropDownContainer}:hover & {
display: block;
}
${NavLeftItem} {
float: none;
padding: 12px 16px;
text-align: left;
}
/* TODO: still not working as intended */
${NavLeftItem}:first-child {
border-top-left-radius: 0;
border-top-right-radius: 0;
}
${NavLeftItem}:last-child {
border-bottom-left-radius: 5px;
border-bottom-right-radius: 5px;
}
@media screen and (max-width: ${minWidthMobile}) {
border: none;
width: 100%;
position: relative;
float: none;
text-align: left;
box-shadow: none;
${NavLeftItem} {
padding-left: 30px;
}
}
`;

const DateTimeDisplay = styled.div`
cursor: pointer;
float: right;
display: block;
color: inherit;
text-align: center;
padding: 14px 13px;
text-decoration: none;
font-size: 17px;
@media screen and (max-width: ${minWidthMobile}) {
width: 100%;
position: relative;
float: none;
text-align: left;
}
`;

// since using conditionals in components we must ensure that the component is mounted before rendering
// either this or use dynamic from next/dynamic
function Navbar() {
const [hasMounted, setHasMounted] = useState(false);
const [dateTime, setDateTime] = useState<string | null>(null);
const [isAuthModalOpen, setIsAuthModalOpen] = useState(false);
const [isMenuOpen, setIsMenuOpen] = useState(false);

const closeMenu = () => {
setIsMenuOpen(false);
};

// redux control of tag filter
// Error: `useDispatch` was conditionally called inside an `if` statement. Fixed: Moved `useDispatch` to the top-level, ensuring consistent hook order across renders.
const dispatch = useDispatch();

const handleToggleFilterClick = () => {
dispatch(toggleTagsFilter());
};

useEffect(() => {
setHasMounted(true);

const updateDateTime = () => {
const currentDate = new Date();
const displayDate = currentDate.toLocaleDateString('en-US', {
day: '2-digit',
month: 'short'
});
const displayTime = currentDate.toLocaleTimeString('en-US', {
hour12: false,
hour: '2-digit',
minute: '2-digit',
second: '2-digit'
});
setDateTime(`${displayDate} ${displayTime}`);
};

updateDateTime();
const interval = setInterval(updateDateTime, 1000);
return () => {
clearInterval(interval);
};
}, []);

if (!hasMounted) {
return null;
}

return (
<NavContainer>
<HamburgerIcon onClick={() => setIsMenuOpen(!isMenuOpen)}>
<FaBars />
</HamburgerIcon>

<NavLeftItem onClick={closeMenu} href="/">
Blog
</NavLeftItem>
<ResponsiveMenu open={isMenuOpen}>
<NavLeftItem onClick={closeMenu} href="/courses">
Courses
</NavLeftItem>
<NavLeftItem onClick={closeMenu} href="/references">
References
</NavLeftItem>
<DropDownContainer>
<DropDownLabel>Dictionaries</DropDownLabel>
<DropDownContent>
<NavLeftItem
onClick={closeMenu}
href="/dictionaries/biology"
>
Biology Dictionary
</NavLeftItem>
<NavLeftItem
onClick={closeMenu}
href="/dictionaries/chemistry"
>
Chemistry Dictionary
</NavLeftItem>
</DropDownContent>
</DropDownContainer>
{/* <NavRightItemLink href='https://www.linkedin.com/in/dereck/' target='_blank' title='LinkedIn'>
<FaLinkedin />
</NavRightItemLink> */}
<DateTimeDisplay>
{dateTime || '00 Jan 00:00:00'}
</DateTimeDisplay>
<NavRightItem onClick={handleToggleFilterClick}>
<FaFilter />
</NavRightItem>
<NavRightItem onClick={() => setIsAuthModalOpen(true)}>
<FaUser />
</NavRightItem>
{isAuthModalOpen && (
<Auth onClose={() => setIsAuthModalOpen(false)} />
)}
</ResponsiveMenu>
</NavContainer>
);
}

export default Navbar;
5 changes: 5 additions & 0 deletions client/yarn.lock
Original file line number Diff line number Diff line change
Expand Up @@ -3718,6 +3718,11 @@ react-dom@^18:
loose-envify "^1.1.0"
scheduler "^0.23.2"

react-icons@^5.2.1:
version "5.2.1"
resolved "https://registry.yarnpkg.com/react-icons/-/react-icons-5.2.1.tgz#28c2040917b2a2eda639b0f797bff1888e018e4a"
integrity sha512-zdbW5GstTzXaVKvGSyTaBalt7HSfuK5ovrzlpyiWHAFXndXTdd/1hdDHI4xBM1Mn7YriT6aqESucFl9kEXzrdw==

react-is@^16.13.1:
version "16.13.1"
resolved "https://registry.yarnpkg.com/react-is/-/react-is-16.13.1.tgz#789729a4dc36de2999dc156dd6c1d9c18cea56a4"
Expand Down

0 comments on commit 98db80e

Please sign in to comment.