Skip to content

Commit 62b087e

Browse files
lucbrinkmanclaude
andcommitted
Fix pin button hover flickering when node layout shifts
When hovering the pin button to preview probability root, the node shows 100% probability (3 digits instead of 2), which can widen the probability badge, cause text wrapping, and make the node taller. This pushes the pin button upward, and if the cursor is at the button's bottom edge, it moves away causing hover to end, which reverts the layout, creating an infinite flashing loop. Solution: Add a conditional extended hover zone that activates only during hover. When isPinHovered is true, a 10px transparent padding zone extends below the button, keeping the cursor within the hover area even when the button shifts upward. Changes: - Add isPinHovered state to track pin button hover - Wrap both pin button instances in divs with conditional padding - Move hover handlers to wrapper divs to control extended zone - Update button background to stay orange during hover 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <[email protected]>
1 parent 298da8e commit 62b087e

File tree

1 file changed

+75
-65
lines changed

1 file changed

+75
-65
lines changed

components/Node.tsx

Lines changed: 75 additions & 65 deletions
Original file line numberDiff line numberDiff line change
@@ -128,6 +128,9 @@ const Node = forwardRef<HTMLDivElement, NodeProps>(
128128
// Pin button click cooldown - prevents hover preview immediately after clicking
129129
const pinClickCooldownRef = useRef(false);
130130

131+
// Pin button hover state - controls extended hover zone to prevent flickering
132+
const [isPinHovered, setIsPinHovered] = useState(false);
133+
131134
// Edit state
132135
const [isEditing, setIsEditing] = useState(false);
133136
const [editText, setEditText] = useState(text);
@@ -642,46 +645,45 @@ const Node = forwardRef<HTMLDivElement, NodeProps>(
642645
>
643646
{/* Pin button (left) */}
644647
{onSetProbabilityRoot && (
645-
<Tooltip
646-
content={isSelected ? "Remove as start" : "Set as start"}
647-
position="top"
648+
<div
649+
onMouseEnter={(e) => {
650+
e.stopPropagation();
651+
if (!pinClickCooldownRef.current) {
652+
setIsPinHovered(true);
653+
onSetProbabilityRootHoverStart?.();
654+
}
655+
}}
656+
onMouseLeave={(e) => {
657+
e.stopPropagation();
658+
setIsPinHovered(false);
659+
pinClickCooldownRef.current = false; // Reset cooldown when mouse leaves
660+
onSetProbabilityRootHoverEnd?.();
661+
}}
662+
style={{
663+
paddingBottom: isPinHovered ? "10px" : "0",
664+
}}
648665
>
649-
<button
650-
onClick={(e) => {
651-
e.stopPropagation();
652-
pinClickCooldownRef.current = true;
653-
onSetProbabilityRootHoverEnd?.(); // Clear any existing hover
654-
onSetProbabilityRoot();
655-
}}
656-
onMouseEnter={(e) => {
657-
e.stopPropagation();
658-
if (!pinClickCooldownRef.current) {
659-
onSetProbabilityRootHoverStart?.();
660-
}
661-
}}
662-
onMouseLeave={(e) => {
663-
e.stopPropagation();
664-
pinClickCooldownRef.current = false; // Reset cooldown when mouse leaves
665-
onSetProbabilityRootHoverEnd?.();
666-
}}
667-
className="text-white rounded w-5 h-5 flex items-center justify-center shadow-lg transition-colors"
668-
style={{
669-
backgroundColor: isSelected ? ORANGE_COLOR : "#9ca3af",
670-
}}
671-
onMouseOver={(e) => {
672-
if (!pinClickCooldownRef.current) {
673-
e.currentTarget.style.backgroundColor = ORANGE_COLOR;
674-
}
675-
}}
676-
onMouseOut={(e) => {
677-
e.currentTarget.style.backgroundColor = isSelected
678-
? ORANGE_COLOR
679-
: "#9ca3af";
680-
}}
666+
<Tooltip
667+
content={isSelected ? "Remove as start" : "Set as start"}
668+
position="top"
681669
>
682-
<Pin size={14} />
683-
</button>
684-
</Tooltip>
670+
<button
671+
onClick={(e) => {
672+
e.stopPropagation();
673+
pinClickCooldownRef.current = true;
674+
onSetProbabilityRootHoverEnd?.(); // Clear any existing hover
675+
onSetProbabilityRoot();
676+
}}
677+
className="text-white rounded w-5 h-5 flex items-center justify-center shadow-lg transition-colors"
678+
style={{
679+
backgroundColor:
680+
isPinHovered || isSelected ? ORANGE_COLOR : "#9ca3af",
681+
}}
682+
>
683+
<Pin size={14} />
684+
</button>
685+
</Tooltip>
686+
</div>
685687
)}
686688

687689
{/* Trash button (right) */}
@@ -713,33 +715,41 @@ const Node = forwardRef<HTMLDivElement, NodeProps>(
713715
right: `${-borderWidth}px`,
714716
}}
715717
>
716-
<Tooltip content="Remove as start" position="top">
717-
<button
718-
onClick={(e) => {
719-
e.stopPropagation();
720-
pinClickCooldownRef.current = true;
721-
onSetProbabilityRootHoverEnd?.(); // Clear hover state before toggling
722-
onSetProbabilityRoot();
723-
}}
724-
onMouseEnter={(e) => {
725-
e.stopPropagation();
726-
if (!pinClickCooldownRef.current) {
727-
onSetProbabilityRootHoverStart?.();
728-
}
729-
}}
730-
onMouseLeave={(e) => {
731-
e.stopPropagation();
732-
pinClickCooldownRef.current = false; // Reset cooldown when mouse leaves
733-
onSetProbabilityRootHoverEnd?.();
734-
}}
735-
className="text-white rounded w-5 h-5 flex items-center justify-center shadow-lg transition-colors"
736-
style={{
737-
backgroundColor: ORANGE_COLOR,
738-
}}
739-
>
740-
<Pin size={14} />
741-
</button>
742-
</Tooltip>
718+
<div
719+
onMouseEnter={(e) => {
720+
e.stopPropagation();
721+
if (!pinClickCooldownRef.current) {
722+
setIsPinHovered(true);
723+
onSetProbabilityRootHoverStart?.();
724+
}
725+
}}
726+
onMouseLeave={(e) => {
727+
e.stopPropagation();
728+
setIsPinHovered(false);
729+
pinClickCooldownRef.current = false; // Reset cooldown when mouse leaves
730+
onSetProbabilityRootHoverEnd?.();
731+
}}
732+
style={{
733+
paddingBottom: isPinHovered ? "10px" : "0",
734+
}}
735+
>
736+
<Tooltip content="Remove as start" position="top">
737+
<button
738+
onClick={(e) => {
739+
e.stopPropagation();
740+
pinClickCooldownRef.current = true;
741+
onSetProbabilityRootHoverEnd?.(); // Clear hover state before toggling
742+
onSetProbabilityRoot();
743+
}}
744+
className="text-white rounded w-5 h-5 flex items-center justify-center shadow-lg transition-colors"
745+
style={{
746+
backgroundColor: ORANGE_COLOR,
747+
}}
748+
>
749+
<Pin size={14} />
750+
</button>
751+
</Tooltip>
752+
</div>
743753
</div>
744754
)}
745755

0 commit comments

Comments
 (0)