From dea89bd3123fec9be1fcd2aee1c3e8701fb9c3d7 Mon Sep 17 00:00:00 2001 From: Julian Reinhardt Date: Tue, 26 Oct 2021 23:39:36 +0200 Subject: [PATCH] Gets navigation bar working on mobile --- .../src/components/Dropdown/DropdownItem.tsx | 45 +++-- frontend/src/components/NavMenu.tsx | 45 ++--- .../Navigation/NavigationHeader.tsx | 176 ++++++++++-------- .../components/Navigation/NavigationMenu.tsx | 48 +++-- frontend/src/components/SiteWrapper.tsx | 3 - frontend/src/components/Table/Table.tsx | 2 +- frontend/src/index.scss | 7 + 7 files changed, 184 insertions(+), 142 deletions(-) diff --git a/frontend/src/components/Dropdown/DropdownItem.tsx b/frontend/src/components/Dropdown/DropdownItem.tsx index db29acae..cc2132cc 100644 --- a/frontend/src/components/Dropdown/DropdownItem.tsx +++ b/frontend/src/components/Dropdown/DropdownItem.tsx @@ -1,6 +1,7 @@ import React, { ReactNode } from "react"; import cn from "classnames"; +import { Link, LinkProps } from "react-router-dom"; export interface DropdownItemProps { /** @@ -28,9 +29,9 @@ export interface DropdownItemProps { */ icon?: ReactNode; /** - * Href + * Optional react-router-dom `to` prop, will convert the item to a link */ - href?: string; + to?: string; /** * onClick handler */ @@ -43,25 +44,37 @@ export const DropdownItem: React.FC = ({ active, disabled, icon, - href, + to, onClick, ...rest }) => { + const getElem = (props: Omit, children: ReactNode) => { + return to ? ( + + {children} + + ) : ( + {children} + ); + }; return divider ? (
) : ( - - {icon && {icon}} - {children} - + getElem( + { + className: cn( + "dropdown-item", + active && "active", + disabled && "disabled", + className, + ), + onClick, + ...rest, + }, + <> + {icon && {icon}} + {children} + , + ) ); }; diff --git a/frontend/src/components/NavMenu.tsx b/frontend/src/components/NavMenu.tsx index 7a7f758b..4f4f1989 100644 --- a/frontend/src/components/NavMenu.tsx +++ b/frontend/src/components/NavMenu.tsx @@ -2,7 +2,6 @@ import React from "react"; import { Dropdown, Navigation } from "components"; import { intl } from "locale"; -import { Link } from "react-router-dom"; import { Book, DeviceDesktop, @@ -13,11 +12,13 @@ import { Users, } from "tabler-icons-react"; -function NavMenu() { +const NavMenu: React.FC<{ openOnMobile: boolean }> = ({ openOnMobile }) => { return ( , dropdownItems: [ - - - - {intl.formatMessage({ - id: "certificates.title", - defaultMessage: "Certificates", - })} - - + + + {intl.formatMessage({ + id: "certificates.title", + defaultMessage: "Certificates", + })} + , - - - - {intl.formatMessage({ - id: "cert_authorities.title", - defaultMessage: "Certificate Authorities", - })} - - + + + {intl.formatMessage({ + id: "cert_authorities.title", + defaultMessage: "Certificate Authorities", + })} + , ], }, @@ -96,6 +99,6 @@ function NavMenu() { ]} /> ); -} +}; export { NavMenu }; diff --git a/frontend/src/components/Navigation/NavigationHeader.tsx b/frontend/src/components/Navigation/NavigationHeader.tsx index d593c60c..e2ca21a1 100644 --- a/frontend/src/components/Navigation/NavigationHeader.tsx +++ b/frontend/src/components/Navigation/NavigationHeader.tsx @@ -3,12 +3,12 @@ import React, { ReactNode, useState, useRef, useEffect } from "react"; import cn from "classnames"; import { Bell } from "tabler-icons-react"; +import { NavMenu } from ".."; import { Badge } from "../Badge"; import { ButtonList } from "../ButtonList"; import { Dropdown } from "../Dropdown"; import { NavigationMenu } from "./NavigationMenu"; import { NavigationMenuItemProps } from "./NavigationMenuItem"; - export interface NavigationHeaderProps { /** * Additional Class @@ -75,9 +75,15 @@ export const NavigationHeader: React.FC = ({ }) => { const [notificationsShown, setNotificationsShown] = useState(false); const [profileShown, setProfileShown] = useState(false); + const [mobileNavShown, setMobileNavShown] = useState(false); const profileRef = useRef(null); const notificationsRef = useRef(null); + const toggleMobileNavShown = () => + setMobileNavShown((prevState) => { + return !prevState; + }); + const handleClickOutside = (event: any) => { if ( profileRef.current && @@ -101,97 +107,105 @@ export const NavigationHeader: React.FC = ({ }, []); return ( -
-
- -

- {brandContent} -

-
- {buttons ? ( -
- {buttons} -
- ) : null} - {notifications ? ( + <> +
+
+ +

+ {brandContent} +

+
+ {buttons ? ( +
+ {buttons} +
+ ) : null} + {notifications ? ( +
+ + +
+
{notifications}
+
+
+
+ ) : null}
+ ref={profileRef} + className={cn("nav-item", { + dropdown: !!profileItems, + })}> - -
-
{notifications}
-
-
-
- ) : null} -
- - {profileItems ? ( - - {profileItems} - - ) : null} +
+ {menuItems ? ( + + ) : null}
- {menuItems ? : null} -
-
+ + + ); }; diff --git a/frontend/src/components/Navigation/NavigationMenu.tsx b/frontend/src/components/Navigation/NavigationMenu.tsx index 382e6c44..b3768ace 100644 --- a/frontend/src/components/Navigation/NavigationMenu.tsx +++ b/frontend/src/components/Navigation/NavigationMenu.tsx @@ -1,6 +1,7 @@ import React, { ReactNode, useState, useRef, useEffect } from "react"; import cn from "classnames"; +import styled from "styled-components"; import { NavigationMenuItem, @@ -15,27 +16,29 @@ import { * the items. */ +const StyledNavWrapper = styled.div<{ shown: boolean }>` + @media (max-width: 767.98px) { + transition: max-height 300ms ease-in-out; + max-height: ${(p) => (p.shown ? `80vh` : `0`)}; + min-height: ${(p) => (p.shown ? `inherit` : `0`)}; + overflow: hidden; + padding: ${(p) => (p.shown ? `inherit` : `0`)}; + } +`; + export interface NavigationMenuProps { - /** - * Additional Class - */ + /** Additional Class */ className?: string; - /** - * Navigation Items - */ + /** Navigation Items */ items: NavigationMenuItemProps[]; - /** - * If this menu sits within a Navigation.Header - */ + /** If this menu sits within a Navigation.Header */ withinHeader?: boolean; - /** - * Color theme for the nav bar - */ + /** Color theme for the nav bar */ theme?: "transparent" | "light" | "dark"; - /** - * Search content - */ + /** Search content */ searchContent?: ReactNode; + /** Navigation is currently hidden on mobile */ + openOnMobile?: boolean; } export const NavigationMenu: React.FC = ({ className, @@ -43,6 +46,7 @@ export const NavigationMenu: React.FC = ({ withinHeader, theme = "transparent", searchContent, + openOnMobile = true, }) => { const [dropdownShown, setDropdownShown] = useState(0); const navRef = useRef(null); @@ -74,16 +78,20 @@ export const NavigationMenu: React.FC = ({ const wrapMenu = (el: ReactNode) => { if (withinHeader) { return ( -
-
- {el} -
+
+ +
{el}
+
); } return (
-
+