mirror of
https://github.com/OFFIS-ESC/constellation-analyzer
synced 2026-03-13 12:08:46 +00:00
Add visual enhancements for parallel edges
Add hover states and badge indicators to improve parallel edge visibility: Features: - Hover effect: Edge stroke increases from 2px to 3px on hover - Selected edges: Stroke increases to 4px for clear selection feedback - Parallel badge: Show "X relations" badge when 4+ parallel edges exist - Badge positioning: Display only on center edge to avoid clutter - Smooth transitions: 150ms ease-in-out for stroke width changes Visual design: - Default stroke: 2px - Hover stroke: 3px - Selected stroke: 4px - Badge color: neutral gray (#6b7280) - Badge appears at edge label position All 517 tests pass. Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
This commit is contained in:
parent
3daedbc0d8
commit
833c130690
1 changed files with 35 additions and 4 deletions
|
|
@ -1,4 +1,4 @@
|
|||
import { memo, useMemo } from 'react';
|
||||
import { memo, useMemo, useState, useCallback } from 'react';
|
||||
import {
|
||||
EdgeProps,
|
||||
EdgeLabelRenderer,
|
||||
|
|
@ -42,6 +42,17 @@ const CustomEdge = ({
|
|||
// Get active filters based on mode (editing vs presentation)
|
||||
const filters = useActiveFilters();
|
||||
|
||||
// Hover state for parallel edge highlighting
|
||||
const [isHovered, setIsHovered] = useState(false);
|
||||
|
||||
const handleMouseEnter = useCallback(() => {
|
||||
setIsHovered(true);
|
||||
}, []);
|
||||
|
||||
const handleMouseLeave = useCallback(() => {
|
||||
setIsHovered(false);
|
||||
}, []);
|
||||
|
||||
// Get internal nodes for floating edge calculations with correct absolute positioning
|
||||
const sourceNode = useInternalNode(source);
|
||||
const targetNode = useInternalNode(target);
|
||||
|
|
@ -105,6 +116,10 @@ const CustomEdge = ({
|
|||
// Check if this is an aggregated edge
|
||||
const isAggregated = !!(data as { aggregatedCount?: number })?.aggregatedCount;
|
||||
|
||||
// Check if this edge is part of a large parallel group (4+ edges)
|
||||
const parallelGroupSize = (data as { parallelGroupSize?: number })?.parallelGroupSize || 0;
|
||||
const showParallelBadge = parallelGroupSize >= 4;
|
||||
|
||||
// Find the edge type configuration
|
||||
const edgeTypeConfig = edgeTypes.find((et) => et.id === data?.type);
|
||||
|
||||
|
|
@ -151,6 +166,9 @@ const CustomEdge = ({
|
|||
// Calculate opacity based on visibility
|
||||
const edgeOpacity = hasActiveFilters && !isMatch ? 0.2 : 1.0;
|
||||
|
||||
// Calculate stroke width based on state (selected, hovered, or default)
|
||||
const strokeWidth = selected ? 4 : isHovered ? 3 : 2;
|
||||
|
||||
// Create unique marker IDs based on color (for reusability)
|
||||
const safeColor = edgeColor.replace('#', '');
|
||||
const markerEndId = `arrow-end-${safeColor}`;
|
||||
|
|
@ -202,16 +220,19 @@ const CustomEdge = ({
|
|||
path={edgePath}
|
||||
style={{
|
||||
stroke: edgeColor,
|
||||
strokeWidth: selected ? 3 : 2,
|
||||
strokeWidth,
|
||||
strokeDasharray,
|
||||
opacity: edgeOpacity,
|
||||
transition: 'stroke-width 150ms ease-in-out',
|
||||
}}
|
||||
markerEnd={markerEnd}
|
||||
markerStart={markerStart}
|
||||
onMouseEnter={handleMouseEnter}
|
||||
onMouseLeave={handleMouseLeave}
|
||||
/>
|
||||
|
||||
{/* Edge label - show custom or type default, plus labels, plus aggregation count */}
|
||||
{(displayLabel || (data?.labels && data.labels.length > 0) || (data as { aggregatedCount?: number })?.aggregatedCount) && (
|
||||
{/* Edge label - show custom or type default, plus labels, plus aggregation count, plus parallel badge */}
|
||||
{(displayLabel || (data?.labels && data.labels.length > 0) || (data as { aggregatedCount?: number })?.aggregatedCount || showParallelBadge) && (
|
||||
<EdgeLabelRenderer>
|
||||
<div
|
||||
style={{
|
||||
|
|
@ -253,6 +274,16 @@ const CustomEdge = ({
|
|||
{(data as { aggregatedCount?: number }).aggregatedCount} relations
|
||||
</div>
|
||||
)}
|
||||
{/* Parallel edge badge for 4+ parallel edges (show only on center edge) */}
|
||||
{showParallelBadge && Math.abs((data as { offsetMultiplier?: number })?.offsetMultiplier || 0) < 0.1 && (
|
||||
<div
|
||||
className="mt-1 px-2 py-0.5 rounded-full text-xs font-semibold text-white"
|
||||
style={{ backgroundColor: '#6b7280' }}
|
||||
title={`${parallelGroupSize} parallel relations between these nodes`}
|
||||
>
|
||||
{parallelGroupSize} relations
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
</EdgeLabelRenderer>
|
||||
)}
|
||||
|
|
|
|||
Loading…
Reference in a new issue