@fluentui/react-charting 5.19.23 → 5.19.24
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.json +24 -1
- package/CHANGELOG.md +11 -2
- package/bundle-size/AreaChart.fixture.js +9 -0
- package/bundle-size/VerticalBarChart.fixture.js +9 -0
- package/dist/react-charting.js +1 -1
- package/dist/react-charting.min.js +1 -1
- package/lib/components/SankeyChart/SankeyChart.base.d.ts +25 -11
- package/lib/components/SankeyChart/SankeyChart.base.js +151 -86
- package/lib/components/SankeyChart/SankeyChart.base.js.map +1 -1
- package/lib/version.js +1 -1
- package/lib/version.js.map +1 -1
- package/lib-amd/components/SankeyChart/SankeyChart.base.d.ts +25 -11
- package/lib-amd/components/SankeyChart/SankeyChart.base.js +156 -88
- package/lib-amd/components/SankeyChart/SankeyChart.base.js.map +1 -1
- package/lib-amd/version.js +1 -1
- package/lib-amd/version.js.map +1 -1
- package/lib-commonjs/components/SankeyChart/SankeyChart.base.d.ts +25 -11
- package/lib-commonjs/components/SankeyChart/SankeyChart.base.js +156 -88
- package/lib-commonjs/components/SankeyChart/SankeyChart.base.js.map +1 -1
- package/lib-commonjs/version.js +1 -1
- package/lib-commonjs/version.js.map +1 -1
- package/monosize.config.mjs +11 -0
- package/package.json +3 -2
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.SankeyChartBase = void 0;
|
|
3
|
+
exports.SankeyChartBase = exports.preRenderLayout = exports.adjustPadding = exports.groupNodesByColumn = void 0;
|
|
4
4
|
var tslib_1 = require("tslib");
|
|
5
5
|
var react_focus_1 = require("@fluentui/react-focus");
|
|
6
6
|
var Callout_1 = require("@fluentui/react/lib/Callout");
|
|
@@ -10,7 +10,7 @@ var d3_sankey_1 = require("d3-sankey");
|
|
|
10
10
|
var d3_selection_1 = require("d3-selection");
|
|
11
11
|
var d3_shape_1 = require("d3-shape");
|
|
12
12
|
var React = require("react");
|
|
13
|
-
var
|
|
13
|
+
var ChartHoverCard_1 = require("../../utilities/ChartHoverCard/ChartHoverCard");
|
|
14
14
|
var getClassNames = (0, Utilities_1.classNamesFunction)();
|
|
15
15
|
var PADDING_PERCENTAGE = 0.3;
|
|
16
16
|
var NON_SELECTED_NODE_AND_STREAM_COLOR = '#757575';
|
|
@@ -117,8 +117,8 @@ function getSelectedLinksforStreamHover(singleLink) {
|
|
|
117
117
|
/**
|
|
118
118
|
* This is used to group nodes by column index.
|
|
119
119
|
*/
|
|
120
|
-
|
|
121
|
-
|
|
120
|
+
// This is exported for unit tests.
|
|
121
|
+
function groupNodesByColumn(graph) {
|
|
122
122
|
var nodesInColumn = {};
|
|
123
123
|
graph.nodes.forEach(function (node) {
|
|
124
124
|
var columnId = node.layer;
|
|
@@ -131,21 +131,25 @@ function populateNodeInColumns(graph, sankey) {
|
|
|
131
131
|
});
|
|
132
132
|
return nodesInColumn;
|
|
133
133
|
}
|
|
134
|
+
exports.groupNodesByColumn = groupNodesByColumn;
|
|
134
135
|
/**
|
|
135
136
|
* This is used to normalize the nodes value whose value is less than 1% of the total column value.
|
|
136
137
|
*/
|
|
137
|
-
function adjustOnePercentHeightNodes(nodesInColumn) {
|
|
138
|
+
function adjustOnePercentHeightNodes(nodesInColumn, computedNodes, originalLinks) {
|
|
138
139
|
var totalColumnValue = Object.values(nodesInColumn).map(function (column) {
|
|
139
140
|
return (0, d3_array_1.sum)(column, function (node) { return node.value; });
|
|
140
141
|
});
|
|
141
142
|
totalColumnValue.forEach(function (columnValue, index) {
|
|
142
143
|
var totalPercentage = 0;
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
144
|
+
var onePercent = 0.01 * columnValue;
|
|
145
|
+
var columnNodes = nodesInColumn[index];
|
|
146
|
+
columnNodes.forEach(function (node) {
|
|
147
|
+
var value = computedNodes[node.nodeId];
|
|
148
|
+
var nodePercentage = (value / columnValue) * 100;
|
|
149
|
+
node.actualValue = value;
|
|
146
150
|
//if the value is less than 1% then we are making it as 1% of total .
|
|
147
151
|
if (nodePercentage < 1) {
|
|
148
|
-
node.value =
|
|
152
|
+
node.value = onePercent;
|
|
149
153
|
totalPercentage = totalPercentage + 1;
|
|
150
154
|
}
|
|
151
155
|
else {
|
|
@@ -155,41 +159,120 @@ function adjustOnePercentHeightNodes(nodesInColumn) {
|
|
|
155
159
|
//since we have adjusted the value to be 1% but we need to keep the sum of the percentage value under 100.
|
|
156
160
|
var scalingRatio = totalPercentage !== 0 ? totalPercentage / 100 : 1;
|
|
157
161
|
if (scalingRatio > 1) {
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
162
|
+
// Loop through each node in that column and scale that node--and its incoming and outgoing links--by the
|
|
163
|
+
// scaling ratio. We need the sankey diagram to re-layout the nodes and links after we do this.
|
|
164
|
+
columnNodes.forEach(function (node) {
|
|
165
|
+
var normalized = (node.value = node.value / scalingRatio);
|
|
166
|
+
// Which Original Value? and Which Normalized Value is needed, here? The Node? The Link? Both?
|
|
167
|
+
changeColumnValue(node, computedNodes[node.nodeId], normalized, originalLinks);
|
|
161
168
|
});
|
|
162
169
|
}
|
|
163
170
|
});
|
|
164
171
|
}
|
|
165
172
|
/**
|
|
166
|
-
* This is used for normalizing each
|
|
173
|
+
* This is used for normalizing each link's value to reflect the normalized node value.
|
|
167
174
|
*/
|
|
168
|
-
function changeColumnValue(node,
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
link.
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
175
|
+
function changeColumnValue(node, originalNodeValue, normalizedNodeValue, linkValues) {
|
|
176
|
+
// For each link in the source and target, compute the proportion that this link contributes to the total
|
|
177
|
+
// then adjust the link's value to reflect its proportion of the normalized node value.
|
|
178
|
+
var updateLinkValue = function (link) {
|
|
179
|
+
var value = linkValue(linkValues, link);
|
|
180
|
+
link.unnormalizedValue = value;
|
|
181
|
+
var linkRatio = value / originalNodeValue;
|
|
182
|
+
link.value = Math.max(normalizedNodeValue * linkRatio, link.value);
|
|
183
|
+
};
|
|
184
|
+
node.sourceLinks.forEach(updateLinkValue);
|
|
185
|
+
node.targetLinks.forEach(updateLinkValue);
|
|
179
186
|
}
|
|
180
187
|
/**
|
|
181
188
|
* This is used for calculating the node non normalized value based on link non normalized value.
|
|
189
|
+
* The links have the original weights. Computed nodes have the total weight of all incoming and outgoing links.
|
|
182
190
|
*/
|
|
183
|
-
function populateNodeActualValue(data) {
|
|
191
|
+
function populateNodeActualValue(data, computedNodes, originalLinks) {
|
|
184
192
|
data.links.forEach(function (link) {
|
|
185
193
|
if (!link.unnormalizedValue) {
|
|
186
|
-
link.unnormalizedValue = link
|
|
194
|
+
link.unnormalizedValue = linkValue(originalLinks, link);
|
|
187
195
|
}
|
|
188
196
|
});
|
|
189
197
|
data.nodes.forEach(function (node) {
|
|
190
|
-
node.actualValue =
|
|
198
|
+
node.actualValue = computedNodes[node.nodeId];
|
|
199
|
+
});
|
|
200
|
+
}
|
|
201
|
+
/**
|
|
202
|
+
* This is used to introduce dynamic padding for cases where the number of nodes in a column is huge
|
|
203
|
+
* so that we maintain a node to space ratio for such columns as if we fail to do so the
|
|
204
|
+
* chart is devoid of nodes and only shows links.
|
|
205
|
+
*/
|
|
206
|
+
// This is exported for unit tests
|
|
207
|
+
function adjustPadding(sankey, height, nodesInColumn) {
|
|
208
|
+
var padding = sankey.nodePadding();
|
|
209
|
+
var minPadding = PADDING_PERCENTAGE * height;
|
|
210
|
+
Object.values(nodesInColumn).forEach(function (column) {
|
|
211
|
+
var totalPaddingInColumn = height - (0, d3_array_1.sum)(column, function (node) { return node.y1 - node.y0; });
|
|
212
|
+
if (minPadding < totalPaddingInColumn) {
|
|
213
|
+
// Here we are calculating the min of default and calculated padding, we will not increase the padding
|
|
214
|
+
// in any scenario.
|
|
215
|
+
padding = Math.min(padding, minPadding / (column.length - 1));
|
|
216
|
+
}
|
|
191
217
|
});
|
|
218
|
+
sankey.nodePadding(padding);
|
|
192
219
|
}
|
|
220
|
+
exports.adjustPadding = adjustPadding;
|
|
221
|
+
function idFromNumberOrSNode(node) {
|
|
222
|
+
if (typeof node === 'number') {
|
|
223
|
+
return node;
|
|
224
|
+
}
|
|
225
|
+
return node.nodeId;
|
|
226
|
+
}
|
|
227
|
+
/**
|
|
228
|
+
* Duplicates the supplied chart data so that we do not alter the original.
|
|
229
|
+
* @param data The data to duplicate.
|
|
230
|
+
* @returns The duplicated data.
|
|
231
|
+
*/
|
|
232
|
+
function duplicateData(data) {
|
|
233
|
+
return {
|
|
234
|
+
nodes: data.nodes.map(function (node) { return (tslib_1.__assign({}, node)); }),
|
|
235
|
+
links: data.links.map(function (link) { return (tslib_1.__assign({}, link)); }),
|
|
236
|
+
};
|
|
237
|
+
}
|
|
238
|
+
function valuesOfNodes(nodes) {
|
|
239
|
+
var result = {};
|
|
240
|
+
nodes.forEach(function (node) {
|
|
241
|
+
result[node.nodeId] = node.value;
|
|
242
|
+
});
|
|
243
|
+
return result;
|
|
244
|
+
}
|
|
245
|
+
function valuesOfLinks(links) {
|
|
246
|
+
var result = {};
|
|
247
|
+
links.forEach(function (link) {
|
|
248
|
+
var sourceId = idFromNumberOrSNode(link.source);
|
|
249
|
+
var sourceToTarget = result[sourceId];
|
|
250
|
+
if (!sourceToTarget) {
|
|
251
|
+
sourceToTarget = {};
|
|
252
|
+
result[sourceId] = sourceToTarget;
|
|
253
|
+
}
|
|
254
|
+
sourceToTarget[idFromNumberOrSNode(link.target)] = link.value;
|
|
255
|
+
});
|
|
256
|
+
return result;
|
|
257
|
+
}
|
|
258
|
+
function linkValue(originalLinks, link) {
|
|
259
|
+
return originalLinks[idFromNumberOrSNode(link.source)][idFromNumberOrSNode(link.target)];
|
|
260
|
+
}
|
|
261
|
+
// This is exported for unit tests.
|
|
262
|
+
function preRenderLayout(margins, containerWidth, containerHeight, isRtl) {
|
|
263
|
+
var left = margins.left, right = margins.right, top = margins.top, bottom = margins.bottom;
|
|
264
|
+
var width = containerWidth - right;
|
|
265
|
+
var height = containerHeight - bottom > 0 ? containerHeight - bottom : 0;
|
|
266
|
+
var sankey = (0, d3_sankey_1.sankey)()
|
|
267
|
+
.nodeWidth(124)
|
|
268
|
+
.extent([
|
|
269
|
+
[left, top],
|
|
270
|
+
[width - 1, height - 6],
|
|
271
|
+
])
|
|
272
|
+
.nodeAlign(isRtl ? d3_sankey_1.sankeyRight : d3_sankey_1.sankeyJustify);
|
|
273
|
+
return { sankey: sankey, height: height, width: width };
|
|
274
|
+
}
|
|
275
|
+
exports.preRenderLayout = preRenderLayout;
|
|
193
276
|
var SankeyChartBase = /** @class */ (function (_super) {
|
|
194
277
|
tslib_1.__extends(SankeyChartBase, _super);
|
|
195
278
|
function SankeyChartBase(props) {
|
|
@@ -242,8 +325,9 @@ var SankeyChartBase = /** @class */ (function (_super) {
|
|
|
242
325
|
_this._calloutId = (0, Utilities_1.getId)('callout');
|
|
243
326
|
_this._linkId = (0, Utilities_1.getId)('link');
|
|
244
327
|
_this._margins = { top: 36, right: 48, bottom: 32, left: 48 };
|
|
245
|
-
_this.
|
|
246
|
-
|
|
328
|
+
_this._normalizeData = (0, Utilities_1.memoizeFunction)(function (data, containerWidth, containerHeight) {
|
|
329
|
+
return _this._normalizeSankeyData(data, containerWidth, containerHeight);
|
|
330
|
+
});
|
|
247
331
|
_this._emptyChartId = (0, Utilities_1.getId)('_SankeyChart_empty');
|
|
248
332
|
return _this;
|
|
249
333
|
}
|
|
@@ -270,18 +354,13 @@ var SankeyChartBase = /** @class */ (function (_super) {
|
|
|
270
354
|
pathColor: pathColor,
|
|
271
355
|
className: className,
|
|
272
356
|
});
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
this.
|
|
280
|
-
this._sankey(sankeyChartData);
|
|
281
|
-
populateNodeActualValue(sankeyChartData);
|
|
282
|
-
this._assignNodeColors();
|
|
283
|
-
var nodeData = this._createNodes(classNames);
|
|
284
|
-
var linkData = this._createLinks();
|
|
357
|
+
var _b = this._normalizeData(this.props.data.SankeyChartData, state.containerWidth, state.containerHeight), sankeyChartData = _b.data, width = _b.width, height = _b.height;
|
|
358
|
+
// NEXT: Move the following into _normalizeSankeyData to see if we can cache computation of
|
|
359
|
+
// colors, nodes, and links.
|
|
360
|
+
var dataNodes = sankeyChartData.nodes;
|
|
361
|
+
this._assignNodeColors(dataNodes); // This can probably safely move.
|
|
362
|
+
var nodeData = this._createNodes(classNames, dataNodes);
|
|
363
|
+
var linkData = this._createLinks(sankeyChartData.links);
|
|
285
364
|
var calloutProps = {
|
|
286
365
|
isCalloutVisible: state.isCalloutVisible,
|
|
287
366
|
directionalHint: Callout_1.DirectionalHint.topAutoEdge,
|
|
@@ -303,54 +382,43 @@ var SankeyChartBase = /** @class */ (function (_super) {
|
|
|
303
382
|
React.createElement("g", { className: classNames.links, strokeOpacity: 1 }, linkData),
|
|
304
383
|
React.createElement("g", { className: classNames.nodes }, nodeData),
|
|
305
384
|
calloutProps.isCalloutVisible && (React.createElement(Callout_1.Callout, tslib_1.__assign({}, calloutProps),
|
|
306
|
-
React.createElement(
|
|
385
|
+
React.createElement(ChartHoverCard_1.ChartHoverCard, { XValue: calloutProps.XValue, YValue: calloutProps.YValue, color: calloutProps.color, descriptionMessage: calloutProps.descriptionMessage ? calloutProps.descriptionMessage : '' })))))));
|
|
307
386
|
}
|
|
308
387
|
return (React.createElement("div", { id: this._emptyChartId, role: 'alert', style: { opacity: '0' }, "aria-label": 'Graph has no data to display' }));
|
|
309
388
|
};
|
|
310
|
-
SankeyChartBase.prototype.
|
|
311
|
-
var _a = this.
|
|
312
|
-
|
|
313
|
-
var
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
Object.values(nodesInColumn).forEach(function (column) {
|
|
337
|
-
var minPadding = PADDING_PERCENTAGE * height;
|
|
338
|
-
var toatlPaddingInColumn = height - (0, d3_array_1.sum)(column, function (node) { return node.y1 - node.y0; });
|
|
339
|
-
if (minPadding < toatlPaddingInColumn) {
|
|
340
|
-
//Here we are calculating the min of default and calculated padding, we will not increase the padding
|
|
341
|
-
//in any scenario.
|
|
342
|
-
padding = Math.min(padding, minPadding / (column.length - 1));
|
|
343
|
-
}
|
|
344
|
-
});
|
|
345
|
-
return padding;
|
|
389
|
+
SankeyChartBase.prototype._normalizeSankeyData = function (data, containerWidth, containerHeight) {
|
|
390
|
+
var _a = preRenderLayout(this._margins, containerWidth, containerHeight, this._isRtl), sankey = _a.sankey, height = _a.height, width = _a.width;
|
|
391
|
+
// Clone the data before mutating it (via the SankeyLayoutGenerator) so that we don't mutate the original data.
|
|
392
|
+
var transformed = duplicateData(data);
|
|
393
|
+
sankey(transformed);
|
|
394
|
+
var nodesInColumn = groupNodesByColumn(transformed);
|
|
395
|
+
// Keep track of the original values of the links and their acccumulated values in the nodes
|
|
396
|
+
// Setting these in external objects so they cannot be mutated by other code.
|
|
397
|
+
// The IDs of nodes can be numbers or strings. But, the IDs of links are always the index into the "nodes" array.
|
|
398
|
+
// After the sankey layout is computed, the each link's `source` and `target` will have the ID of the node in the
|
|
399
|
+
// type originally specified in the Nodes array. Consequently, we get the values of those links after the sankey
|
|
400
|
+
// transformation.
|
|
401
|
+
var nodeValues = valuesOfNodes(transformed.nodes);
|
|
402
|
+
var linkValues = valuesOfLinks(transformed.links);
|
|
403
|
+
adjustOnePercentHeightNodes(nodesInColumn, nodeValues, linkValues);
|
|
404
|
+
adjustPadding(sankey, height - 6, nodesInColumn);
|
|
405
|
+
// `sankey` is called a second time, probably to re-layout the nodes with the one-percent adjusted weights.
|
|
406
|
+
// NOTE: The second call to `sankey` is required to allow links to be hoverable.
|
|
407
|
+
// Without the second call, the links are not hoverable.
|
|
408
|
+
sankey(transformed);
|
|
409
|
+
populateNodeActualValue(transformed, nodeValues, linkValues);
|
|
410
|
+
return {
|
|
411
|
+
width: width,
|
|
412
|
+
height: height,
|
|
413
|
+
data: transformed,
|
|
414
|
+
};
|
|
346
415
|
};
|
|
347
|
-
SankeyChartBase.prototype._createLinks = function () {
|
|
416
|
+
SankeyChartBase.prototype._createLinks = function (dataLinks) {
|
|
348
417
|
var _this = this;
|
|
349
418
|
var links = [];
|
|
350
|
-
|
|
351
|
-
if (sankeyChartData) {
|
|
419
|
+
if (dataLinks) {
|
|
352
420
|
var linkId_1 = this._linkId;
|
|
353
|
-
|
|
421
|
+
dataLinks.forEach(function (singleLink, index) {
|
|
354
422
|
var onMouseOut = function () {
|
|
355
423
|
_this._onStreamLeave(singleLink);
|
|
356
424
|
};
|
|
@@ -383,13 +451,12 @@ var SankeyChartBase = /** @class */ (function (_super) {
|
|
|
383
451
|
}
|
|
384
452
|
return links;
|
|
385
453
|
};
|
|
386
|
-
SankeyChartBase.prototype._createNodes = function (classNames) {
|
|
454
|
+
SankeyChartBase.prototype._createNodes = function (classNames, dataNodes) {
|
|
387
455
|
var _this = this;
|
|
388
456
|
var nodes = [];
|
|
389
|
-
|
|
390
|
-
if (sankeyChartData) {
|
|
457
|
+
if (dataNodes) {
|
|
391
458
|
var state_1 = this.state;
|
|
392
|
-
|
|
459
|
+
dataNodes.forEach(function (singleNode, index) {
|
|
393
460
|
var onMouseOut = function () {
|
|
394
461
|
_this._onLeave(singleNode);
|
|
395
462
|
};
|
|
@@ -513,7 +580,7 @@ var SankeyChartBase = /** @class */ (function (_super) {
|
|
|
513
580
|
* This is used to assign node fillcolors and borderColor cyclically when the user doesnt
|
|
514
581
|
* provide color to individual node.
|
|
515
582
|
*/
|
|
516
|
-
SankeyChartBase.prototype._assignNodeColors = function () {
|
|
583
|
+
SankeyChartBase.prototype._assignNodeColors = function (nodes) {
|
|
517
584
|
var colors;
|
|
518
585
|
var borders;
|
|
519
586
|
var _a = this.props, colorsForNodes = _a.colorsForNodes, borderColorsForNodes = _a.borderColorsForNodes;
|
|
@@ -526,7 +593,7 @@ var SankeyChartBase = /** @class */ (function (_super) {
|
|
|
526
593
|
borders = DEFAULT_NODE_COLORS.map(function (color) { return color.borderColor; });
|
|
527
594
|
}
|
|
528
595
|
var currentIndex = 0;
|
|
529
|
-
|
|
596
|
+
nodes.forEach(function (node) {
|
|
530
597
|
if (!node.color && !node.borderColor) {
|
|
531
598
|
node.color = colors[currentIndex];
|
|
532
599
|
node.borderColor = borders[currentIndex];
|
|
@@ -585,6 +652,7 @@ var SankeyChartBase = /** @class */ (function (_super) {
|
|
|
585
652
|
var _this = this;
|
|
586
653
|
var _a = this.state, containerWidth = _a.containerWidth, containerHeight = _a.containerHeight;
|
|
587
654
|
this._reqID = requestAnimationFrame(function () {
|
|
655
|
+
// NOTE: Calls to this method trigger a re-render.
|
|
588
656
|
var container = _this.props.parentRef ? _this.props.parentRef : _this.chartContainer;
|
|
589
657
|
var currentContainerWidth = container && container.getBoundingClientRect().width;
|
|
590
658
|
var currentContainerHeight = container && container.getBoundingClientRect().height;
|