Improvements to menus and suspense

This commit is contained in:
Jamie Curnow
2021-07-15 09:47:24 +10:00
parent 52f3801b4e
commit 9288fa4757
7 changed files with 85 additions and 13 deletions

View File

@@ -37,6 +37,7 @@ export const Dropdown: React.FC<DropdownProps> = ({
arrow, arrow,
dark, dark,
show, show,
...rest
}) => { }) => {
return ( return (
<div <div
@@ -46,7 +47,8 @@ export const Dropdown: React.FC<DropdownProps> = ({
dark && ["bg-dark", "text-white"], dark && ["bg-dark", "text-white"],
show && "show", show && "show",
className, className,
)}> )}
{...rest}>
{header && <span className="dropdown-header">{header}</span>} {header && <span className="dropdown-header">{header}</span>}
{children} {children}
</div> </div>

View File

@@ -1,4 +1,4 @@
import React, { ReactNode, useState } from "react"; import React, { ReactNode, useState, useRef, useEffect } from "react";
import cn from "classnames"; import cn from "classnames";
import { Bell } from "tabler-icons-react"; import { Bell } from "tabler-icons-react";
@@ -75,6 +75,30 @@ export const NavigationHeader: React.FC<NavigationHeaderProps> = ({
}) => { }) => {
const [notificationsShown, setNotificationsShown] = useState(false); const [notificationsShown, setNotificationsShown] = useState(false);
const [profileShown, setProfileShown] = useState(false); const [profileShown, setProfileShown] = useState(false);
const profileRef = useRef(null);
const notificationsRef = useRef(null);
const handleClickOutside = (event: any) => {
if (
profileRef.current &&
// @ts-expect-error ts-migrate(2531) FIXME: Object is possibly 'null'.
!profileRef.current.contains(event.target)
) {
setProfileShown(false);
}
if (
notificationsRef.current &&
// @ts-expect-error ts-migrate(2531) FIXME: Object is possibly 'null'.
!notificationsRef.current.contains(event.target)
) {
setNotificationsShown(false);
}
};
useEffect(() => {
document.addEventListener("mousedown", handleClickOutside);
return () => document.removeEventListener("mousedown", handleClickOutside);
}, []);
return ( return (
<header <header
@@ -100,7 +124,9 @@ export const NavigationHeader: React.FC<NavigationHeaderProps> = ({
</div> </div>
) : null} ) : null}
{notifications ? ( {notifications ? (
<div className="nav-item dropdown d-none d-md-flex me-3"> <div
className="nav-item dropdown d-none d-md-flex me-3"
ref={notificationsRef}>
<button <button
style={{ style={{
border: 0, border: 0,
@@ -125,6 +151,7 @@ export const NavigationHeader: React.FC<NavigationHeaderProps> = ({
</div> </div>
) : null} ) : null}
<div <div
ref={profileRef}
className={cn("nav-item", { className={cn("nav-item", {
dropdown: !!profileItems, dropdown: !!profileItems,
})}> })}>

View File

@@ -1,4 +1,4 @@
import React, { ReactNode, useState } from "react"; import React, { ReactNode, useState, useRef, useEffect } from "react";
import cn from "classnames"; import cn from "classnames";
@@ -45,6 +45,22 @@ export const NavigationMenu: React.FC<NavigationMenuProps> = ({
searchContent, searchContent,
}) => { }) => {
const [dropdownShown, setDropdownShown] = useState(0); const [dropdownShown, setDropdownShown] = useState(0);
const navRef = useRef(null);
const handleClickOutside = (event: any) => {
if (
navRef.current &&
// @ts-expect-error ts-migrate(2531) FIXME: Object is possibly 'null'.
!navRef.current.contains(event.target)
) {
setDropdownShown(0);
}
};
useEffect(() => {
document.addEventListener("mousedown", handleClickOutside);
return () => document.removeEventListener("mousedown", handleClickOutside);
}, []);
const itemClicked = ( const itemClicked = (
e: React.MouseEvent<HTMLAnchorElement, MouseEvent>, e: React.MouseEvent<HTMLAnchorElement, MouseEvent>,
@@ -82,7 +98,7 @@ export const NavigationMenu: React.FC<NavigationMenuProps> = ({
}; };
return wrapMenu( return wrapMenu(
<ul className="navbar-nav"> <ul className="navbar-nav" ref={navRef}>
{items.map((item: any, idx: number) => { {items.map((item: any, idx: number) => {
const onClickItem = ( const onClickItem = (
e: React.MouseEvent<HTMLAnchorElement, MouseEvent>, e: React.MouseEvent<HTMLAnchorElement, MouseEvent>,

View File

@@ -1,6 +1,6 @@
import React, { lazy, Suspense } from "react"; import React, { lazy, Suspense } from "react";
import { Loading, SiteWrapper, SinglePage } from "components"; import { SiteWrapper, SuspenseLoader } from "components";
import { useAuthState, useHealthState, UserProvider } from "context"; import { useAuthState, useHealthState, UserProvider } from "context";
import { BrowserRouter, Switch, Route } from "react-router-dom"; import { BrowserRouter, Switch, Route } from "react-router-dom";
@@ -17,11 +17,7 @@ const Users = lazy(() => import("pages/Users"));
function Router() { function Router() {
const { health } = useHealthState(); const { health } = useHealthState();
const { authenticated } = useAuthState(); const { authenticated } = useAuthState();
const Spinner = ( const Spinner = <SuspenseLoader />;
<SinglePage>
<Loading />
</SinglePage>
);
if (health.loading) { if (health.loading) {
return Spinner; return Spinner;

View File

@@ -1,6 +1,5 @@
import React, { ReactNode } from "react"; import React, { ReactNode } from "react";
import { Footer } from "components";
import styled from "styled-components"; import styled from "styled-components";
const Root = styled.div` const Root = styled.div`
@@ -24,7 +23,6 @@ function SinglePage({ children }: Props) {
return ( return (
<Root> <Root>
<Wrapper>{children}</Wrapper> <Wrapper>{children}</Wrapper>
<Footer />
</Root> </Root>
); );
} }

View File

@@ -0,0 +1,31 @@
import React from "react";
import { Loading } from "components";
import styled from "styled-components";
const Root = styled.div`
display: flex;
flex-direction: column;
justify-content: center;
min-height: 100%;
`;
const Wrapper = styled.div`
flex: 1 1 auto;
display: flex;
align-items: center;
justify-content: center;
min-height: 100px;
`;
function SuspenseLoader() {
return (
<Root>
<Wrapper>
<Loading />
</Wrapper>
</Root>
);
}
export { SuspenseLoader };

View File

@@ -9,7 +9,9 @@ export * from "./Footer";
export * from "./Loader"; export * from "./Loader";
export * from "./Loading"; export * from "./Loading";
export * from "./Navigation"; export * from "./Navigation";
export * from "./NavMenu";
export * from "./Router"; export * from "./Router";
export * from "./SinglePage"; export * from "./SinglePage";
export * from "./SiteWrapper"; export * from "./SiteWrapper";
export * from "./SuspenseLoader";
export * from "./Unhealthy"; export * from "./Unhealthy";