feat(ui): responsive sidebar and full-width navigation cards #8

Merged
nprasad2077 merged 1 commits from dev into main 2026-09-18 00:27:16 +00:00
+77 -4
View File
@@ -8,6 +8,55 @@ from pathlib import Path
st.set_page_config(page_title="NBA Analytics", page_icon="🏀", layout="wide")
# Custom Responsive Sidebar & Navigation Styling
st.markdown("""
<style>
/* Sidebar Navigation Cards */
[data-testid="stSidebar"] [data-testid="stRadio"] > div {
gap: 0.35rem;
}
[data-testid="stSidebar"] [data-testid="stRadio"] label {
background-color: rgba(128, 128, 128, 0.06);
border: 1px solid rgba(128, 128, 128, 0.12);
border-radius: 8px;
padding: 0.6rem 0.85rem;
margin-bottom: 0.15rem;
cursor: pointer !important;
transition: all 0.15s ease-in-out;
width: 100%;
display: flex;
align-items: center;
user-select: none;
}
[data-testid="stSidebar"] [data-testid="stRadio"] label:hover {
background-color: rgba(128, 128, 128, 0.14);
border-color: rgba(255, 107, 0, 0.4);
transform: translateX(3px);
}
[data-testid="stSidebar"] [data-testid="stRadio"] label:has(input:checked),
[data-testid="stSidebar"] [data-testid="stRadio"] label[data-checked="true"] {
background: linear-gradient(90deg, rgba(255, 107, 0, 0.22), rgba(255, 107, 0, 0.06)) !important;
border-color: #ff6b00 !important;
font-weight: 600;
box-shadow: 0 0 10px rgba(255, 107, 0, 0.15);
}
/* Hide the default radio circle for cleaner button appearance */
[data-testid="stSidebar"] [data-testid="stRadio"] label input[type="radio"] {
accent-color: #ff6b00;
}
/* Season Selectbox Hit Area & Styling */
[data-testid="stSidebar"] [data-testid="stSelectbox"] div[data-baseweb="select"] {
cursor: pointer !important;
border-radius: 8px;
min-height: 42px;
}
[data-testid="stSidebar"] [data-testid="stSelectbox"] div[data-baseweb="select"]:hover {
border-color: #ff6b00;
}
</style>
""", unsafe_allow_html=True)
# ============================================================
# CONNECTION & QUERY INFRASTRUCTURE
# ============================================================
@@ -46,23 +95,47 @@ seasons = get_seasons()
season_options = seasons["season_start_year"].tolist()
season_labels = dict(zip(seasons["season_start_year"], seasons["season_display"]))
PAGE_OPTIONS = [
"Overview",
"Teams",
"Player Lab",
"Shot Charts",
"Head to Head",
"Game Trends",
"Quarter Analysis",
]
PAGE_ICONS = {
"Overview": "📊 Overview",
"Teams": "🛡️ Teams",
"Player Lab": "🧪 Player Lab",
"Shot Charts": "🎯 Shot Charts",
"Head to Head": "⚔️ Head to Head",
"Game Trends": "📈 Game Trends",
"Quarter Analysis": "⏱️ Quarter Analysis",
}
if "season" not in st.session_state:
st.session_state["season"] = season_options[0]
st.sidebar.title("🏀 NBA Analytics")
st.session_state["season"] = st.sidebar.selectbox(
# Direct key binding eliminates dropped clicks and rerun desync
st.sidebar.selectbox(
"Season",
season_options,
index=season_options.index(st.session_state["season"]),
format_func=lambda x: season_labels[x],
key="season",
format_func=lambda x: season_labels.get(x, str(x)),
)
st.sidebar.divider()
# Full-width clickable navigation cards
page = st.sidebar.radio(
"Navigate",
["Overview", "Teams", "Player Lab", "Shot Charts", "Head to Head", "Game Trends", "Quarter Analysis"],
PAGE_OPTIONS,
format_func=lambda x: PAGE_ICONS.get(x, x),
key="current_page",
)
# Convenience accessors