Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions draftlogs/7888_add.md
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
- Add permanent on-diagram link labels to `sankey` traces via `link.textinfo`, `link.texttemplate`, `link.textfont`, `link.valueformat` and `link.valuesuffix` [[#7888](https://github.com/plotly/plotly.js/pull/7888)]
44 changes: 44 additions & 0 deletions src/traces/sankey/attributes.js
Original file line number Diff line number Diff line change
Expand Up @@ -185,6 +185,50 @@ var attrs = (module.exports = overrideAll(
dflt: [],
description: 'The shown name of the link.'
},
textinfo: {
valType: 'flaglist',
flags: ['label', 'value'],
extras: ['none'],
dflt: 'none',
description: [
'Determines which trace information appears permanently on the links.',
'Any combination of *label* and *value* joined with a *+* OR *none*.'
].join(' ')
},
texttemplate: {
valType: 'string',
dflt: '',
arrayOk: true,
description: [
'Template string used for rendering the information text that appears',
'permanently on the links. Note that this will override `textinfo`.',
'Variables are inserted using %{variable}, for example',
'*%{label}: %{value}*. Available variables are `label`, `value`,',
'`valueLabel` (the value formatted with `valueformat`/`valuesuffix`),',
'`source`, `target` and `customdata`.'
].join(' ')
},
textfont: fontAttrs({
autoShadowDflt: true,
description: 'Sets the font for the permanent link labels.'
}),
valueformat: {
valType: 'string',
dflt: '',
description: [
'Sets the value formatting rule for the permanent link labels,',
'using d3 formatting mini-languages. Falls back to the trace-level',
'`valueformat` when empty.'
].join(' ')
},
valuesuffix: {
valType: 'string',
dflt: '',
description: [
'Adds a unit to follow the value in the permanent link labels.',
'Falls back to the trace-level `valuesuffix` when empty.'
].join(' ')
},
color: {
valType: 'color',
arrayOk: true,
Expand Down
1 change: 1 addition & 0 deletions src/traces/sankey/constants.js
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ module.exports = {
sankey: 'sankey',
sankeyLinks: 'sankey-links',
sankeyLink: 'sankey-link',
sankeyLinkLabel: 'sankey-link-label',
sankeyNodeSet: 'sankey-node-set',
sankeyNode: 'sankey-node',
nodeRect: 'node-rect',
Expand Down
9 changes: 9 additions & 0 deletions src/traces/sankey/defaults.js
Original file line number Diff line number Diff line change
Expand Up @@ -108,6 +108,15 @@ module.exports = function supplyDefaults(traceIn, traceOut, defaultColor, layout

Lib.coerceFont(coerce, 'textfont', layout.font, { autoShadowDflt: true });

// permanent link text labels (opt-in via link.textinfo / link.texttemplate)
var linkTextInfo = coerceLink('textinfo');
var linkTextTemplate = coerceLink('texttemplate');
if(linkTextInfo !== 'none' || linkTextTemplate) {
Lib.coerceFont(coerceLink, 'textfont', traceOut.textfont, { autoShadowDflt: true });
coerceLink('valueformat', traceOut.valueformat);
coerceLink('valuesuffix', traceOut.valuesuffix);
}

// disable 1D transforms - arrays here are 1D but their lengths/meanings
// don't match, between nodes and links
traceOut._length = null;
Expand Down
93 changes: 93 additions & 0 deletions src/traces/sankey/render.js
Original file line number Diff line number Diff line change
Expand Up @@ -534,6 +534,60 @@ function linkPath() {
return path;
}

// Builds the permanent-label string for a link from textinfo / texttemplate.
function linkTextGetter(trace) {
var linkAttr = trace.link;
var textinfo = linkAttr.textinfo;
var texttemplate = linkAttr.texttemplate;
var vFmt = linkAttr.valueformat;
var vSuf = linkAttr.valuesuffix;
var flags = (textinfo && textinfo !== 'none') ? textinfo.split('+') : [];

return function(l, i) {
var valueLabel = Lib.numberFormat(vFmt)(l.value) + vSuf;

var tt = Array.isArray(texttemplate) ? texttemplate[i] : texttemplate;
if(tt) {
return Lib.texttemplateString({
template: tt,
labels: {valueLabel: valueLabel},
data: [{
label: l.label,
value: l.value,
valueLabel: valueLabel,
source: l.source.label,
target: l.target.label,
customdata: l.customdata
}]
});
}

if(!flags.length) return '';
var parts = [];
if(flags.indexOf('label') !== -1 && l.label) parts.push(l.label);
if(flags.indexOf('value') !== -1) parts.push(valueLabel);
return parts.join('<br>');
};
}

// Positions a permanent link label at the link midpoint (layout frame) and keeps
// the glyphs upright.
function linkLabelTransform(d) {
var l = d.link;
var midX, midY;
if(l.circular) {
// same anchor as the hover label (see hoverCenterPosition in plot.js)
midX = (l.circularPathData.leftInnerExtent + l.circularPathData.rightInnerExtent) / 2;
midY = l.circularPathData.verticalFullExtent;
} else {
midX = (l.source.x1 + l.target.x0) / 2;
midY = (l.y0 + l.y1) / 2;
}
var p = d.parent;
var flip = p.horizontal ? '' : ('scale(-1,1)' + strRotate(90));
return strTranslate(midX, midY) + flip;
}

function nodeModel(d, n) {
var tc = tinycolor(n.color);
var zoneThicknessPad = c.nodePadAcross;
Expand Down Expand Up @@ -607,6 +661,11 @@ function updateNodeShapes(sankeyNode) {
function updateShapes(sankeyNode, sankeyLink) {
sankeyNode.call(updateNodeShapes);
sankeyLink.attr('d', linkPath());
var linkNode = sankeyLink.node();
if(linkNode) {
d3.select(linkNode.parentNode).selectAll('.' + c.cn.sankeyLinkLabel)
.attr('transform', linkLabelTransform);
}
}

function sizeNode(rect) {
Expand Down Expand Up @@ -966,6 +1025,40 @@ module.exports = function(gd, svg, calcData, layout, callbacks) {
.style('opacity', 0)
.remove();

var sankeyLinkLabel = sankeyLinks.selectAll('.' + c.cn.sankeyLinkLabel)
.data(function(d) {
var getText = linkTextGetter(d.trace);
var out = [];
d.graph.links.forEach(function(l, i) {
if(!l.value) return;
var txt = getText(l, i);
if(!txt) return;
var m = linkModel(d, l, i);
m.linkLabelText = txt;
out.push(m);
});
return out;
}, keyFun);

sankeyLinkLabel.enter()
.append('text')
.classed(c.cn.sankeyLinkLabel, true)
.attr('text-anchor', 'middle')
.attr('dominant-baseline', 'central')
.style('pointer-events', 'none');

sankeyLinkLabel
.attr('data-notex', 1)
.text(function(d) { return d.linkLabelText; })
.each(function(d) {
var e = d3.select(this);
Drawing.font(e, d.link.trace.link.textfont);
svgTextUtils.convertToTspans(e, gd);
})
.attr('transform', linkLabelTransform);

sankeyLinkLabel.exit().remove();

var sankeyNodeSet = sankey.selectAll('.' + c.cn.sankeyNodeSet)
.data(repeat, keyFun);

Expand Down