now left menu in admin panel can be minimized
This commit is contained in:
parent
db40f100cd
commit
eb69c8500c
8 changed files with 119 additions and 16 deletions
|
|
@ -5,17 +5,20 @@ export function AppLink({
|
|||
href,
|
||||
navigate,
|
||||
className,
|
||||
title,
|
||||
children
|
||||
}: {
|
||||
href: string;
|
||||
navigate: Navigate;
|
||||
className?: string;
|
||||
title?: string;
|
||||
children: ReactNode;
|
||||
}) {
|
||||
return (
|
||||
<a
|
||||
className={className}
|
||||
href={href}
|
||||
title={title}
|
||||
onClick={(event) => {
|
||||
event.preventDefault();
|
||||
navigate(href);
|
||||
|
|
|
|||
|
|
@ -9,6 +9,8 @@ import {
|
|||
LogOut,
|
||||
Megaphone,
|
||||
MessageSquareText,
|
||||
PanelLeftClose,
|
||||
PanelLeftOpen,
|
||||
Settings,
|
||||
UserCog,
|
||||
UserRound,
|
||||
|
|
@ -113,6 +115,29 @@ export function Shell({
|
|||
// sections are granted independently, so one entry can be visible without the other.
|
||||
const canReviewBotVerification = useCan(permissionBotVerificationReview);
|
||||
const canManageServer = useCan(permissionServerManage);
|
||||
// Remembered per browser: an operator who works in a narrow window should not
|
||||
// have to re-collapse the navigation on every visit. A failed read (private
|
||||
// mode, blocked storage) just means the default.
|
||||
const [navCollapsed, setNavCollapsed] = useState(() => {
|
||||
try {
|
||||
return localStorage.getItem("owpengram.nav.collapsed") === "1";
|
||||
} catch {
|
||||
return false;
|
||||
}
|
||||
});
|
||||
|
||||
function toggleNav() {
|
||||
setNavCollapsed((current) => {
|
||||
const next = !current;
|
||||
try {
|
||||
localStorage.setItem("owpengram.nav.collapsed", next ? "1" : "0");
|
||||
} catch {
|
||||
// Not being able to remember the choice is not a reason to refuse it.
|
||||
}
|
||||
return next;
|
||||
});
|
||||
}
|
||||
|
||||
const [addServerLinkOpen, setAddServerLinkOpen] = useState(false);
|
||||
const [connecting, setConnecting] = useState(false);
|
||||
const [connectError, setConnectError] = useState("");
|
||||
|
|
@ -221,7 +246,7 @@ export function Shell({
|
|||
}
|
||||
|
||||
return (
|
||||
<div className="shell">
|
||||
<div className={`shell ${navCollapsed ? "shell--nav-collapsed" : ""}`.trim()}>
|
||||
<aside className="sidebar">
|
||||
<AppLink className="brand" href="/" navigate={navigate}>
|
||||
<span className="brand-mark"><img src={brandIconSrc} alt={brandName} onError={() => setBrandIconFailed(true)} /></span>
|
||||
|
|
@ -326,7 +351,17 @@ export function Shell({
|
|||
</aside>
|
||||
<div className="workspace">
|
||||
<header className="topbar">
|
||||
<div>
|
||||
<div className="topbar-lead">
|
||||
<button
|
||||
className="icon-btn nav-toggle"
|
||||
type="button"
|
||||
onClick={toggleNav}
|
||||
aria-expanded={!navCollapsed}
|
||||
aria-label={navCollapsed ? "Expand navigation" : "Collapse navigation"}
|
||||
title={navCollapsed ? "Expand navigation" : "Collapse navigation"}
|
||||
>
|
||||
{navCollapsed ? <PanelLeftOpen size={16} /> : <PanelLeftClose size={16} />}
|
||||
</button>
|
||||
<h1>{routeTitle(route.path)}</h1>
|
||||
</div>
|
||||
<div className="topbar-actions">
|
||||
|
|
@ -363,9 +398,16 @@ function NavLink({
|
|||
}) {
|
||||
const active = activeWhen ? activeWhen(route.path) : href === "/" ? route.path === "/" : route.path.startsWith(href);
|
||||
return (
|
||||
<AppLink className={`nav-item ${active ? "active" : ""}`} href={href} navigate={navigate}>
|
||||
<AppLink
|
||||
className={`nav-item ${active ? "active" : ""}`}
|
||||
href={href}
|
||||
navigate={navigate}
|
||||
// The label is hidden when the sidebar is collapsed, so it moves to the
|
||||
// tooltip -- an icon rail with no names is a memory test.
|
||||
title={typeof children === "string" ? children : undefined}
|
||||
>
|
||||
{icon ?? <span aria-hidden="true" className="nav-dot" />}
|
||||
<span>{children}</span>
|
||||
<span className="nav-item-label">{children}</span>
|
||||
</AppLink>
|
||||
);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -506,3 +506,61 @@ a {
|
|||
text-transform: uppercase;
|
||||
letter-spacing: 0.04em;
|
||||
}
|
||||
|
||||
/* Collapsible navigation.
|
||||
Scoped to the width where the sidebar is actually a sidebar: below 1120px the
|
||||
shell already stacks it into a horizontal strip (05-responsive.css), and
|
||||
collapsing that would just hide the labels of a bar that has room for them. */
|
||||
@media (min-width: 1121px) {
|
||||
.shell--nav-collapsed {
|
||||
grid-template-columns: 68px minmax(0, 1fr);
|
||||
}
|
||||
|
||||
/* Everything that only makes sense with room for words goes away: the
|
||||
wordmark beside the logo, the "Navigation" heading, the version block and
|
||||
the Connect/Share buttons. The icons and their tooltips remain. */
|
||||
.shell--nav-collapsed .brand > span:not(.brand-mark),
|
||||
.shell--nav-collapsed .sidebar-label,
|
||||
.shell--nav-collapsed .sidebar-status,
|
||||
.shell--nav-collapsed .sidebar-server-actions,
|
||||
.shell--nav-collapsed .nav-item-label {
|
||||
display: none;
|
||||
}
|
||||
|
||||
.shell--nav-collapsed .sidebar {
|
||||
padding: 18px 10px;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.shell--nav-collapsed .brand {
|
||||
justify-content: center;
|
||||
padding: 0;
|
||||
}
|
||||
|
||||
/* One column instead of icon + label, so the glyph sits centred in the rail
|
||||
rather than clinging to the left edge where the text used to start. */
|
||||
.shell--nav-collapsed .nav-item {
|
||||
grid-template-columns: 1fr;
|
||||
justify-items: center;
|
||||
padding: 0;
|
||||
width: 44px;
|
||||
}
|
||||
|
||||
.shell--nav-collapsed .nav-list {
|
||||
justify-items: center;
|
||||
}
|
||||
}
|
||||
|
||||
/* The toggle sits with the page title rather than inside the sidebar: it has to
|
||||
stay reachable at 68px wide, and a control that hides with the thing it
|
||||
controls is a trap. */
|
||||
.topbar-lead {
|
||||
display: flex;
|
||||
min-width: 0;
|
||||
align-items: center;
|
||||
gap: 12px;
|
||||
}
|
||||
|
||||
.nav-toggle {
|
||||
flex: 0 0 auto;
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue