sidebar working as expected

modalbox working as expected
This commit is contained in:
2025-06-17 20:39:02 +00:00
parent f1d9d05519
commit dc7fa6b2cb
4 changed files with 99 additions and 90 deletions

View File

@@ -2,7 +2,7 @@
import sys
sys.path.append('/workspace')
from dash import Dash, html, dcc, page_container
from dash import Dash, html, dcc, page_container, Output, Input, State, callback
from flask import Flask
import dash_bootstrap_components as dbc
import dash_mantine_components as dmc
@@ -29,11 +29,13 @@ app = Dash(
app.layout = dmc.MantineProvider([
Header(),
html.Div([
html.Div(id="sidebar", className="sidebar"),
html.Div(id="sidebar"), # KEINE className="sidebar" hier!
html.Div(page_container, className="page-content"),
dcc.Store(id="sidebar-state", data={"collapsed": False}),
], style={"display": "flex"}),
])
if __name__ == "__main__":
app.run(host="0.0.0.0", port=8050, debug=(ENV=="development"))

View File

@@ -1,6 +1,14 @@
/* ==========================
Allgemeines Layout
========================== */
:root {
--mb-z-index: 2000 !important;
--mantine-z-index-popover: 2100 !important;
--mantine-z-index-overlay: 2999 !important;
--mantine-z-index-dropdown: 2100 !important;
--mantine-z-index-max: 9999 !important;
--mantine-z-index-modal: 3000 !important;
}
body {
margin: 0;
font-family: "Segoe UI", Tahoma, Geneva, Verdana, sans-serif;
@@ -15,6 +23,7 @@ body {
min-width: 0; /* verhindert Überlauf bei zu breitem Inhalt */
transition: margin-left 0.3s ease;
min-height: calc(100vh - 60px); /* Mindesthöhe minus Header-Höhe */
margin-left: 220px; /* <--- Ergänzen */
}
/* Wenn Sidebar collapsed ist, reduziere margin-left */
@@ -73,7 +82,7 @@ body {
top: 60px; /* Den gleichen Wert wie Header-Höhe verwenden */
left: 0;
z-index: 1000;
position: relative; /* oder fixed, je nach Layout */
position: fixed; /* <--- Ändere das von relative zu fixed */
overflow-x: hidden;
overflow-y: auto;
}
@@ -164,10 +173,10 @@ body {
/* ==========================
Responsive (bei Bedarf)
========================== */
@media (max-width: 768px) {
/* @media (max-width: 768px) {
body {
padding-top: 60px; /* Header-Platz auch auf mobilen Geräten */
}
/* }
.sidebar {
position: fixed;
@@ -186,34 +195,36 @@ body {
.sidebar.collapsed ~ .page-content {
margin-left: 0;
}
}
/* :root {
--mb-z-index: 2000 !important;
--mantine-z-index-popover: 2100 !important;
--mantine-z-index-overlay: 2100 !important;
--mantine-z-index-dropdown: 2100 !important;
--mantine-z-index-max: 9999 !important;
}
.mantine-Modal-root,
.mantine-Modal-modal,
.mantine-Modal-overlay {
z-index: 3000 !important;
}
.mantine-Modal-overlay {
z-index: 2000 !important;
}
} */
.mantine-Modal-modal {
z-index: 2100 !important;
z-index: var(--mantine-z-index-modal, 3000) !important;
}
.mantine-Popover-dropdown,
.mantine-DatePicker-dropdown,
.mantine-Select-dropdown {
z-index: 2200 !important;
} */
.mantine-Modal-inner,
.mantine-Modal-content {
z-index: 4000 !important;
}
/* Sidebar collapsed: Icon-Farbe normal */
.sidebar.collapsed .sidebar-item-collapsed svg {
color: #7c5617; /* Icon-Linie/Text */
fill: #e4d5c1; /* Icon-Fläche */
width: 24px;
height: 24px;
margin: 0 auto;
display: block;
transition: color 0.2s, fill 0.2s;
}
/* Sidebar collapsed: Hintergrund und Icon invertieren bei Hover/Active */
.sidebar.collapsed .nav-link:hover,
.sidebar.collapsed .nav-link.active {
background-color: #7c5617 !important;
}
.sidebar.collapsed .nav-link:hover svg,
.sidebar.collapsed .nav-link.active svg {
color: #e4d5c1; /* Icon-Linie/Text invertiert */
fill: #7c5617; /* Icon-Fläche invertiert */
}

View File

@@ -3,29 +3,24 @@
from dash import Input, Output, State, callback
from components.sidebar import Sidebar
# 1) Toggle-Callback: Umschalten von collapsed = False ↔ True
@callback(
Output("sidebar", "children"),
Output("sidebar", "className"),
Input("sidebar-state", "data"),
)
def render_sidebar(data):
collapsed = data.get("collapsed", False)
return Sidebar(collapsed=collapsed), f"sidebar{' collapsed' if collapsed else ''}"
@callback(
Output("sidebar-state", "data"),
Input("btn-toggle-sidebar", "n_clicks"),
State("sidebar-state", "data"),
prevent_initial_call=True
prevent_initial_call=True,
)
def toggle_sidebar(n_clicks, state):
# Wenn der Button geklickt wurde, invertiere den collapsed-Wert
collapsed = state.get("collapsed", False)
return {"collapsed": not collapsed}
# 2) Render-Callback: Zeichnet die Sidebar neu und setzt die CSS-Klasse
@callback(
[Output("sidebar", "children"), Output("sidebar", "className")],
Input("sidebar-state", "data")
)
def render_sidebar(state):
collapsed = state.get("collapsed", False)
sidebar_class = "sidebar collapsed" if collapsed else "sidebar"
# Sidebar() gibt jetzt nur den Inhalt zurück
sidebar_content = Sidebar(collapsed=collapsed)
return sidebar_content, sidebar_class
def toggle_sidebar(n, data):
if n is None:
# Kein Klick, nichts ändern!
return data
collapsed = not data.get("collapsed", False)
return {"collapsed": collapsed}

View File

@@ -3,9 +3,8 @@
from dash import html
import dash_bootstrap_components as dbc
from dash_iconify import DashIconify
from typing import List, Any
def Sidebar(collapsed: bool = False) -> List[Any]:
def Sidebar(collapsed: bool = False):
"""
Gibt nur den Inhalt der Sidebar zurück (ohne das äußere div mit id="sidebar").
Das äußere div wird bereits in app.py definiert.
@@ -14,50 +13,37 @@ def Sidebar(collapsed: bool = False) -> List[Any]:
nav_items = [
{"label": "Übersicht", "href": "/overview", "icon": "mdi:view-dashboard"},
{"label": "Termine", "href": "/appointments","icon": "mdi:calendar"},
{"label": "Bildschirme", "href": "/clients", "icon": "mdi:monitor"},
{"label": "Bildschirme", "href": "/clients", "icon": "mdi:monitor"},
{"label": "Einstellungen","href": "/settings", "icon": "mdi:cog"},
{"label": "Benutzer", "href": "/users", "icon": "mdi:account"},
]
nav_links = []
for item in nav_items:
# Die ID muss in den Callbacks exakt so referenziert werden:
link_id = {"type": "nav-item", "index": item["label"]}
# ✅ NavLink erstellen
nav_link = dbc.NavLink(
[
if collapsed:
nav_links = [
dbc.NavLink(
DashIconify(icon=item["icon"], width=24),
html.Span(item["label"], className="ms-2 sidebar-label"),
],
href=item["href"],
active="exact",
className="sidebar-item",
id=link_id,
)
# ✅ Conditional List Construction - keine append() nötig
if collapsed:
tooltip = dbc.Tooltip(
item["label"],
target=link_id,
placement="right",
id=f"tooltip-{item['label']}",
href=item["href"],
active="exact",
className="sidebar-item-collapsed",
id={"type": "nav-item", "index": item["label"]},
)
link_components = [nav_link, tooltip]
else:
link_components = [nav_link]
# ✅ Alle Komponenten in einem div-Container
nav_links.append(
html.Div(
children=link_components,
className="nav-item-container"
for item in nav_items
]
else:
nav_links = [
dbc.NavLink(
[
DashIconify(icon=item["icon"], width=24),
html.Span(item["label"], className="ms-2 sidebar-label"),
],
href=item["href"],
active="exact",
className="sidebar-item",
id={"type": "nav-item", "index": item["label"]},
)
)
for item in nav_items
]
# Gib nur den Inhalt zurück, nicht das äußere div
return [
html.Div(
className="sidebar-toggle",
@@ -67,5 +53,20 @@ def Sidebar(collapsed: bool = False) -> List[Any]:
className="toggle-button",
)
),
dbc.Nav(nav_links, vertical=True, pills=True, className="sidebar-nav")
dbc.Collapse(
dbc.Nav(
nav_links,
vertical=True,
pills=True,
className="sidebar-nav",
),
is_open=not collapsed,
className="sidebar-nav",
) if not collapsed else
dbc.Nav(
nav_links,
vertical=True,
pills=True,
className="sidebar-nav-collapsed",
),
]