This commit is contained in:
ponzischeme89
2026-08-24 09:16:19 +12:00
parent 30155e1c5f
commit 067395a362
38 changed files with 104 additions and 60756 deletions
+7 -30
View File
@@ -1,5 +1,5 @@
import { useEffect, useRef, useState } from 'react';
import { matchPath, NavLink, Outlet, useLocation } from 'react-router-dom';
import { NavLink, Outlet, useLocation } from 'react-router-dom';
import { Icon } from './Icon';
import { OmniSearch } from './OmniSearch';
import { NotificationBell } from './NotificationBell';
@@ -16,13 +16,12 @@ import { Confirm } from './ui';
function Rail({ open, onNavigate }: { open: boolean; onNavigate: () => void }) {
const { unread } = useNotifications();
const location = useLocation();
// A group holding the current page is open regardless of what was collapsed last time:
// a rail that hides the page you are on is a rail that has lost its place.
// Every category is visible. Children can be folded independently and the choice is
// remembered between visits.
const [collapsed, setCollapsed] = useState<Record<string, boolean>>(() => {
try {
return JSON.parse(localStorage.getItem('memby-admin-nav') ?? '{}') as Record<string, boolean>;
return JSON.parse(localStorage.getItem('memby-admin-nav-v2') ?? '{}') as Record<string, boolean>;
} catch {
return {};
}
@@ -30,37 +29,18 @@ function Rail({ open, onNavigate }: { open: boolean; onNavigate: () => void }) {
const storeCollapsed = (next: Record<string, boolean>) => {
try {
localStorage.setItem('memby-admin-nav', JSON.stringify(next));
localStorage.setItem('memby-admin-nav-v2', JSON.stringify(next));
} catch {
// Private-mode storage refusals cost the memory of which group is open, and
// nothing else.
}
};
// The labelled groups are an accordion. Following a destination opens its group and
// folds the one the operator has just left, including navigation from search or Back.
useEffect(() => {
const activeGroup = nav.find((group) =>
group.items.some((item) => matchPath({ path: item.path, end: true }, location.pathname)),
);
if (!activeGroup) return;
setCollapsed((current) => {
const next = { ...current };
nav.forEach((group) => {
if (group.collapsible !== false) next[group.id] = group.id !== activeGroup.id;
});
storeCollapsed(next);
return next;
});
}, [location.pathname]);
const toggle = (id: string, defaultCollapsed = false) => {
setCollapsed((current) => {
const next = { ...current };
const collapseSelected = !(current[id] ?? defaultCollapsed);
nav.forEach((group) => {
if (group.collapsible !== false) next[group.id] = group.id === id ? collapseSelected : true;
});
next[id] = collapseSelected;
storeCollapsed(next);
return next;
});
@@ -71,11 +51,8 @@ function Rail({ open, onNavigate }: { open: boolean; onNavigate: () => void }) {
{nav.map((group) => {
const items = group.items.filter((item) => !item.hidden);
if (items.length === 0) return null;
const holdsCurrent = items.some((item) =>
matchPath({ path: item.path, end: true }, location.pathname),
);
const collapsible = group.collapsible !== false;
const expanded = holdsCurrent || !collapsible || !(collapsed[group.id] ?? group.defaultCollapsed ?? false);
const expanded = !collapsible || !(collapsed[group.id] ?? group.defaultCollapsed ?? false);
return (
<div className="rail-group" key={group.id}>
{group.label && collapsible ? (