Browse Source

Merge pull request #4928 from 7heMech/develop

UI/UX improvements
jc21 1 month ago
parent
commit
f1039ce2ef

+ 1 - 0
frontend/index.html

@@ -5,6 +5,7 @@
 		<meta name="viewport" content="width=device-width, initial-scale=1" />
 		<title>Nginx Proxy Manager</title>
 		<meta name="description" content="In The Office Planner" />
+		<link rel="preload" href="/images/logo-no-text.svg" as="image" type="image/svg+xml" fetchPriority="high">
 		<link
 			rel="apple-touch-icon"
 			sizes="180x180"

+ 21 - 0
frontend/src/App.css

@@ -13,6 +13,15 @@
 	--tblr-backdrop-opacity: 0.8 !important;
 }
 
+[data-bs-theme="dark"] .modal-content {
+	--tblr-modal-box-shadow: 0 0.5rem 1rem rgba(0, 0, 0, 0.15) !important;
+}
+
+[data-bs-theme="dark"] .modal-backdrop {
+	--tblr-backdrop-bg: #000 !important;
+	--tblr-backdrop-opacity: 0.65 !important;
+}
+
 .domain-name {
 	font-family: monospace;
 }
@@ -95,3 +104,15 @@ label.row {
 		border-radius: var(--tblr-border-radius) 0 0 var(--tblr-border-radius);
 	}
 }
+
+/* Fix for dropdown menus being clipped by table-responsive containers. */
+.table-responsive .dropdown {
+	position: static;
+}
+
+/* Fix for Tabler scrollbar compensation */
+@media (min-width: 992px) {
+  :host, :root {
+    margin-left: 0;
+  }
+}

+ 9 - 2
frontend/src/components/LocalePicker.tsx

@@ -5,7 +5,11 @@ import { useTheme } from "src/hooks";
 import { changeLocale, getFlagCodeForLocale, localeOptions, T } from "src/locale";
 import styles from "./LocalePicker.module.css";
 
-function LocalePicker() {
+interface Props {
+	menuAlign?: "start" | "end";
+}
+
+function LocalePicker({ menuAlign = "start" }: Props) {
 	const { locale, setLocale } = useLocaleState();
 	const { getTheme } = useTheme();
 
@@ -23,7 +27,10 @@ function LocalePicker() {
 			<button type="button" className={cns} data-bs-toggle="dropdown">
 				<Flag countryCode={getFlagCodeForLocale(locale)} />
 			</button>
-			<div className="dropdown-menu">
+			<div className={cn("dropdown-menu", {
+				"dropdown-menu-end": menuAlign === "end",
+			})}
+			>
 				{localeOptions.map((item) => {
 					return (
 						<a

+ 1 - 1
frontend/src/components/SiteContainer.tsx

@@ -2,5 +2,5 @@ interface Props {
 	children: React.ReactNode;
 }
 export function SiteContainer({ children }: Props) {
-	return <div className="container-xl py-3">{children}</div>;
+	return <div className="container-xl py-3 min-w-0 overflow-x-auto">{children}</div>;
 }

+ 19 - 3
frontend/src/components/SiteHeader.tsx

@@ -25,7 +25,7 @@ export function SiteHeader() {
 				>
 					<span className="navbar-toggler-icon" />
 				</button>
-				<div className="navbar-brand navbar-brand-autodark d-none-navbar-horizontal pe-0 pe-md-3">
+				<div className="navbar-brand navbar-brand-autodark pe-0 pe-md-3">
 					<NavLink to="/">
 						<div className={styles.logo}>
 							<img
@@ -48,11 +48,11 @@ export function SiteHeader() {
 							<ThemeSwitcher />
 						</div>
 					</div>
-					<div className="nav-item d-none d-md-flex me-3">
+					<div className="nav-item d-md-flex">
 						<div className="nav-item dropdown">
 							<a
 								href="/"
-								className="nav-link d-flex lh-1 p-0 px-2"
+								className="nav-link d-flex lh-1"
 								data-bs-toggle="dropdown"
 								aria-label="Open user menu"
 							>
@@ -70,6 +70,22 @@ export function SiteHeader() {
 								</div>
 							</a>
 							<div className="dropdown-menu dropdown-menu-end dropdown-menu-arrow">
+								<div className="d-md-none">
+									{/* biome-ignore lint/a11y/noStaticElementInteractions lint/a11y/useKeyWithClickEvents: This div is not interactive. */}
+									<div className="p-2 pb-1 pe-1 d-flex align-items-center" onClick={e => e.stopPropagation()}>
+										<div className="ps-2 pe-1 me-auto">
+											<div>{currentUser?.nickname}</div>
+											<div className="mt-1 small text-secondary text-nowrap">
+												<T id={isAdmin ? "role.admin" : "role.standard-user"} />
+											</div>
+										</div>
+										<div className="d-flex align-items-center">
+											<ThemeSwitcher className="me-n2" />
+											<LocalePicker menuAlign="end" />
+										</div>
+									</div>
+									<div className="dropdown-divider" />
+								</div>
 								<a
 									href="?"
 									className="dropdown-item"

+ 8 - 12
frontend/src/components/SiteMenu.tsx

@@ -176,17 +176,13 @@ const getMenuDropown = (item: MenuItem, onClick?: () => void) => {
 };
 
 export function SiteMenu() {
-	// This is hacky AF. But that's the price of using a non-react UI kit.
-	const closeMenus = () => {
-		const navMenus = document.querySelectorAll(".nav-item.dropdown");
-		navMenus.forEach((menu) => {
-			menu.classList.remove("show");
-			const dropdown = menu.querySelector(".dropdown-menu");
-			if (dropdown) {
-				dropdown.classList.remove("show");
-			}
-		});
-	};
+	const closeMenu = () => setTimeout(() => {
+		const navbarToggler = document.querySelector<HTMLElement>(".navbar-toggler");
+		const navbarMenu = document.querySelector("#navbar-menu");
+		if (navbarToggler && navbarMenu?.classList.contains("show")) {
+			navbarToggler.click();
+		}
+	}, 300);
 
 	return (
 		<header className="navbar-expand-md">
@@ -198,7 +194,7 @@ export function SiteMenu() {
 								<ul className="navbar-nav">
 									{menuItems.length > 0 &&
 										menuItems.map((item) => {
-											return getMenuItem(item, closeMenus);
+											return getMenuItem(item, closeMenu);
 										})}
 								</ul>
 							</div>

+ 6 - 4
frontend/src/components/Table/TableLayout.tsx

@@ -12,10 +12,12 @@ interface TableLayoutProps<TFields> {
 function TableLayout<TFields>(props: TableLayoutProps<TFields>) {
 	const hasRows = props.tableInstance.getRowModel().rows.length > 0;
 	return (
-		<table className="table table-vcenter table-selectable mb-0">
-			{hasRows ? <TableHeader tableInstance={props.tableInstance} /> : null}
-			<TableBody {...props} />
-		</table>
+		<div className="table-responsive">
+			<table className="table table-vcenter table-selectable mb-0">
+				{hasRows ? <TableHeader tableInstance={props.tableInstance} /> : null}
+				<TableBody {...props} />
+			</table>
+		</div>
 	);
 }
 

+ 0 - 7
frontend/src/pages/Login/index.module.css

@@ -1,10 +1,3 @@
 .logo {
 	width: 200px;
 }
-
-.helperBtns {
-	position: absolute;
-	top: 10px;
-	right: 10px;
-	z-index: 1000;
-}

+ 5 - 6
frontend/src/pages/Login/index.tsx

@@ -1,4 +1,3 @@
-import cn from "classnames";
 import { Field, Form, Formik } from "formik";
 import { useEffect, useRef, useState } from "react";
 import Alert from "react-bootstrap/Alert";
@@ -43,17 +42,17 @@ export default function Login() {
 
 	return (
 		<Page className="page page-center">
-			<div className={cn("d-none", "d-md-flex", styles.helperBtns)}>
-				<LocalePicker />
-				<ThemeSwitcher />
-			</div>
 			<div className="container container-tight py-4">
-				<div className="text-center mb-4">
+				<div className="d-flex justify-content-between align-items-center mb-4 ps-4 pe-3">
 					<img
 						className={styles.logo}
 						src="/images/logo-text-horizontal-grey.png"
 						alt="Nginx Proxy Manager"
 					/>
+					<div className="d-flex align-items-center gap-1">
+						<LocalePicker />
+						<ThemeSwitcher />
+					</div>
 				</div>
 				<div className="card card-md">
 					<div className="card-body">