feat: implement top navigation bar (Task 2.1)
- Add chart_tab() component for chart type pills - Implement full top_bar() with logo, title, chart tabs, data freshness - Heritage Blue background with white text, 64px fixed height - Reactive data freshness indicator using rx.cond for loading states - Added Transitions import to styles
This commit is contained in:
@@ -51,14 +51,14 @@ cd pathways_app && timeout 60 python -m reflex run 2>&1 | head -30
|
|||||||
## Phase 2: Layout Components
|
## Phase 2: Layout Components
|
||||||
|
|
||||||
### 2.1 Top Navigation Bar
|
### 2.1 Top Navigation Bar
|
||||||
- [ ] Create `top_bar()` component:
|
- [x] Create `top_bar()` component:
|
||||||
- Logo (use existing NHS person logo from assets)
|
- Logo (use existing NHS person logo from assets)
|
||||||
- App title "HCD Analysis"
|
- App title "HCD Analysis"
|
||||||
- Chart type tabs/pills (Icicle active, placeholders for future charts)
|
- Chart type tabs/pills (Icicle active, placeholders for future charts)
|
||||||
- Data freshness indicator (right side): "12,450 records (2d ago)"
|
- Data freshness indicator (right side): "12,450 records (2d ago)"
|
||||||
- [ ] Style with Heritage Blue accents, clean typography
|
- [x] Style with Heritage Blue accents, clean typography
|
||||||
- [ ] Fixed height: 64px
|
- [x] Fixed height: 64px
|
||||||
- [ ] Verify renders correctly
|
- [x] Verify renders correctly
|
||||||
|
|
||||||
### 2.2 Filter Section
|
### 2.2 Filter Section
|
||||||
- [ ] Create `filter_section()` component with card styling
|
- [ ] Create `filter_section()` component with card styling
|
||||||
|
|||||||
+82
-16
@@ -13,6 +13,7 @@ from pathways_app.styles import (
|
|||||||
Spacing,
|
Spacing,
|
||||||
Radii,
|
Radii,
|
||||||
Shadows,
|
Shadows,
|
||||||
|
Transitions,
|
||||||
TOP_BAR_HEIGHT,
|
TOP_BAR_HEIGHT,
|
||||||
PAGE_MAX_WIDTH,
|
PAGE_MAX_WIDTH,
|
||||||
PAGE_PADDING,
|
PAGE_PADDING,
|
||||||
@@ -47,49 +48,114 @@ class AppState(rx.State):
|
|||||||
# Layout Components
|
# Layout Components
|
||||||
# =============================================================================
|
# =============================================================================
|
||||||
|
|
||||||
|
def chart_tab(label: str, chart_type: str, is_active: bool = False) -> rx.Component:
|
||||||
|
"""
|
||||||
|
Individual chart type tab/pill for top bar navigation.
|
||||||
|
|
||||||
|
Active state: White background with Heritage Blue text
|
||||||
|
Inactive state: Transparent with white text, hover shows Vibrant Blue background
|
||||||
|
"""
|
||||||
|
return rx.box(
|
||||||
|
rx.text(
|
||||||
|
label,
|
||||||
|
font_size=Typography.BODY_SMALL_SIZE,
|
||||||
|
font_weight="500",
|
||||||
|
color=Colors.HERITAGE_BLUE if is_active else Colors.WHITE,
|
||||||
|
font_family=Typography.FONT_FAMILY,
|
||||||
|
),
|
||||||
|
background_color=Colors.WHITE if is_active else "transparent",
|
||||||
|
padding_x=Spacing.LG,
|
||||||
|
padding_y=Spacing.SM,
|
||||||
|
border_radius=Radii.FULL,
|
||||||
|
cursor="pointer",
|
||||||
|
transition=f"background-color {Transitions.COLOR}",
|
||||||
|
_hover={
|
||||||
|
"background_color": Colors.WHITE if is_active else "rgba(255,255,255,0.15)",
|
||||||
|
},
|
||||||
|
# Future: on_click handler to switch chart type
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
def top_bar() -> rx.Component:
|
def top_bar() -> rx.Component:
|
||||||
"""
|
"""
|
||||||
Top navigation bar component.
|
Top navigation bar component.
|
||||||
|
|
||||||
Contains: Logo + App Name | Chart Type Tabs | Data Freshness Indicator
|
Contains: Logo + App Name | Chart Type Tabs | Data Freshness Indicator
|
||||||
Fixed height: 64px (from design system)
|
Fixed height: 64px (from design system)
|
||||||
|
Heritage Blue background with white text.
|
||||||
Will be fully implemented in Task 2.1.
|
|
||||||
"""
|
"""
|
||||||
return rx.box(
|
return rx.box(
|
||||||
rx.hstack(
|
rx.hstack(
|
||||||
# Left: Logo and title (placeholder)
|
# Left: Logo and App Title
|
||||||
rx.hstack(
|
rx.hstack(
|
||||||
|
rx.image(
|
||||||
|
src="/logo.png",
|
||||||
|
height="36px",
|
||||||
|
alt="NHS Logo",
|
||||||
|
),
|
||||||
rx.text(
|
rx.text(
|
||||||
"HCD Analysis",
|
"HCD Analysis",
|
||||||
font_size=Typography.H2_SIZE,
|
font_size=Typography.H2_SIZE,
|
||||||
font_weight=Typography.H2_WEIGHT,
|
font_weight=Typography.H2_WEIGHT,
|
||||||
color=Colors.WHITE,
|
color=Colors.WHITE,
|
||||||
font_family=Typography.FONT_FAMILY,
|
font_family=Typography.FONT_FAMILY,
|
||||||
|
letter_spacing="-0.01em",
|
||||||
),
|
),
|
||||||
align="center",
|
align="center",
|
||||||
spacing="3",
|
spacing="3",
|
||||||
),
|
),
|
||||||
# Center: Chart tabs (placeholder)
|
# Center: Chart Type Tabs
|
||||||
rx.hstack(
|
rx.hstack(
|
||||||
rx.text(
|
chart_tab("Icicle", "icicle", is_active=True),
|
||||||
"Icicle Chart",
|
chart_tab("Sankey", "sankey", is_active=False),
|
||||||
font_size=Typography.BODY_SIZE,
|
chart_tab("Timeline", "timeline", is_active=False),
|
||||||
color=Colors.WHITE,
|
spacing="2",
|
||||||
opacity="0.9",
|
align="center",
|
||||||
|
background_color="rgba(255,255,255,0.1)",
|
||||||
|
padding=Spacing.XS,
|
||||||
|
border_radius=Radii.FULL,
|
||||||
|
),
|
||||||
|
# Right: Data Freshness Indicator
|
||||||
|
rx.hstack(
|
||||||
|
rx.icon(
|
||||||
|
"database",
|
||||||
|
size=16,
|
||||||
|
color=Colors.SKY,
|
||||||
|
),
|
||||||
|
rx.vstack(
|
||||||
|
rx.text(
|
||||||
|
rx.cond(
|
||||||
|
AppState.data_loaded,
|
||||||
|
AppState.total_records.to_string() + " records",
|
||||||
|
"Loading data...",
|
||||||
|
),
|
||||||
|
font_size=Typography.CAPTION_SIZE,
|
||||||
|
font_weight="500",
|
||||||
|
color=Colors.WHITE,
|
||||||
|
font_family=Typography.FONT_FAMILY,
|
||||||
|
),
|
||||||
|
rx.text(
|
||||||
|
rx.cond(
|
||||||
|
AppState.data_loaded,
|
||||||
|
"Last refreshed: recently",
|
||||||
|
"Connecting...",
|
||||||
|
),
|
||||||
|
font_size="11px",
|
||||||
|
color=Colors.WHITE,
|
||||||
|
opacity="0.7",
|
||||||
|
font_family=Typography.FONT_FAMILY,
|
||||||
|
),
|
||||||
|
spacing="0",
|
||||||
|
align="end",
|
||||||
),
|
),
|
||||||
spacing="2",
|
spacing="2",
|
||||||
),
|
align="center",
|
||||||
# Right: Data freshness (placeholder)
|
|
||||||
rx.text(
|
|
||||||
"Data loading...",
|
|
||||||
font_size=Typography.CAPTION_SIZE,
|
|
||||||
color=Colors.WHITE,
|
|
||||||
opacity="0.7",
|
|
||||||
),
|
),
|
||||||
justify="between",
|
justify="between",
|
||||||
align="center",
|
align="center",
|
||||||
width="100%",
|
width="100%",
|
||||||
|
max_width=PAGE_MAX_WIDTH,
|
||||||
|
margin_x="auto",
|
||||||
padding_x=Spacing.XL,
|
padding_x=Spacing.XL,
|
||||||
),
|
),
|
||||||
background_color=Colors.HERITAGE_BLUE,
|
background_color=Colors.HERITAGE_BLUE,
|
||||||
|
|||||||
Reference in New Issue
Block a user