Sankey
Basic Configuration
Sankey is a popular kind of flow diagram that visualizes flows between multiple nodes. To define a Sankey diagram you'll need to have data about its nodes and flows between them.
- React
- Angular
- Svelte
- Vue
- TypeScript
import { VisSingleContainer, VisSankey } from '@unovis/react'
function Component(props) {
const data: SankeyData = props.data
return (
<VisSingleContainer data={data}>
<VisSankey/>
</VisSingleContainer>
)
}
@Component({
templateUrl: 'template.html'
})
export class Component {
@Input data: SankeyData;
}
<vis-single-container [data]="data">
<vis-sankey></vis-sankey>
</vis-single-container>
<script lang='ts'>
import { VisSingleContainer, VisSankey } from '@unovis/svelte'
export let data: SankeyData
</script>
<VisSingleContainer {data}>
<VisSankey/>
</VisSingleContainer>
<script setup lang="ts">
import { VisSingleContainer, VisSankey } from '@unovis/vue'
const props = defineProps<{ data: SankeyData }>()
</script>
<template>
<VisSingleContainer :data="data">
<VisSankey />
</VisSingleContainer>
</template>
import { SingleContainer, Sankey } from '@unovis/ts'
import { data, SankeyNode, SankeyLink } from './data'
const container = new SingleContainer<SankeyNode, SankeyLink>(node, {
component: new Sankey<SankeyNode, SankeyLink>({ })
}, data)
Specifically, Sankey accepts data in the following form:
type SankeyData<NodeDatum, LinkDatum> = {
nodes: NodeDatum[];
links: LinkDatum[];
}
Links
The minimal configuration for a Link datum contains source
and target
properties, which
correspond to the starting and ending nodes of the link, and a numerical value
.
type SankeyLink = {
source: string | number | SankeyNode;
target: string | number | SankeyNode;
value?: number;
}
Note that the value
is not required, but recommended since by default Sankey will use
this property to calculate the width of each link. Alternatively, you can provide a numeric accessor function
to the linkValue
property.
Nodes
While there are no explicitly required properties for NodeDatum
, a common configuration looks like:
type SankeyNode = {
id: string;
color: string;
label: string;
}
Alternatively, you can provide accessor functions to id
, nodeColor
, nodeLabel
properties
to achieve the same effect.
Component Sizing
Sankey supports three different sizing options that can be set via SingleContainer: Sizing.Fit
(default),
Sizing.Extend
and Sizing.FitWidth
.
By default, SingleContainer and Sankey will take all the available space of its parent HTML element. However, if you
set SingleContainer's sizing
to Sizing.Extend
(or "extend"
), the diagram will be able to go beyond its parent
size and become scrollable. In that case you'll be able to control the diagram size by using the following properties:
nodeWidth
, nodeHorizontalSpacing
, nodeMinHeight
, nodeMaxHeight
, and nodePadding
(see
Node Sizing).
The Sizing.FitWidth
(or "fit_width"
) option is similar to the Sizing.Extend
option, but the whole component will be scaled down
proportionally to fit horizontally into its container; vertical scrolling will remain available.
Labels
The following customization options are available for Node labels:
Label Background
For a chart with many nodes, it might be useful to add a background by setting the labelBackground
property to true
:
- React
- Angular
- Svelte
- Vue
- TypeScript
<VisSankey labelBackground={true}/>
<vis-sankey [labelBackground]="true"></vis-sankey>
<VisSankey labelBackground={true}/>
<VisSankey :labelBackground="true" />
const sankey = new Sankey<SankeyNode, SankeyLink>({ labelBackground: true })
Label Fitting
By default, node labels that exceed the width constraint will be trimmed to exclude the middle. For the following properties, the default configuration for Sankey looks like:
{
labelFit: FitMode.Trim,
labelMaxWidth: 70,
labelTrimMode: TrimMode.Middle,
labelExpandTrimmedOnHover: true,
}
For overflowing labels, the default configuration renders as:
FitMode.Wrap
You can disable trimming by setting labelFit
to FitMode.Wrap
or 'wrap'
, which forces line breaking:
- React
- Angular
- Svelte
- Vue
- TypeScript
import { VisSingleContainer, VisSankey } from '@unovis/react'
function Component(props) {
const data: SankeyData = props.data
const label = (d: SankeyNode) => `Long node name : ${d.id}`
return (
<VisSingleContainer data={data}>
<VisSankey label={label} labelFit="wrap"/>
</VisSingleContainer>
)
}
@Component({
templateUrl: 'template.html'
})
export class Component {
@Input data: SankeyData;
label = (d: SankeyNode) => `Long node name : ${d.id}`
}
<vis-single-container [data]="data">
<vis-sankey [label]="label" labelFit="wrap"></vis-sankey>
</vis-single-container>
<script lang='ts'>
import { VisSingleContainer, VisSankey } from '@unovis/svelte'
export let data: SankeyData
const label = (d: SankeyNode) => `Long node name : ${d.id}`
</script>
<VisSingleContainer {data}>
<VisSankey {label} labelFit="wrap"/>
</VisSingleContainer>
<script setup lang="ts">
import { VisSingleContainer, VisSankey } from '@unovis/vue'
const props = defineProps<{ data: SankeyData }>()
const label = (d: SankeyNode) => `Long node name : ${d.id}`
</script>
<template>
<VisSingleContainer :data="data">
<VisSankey :label="label" labelFit="wrap" />
</VisSingleContainer>
</template>
import { SingleContainer, Sankey } from '@unovis/ts'
import { data, SankeyNode, SankeyLink } from './data'
const container = new SingleContainer<SankeyNode, SankeyLink>(node, {
component: new Sankey<SankeyNode, SankeyLink>({
label: (d: SankeyNode) => `Long node name : ${d.id}`,
labelFit: "wrap"
})
}, data)
When labelFit
is set to FitMode.Wrap
, you can change which characters to denote a new line
with the labelTextSeparator
property.
(default: [' ', '-']
).
FitMode.Trim
You can the labelTrimMode
property to change which portion of the labels you want to trim:
- React
- Angular
- Svelte
- Vue
- TypeScript
<VisSankey labelTrimMode="start" label={label}/>
<vis-sankey labelTrimMode="start" [label]="label"></vis-sankey>
<VisSankey labelTrimMode="start" {label}/>
<VisSankey labelTrimMode="start" :label="label" />
const sankey = new Sankey<SankeyNode, SankeyLink>({ labelTrimMode: "start", label })
Label Placement
The following properties deal with node label placement:
labelPosition
, which corresponds to the horizontal placement relative to the node (default:Position.Auto
);labelVerticalAlign
, for vertical alignment (default:VerticalAlign.Middle
);labelVisibility
, which accepts a custom function that when returns false, the label will be hidden altogether.
Sub-labels
You can add a secondary label with the subLabel
property. Sub-label color and font size can be configured with the
subLabelColor
and subLabelFontSize
properties.
- React
- Angular
- Svelte
- Vue
- TypeScript
function Component(props) {
const data: SankeyData = props.data
const label = (d: SankeyNode) => d.id
const subLabel = (d: SankeyNode) => d.val
return (
<VisSankey label={label} subLabel={subLabel}/>
)
}
@Component({
template: '<vis-sankey [label]="label" [subLabel]="subLabel"></vis-sankey>'
})
export class Component {
@Input data: SankeyData;
label = (d: SankeyNode) => d.id
subLabel = (d: SankeyNode) => d.val
}
<script lang='ts'>
import { VisSingleContainer, VisSankey } from '@unovis/svelte'
export let data: SankeyData
const label = (d: SankeyNode) => d.id
const subLabel = (d: SankeyNode) => d.val
</script>
<VisSankey {label} {subLabel}/>
<script setup lang="ts">
import { VisSingleContainer, VisSankey } from '@unovis/vue'
const props = defineProps<{ data: SankeyData }>()
const label = (d: SankeyNode) => d.id
const subLabel = (d: SankeyNode) => d.val
</script>
<template>
<VisSankey :label="label" :subLabel="subLabel" />
</template>
const sankey = new Sankey<SankeyNode, SankeyLink>({
label: (d: SankeyNode) => d.id,
subLabel: (d: SankeyNode) => d.val
})
By default, sub-labels will be placed below the main labels. However, if you set the subLabelPlacement
property to
SankeySubLabelPlacement.Inline
(or "inline"
), they will be placed right next to the main label on the left or on the
right (depending on labelPosition
).
- React
- Angular
- Svelte
- Vue
- TypeScript
<VisSankey
label={label}
subLabel={subLabel}
subLabelPlacement="inline"
/>
<vis-sankey
[label]="label"
[subLabel]="subLabel"
subLabelPlacement="inline"
></vis-sankey>
<VisSankey {label} {subLabel} subLabelPlacement="inline"/>
<VisSankey
:label="label"
:subLabel="subLabel"
subLabelPlacement="inline"
/>
const sankey = new Sankey<SankeyNode, SankeyLink>({
label,
subLabel,
subLabelPlacement: "inline"
})
Node Customization
Node Alignment
You can override the default node alignment with the nodeAlign
property. Accepted values are
SankeyNodeAlign.Left
, SankeyNodeAlign.Right
, SankeyNodeAlign.Center
and SankeyNodeAlign.Justify
(default)
- React
- Angular
- Svelte
- Vue
- TypeScript
<VisSankey nodeAlign="left"/>
<vis-sankey nodeAlign="left"></vis-sankey>
<VisSankey nodeAlign="left"/>
<VisSankey nodeAlign="left" />
const sankey = new Sankey<SankeyNode, SankeyLink>({ nodeAlign: "left" })
Node Sizing
By default, the height of the nodes will be calculated automatically based on the height of Sankey's container and the
nodePadding
property. The width of the nodes can be set with the nodeWidth
configuration option (measurement is in
pixels).
- React
- Angular
- Svelte
- Vue
- TypeScript
<VisSankey nodeWidth={100} nodePadding={20}/>
<vis-sankey [nodeWidth]="100" [nodePadding]="20"></vis-sankey>
<VisSankey nodeWidth={100} nodePadding={20}/>
<VisSankey :nodeWidth="100" :nodePadding="20" />
const sankey = new Sankey<SankeyNode, SankeyLink>({ nodeWidth: 100, nodePadding: 20 })
If sizing
of SingleContainer is set to Sizing.Extend
or Sizing.FitWidth
(see
Component Sizing), you can control the height of the nodes by setting the nodeMinHeight
and
nodeMaxHeight
properties. Note that those options are approximate since d3-sankey
doesn't allow setting the node height explicitly.
Node Icons
Provide an accessor function to nodeIcon
to add a label/symbol over the node itself. Customize the
icon's color with iconColor
:
- React
- Angular
- Svelte
- Vue
- TypeScript
function Component(props) {
const data: SankeyData = props.data
const nodeIcon = (d: NodeDatum) => d.currencySymbol
return (
<VisSankey nodeIcon={nodeIcon} iconColor="white"/>
)
}
@Component({
template: '<vis-sankey [nodeIcon]="nodeIcon" iconColor="white"></vis-sankey>'
})
export class Component {
@Input data: SankeyData;
nodeIcon = (d: NodeDatum) => d.currencySymbol
}
<script lang='ts'>
import { VisSingleContainer, VisSankey } from '@unovis/svelte'
export let data: SankeyData
const nodeIcon = (d: NodeDatum) => d.currencySymbol
</script>
<VisSankey {nodeIcon} iconColor="white"/>
<script setup lang="ts">
import { VisSingleContainer, VisSankey } from '@unovis/vue'
const props = defineProps<{ data: SankeyData }>()
const nodeIcon = (d: NodeDatum) => d.currencySymbol
</script>
<template>
<VisSankey :nodeIcon="nodeIcon" iconColor="white" />
</template>
const sankey = new Sankey<SankeyNode, SankeyLink>({
nodeIcon: (d: NodeDatum) => d.currencySymbol,
iconColor: "white"
})
Sorting
By default, Sankey will sort the links based on their value
in descending order from top to bottom.
To change the order of the links, provide a custom sorting function to linkSort
.
Alternatively, if you want to set the order of the nodes explicitly, you can provide a custom sorting function to
nodeSort
. It'll take precedence over the linkSort
function.
See the following example, where nodes are sorted by property x
, a number in the range [0,4]
,
which also configures the node's color:
- React
- Angular
- Svelte
- Vue
- TypeScript
function Component(props) {
const data: SankeyData = props.data
const nodeSort = (node1: NodeDatum, node2: NodeDatum) => node1.x - node2.x
const nodeColor = (d: SankeyNode) => `var( -- vis - color${d.x})`
return (
<VisSankey nodeColor={nodeColor} nodeSort={nodeSort}/>
)
}
@Component({
template: '<vis-sankey [nodeColor]="nodeColor" [nodeSort]="nodeSort"></vis-sankey>'
})
export class Component {
@Input data: SankeyData;
nodeSort = (node1: NodeDatum, node2: NodeDatum) => node1.x - node2.x
nodeColor = (d: SankeyNode) => `var( -- vis - color${d.x})`
}
<script lang='ts'>
import { VisSingleContainer, VisSankey } from '@unovis/svelte'
export let data: SankeyData
const nodeSort = (node1: NodeDatum, node2: NodeDatum) => node1.x - node2.x
const nodeColor = (d: SankeyNode) => `var( -- vis - color${d.x})`
</script>
<VisSankey {nodeColor} {nodeSort}/>
<script setup lang="ts">
import { VisSingleContainer, VisSankey } from '@unovis/vue'
const props = defineProps<{ data: SankeyData }>()
const nodeSort = (node1: NodeDatum, node2: NodeDatum) => node1.x - node2.x
const nodeColor = (d: SankeyNode) => `var( -- vis - color${d.x})`
</script>
<template>
<VisSankey :nodeColor="nodeColor" :nodeSort="nodeSort" />
</template>
const sankey = new Sankey<SankeyNode, SankeyLink>({
nodeColor: (d: SankeyNode) => `var( -- vis - color${d.x})`,
nodeSort: (node1: NodeDatum, node2: NodeDatum) => node1.x - node2.x
})
Events
The following selectors are available for events:
import { Sankey } from '@unovis/ts'
const events = {
[Sankey.selectors.node]: { ... }
[Sankey.selectors.nodeGroup]: { ... }
[Sankey.selectors.link]: { ... },
[Sankey.selectors.label]: { ... },
[Sankey.selectors.sublabel]: { ... },
[Sankey.selectors.labelGroup]: { ... },
}
CSS Variables
All supported CSS variables and their default values
/* Links */
--vis-sankey-link-cursor: default;
--vis-sankey-link-color: var(--vis-color-main-light);
--vis-sankey-link-opacity: 0.5;
--vis-sankey-link-hover-opacity: 1.0;
/* Nodes */
--vis-sankey-node-cursor: default;
--vis-sankey-node-color: var(--vis-color-main);
--vis-sankey-node-label-color: #575c65;
--vis-sankey-node-opacity: 0.9;
--vis-sankey-node-hover-opacity: 1.0;
/* Node Labels */
--vis-sankey-node-label-background-fill-color: #ffffff;
--vis-sankey-node-label-background-stroke-color: #eaeaea;
--vis-sankey-node-label-background-opacity: 0.9;
--vis-sankey-node-label-color: #575c65;
--vis-sankey-node-label-cursor: default;
--vis-sankey-node-label-font-weight: 600;
--vis-sankey-node-label-font-size: 12px;
--vis-sankey-node-label-text-decoration: none;
--vis-sankey-node-sublabel-font-size: 10px;
--vis-sankey-node-sublabel-font-weight: 500;
/* Icons */
--vis-sankey-icon-size: 22px;
--vis-sankey-icon-color: #ffffff;
--vis-sankey-icon-stroke-opacity: 0.6;
--vis-sankey-icon-font-family: FontAwesome;
/* --vis-sankey-label-font-family: */ // Undefined by default to allow proper fallback to var(--vis-font-family)
/* Dark Theme */
--vis-dark-sankey-link-color: var(--vis-color-main-dark);
--vis-dark-sankey-node-color: var(--vis-color-main);
--vis-dark-sankey-node-label-color: #eaeaea;
--vis-dark-sankey-node-label-background-fill-color: #292b34;
--vis-dark-sankey-node-label-background-stroke-color: #575c65;
--vis-dark-sankey-icon-color: #292b34;
Component Props
Name | Type | Description |
---|---|---|
* required property |