fix: restyle sankey to match reference - thinner nodes, all labels, scrollable

- Switch to fixed-width Sankey with horizontal scroll (250px per step)
- Thinner nodes (8px), tighter spacing (8px)
- Labels on all columns, not just first/last
- Lower link opacity (0.15) for cleaner look
- Increased node cap to 25 per step
This commit is contained in:
Usman Baig
2026-03-16 14:22:06 +01:00
parent 1aace48d73
commit f797d89131

View File

@@ -1,7 +1,7 @@
'use client'
import { useCallback, useEffect, useMemo, useState } from 'react'
import { ResponsiveSankey } from '@nivo/sankey'
import { Sankey } from '@nivo/sankey'
import { TreeStructure, X } from '@phosphor-icons/react'
import type { PathTransition } from '@/lib/api/journeys'
@@ -36,7 +36,7 @@ const COLUMN_COLORS = [
'#EC4899', '#06B6D4', '#EF4444', '#84CC16', '#F97316', '#6366F1',
]
const MAX_NODES_PER_STEP = 15
const MAX_NODES_PER_STEP = 25
// ─── Helpers ────────────────────────────────────────────────────────
@@ -257,9 +257,11 @@ export default function SankeyJourney({
}
const labelColor = isDark ? '#a3a3a3' : '#525252'
const steps = data.nodes.map((n) => n.stepIndex)
const minStep = Math.min(...steps)
const maxStep = Math.max(...steps)
// Calculate dimensions: give each step ~250px of horizontal space
const numSteps = new Set(data.nodes.map((n) => n.stepIndex)).size
const chartWidth = Math.max(800, numSteps * 250)
const chartHeight = 500
return (
<div>
@@ -283,38 +285,35 @@ export default function SankeyJourney({
</div>
)}
<div style={{ height: 500 }}>
<ResponsiveSankey<SankeyNode, SankeyLink>
<div className="overflow-x-auto -mx-6 px-6" style={{ maxHeight: chartHeight + 16 }}>
<div style={{ width: chartWidth, height: chartHeight }}>
<Sankey<SankeyNode, SankeyLink>
data={data}
margin={{ top: 8, right: 140, bottom: 8, left: 140 }}
width={chartWidth}
height={chartHeight}
margin={{ top: 8, right: 160, bottom: 8, left: 160 }}
align="justify"
sort="descending"
colors={(node) =>
COLUMN_COLORS[node.stepIndex % COLUMN_COLORS.length]
}
nodeThickness={12}
nodeSpacing={20}
nodeThickness={8}
nodeSpacing={8}
nodeInnerPadding={0}
nodeBorderWidth={0}
nodeBorderRadius={3}
nodeBorderRadius={2}
nodeOpacity={1}
nodeHoverOpacity={1}
nodeHoverOthersOpacity={0.3}
linkOpacity={0.2}
linkOpacity={0.15}
linkHoverOpacity={0.5}
linkHoverOthersOpacity={0.05}
linkContract={2}
linkHoverOthersOpacity={0.03}
linkContract={1}
enableLinkGradient
enableLabels
label={(node) => {
// Only show labels for first and last step columns
if (node.stepIndex === minStep || node.stepIndex === maxStep) {
return smartLabel(pathFromId(node.id))
}
return ''
}}
label={(node) => smartLabel(pathFromId(node.id))}
labelPosition="outside"
labelPadding={8}
labelPadding={6}
labelTextColor={labelColor}
isInteractive
onClick={handleClick}
@@ -352,5 +351,6 @@ export default function SankeyJourney({
/>
</div>
</div>
</div>
)
}