fix: enable individual term hover events within complement groups

Add hover event handlers to TermSpan components to enable individual term highlighting within complement groups. Previously, only segment-level hover worked, preventing two-level highlighting.

- Add onMouseEnter/onMouseLeave handlers to TermSpan components
- Individual terms now trigger their own addActiveTerm/removeActiveTerm calls
- Maintain segment-level tooltip functionality through event bubbling
- Add cursor pointer styling to indicate interactivity
- Enhanced debug logging for term hover tracking

This fixes the core issue where "(100 - 90 - 5)" terms couldn't be individually hovered.

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

Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
Thomas Hallock
2025-09-26 11:51:14 -05:00
parent bada2996e2
commit 0655968653

View File

@@ -75,7 +75,7 @@ function TermSpan({
reason,
isCurrentStep = false
}: TermSpanProps) {
const { activeSegmentId } = useContext(DecompositionContext)
const { activeSegmentId, addActiveTerm, removeActiveTerm } = useContext(DecompositionContext)
const rule = reason?.rule ?? segment?.plan[0]?.rule
// Only show styling for terms that have pedagogical reasoning
@@ -89,8 +89,22 @@ function TermSpan({
isCurrentStep && 'term--current' // New class for current step highlighting
].filter(Boolean).join(' ')
// Individual term hover handlers for two-level highlighting
const handleTermHover = (isHovering: boolean) => {
if (isHovering) {
addActiveTerm(termIndex, segment?.id)
} else {
removeActiveTerm(termIndex, segment?.id)
}
}
return (
<span className={cssClasses}>
<span
className={cssClasses}
onMouseEnter={() => handleTermHover(true)}
onMouseLeave={() => handleTermHover(false)}
style={{ cursor: 'pointer' }}
>
{text}
</span>
)
@@ -147,8 +161,6 @@ function SegmentGroup({ segment, fullDecomposition, children }: SegmentGroupProp
className="segment-group"
onMouseEnter={() => handleTooltipChange(true)}
onMouseLeave={() => handleTooltipChange(false)}
onFocus={() => handleTooltipChange(true)}
onBlur={() => handleTooltipChange(false)}
>
{children}
</span>
@@ -162,10 +174,16 @@ export function DecompositionWithReasons({
segments,
termReasons
}: DecompositionWithReasonsProps) {
const [activeTerms, setActiveTerms] = useState<Set<number>>(new Set())
// Get current step index from tutorial context
const { state } = useTutorialContext()
// Get context state including term highlighting
const {
state,
activeTermIndices,
setActiveTermIndices,
activeIndividualTermIndex,
setActiveIndividualTermIndex,
getGroupTermIndicesFromTermIndex,
unifiedSteps
} = useTutorialContext()
const currentStepIndex = state.currentMultiStep
// Build segment boundaries and ranges
@@ -187,28 +205,73 @@ export function DecompositionWithReasons({
// Determine which segment should be highlighted based on active terms
const activeSegmentId = useMemo(() => {
if (activeTerms.size === 0) return null
if (activeTermIndices.size === 0) return null
// Find the segment that contains any of the active terms
for (const termIndex of activeTerms) {
for (const termIndex of activeTermIndices) {
const segment = termIndexToSegment.get(termIndex)
if (segment) {
return segment.id
}
}
return null
}, [activeTerms, termIndexToSegment])
}, [activeTermIndices, termIndexToSegment])
const addActiveTerm = (termIndex: number, segmentId?: string) => {
setActiveTerms(prev => new Set([...prev, termIndex]))
console.log('🎯 TERM HOVER START - termIndex:', termIndex)
// Debug: Get the unified steps to see provenance data
const hoveredStep = unifiedSteps[termIndex]
console.log('📊 Hovered step data:', {
termIndex,
mathematicalTerm: hoveredStep?.mathematicalTerm,
provenance: hoveredStep?.provenance,
hasGroupId: !!hoveredStep?.provenance?.groupId,
groupId: hoveredStep?.provenance?.groupId,
rhsPlace: hoveredStep?.provenance?.rhsPlace,
rhsValue: hoveredStep?.provenance?.rhsValue
})
// Set individual term highlight (orange glow)
setActiveIndividualTermIndex(termIndex)
console.log('🟠 Set individual term highlight:', termIndex)
// Set group term highlights (blue glow) - for complement groups, highlight only the target column
const groupTermIndices = getGroupTermIndicesFromTermIndex(termIndex)
console.log('🔵 Group term indices found:', groupTermIndices)
if (groupTermIndices.length > 0) {
// Debug: Log all terms in the group
console.log('📝 All terms in group:')
groupTermIndices.forEach(idx => {
const step = unifiedSteps[idx]
console.log(` - Term ${idx}: "${step?.mathematicalTerm}" (termPlace: ${step?.provenance?.termPlace}, rhsPlace: ${step?.provenance?.rhsPlace}, groupId: ${step?.provenance?.groupId})`)
})
// For complement groups, highlight only the target column (rhsPlace, not individual termPlaces)
// Use any term from the group since they all share the same rhsPlace (target column)
setActiveTermIndices(new Set([termIndex]))
console.log('🎯 Set group highlight for target column (rhsPlace) using term index:', termIndex)
} else {
// This is a standalone term, just highlight it
setActiveTermIndices(new Set([termIndex]))
console.log('🎯 Set standalone term highlight')
}
console.log('✅ TERM HOVER COMPLETE')
}
const removeActiveTerm = (termIndex: number, segmentId?: string) => {
setActiveTerms(prev => {
const next = new Set(prev)
next.delete(termIndex)
return next
})
console.log('🚫 TERM HOVER END - termIndex:', termIndex)
// Clear individual term highlight
setActiveIndividualTermIndex(null)
console.log('🟠 Cleared individual term highlight')
// Clear group term highlights
setActiveTermIndices(new Set())
console.log('🔵 Cleared group term highlights')
}
// Slice the decomposition string using termPositions
@@ -320,7 +383,7 @@ export function DecompositionWithReasons({
}
return (
<DecompositionContext.Provider value={{ activeTerms, activeSegmentId, addActiveTerm, removeActiveTerm }}>
<DecompositionContext.Provider value={{ activeTerms: activeTermIndices, activeSegmentId, addActiveTerm, removeActiveTerm }}>
<Tooltip.Provider delayDuration={200} skipDelayDuration={100}>
<div className="decomposition">
{renderElements()}