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,
dark,
show,
...rest
}) => {
return (
<div
@@ -46,7 +47,8 @@ export const Dropdown: React.FC<DropdownProps> = ({
dark && ["bg-dark", "text-white"],
show && "show",
className,
)}>
)}
{...rest}>
{header && <span className="dropdown-header">{header}</span>}
{children}
</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 { Bell } from "tabler-icons-react";
@@ -75,6 +75,30 @@ export const NavigationHeader: React.FC<NavigationHeaderProps> = ({
}) => {
const [notificationsShown, setNotificationsShown] = 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 (
<header
@@ -100,7 +124,9 @@ export const NavigationHeader: React.FC<NavigationHeaderProps> = ({
</div>
) : null}
{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
style={{
border: 0,
@@ -125,6 +151,7 @@ export const NavigationHeader: React.FC<NavigationHeaderProps> = ({
</div>
) : null}
<div
ref={profileRef}
className={cn("nav-item", {
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";
@@ -45,6 +45,22 @@ export const NavigationMenu: React.FC<NavigationMenuProps> = ({
searchContent,
}) => {
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 = (
e: React.MouseEvent<HTMLAnchorElement, MouseEvent>,
@@ -82,7 +98,7 @@ export const NavigationMenu: React.FC<NavigationMenuProps> = ({
};
return wrapMenu(
<ul className="navbar-nav">
<ul className="navbar-nav" ref={navRef}>
{items.map((item: any, idx: number) => {
const onClickItem = (
e: React.MouseEvent<HTMLAnchorElement, MouseEvent>,

View File

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

View File

@@ -1,6 +1,5 @@
import React, { ReactNode } from "react";
import { Footer } from "components";
import styled from "styled-components";
const Root = styled.div`
@@ -24,7 +23,6 @@ function SinglePage({ children }: Props) {
return (
<Root>
<Wrapper>{children}</Wrapper>
<Footer />
</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 "./Loading";
export * from "./Navigation";
export * from "./NavMenu";
export * from "./Router";
export * from "./SinglePage";
export * from "./SiteWrapper";
export * from "./SuspenseLoader";
export * from "./Unhealthy";