@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,7 +1,7 @@
|
|
|
1
|
-
define(["require", "exports", "tslib", "@fluentui/react-focus", "@fluentui/react/lib/Callout", "@fluentui/react/lib/Utilities", "d3-array", "d3-sankey", "d3-selection", "d3-shape", "react", "../../
|
|
1
|
+
define(["require", "exports", "tslib", "@fluentui/react-focus", "@fluentui/react/lib/Callout", "@fluentui/react/lib/Utilities", "d3-array", "d3-sankey", "d3-selection", "d3-shape", "react", "../../utilities/ChartHoverCard/ChartHoverCard"], function (require, exports, tslib_1, react_focus_1, Callout_1, Utilities_1, d3_array_1, d3_sankey_1, d3_selection_1, d3_shape_1, React, ChartHoverCard_1) {
|
|
2
2
|
"use strict";
|
|
3
3
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
4
|
-
exports.SankeyChartBase = void 0;
|
|
4
|
+
exports.SankeyChartBase = exports.preRenderLayout = exports.adjustPadding = exports.groupNodesByColumn = void 0;
|
|
5
5
|
var getClassNames = (0, Utilities_1.classNamesFunction)();
|
|
6
6
|
var PADDING_PERCENTAGE = 0.3;
|
|
7
7
|
var NON_SELECTED_NODE_AND_STREAM_COLOR = '#757575';
|
|
@@ -108,8 +108,8 @@ define(["require", "exports", "tslib", "@fluentui/react-focus", "@fluentui/react
|
|
|
108
108
|
/**
|
|
109
109
|
* This is used to group nodes by column index.
|
|
110
110
|
*/
|
|
111
|
-
|
|
112
|
-
|
|
111
|
+
// This is exported for unit tests.
|
|
112
|
+
function groupNodesByColumn(graph) {
|
|
113
113
|
var nodesInColumn = {};
|
|
114
114
|
graph.nodes.forEach(function (node) {
|
|
115
115
|
var columnId = node.layer;
|
|
@@ -122,21 +122,25 @@ define(["require", "exports", "tslib", "@fluentui/react-focus", "@fluentui/react
|
|
|
122
122
|
});
|
|
123
123
|
return nodesInColumn;
|
|
124
124
|
}
|
|
125
|
+
exports.groupNodesByColumn = groupNodesByColumn;
|
|
125
126
|
/**
|
|
126
127
|
* This is used to normalize the nodes value whose value is less than 1% of the total column value.
|
|
127
128
|
*/
|
|
128
|
-
function adjustOnePercentHeightNodes(nodesInColumn) {
|
|
129
|
+
function adjustOnePercentHeightNodes(nodesInColumn, computedNodes, originalLinks) {
|
|
129
130
|
var totalColumnValue = Object.values(nodesInColumn).map(function (column) {
|
|
130
131
|
return (0, d3_array_1.sum)(column, function (node) { return node.value; });
|
|
131
132
|
});
|
|
132
133
|
totalColumnValue.forEach(function (columnValue, index) {
|
|
133
134
|
var totalPercentage = 0;
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
135
|
+
var onePercent = 0.01 * columnValue;
|
|
136
|
+
var columnNodes = nodesInColumn[index];
|
|
137
|
+
columnNodes.forEach(function (node) {
|
|
138
|
+
var value = computedNodes[node.nodeId];
|
|
139
|
+
var nodePercentage = (value / columnValue) * 100;
|
|
140
|
+
node.actualValue = value;
|
|
137
141
|
//if the value is less than 1% then we are making it as 1% of total .
|
|
138
142
|
if (nodePercentage < 1) {
|
|
139
|
-
node.value =
|
|
143
|
+
node.value = onePercent;
|
|
140
144
|
totalPercentage = totalPercentage + 1;
|
|
141
145
|
}
|
|
142
146
|
else {
|
|
@@ -146,41 +150,120 @@ define(["require", "exports", "tslib", "@fluentui/react-focus", "@fluentui/react
|
|
|
146
150
|
//since we have adjusted the value to be 1% but we need to keep the sum of the percentage value under 100.
|
|
147
151
|
var scalingRatio = totalPercentage !== 0 ? totalPercentage / 100 : 1;
|
|
148
152
|
if (scalingRatio > 1) {
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
153
|
+
// Loop through each node in that column and scale that node--and its incoming and outgoing links--by the
|
|
154
|
+
// scaling ratio. We need the sankey diagram to re-layout the nodes and links after we do this.
|
|
155
|
+
columnNodes.forEach(function (node) {
|
|
156
|
+
var normalized = (node.value = node.value / scalingRatio);
|
|
157
|
+
// Which Original Value? and Which Normalized Value is needed, here? The Node? The Link? Both?
|
|
158
|
+
changeColumnValue(node, computedNodes[node.nodeId], normalized, originalLinks);
|
|
152
159
|
});
|
|
153
160
|
}
|
|
154
161
|
});
|
|
155
162
|
}
|
|
156
163
|
/**
|
|
157
|
-
* This is used for normalizing each
|
|
164
|
+
* This is used for normalizing each link's value to reflect the normalized node value.
|
|
158
165
|
*/
|
|
159
|
-
function changeColumnValue(node,
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
link.
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
166
|
+
function changeColumnValue(node, originalNodeValue, normalizedNodeValue, linkValues) {
|
|
167
|
+
// For each link in the source and target, compute the proportion that this link contributes to the total
|
|
168
|
+
// then adjust the link's value to reflect its proportion of the normalized node value.
|
|
169
|
+
var updateLinkValue = function (link) {
|
|
170
|
+
var value = linkValue(linkValues, link);
|
|
171
|
+
link.unnormalizedValue = value;
|
|
172
|
+
var linkRatio = value / originalNodeValue;
|
|
173
|
+
link.value = Math.max(normalizedNodeValue * linkRatio, link.value);
|
|
174
|
+
};
|
|
175
|
+
node.sourceLinks.forEach(updateLinkValue);
|
|
176
|
+
node.targetLinks.forEach(updateLinkValue);
|
|
170
177
|
}
|
|
171
178
|
/**
|
|
172
179
|
* This is used for calculating the node non normalized value based on link non normalized value.
|
|
180
|
+
* The links have the original weights. Computed nodes have the total weight of all incoming and outgoing links.
|
|
173
181
|
*/
|
|
174
|
-
function populateNodeActualValue(data) {
|
|
182
|
+
function populateNodeActualValue(data, computedNodes, originalLinks) {
|
|
175
183
|
data.links.forEach(function (link) {
|
|
176
184
|
if (!link.unnormalizedValue) {
|
|
177
|
-
link.unnormalizedValue = link
|
|
185
|
+
link.unnormalizedValue = linkValue(originalLinks, link);
|
|
178
186
|
}
|
|
179
187
|
});
|
|
180
188
|
data.nodes.forEach(function (node) {
|
|
181
|
-
node.actualValue =
|
|
189
|
+
node.actualValue = computedNodes[node.nodeId];
|
|
190
|
+
});
|
|
191
|
+
}
|
|
192
|
+
/**
|
|
193
|
+
* This is used to introduce dynamic padding for cases where the number of nodes in a column is huge
|
|
194
|
+
* so that we maintain a node to space ratio for such columns as if we fail to do so the
|
|
195
|
+
* chart is devoid of nodes and only shows links.
|
|
196
|
+
*/
|
|
197
|
+
// This is exported for unit tests
|
|
198
|
+
function adjustPadding(sankey, height, nodesInColumn) {
|
|
199
|
+
var padding = sankey.nodePadding();
|
|
200
|
+
var minPadding = PADDING_PERCENTAGE * height;
|
|
201
|
+
Object.values(nodesInColumn).forEach(function (column) {
|
|
202
|
+
var totalPaddingInColumn = height - (0, d3_array_1.sum)(column, function (node) { return node.y1 - node.y0; });
|
|
203
|
+
if (minPadding < totalPaddingInColumn) {
|
|
204
|
+
// Here we are calculating the min of default and calculated padding, we will not increase the padding
|
|
205
|
+
// in any scenario.
|
|
206
|
+
padding = Math.min(padding, minPadding / (column.length - 1));
|
|
207
|
+
}
|
|
182
208
|
});
|
|
209
|
+
sankey.nodePadding(padding);
|
|
183
210
|
}
|
|
211
|
+
exports.adjustPadding = adjustPadding;
|
|
212
|
+
function idFromNumberOrSNode(node) {
|
|
213
|
+
if (typeof node === 'number') {
|
|
214
|
+
return node;
|
|
215
|
+
}
|
|
216
|
+
return node.nodeId;
|
|
217
|
+
}
|
|
218
|
+
/**
|
|
219
|
+
* Duplicates the supplied chart data so that we do not alter the original.
|
|
220
|
+
* @param data The data to duplicate.
|
|
221
|
+
* @returns The duplicated data.
|
|
222
|
+
*/
|
|
223
|
+
function duplicateData(data) {
|
|
224
|
+
return {
|
|
225
|
+
nodes: data.nodes.map(function (node) { return (tslib_1.__assign({}, node)); }),
|
|
226
|
+
links: data.links.map(function (link) { return (tslib_1.__assign({}, link)); }),
|
|
227
|
+
};
|
|
228
|
+
}
|
|
229
|
+
function valuesOfNodes(nodes) {
|
|
230
|
+
var result = {};
|
|
231
|
+
nodes.forEach(function (node) {
|
|
232
|
+
result[node.nodeId] = node.value;
|
|
233
|
+
});
|
|
234
|
+
return result;
|
|
235
|
+
}
|
|
236
|
+
function valuesOfLinks(links) {
|
|
237
|
+
var result = {};
|
|
238
|
+
links.forEach(function (link) {
|
|
239
|
+
var sourceId = idFromNumberOrSNode(link.source);
|
|
240
|
+
var sourceToTarget = result[sourceId];
|
|
241
|
+
if (!sourceToTarget) {
|
|
242
|
+
sourceToTarget = {};
|
|
243
|
+
result[sourceId] = sourceToTarget;
|
|
244
|
+
}
|
|
245
|
+
sourceToTarget[idFromNumberOrSNode(link.target)] = link.value;
|
|
246
|
+
});
|
|
247
|
+
return result;
|
|
248
|
+
}
|
|
249
|
+
function linkValue(originalLinks, link) {
|
|
250
|
+
return originalLinks[idFromNumberOrSNode(link.source)][idFromNumberOrSNode(link.target)];
|
|
251
|
+
}
|
|
252
|
+
// This is exported for unit tests.
|
|
253
|
+
function preRenderLayout(margins, containerWidth, containerHeight, isRtl) {
|
|
254
|
+
var left = margins.left, right = margins.right, top = margins.top, bottom = margins.bottom;
|
|
255
|
+
var width = containerWidth - right;
|
|
256
|
+
var height = containerHeight - bottom > 0 ? containerHeight - bottom : 0;
|
|
257
|
+
var sankey = (0, d3_sankey_1.sankey)()
|
|
258
|
+
.nodeWidth(124)
|
|
259
|
+
.extent([
|
|
260
|
+
[left, top],
|
|
261
|
+
[width - 1, height - 6],
|
|
262
|
+
])
|
|
263
|
+
.nodeAlign(isRtl ? d3_sankey_1.sankeyRight : d3_sankey_1.sankeyJustify);
|
|
264
|
+
return { sankey: sankey, height: height, width: width };
|
|
265
|
+
}
|
|
266
|
+
exports.preRenderLayout = preRenderLayout;
|
|
184
267
|
var SankeyChartBase = /** @class */ (function (_super) {
|
|
185
268
|
tslib_1.__extends(SankeyChartBase, _super);
|
|
186
269
|
function SankeyChartBase(props) {
|
|
@@ -233,8 +316,9 @@ define(["require", "exports", "tslib", "@fluentui/react-focus", "@fluentui/react
|
|
|
233
316
|
_this._calloutId = (0, Utilities_1.getId)('callout');
|
|
234
317
|
_this._linkId = (0, Utilities_1.getId)('link');
|
|
235
318
|
_this._margins = { top: 36, right: 48, bottom: 32, left: 48 };
|
|
236
|
-
_this.
|
|
237
|
-
|
|
319
|
+
_this._normalizeData = (0, Utilities_1.memoizeFunction)(function (data, containerWidth, containerHeight) {
|
|
320
|
+
return _this._normalizeSankeyData(data, containerWidth, containerHeight);
|
|
321
|
+
});
|
|
238
322
|
_this._emptyChartId = (0, Utilities_1.getId)('_SankeyChart_empty');
|
|
239
323
|
return _this;
|
|
240
324
|
}
|
|
@@ -261,18 +345,13 @@ define(["require", "exports", "tslib", "@fluentui/react-focus", "@fluentui/react
|
|
|
261
345
|
pathColor: pathColor,
|
|
262
346
|
className: className,
|
|
263
347
|
});
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
this.
|
|
271
|
-
this._sankey(sankeyChartData);
|
|
272
|
-
populateNodeActualValue(sankeyChartData);
|
|
273
|
-
this._assignNodeColors();
|
|
274
|
-
var nodeData = this._createNodes(classNames);
|
|
275
|
-
var linkData = this._createLinks();
|
|
348
|
+
var _b = this._normalizeData(this.props.data.SankeyChartData, state.containerWidth, state.containerHeight), sankeyChartData = _b.data, width = _b.width, height = _b.height;
|
|
349
|
+
// NEXT: Move the following into _normalizeSankeyData to see if we can cache computation of
|
|
350
|
+
// colors, nodes, and links.
|
|
351
|
+
var dataNodes = sankeyChartData.nodes;
|
|
352
|
+
this._assignNodeColors(dataNodes); // This can probably safely move.
|
|
353
|
+
var nodeData = this._createNodes(classNames, dataNodes);
|
|
354
|
+
var linkData = this._createLinks(sankeyChartData.links);
|
|
276
355
|
var calloutProps = {
|
|
277
356
|
isCalloutVisible: state.isCalloutVisible,
|
|
278
357
|
directionalHint: Callout_1.DirectionalHint.topAutoEdge,
|
|
@@ -294,54 +373,43 @@ define(["require", "exports", "tslib", "@fluentui/react-focus", "@fluentui/react
|
|
|
294
373
|
React.createElement("g", { className: classNames.links, strokeOpacity: 1 }, linkData),
|
|
295
374
|
React.createElement("g", { className: classNames.nodes }, nodeData),
|
|
296
375
|
calloutProps.isCalloutVisible && (React.createElement(Callout_1.Callout, tslib_1.__assign({}, calloutProps),
|
|
297
|
-
React.createElement(
|
|
376
|
+
React.createElement(ChartHoverCard_1.ChartHoverCard, { XValue: calloutProps.XValue, YValue: calloutProps.YValue, color: calloutProps.color, descriptionMessage: calloutProps.descriptionMessage ? calloutProps.descriptionMessage : '' })))))));
|
|
298
377
|
}
|
|
299
378
|
return (React.createElement("div", { id: this._emptyChartId, role: 'alert', style: { opacity: '0' }, "aria-label": 'Graph has no data to display' }));
|
|
300
379
|
};
|
|
301
|
-
SankeyChartBase.prototype.
|
|
302
|
-
var _a = this.
|
|
303
|
-
|
|
304
|
-
var
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
Object.values(nodesInColumn).forEach(function (column) {
|
|
328
|
-
var minPadding = PADDING_PERCENTAGE * height;
|
|
329
|
-
var toatlPaddingInColumn = height - (0, d3_array_1.sum)(column, function (node) { return node.y1 - node.y0; });
|
|
330
|
-
if (minPadding < toatlPaddingInColumn) {
|
|
331
|
-
//Here we are calculating the min of default and calculated padding, we will not increase the padding
|
|
332
|
-
//in any scenario.
|
|
333
|
-
padding = Math.min(padding, minPadding / (column.length - 1));
|
|
334
|
-
}
|
|
335
|
-
});
|
|
336
|
-
return padding;
|
|
380
|
+
SankeyChartBase.prototype._normalizeSankeyData = function (data, containerWidth, containerHeight) {
|
|
381
|
+
var _a = preRenderLayout(this._margins, containerWidth, containerHeight, this._isRtl), sankey = _a.sankey, height = _a.height, width = _a.width;
|
|
382
|
+
// Clone the data before mutating it (via the SankeyLayoutGenerator) so that we don't mutate the original data.
|
|
383
|
+
var transformed = duplicateData(data);
|
|
384
|
+
sankey(transformed);
|
|
385
|
+
var nodesInColumn = groupNodesByColumn(transformed);
|
|
386
|
+
// Keep track of the original values of the links and their acccumulated values in the nodes
|
|
387
|
+
// Setting these in external objects so they cannot be mutated by other code.
|
|
388
|
+
// The IDs of nodes can be numbers or strings. But, the IDs of links are always the index into the "nodes" array.
|
|
389
|
+
// After the sankey layout is computed, the each link's `source` and `target` will have the ID of the node in the
|
|
390
|
+
// type originally specified in the Nodes array. Consequently, we get the values of those links after the sankey
|
|
391
|
+
// transformation.
|
|
392
|
+
var nodeValues = valuesOfNodes(transformed.nodes);
|
|
393
|
+
var linkValues = valuesOfLinks(transformed.links);
|
|
394
|
+
adjustOnePercentHeightNodes(nodesInColumn, nodeValues, linkValues);
|
|
395
|
+
adjustPadding(sankey, height - 6, nodesInColumn);
|
|
396
|
+
// `sankey` is called a second time, probably to re-layout the nodes with the one-percent adjusted weights.
|
|
397
|
+
// NOTE: The second call to `sankey` is required to allow links to be hoverable.
|
|
398
|
+
// Without the second call, the links are not hoverable.
|
|
399
|
+
sankey(transformed);
|
|
400
|
+
populateNodeActualValue(transformed, nodeValues, linkValues);
|
|
401
|
+
return {
|
|
402
|
+
width: width,
|
|
403
|
+
height: height,
|
|
404
|
+
data: transformed,
|
|
405
|
+
};
|
|
337
406
|
};
|
|
338
|
-
SankeyChartBase.prototype._createLinks = function () {
|
|
407
|
+
SankeyChartBase.prototype._createLinks = function (dataLinks) {
|
|
339
408
|
var _this = this;
|
|
340
409
|
var links = [];
|
|
341
|
-
|
|
342
|
-
if (sankeyChartData) {
|
|
410
|
+
if (dataLinks) {
|
|
343
411
|
var linkId_1 = this._linkId;
|
|
344
|
-
|
|
412
|
+
dataLinks.forEach(function (singleLink, index) {
|
|
345
413
|
var onMouseOut = function () {
|
|
346
414
|
_this._onStreamLeave(singleLink);
|
|
347
415
|
};
|
|
@@ -374,13 +442,12 @@ define(["require", "exports", "tslib", "@fluentui/react-focus", "@fluentui/react
|
|
|
374
442
|
}
|
|
375
443
|
return links;
|
|
376
444
|
};
|
|
377
|
-
SankeyChartBase.prototype._createNodes = function (classNames) {
|
|
445
|
+
SankeyChartBase.prototype._createNodes = function (classNames, dataNodes) {
|
|
378
446
|
var _this = this;
|
|
379
447
|
var nodes = [];
|
|
380
|
-
|
|
381
|
-
if (sankeyChartData) {
|
|
448
|
+
if (dataNodes) {
|
|
382
449
|
var state_1 = this.state;
|
|
383
|
-
|
|
450
|
+
dataNodes.forEach(function (singleNode, index) {
|
|
384
451
|
var onMouseOut = function () {
|
|
385
452
|
_this._onLeave(singleNode);
|
|
386
453
|
};
|
|
@@ -504,7 +571,7 @@ define(["require", "exports", "tslib", "@fluentui/react-focus", "@fluentui/react
|
|
|
504
571
|
* This is used to assign node fillcolors and borderColor cyclically when the user doesnt
|
|
505
572
|
* provide color to individual node.
|
|
506
573
|
*/
|
|
507
|
-
SankeyChartBase.prototype._assignNodeColors = function () {
|
|
574
|
+
SankeyChartBase.prototype._assignNodeColors = function (nodes) {
|
|
508
575
|
var colors;
|
|
509
576
|
var borders;
|
|
510
577
|
var _a = this.props, colorsForNodes = _a.colorsForNodes, borderColorsForNodes = _a.borderColorsForNodes;
|
|
@@ -517,7 +584,7 @@ define(["require", "exports", "tslib", "@fluentui/react-focus", "@fluentui/react
|
|
|
517
584
|
borders = DEFAULT_NODE_COLORS.map(function (color) { return color.borderColor; });
|
|
518
585
|
}
|
|
519
586
|
var currentIndex = 0;
|
|
520
|
-
|
|
587
|
+
nodes.forEach(function (node) {
|
|
521
588
|
if (!node.color && !node.borderColor) {
|
|
522
589
|
node.color = colors[currentIndex];
|
|
523
590
|
node.borderColor = borders[currentIndex];
|
|
@@ -576,6 +643,7 @@ define(["require", "exports", "tslib", "@fluentui/react-focus", "@fluentui/react
|
|
|
576
643
|
var _this = this;
|
|
577
644
|
var _a = this.state, containerWidth = _a.containerWidth, containerHeight = _a.containerHeight;
|
|
578
645
|
this._reqID = requestAnimationFrame(function () {
|
|
646
|
+
// NOTE: Calls to this method trigger a re-render.
|
|
579
647
|
var container = _this.props.parentRef ? _this.props.parentRef : _this.chartContainer;
|
|
580
648
|
var currentContainerWidth = container && container.getBoundingClientRect().width;
|
|
581
649
|
var currentContainerHeight = container && container.getBoundingClientRect().height;
|