- Add floating pill-shaped sidebar with navigation icons - Implement dark/light theme toggle with localStorage persistence - Update all figure factories for transparent backgrounds - Use carto-darkmatter map style for choropleths - Add methodology link button to Toronto dashboard header - Add back to dashboard button on methodology page - Remove social links from home page (now in sidebar) - Update CLAUDE.md to Sprint 7 Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
180 lines
4.6 KiB
Python
180 lines
4.6 KiB
Python
"""Floating sidebar navigation component."""
|
|
|
|
import dash_mantine_components as dmc
|
|
from dash import dcc, html
|
|
from dash_iconify import DashIconify
|
|
|
|
# Navigation items configuration
|
|
NAV_ITEMS = [
|
|
{"path": "/", "icon": "tabler:home", "label": "Home"},
|
|
{"path": "/toronto", "icon": "tabler:map-2", "label": "Toronto Housing"},
|
|
]
|
|
|
|
# External links configuration
|
|
EXTERNAL_LINKS = [
|
|
{
|
|
"url": "https://github.com/leomiranda",
|
|
"icon": "tabler:brand-github",
|
|
"label": "GitHub",
|
|
},
|
|
{
|
|
"url": "https://linkedin.com/in/leobmiranda",
|
|
"icon": "tabler:brand-linkedin",
|
|
"label": "LinkedIn",
|
|
},
|
|
]
|
|
|
|
|
|
def create_brand_logo() -> html.Div:
|
|
"""Create the brand initials logo."""
|
|
return html.Div(
|
|
dcc.Link(
|
|
"LM",
|
|
href="/",
|
|
className="sidebar-brand-link",
|
|
),
|
|
className="sidebar-brand",
|
|
)
|
|
|
|
|
|
def create_nav_icon(
|
|
icon: str,
|
|
label: str,
|
|
path: str,
|
|
current_path: str,
|
|
) -> dmc.Tooltip:
|
|
"""Create a navigation icon with tooltip.
|
|
|
|
Args:
|
|
icon: Iconify icon string.
|
|
label: Tooltip label.
|
|
path: Navigation path.
|
|
current_path: Current page path for active state.
|
|
|
|
Returns:
|
|
Tooltip-wrapped navigation icon.
|
|
"""
|
|
is_active = current_path == path or (path != "/" and current_path.startswith(path))
|
|
|
|
return dmc.Tooltip(
|
|
dcc.Link(
|
|
dmc.ActionIcon(
|
|
DashIconify(icon=icon, width=20),
|
|
variant="subtle" if not is_active else "filled",
|
|
size="lg",
|
|
radius="xl",
|
|
color="blue" if is_active else "gray",
|
|
className="nav-icon-active" if is_active else "",
|
|
),
|
|
href=path,
|
|
),
|
|
label=label,
|
|
position="right",
|
|
withArrow=True,
|
|
)
|
|
|
|
|
|
def create_theme_toggle(current_theme: str = "dark") -> dmc.Tooltip:
|
|
"""Create the theme toggle button.
|
|
|
|
Args:
|
|
current_theme: Current theme ('dark' or 'light').
|
|
|
|
Returns:
|
|
Tooltip-wrapped theme toggle icon.
|
|
"""
|
|
icon = "tabler:sun" if current_theme == "dark" else "tabler:moon"
|
|
label = "Switch to light mode" if current_theme == "dark" else "Switch to dark mode"
|
|
|
|
return dmc.Tooltip(
|
|
dmc.ActionIcon(
|
|
DashIconify(icon=icon, width=20, id="theme-toggle-icon"),
|
|
id="theme-toggle",
|
|
variant="subtle",
|
|
size="lg",
|
|
radius="xl",
|
|
color="gray",
|
|
),
|
|
label=label,
|
|
position="right",
|
|
withArrow=True,
|
|
)
|
|
|
|
|
|
def create_external_link(url: str, icon: str, label: str) -> dmc.Tooltip:
|
|
"""Create an external link icon with tooltip.
|
|
|
|
Args:
|
|
url: External URL.
|
|
icon: Iconify icon string.
|
|
label: Tooltip label.
|
|
|
|
Returns:
|
|
Tooltip-wrapped external link icon.
|
|
"""
|
|
return dmc.Tooltip(
|
|
dmc.Anchor(
|
|
dmc.ActionIcon(
|
|
DashIconify(icon=icon, width=20),
|
|
variant="subtle",
|
|
size="lg",
|
|
radius="xl",
|
|
color="gray",
|
|
),
|
|
href=url,
|
|
target="_blank",
|
|
),
|
|
label=label,
|
|
position="right",
|
|
withArrow=True,
|
|
)
|
|
|
|
|
|
def create_sidebar_divider() -> html.Div:
|
|
"""Create a horizontal divider for the sidebar."""
|
|
return html.Div(className="sidebar-divider")
|
|
|
|
|
|
def create_sidebar(current_path: str = "/", current_theme: str = "dark") -> html.Div:
|
|
"""Create the floating sidebar navigation.
|
|
|
|
Args:
|
|
current_path: Current page path for active state highlighting.
|
|
current_theme: Current theme for toggle icon state.
|
|
|
|
Returns:
|
|
Complete sidebar component.
|
|
"""
|
|
return html.Div(
|
|
[
|
|
# Brand logo
|
|
create_brand_logo(),
|
|
create_sidebar_divider(),
|
|
# Navigation icons
|
|
*[
|
|
create_nav_icon(
|
|
icon=item["icon"],
|
|
label=item["label"],
|
|
path=item["path"],
|
|
current_path=current_path,
|
|
)
|
|
for item in NAV_ITEMS
|
|
],
|
|
create_sidebar_divider(),
|
|
# Theme toggle
|
|
create_theme_toggle(current_theme),
|
|
create_sidebar_divider(),
|
|
# External links
|
|
*[
|
|
create_external_link(
|
|
url=link["url"],
|
|
icon=link["icon"],
|
|
label=link["label"],
|
|
)
|
|
for link in EXTERNAL_LINKS
|
|
],
|
|
],
|
|
className="floating-sidebar",
|
|
id="floating-sidebar",
|
|
)
|