API Sankey
Des flux entre nœuds disposés en colonnes, avec une épaisseur de bande proportionnelle à la valeur du flux (construit sur d3-sankey) - voir la démo Sankey.
Import
import "@michi-vz/wc/sankey-chart";
// <michi-vz-sankey-chart> is now definedimport { mountSankeyChart } from "@michi-vz/core";
const chart = mountSankeyChart(el, props);Propriétés
| Prop | Type | Default | Description |
|---|---|---|---|
timeline | boolean | TimelinePeriodConfig | - | Opt-in "play through years": snapshots one period at a time over the period tags in the data, with a headless controller (`chart.timeline()`) plus the built-in play button + scrubber. Values tween between periods unless `interpolate: false`. Off by default; wins over `progressiveDraw` when both are set. |
progressiveDraw | boolean | ProgressiveDrawConfig | - | Opt-in reveal animation: wipes the marks in left to right on mount (a clip reveal; axes and titles stay put). `true` uses defaults (1200 ms, easeInOutCubic); a config tunes durationMs, easing, autoplay, and replayOnUpdate (`tipLabel` is LineChart-only and ignored here). Off by default; respects prefers-reduced-motion (renders fully drawn instantly) and `replay()` re-runs it. |
nodes* | SankeyNodeItem[] | - | Flow nodes; each needs a unique id that links reference |
links* | SankeyLinkItem[] | - | Directed flows between nodes (source -> target); band thickness is proportional to value |
title | string | - | Optional chart title rendered above the plot |
nodeWidth | number | 18 | Node rect width in px (default 18). |
nodePadding | number | 12 | Vertical gap between nodes in a column in px (default 12). |
nodeRadius | number | 2 | Corner radius of the node rects in px (default 2; 0 = square corners). Clamped to half the node's shorter side. |
linkRadius | number | 2 | Corner radius (px) of the filled flow ribbons where they meet the nodes (default 2; 0 = sharp corners). Clamped to half the band's thickness. |
linkColorMode | "source" | "target" | "source" | Colour links by their "source" (default) or "target" node. |
linkOpacity | number | 0.45 | Link opacity in [0,1] (default 0.45). |
showLabels | boolean | true | Draw node labels (default true). |
isLoading | boolean | - | Show the loading overlay and skip the no-data check (legacy michi-vz parity). |
isNodata | boolean | ((dataSet: SankeyNodeItem[] | null | undefined) => boolean) | - | No-data override: boolean, or a predicate on nodes; default = empty nodes. |
noDataLabel | string | - | Text for the vanilla default no-data overlay (ignored when suppressed). |
suppressDefaultOverlay | boolean | - | A framework wrapper sets this to render its OWN loading/no-data node instead. |
valueFormatter | (n: number) => string | - | Formats a numeric value for labels and tooltips |
tooltipFormatter | (mark: SankeyNodeContext | SankeyLinkContext) => string | - | Returns custom tooltip HTML for a hovered datum/mark (sanitized before it is inserted) |
onHighlightItem | (labels: string[]) => void | - | Called when the hovered/highlighted label(s) change |
Common props - shared by every chart (14)
| Prop | Type | Default | Description |
|---|---|---|---|
width | number | 800 | Chart width in pixels |
height | number | 500 | Chart height in pixels |
margin | Margin | { top: 36, right: 12, bottom: 12, left: 12 } | Inner margins (top/right/bottom/left, in px) reserved for axes, titles, and labels |
colors | string[] | - | Categorical palette for series/labels without an explicit colour or colorsMapping entry |
colorsMapping | Record<string, string> | - | Explicit label -> colour map; takes precedence over the palette and per-item colours |
highlightItems | string[] | - | Labels to emphasise; all other marks dim |
disabledItems | string[] | - | Labels to hide and exclude from scales/stacks |
renderer | "svg" | "canvas" | "webgpu" | "svg" | Render as inline SVG (default) or to a canvas (faster for large datasets); getContext() is identical either way |
locale | string | - | BCP-47 locale used for number and date formatting |
skipColorMappingDispatch | boolean | false | External-CSS mode: unmapped labels resolve to transparent and onColorMappingGenerated is not emitted, so mark colours come from your CSS via the data-label-safe contract |
enableTransitions | boolean | true | Animate updates with CSS transitions (default true) |
onColorMappingGenerated | (mapping: Record<string, string>) => void | - | Called with the resolved label -> colour map after the chart assigns colours |
onChartDataProcessed | (context: ChartContext) => void | - | Called with the renderer-agnostic ChartContext whenever the data is (re)processed |
onDataWarning | (warnings: DataWarning[]) => void | - | Called with any non-fatal data warnings (duplicate labels, non-finite values, non-monotonic dates, ...) |
Les données sont deux tableaux
Contrairement aux autres graphiques, un Sankey prend nodes ({ id, label?, color? }[]) et links ({ source, target, value }[]) au lieu d'un unique dataSet.
Événements
Le composant web émet ces CustomEvents à bouillonnement (bubbling) (le moteur expose les mêmes via les callbacks on* du tableau ci-dessus) :
| Événement | Détail | Se déclenche quand |
|---|---|---|
michi-vz:highlight | string[] | la surbrillance au survol change (id de nœud, ou la source + la cible d'un lien) |
michi-vz:colormapping | Record<string, string> | un mapping de couleurs est généré |
michi-vz:dataprocessed | ChartContext | les données sont (re)traitées |
michi-vz:datawarning | DataWarning[] | des avertissements sur les données d'entrée sont détectés (par exemple un lien vers un nœud inconnu) |
getContext()
mountSankeyChart(el, props).getContext() retourne un SankeyChartContext indépendant du moteur de rendu : les nodes (id / étiquette / couleur / valeur / profondeur de colonne), les links (source / cible / valeur / couleur), des statistiques de synthèse (nombre de nœuds et de liens, nombre de colonnes, flux total, plus grand lien, nœud le plus actif), un résumé déterministe en langage naturel, et une table d'accessibilité listant les flux. Voir Contexte LLM.
Source
Les propriétés sont typées comme SankeyChartProps dans @michi-vz/core.
