Generated by Spark: menu can have folding

This commit is contained in:
2026-01-18 22:29:57 +00:00
committed by GitHub
parent 7b9a7336e4
commit f5efbef9bb
3 changed files with 67 additions and 32 deletions

7
PRD.md
View File

@@ -123,7 +123,7 @@ Animations should reinforce the sense of **authoritative transitions and data re
- **Components**:
- `Card` - Primary container for Strategy Cards, initiative summaries, and portfolio groups; add subtle shadow and border treatment for depth
- **Sidebar Navigation** - Hierarchical left sidebar with grouped sections (Planning, Execution, Hoshin Kanri, Roadmaps, Reporting) replacing horizontal tabs for better scalability and visual organization
- **Sidebar Navigation** - Hierarchical left sidebar with grouped sections (Planning, Execution, Hoshin Kanri, Roadmaps, Reporting) with collapsible/expandable sections for better space management and visual organization
- `Dialog` - Full-screen modals for creating/editing Strategy Cards and initiatives with structured forms
- `Table` - Initiative lists, KPI scorecards, and portfolio views with sortable columns and row hover states
- `Badge` - Status indicators (On Track, At Risk, Blocked), priority levels, and portfolio tags with semantic colors
@@ -146,11 +146,13 @@ Animations should reinforce the sense of **authoritative transitions and data re
- Buttons: Navy default → Gold hover → Pressed with slight scale → Disabled with reduced opacity
- Cards: Subtle hover elevation; active state with gold left border; selected state with gold outline
- **Sidebar Items**: Default state with regular icon weight → Hover with accent background → Active with filled icon, primary background and shadow
- **Sidebar Section Headers**: Clickable with caret icon → Hover with accent background → Collapsed state rotates caret 90° with smooth animation
- Table rows: Hover with light slate background; selected with stronger slate background
- Status badges: Green (On Track), Amber (At Risk), Red (Blocked), Gray (Not Started)
- **Icon Selection**:
- `House` - Home dashboard navigation
- `CaretDown` - Collapsible section indicators in sidebar
- `Strategy` - Use structured grid or layers icon for Strategy Cards
- `ChartBar` - Workbench and execution tracking
- `FolderOpen` - Portfolio management
@@ -171,7 +173,8 @@ Animations should reinforce the sense of **authoritative transitions and data re
- Card padding: `p-6` for substantial internal space
- Section gaps: `gap-6` between major sections; `gap-4` between related elements
- List items: `py-3` for comfortable touch targets and scannability
- **Sidebar spacing**: `p-4` container padding; `space-y-6` between sections; `space-y-1` within sections
- **Sidebar spacing**: `p-4` container padding; `space-y-6` between sections; `space-y-1` within sections and for collapsible content
- **Sidebar section headers**: `py-2` for clickable area; smooth 200ms transition for collapse/expand animations
- **Mobile**:
- Sidebar converts to collapsible drawer with hamburger menu

View File

@@ -1,3 +1,4 @@
{
"dbType": null
{
"templateVersion": 0,
"dbType": null
}

View File

@@ -14,7 +14,8 @@ import {
Tree,
GridFour,
Circle,
House
House,
CaretDown
} from '@phosphor-icons/react'
import { cn } from '@/lib/utils'
import StrategyCards from './components/StrategyCards'
@@ -94,6 +95,7 @@ function App() {
const [initiatives] = useKV<Initiative[]>('initiatives', [])
const [activeView, setActiveView] = useState('strategy')
const [showHome, setShowHome] = useState(true)
const [collapsedSections, setCollapsedSections] = useKV<string[]>('sidebar-collapsed-sections', [])
const handleNavigate = (viewId: string) => {
setActiveView(viewId)
@@ -104,6 +106,16 @@ function App() {
setShowHome(true)
}
const toggleSection = (sectionId: string) => {
setCollapsedSections((current) => {
const sections = current || []
if (sections.includes(sectionId)) {
return sections.filter(id => id !== sectionId)
}
return [...sections, sectionId]
})
}
const activeItem = navigationSections
.flatMap(section => section.items)
.find(item => item.id === activeView)
@@ -152,33 +164,52 @@ function App() {
<span className="font-semibold text-sm">Home</span>
</button>
{navigationSections.map(section => (
<div key={section.id} className="space-y-1">
<h3 className="px-4 text-xs font-semibold uppercase tracking-wider text-muted-foreground mb-2">
{section.label}
</h3>
<div className="space-y-1">
{section.items.map(item => {
const Icon = item.icon
const isActive = activeView === item.id && !showHome
return (
<button
key={item.id}
onClick={() => handleNavigate(item.id)}
className={cn(
"w-full flex items-center gap-3 px-4 py-2.5 rounded-md transition-all text-left",
"hover:bg-accent hover:text-accent-foreground",
isActive && "bg-primary text-primary-foreground shadow-sm"
)}
>
<Icon size={18} weight={isActive ? "fill" : "regular"} />
<span className="font-medium text-sm">{item.label}</span>
</button>
)
})}
{navigationSections.map(section => {
const isCollapsed = collapsedSections?.includes(section.id)
return (
<div key={section.id} className="space-y-1">
<button
onClick={() => toggleSection(section.id)}
className="w-full flex items-center justify-between px-4 py-2 text-xs font-semibold uppercase tracking-wider text-muted-foreground hover:text-foreground transition-colors rounded-md hover:bg-accent/50"
>
<span>{section.label}</span>
<CaretDown
size={14}
weight="bold"
className={cn(
"transition-transform duration-200",
isCollapsed && "-rotate-90"
)}
/>
</button>
<div
className={cn(
"space-y-1 overflow-hidden transition-all duration-200",
isCollapsed ? "max-h-0 opacity-0" : "max-h-[500px] opacity-100"
)}
>
{section.items.map(item => {
const Icon = item.icon
const isActive = activeView === item.id && !showHome
return (
<button
key={item.id}
onClick={() => handleNavigate(item.id)}
className={cn(
"w-full flex items-center gap-3 px-4 py-2.5 rounded-md transition-all text-left",
"hover:bg-accent hover:text-accent-foreground",
isActive && "bg-primary text-primary-foreground shadow-sm"
)}
>
<Icon size={18} weight={isActive ? "fill" : "regular"} />
<span className="font-medium text-sm">{item.label}</span>
</button>
)
})}
</div>
</div>
</div>
))}
)
})}
</nav>
</aside>