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