Seitenleiste: die vier Bestandslisten klappen zusammen
Lebensmittel, Gegenstaende, Einzelstuecke und Chargen stehen jetzt unter einer Gruppe "Listen". Sie ist der laengste Block der Leiste und wird im Alltag selten gebraucht - wer einlagert, nimmt die Knoepfe darueber. Vorgabe ist zugeklappt, der Zustand bleibt im Browser (wie die Spalteneinstellungen der Tabellen). Steht man auf einer dieser Seiten, klappt die Gruppe auf, ohne den gemerkten Zustand zu ueberschreiben - sonst saehe man nicht, wo man ist. Die Eintraege bleiben dabei im Dokument und werden nur per Klasse verborgen: Auf schmalen Bildschirmen wird die Leiste waagerecht, dort gibt es keine Abschnitte und kein Platzproblem, also blendet die CSS sie wieder ein und die Gruppe selbst aus. Waeren sie ausgehaengt, waeren sie dort weg. Die Mobil-Regeln brauchen `.sidebar` davor: Ein Media-Query erhoeht die Spezifitaet nicht, und die Grundregeln stehen weiter unten in der Datei - ohne das gewinnt die spaetere Regel und die Ausnahme greift nie. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Opus 5
parent
afacf01f11
commit
a182037153
+50
-4
@@ -34,6 +34,17 @@ import Konto from "./pages/Konto";
|
|||||||
|
|
||||||
const navClass = ({ isActive }) => (isActive ? "nav-link active" : "nav-link");
|
const navClass = ({ isActive }) => (isActive ? "nav-link active" : "nav-link");
|
||||||
|
|
||||||
|
// Die vier Bestandslisten stehen unter einer zuklappbaren Gruppe. Sie sind der
|
||||||
|
// laengste Block der Leiste und wird im Alltag selten gebraucht – wer einlagert,
|
||||||
|
// nutzt die Knoepfe darueber.
|
||||||
|
const LISTEN = [
|
||||||
|
{ to: "/lebensmittel", icon: "food", label: "Lebensmittel" },
|
||||||
|
{ to: "/gegenstaende", icon: "package", label: "Gegenstände" },
|
||||||
|
{ to: "/items", icon: "tag", label: "Einzelstücke" },
|
||||||
|
{ to: "/chargen", icon: "box", label: "Chargen" },
|
||||||
|
];
|
||||||
|
const LISTEN_OFFEN_KEY = "nav:listen-offen";
|
||||||
|
|
||||||
function NavItem({ to, icon, label, end }) {
|
function NavItem({ to, icon, label, end }) {
|
||||||
return (
|
return (
|
||||||
<NavLink to={to} end={end} className={navClass}>
|
<NavLink to={to} end={end} className={navClass}>
|
||||||
@@ -46,7 +57,24 @@ function NavItem({ to, icon, label, end }) {
|
|||||||
function Sidebar() {
|
function Sidebar() {
|
||||||
const { user, isAdmin, logout } = useAuth();
|
const { user, isAdmin, logout } = useAuth();
|
||||||
const navigate = useNavigate();
|
const navigate = useNavigate();
|
||||||
|
const { pathname } = useLocation();
|
||||||
const [dashboards, setDashboards] = useState([]);
|
const [dashboards, setDashboards] = useState([]);
|
||||||
|
// Zugeklappt als Vorgabe; der Zustand bleibt im Browser, wie die
|
||||||
|
// Spalteneinstellungen der Tabellen.
|
||||||
|
const [listenOffen, setListenOffen] = useState(
|
||||||
|
() => localStorage.getItem(LISTEN_OFFEN_KEY) === "1",
|
||||||
|
);
|
||||||
|
// Steht man auf einer dieser Seiten, ist die Gruppe offen – ohne den
|
||||||
|
// gemerkten Zustand zu ueberschreiben. Sonst saehe man nicht, wo man ist.
|
||||||
|
const aufEinerListe = LISTEN.some((e) => pathname.startsWith(e.to));
|
||||||
|
|
||||||
|
function klappeListen() {
|
||||||
|
setListenOffen((alt) => {
|
||||||
|
const neu = !alt;
|
||||||
|
localStorage.setItem(LISTEN_OFFEN_KEY, neu ? "1" : "0");
|
||||||
|
return neu;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
// Die Dashboards des Benutzers stehen als eigene Eintraege in der Leiste.
|
// Die Dashboards des Benutzers stehen als eigene Eintraege in der Leiste.
|
||||||
// Sie aendern sich beim Anlegen und Loeschen, deshalb horcht die Leiste auf
|
// Sie aendern sich beim Anlegen und Loeschen, deshalb horcht die Leiste auf
|
||||||
@@ -82,10 +110,28 @@ function Sidebar() {
|
|||||||
))}
|
))}
|
||||||
<NavItem to="/checkin" icon="checkin" label="Einlagern" />
|
<NavItem to="/checkin" icon="checkin" label="Einlagern" />
|
||||||
<NavItem to="/checkout" icon="checkout" label="Auslagern" />
|
<NavItem to="/checkout" icon="checkout" label="Auslagern" />
|
||||||
<NavItem to="/lebensmittel" icon="food" label="Lebensmittel" />
|
{/* Auf schmalen Bildschirmen wird die Leiste waagerecht; dort gibt es
|
||||||
<NavItem to="/gegenstaende" icon="package" label="Gegenstände" />
|
keine Abschnitte (styles.css) und die Eintraege stehen flach
|
||||||
<NavItem to="/items" icon="tag" label="Einzelstücke" />
|
nebeneinander – zuklappen loest dort kein Platzproblem. */}
|
||||||
<NavItem to="/chargen" icon="box" label="Chargen" />
|
<button type="button" className="nav-group" onClick={klappeListen}
|
||||||
|
aria-expanded={listenOffen || aufEinerListe}>
|
||||||
|
<Icon name="chevron" size={14}
|
||||||
|
className={listenOffen || aufEinerListe ? "" : "rot-90"} />
|
||||||
|
<span>Listen</span>
|
||||||
|
</button>
|
||||||
|
{LISTEN.map((e) => (
|
||||||
|
<NavLink key={e.to} to={e.to}
|
||||||
|
className={({ isActive }) => [
|
||||||
|
"nav-link nav-child",
|
||||||
|
isActive ? "active" : "",
|
||||||
|
// Nicht ausgehaengt, nur verborgen: auf schmalen Bildschirmen
|
||||||
|
// blendet die CSS sie wieder ein, dort gibt es keine Gruppe.
|
||||||
|
listenOffen || aufEinerListe ? "" : "nav-zu",
|
||||||
|
].filter(Boolean).join(" ")}>
|
||||||
|
<Icon name={e.icon} size={16} />
|
||||||
|
<span className="label">{e.label}</span>
|
||||||
|
</NavLink>
|
||||||
|
))}
|
||||||
<NavItem to="/shopping" icon="cart" label="Einkaufsliste" />
|
<NavItem to="/shopping" icon="cart" label="Einkaufsliste" />
|
||||||
<NavItem to="/mindestbestaende" icon="check" label="Mindestbestände" />
|
<NavItem to="/mindestbestaende" icon="check" label="Mindestbestände" />
|
||||||
<NavItem to="/verliehen" icon="loan" label="Verliehen" />
|
<NavItem to="/verliehen" icon="loan" label="Verliehen" />
|
||||||
|
|||||||
@@ -1036,6 +1036,15 @@ select.zeitraum:hover { color: var(--text); }
|
|||||||
.sidebar-brand { padding: var(--sp-3) var(--sp-4); }
|
.sidebar-brand { padding: var(--sp-3) var(--sp-4); }
|
||||||
.sidebar-nav { flex-direction: row; overflow-x: auto; padding: var(--sp-2) var(--sp-3); gap: var(--sp-1); }
|
.sidebar-nav { flex-direction: row; overflow-x: auto; padding: var(--sp-2) var(--sp-3); gap: var(--sp-1); }
|
||||||
.nav-section { display: none; }
|
.nav-section { display: none; }
|
||||||
|
/* Waagerechte Leiste: Zuklappen loest hier kein Platzproblem, und
|
||||||
|
untereinander ist ohnehin kein Platz. Die Gruppe verschwindet, ihre
|
||||||
|
Eintraege stehen flach zwischen den anderen – wie vorher.
|
||||||
|
Mit `.sidebar` davor, weil die Grundregeln weiter unten in der Datei
|
||||||
|
stehen: ein Media-Query erhoeht die Spezifitaet nicht, und bei
|
||||||
|
Gleichstand gewinnt die spaetere Regel. */
|
||||||
|
.sidebar .nav-group { display: none; }
|
||||||
|
.sidebar .nav-link.nav-zu { display: flex; }
|
||||||
|
.sidebar .nav-link.nav-child { padding-left: 8px; }
|
||||||
.nav-link { white-space: nowrap; }
|
.nav-link { white-space: nowrap; }
|
||||||
.nav-link span.label { display: none; }
|
.nav-link span.label { display: none; }
|
||||||
.nav-link { padding: 8px; }
|
.nav-link { padding: 8px; }
|
||||||
@@ -1088,6 +1097,21 @@ select.zeitraum:hover { color: var(--text); }
|
|||||||
}
|
}
|
||||||
.nav-link.sub.active { font-weight: 600; }
|
.nav-link.sub.active { font-weight: 600; }
|
||||||
|
|
||||||
|
/* Zuklappbare Gruppe in der Seitenleiste: sieht aus wie eine
|
||||||
|
Abschnittsueberschrift, ist aber ein Knopf. */
|
||||||
|
.nav-group {
|
||||||
|
display: flex; align-items: center; gap: var(--sp-2);
|
||||||
|
width: 100%; background: none; border: none; cursor: pointer;
|
||||||
|
padding: var(--sp-4) var(--sp-3) var(--sp-1);
|
||||||
|
font-size: 0.68rem; font-weight: 600; letter-spacing: 0.06em;
|
||||||
|
text-transform: uppercase; color: var(--muted); opacity: 0.7;
|
||||||
|
font-family: inherit; text-align: left;
|
||||||
|
}
|
||||||
|
.nav-group:hover { opacity: 1; }
|
||||||
|
/* Eingerueckt wie die Dashboard-Unterpunkte, aber mit Platz fuers Symbol. */
|
||||||
|
.nav-link.nav-child { padding-left: 26px; font-size: 0.86rem; }
|
||||||
|
.nav-link.nav-zu { display: none; }
|
||||||
|
|
||||||
/* ---- Aktionskarten: Ein- und Auslagern ohne Seitenwechsel ---- */
|
/* ---- Aktionskarten: Ein- und Auslagern ohne Seitenwechsel ---- */
|
||||||
.aktion-karte { gap: 10px; }
|
.aktion-karte { gap: 10px; }
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user