mirror of
https://github.com/NginxProxyManager/nginx-proxy-manager.git
synced 2025-08-28 03:30:05 +00:00
Improvements to menus and suspense
This commit is contained in:
@@ -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>
|
||||
|
@@ -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,
|
||||
})}>
|
||||
|
@@ -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>,
|
||||
|
@@ -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;
|
||||
|
@@ -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>
|
||||
);
|
||||
}
|
||||
|
31
frontend/src/components/SuspenseLoader.tsx
Normal file
31
frontend/src/components/SuspenseLoader.tsx
Normal 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 };
|
@@ -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";
|
||||
|
Reference in New Issue
Block a user