feat: add dynamic operator icon to tab navigation

Make Operator tab icon reflect current operator setting:
- Addition only: 
- Subtraction only: 
- Mixed (both): ± (larger and bold for visibility)

Tab icon updates in real-time as user toggles checkboxes.
Provides immediate visual feedback of operator selection.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
Thomas Hallock 2025-11-11 10:44:27 -06:00
parent c997c4a7ba
commit b6ff995a8c
2 changed files with 227 additions and 0 deletions

View File

@ -0,0 +1,111 @@
'use client'
import { css } from '@styled/css'
import { useState } from 'react'
import { useTheme } from '@/contexts/ThemeContext'
import { StudentNameInput } from './config-panel/StudentNameInput'
import { ContentTab } from './config-sidebar/ContentTab'
import { DifficultyTab } from './config-sidebar/DifficultyTab'
import { LayoutTab } from './config-sidebar/LayoutTab'
import { ScaffoldingTab } from './config-sidebar/ScaffoldingTab'
import { TabNavigation } from './config-sidebar/TabNavigation'
import { useWorksheetConfig } from './WorksheetConfigContext'
interface ConfigSidebarProps {
isSaving: boolean
lastSaved: Date | null
}
export function ConfigSidebar({ isSaving, lastSaved }: ConfigSidebarProps) {
const { resolvedTheme } = useTheme()
const isDark = resolvedTheme === 'dark'
const [activeTab, setActiveTab] = useState('operator')
const { formState, onChange } = useWorksheetConfig()
return (
<div
data-component="config-sidebar"
className={css({
h: 'full',
bg: isDark ? 'gray.800' : 'white',
p: '4',
overflow: 'auto',
display: 'flex',
flexDirection: 'column',
})}
>
{/* Header */}
<div
className={css({
display: 'flex',
justifyContent: 'space-between',
alignItems: 'center',
pb: '3',
borderBottom: '1px solid',
borderColor: isDark ? 'gray.700' : 'gray.200',
mb: '4',
})}
>
<h2
className={css({
fontSize: 'lg',
fontWeight: 'bold',
color: isDark ? 'gray.100' : 'gray.900',
})}
>
Worksheet Studio
</h2>
<div
className={css({
fontSize: 'xs',
color: isSaving
? isDark
? 'gray.400'
: 'gray.500'
: isDark
? 'green.400'
: 'green.600',
})}
>
{isSaving ? 'Saving...' : lastSaved ? '✓ Saved' : ''}
</div>
</div>
{/* Student Name - Global field above tabs */}
<div className={css({ mb: '4' })}>
<StudentNameInput
value={formState.name}
onChange={(name) => onChange({ name })}
isDark={isDark}
/>
</div>
{/* Tab Navigation */}
<div className={css({ mb: '4' })}>
<TabNavigation
activeTab={activeTab}
onChange={setActiveTab}
operator={formState.operator}
/>
</div>
{/* Tab Content */}
<div
className={css({
flex: 1,
bg: isDark ? 'gray.750' : 'gray.50',
rounded: 'lg',
p: '4',
overflow: 'auto',
display: 'flex',
flexDirection: 'column',
})}
>
{activeTab === 'operator' && <ContentTab />}
{activeTab === 'layout' && <LayoutTab />}
{activeTab === 'scaffolding' && <ScaffoldingTab />}
{activeTab === 'difficulty' && <DifficultyTab />}
</div>
</div>
)
}

View File

@ -0,0 +1,116 @@
'use client'
import { css } from '@styled/css'
import { useTheme } from '@/contexts/ThemeContext'
export interface Tab {
id: string
label: string
icon: string | ((operator?: 'addition' | 'subtraction' | 'mixed') => string)
}
export const TABS: Tab[] = [
{
id: 'operator',
label: 'Operator',
icon: (operator) => {
if (operator === 'mixed') return '±'
if (operator === 'subtraction') return ''
return ''
},
},
{ id: 'layout', label: 'Layout', icon: '📐' },
{ id: 'scaffolding', label: 'Scaffolding', icon: '🎨' },
{ id: 'difficulty', label: 'Difficulty', icon: '📊' },
]
interface TabNavigationProps {
activeTab: string
onChange: (tabId: string) => void
operator?: 'addition' | 'subtraction' | 'mixed'
}
export function TabNavigation({ activeTab, onChange, operator }: TabNavigationProps) {
const { resolvedTheme } = useTheme()
const isDark = resolvedTheme === 'dark'
const getTabIcon = (tab: Tab) => {
if (typeof tab.icon === 'function') {
return tab.icon(operator)
}
return tab.icon
}
return (
<div
data-component="tab-navigation"
className={css({
display: 'grid',
gridTemplateColumns: '1fr 1fr',
gap: '2',
})}
>
{TABS.map((tab) => {
const icon = getTabIcon(tab)
const isPlusMinus = icon === '±'
return (
<button
key={tab.id}
data-action={`select-tab-${tab.id}`}
onClick={() => onChange(tab.id)}
className={css({
px: '3',
py: '2',
rounded: 'lg',
fontSize: 'sm',
fontWeight: 'medium',
cursor: 'pointer',
transition: 'all 0.2s',
bg:
activeTab === tab.id
? isDark
? 'brand.900'
: 'brand.50'
: isDark
? 'gray.700'
: 'gray.100',
color:
activeTab === tab.id
? isDark
? 'brand.200'
: 'brand.700'
: isDark
? 'gray.300'
: 'gray.600',
border: '2px solid',
borderColor: activeTab === tab.id ? 'brand.500' : 'transparent',
_hover: {
borderColor: 'brand.400',
},
})}
>
<div
className={css({
display: 'flex',
alignItems: 'center',
justifyContent: 'center',
gap: '1.5',
})}
>
<span
className={css({
fontSize: isPlusMinus ? 'lg' : undefined,
fontWeight: isPlusMinus ? 'bold' : undefined,
})}
>
{icon}
</span>
<span>{tab.label}</span>
</div>
</button>
)
})}
</div>
)
}