feat: US-005 - Hover-to-highlight interaction on desktop
This commit is contained in:
+1
-1
@@ -96,7 +96,7 @@
|
||||
"Verify in browser: hover on/off roles cycles highlight cleanly with no stuck states"
|
||||
],
|
||||
"priority": 5,
|
||||
"passes": false,
|
||||
"passes": true,
|
||||
"notes": "The interaction handlers are in the D3 useEffect where mouseenter/mouseleave/click are attached to node groups. supportsCoarsePointer is a module-level window.matchMedia('(pointer: coarse)').matches check. For fine pointer (desktop): mouseenter calls applyGraphHighlight(nodeId) + fires onNodeHover(nodeId), mouseleave calls applyGraphHighlight(null) + fires onNodeHover(null). Remove the click handler's pin/unpin toggle for fine pointer. For coarse pointer (touch): keep existing tap-to-pin unchanged. The pinnedNodeId useState remains but only gets set on coarse pointer or keyboard interactions. The callbacksRef pattern prevents stale closures — use it for onNodeHover. The onNodeHover callback propagates to DashboardLayout for bidirectional highlighting (graph→timeline). Use the d3-viz skill."
|
||||
},
|
||||
{
|
||||
|
||||
@@ -95,3 +95,18 @@
|
||||
- Math.round() wraps all scaled values to avoid sub-pixel rendering artifacts
|
||||
- Accessibility overlay buttons in the React JSX also need scaling — they use base constants directly, not the D3-scoped variables
|
||||
---
|
||||
|
||||
## 2026-02-16 - US-005
|
||||
- Changed mouseenter handler: on desktop (supportsCoarsePointer === false), calls applyGraphHighlight(d.id) + onNodeHover(d.id) for hover-to-highlight
|
||||
- Changed mouseleave handler: resets to highlightedNodeId ?? null (external timeline state or resting), NOT pinnedNodeId
|
||||
- Changed click handler: desktop clicks only fire detail callbacks (onRoleClick/onSkillClick), no pin toggle
|
||||
- Touch (coarse pointer) retains tap-to-pin toggle unchanged inside click handler
|
||||
- pinnedNodeId state only set/cleared for touch interactions
|
||||
- Files changed: src/components/CareerConstellation.tsx
|
||||
- Browser verified: hover on "Interim Head" → 12 connected skills at fill-opacity 0.9, 9 dimmed at opacity 0.15; hover off → all reset to resting (fill-opacity 0.35, label opacity 0.5); desktop click → no pin state
|
||||
- **Learnings for future iterations:**
|
||||
- D3 mouseenter/mouseleave events require dispatchEvent() in Playwright headless — native page.hover() on SVG <g> elements doesn't reliably trigger D3 handlers
|
||||
- Role rect fill-opacity 0.12 IS the resting state (initialized at line 384), not a dimmed state — don't confuse with skill resting at 0.35
|
||||
- mouseleave should reset to highlightedNodeId (external prop) not pinnedNodeId — on desktop there is no pin, so fallback is null (resting)
|
||||
- The supportsCoarsePointer guard at top of each handler cleanly separates desktop/touch paths without duplicating the handler
|
||||
---
|
||||
|
||||
Reference in New Issue
Block a user