Carga
Carga
This commit is contained in:
@@ -0,0 +1,449 @@
|
||||
/* *
|
||||
*
|
||||
* (c) 2010-2021 Torstein Honsi
|
||||
*
|
||||
* License: www.highcharts.com/license
|
||||
*
|
||||
* !!!!!!! SOURCE GETS TRANSPILED BY TYPESCRIPT. EDIT TS FILE ONLY. !!!!!!!
|
||||
*
|
||||
* */
|
||||
'use strict';
|
||||
import DataLabel from '../../Core/Series/DataLabel.js';
|
||||
import H from '../../Core/Globals.js';
|
||||
var noop = H.noop;
|
||||
import R from '../../Core/Renderer/RendererUtilities.js';
|
||||
var distribute = R.distribute;
|
||||
import SeriesRegistry from '../../Core/Series/SeriesRegistry.js';
|
||||
var Series = SeriesRegistry.series;
|
||||
import U from '../../Core/Utilities.js';
|
||||
var arrayMax = U.arrayMax, clamp = U.clamp, defined = U.defined, merge = U.merge, pick = U.pick, relativeLength = U.relativeLength;
|
||||
/* *
|
||||
*
|
||||
* Composition
|
||||
*
|
||||
* */
|
||||
var ColumnDataLabel;
|
||||
(function (ColumnDataLabel) {
|
||||
/* *
|
||||
*
|
||||
* Constants
|
||||
*
|
||||
* */
|
||||
var composedClasses = [];
|
||||
var dataLabelPositioners = {
|
||||
// Based on the value computed in Highcharts' distribute algorithm.
|
||||
radialDistributionY: function (point) {
|
||||
return point.top + point.distributeBox.pos;
|
||||
},
|
||||
// get the x - use the natural x position for labels near the
|
||||
// top and bottom, to prevent the top and botton slice
|
||||
// connectors from touching each other on either side
|
||||
// Based on the value computed in Highcharts' distribute algorithm.
|
||||
radialDistributionX: function (series, point, y, naturalY) {
|
||||
return series.getX(y < point.top + 2 || y > point.bottom - 2 ?
|
||||
naturalY :
|
||||
y, point.half, point);
|
||||
},
|
||||
// dataLabels.distance determines the x position of the label
|
||||
justify: function (point, radius, seriesCenter) {
|
||||
return seriesCenter[0] + (point.half ? -1 : 1) *
|
||||
(radius + point.labelDistance);
|
||||
},
|
||||
// Left edges of the left-half labels touch the left edge of the plot
|
||||
// area. Right edges of the right-half labels touch the right edge of
|
||||
// the plot area.
|
||||
alignToPlotEdges: function (dataLabel, half, plotWidth, plotLeft) {
|
||||
var dataLabelWidth = dataLabel.getBBox().width;
|
||||
return half ? dataLabelWidth + plotLeft :
|
||||
plotWidth - dataLabelWidth - plotLeft;
|
||||
},
|
||||
// Connectors of each side end in the same x position. Labels are
|
||||
// aligned to them. Left edge of the widest left-half label touches the
|
||||
// left edge of the plot area. Right edge of the widest right-half label
|
||||
// touches the right edge of the plot area.
|
||||
alignToConnectors: function (points, half, plotWidth, plotLeft) {
|
||||
var maxDataLabelWidth = 0, dataLabelWidth;
|
||||
// find widest data label
|
||||
points.forEach(function (point) {
|
||||
dataLabelWidth = point.dataLabel.getBBox().width;
|
||||
if (dataLabelWidth > maxDataLabelWidth) {
|
||||
maxDataLabelWidth = dataLabelWidth;
|
||||
}
|
||||
});
|
||||
return half ? maxDataLabelWidth + plotLeft :
|
||||
plotWidth - maxDataLabelWidth - plotLeft;
|
||||
}
|
||||
};
|
||||
/* *
|
||||
*
|
||||
* Functions
|
||||
*
|
||||
* */
|
||||
/* eslint-disable valid-jsdoc */
|
||||
/** @private */
|
||||
function compose(PieSeriesClass) {
|
||||
DataLabel.compose(Series);
|
||||
if (composedClasses.indexOf(PieSeriesClass) === -1) {
|
||||
composedClasses.push(PieSeriesClass);
|
||||
var pieProto = PieSeriesClass.prototype;
|
||||
pieProto.dataLabelPositioners = dataLabelPositioners;
|
||||
pieProto.alignDataLabel = noop;
|
||||
pieProto.drawDataLabels = drawDataLabels;
|
||||
pieProto.placeDataLabels = placeDataLabels;
|
||||
pieProto.verifyDataLabelOverflow = verifyDataLabelOverflow;
|
||||
}
|
||||
}
|
||||
ColumnDataLabel.compose = compose;
|
||||
/**
|
||||
* Override the base drawDataLabels method by pie specific functionality
|
||||
* @private
|
||||
*/
|
||||
function drawDataLabels() {
|
||||
var series = this, data = series.data, chart = series.chart, options = series.options.dataLabels || {}, connectorPadding = options.connectorPadding, plotWidth = chart.plotWidth, plotHeight = chart.plotHeight, plotLeft = chart.plotLeft, maxWidth = Math.round(chart.chartWidth / 3), seriesCenter = series.center, radius = seriesCenter[2] / 2, centerY = seriesCenter[1], halves = [
|
||||
[],
|
||||
[] // left
|
||||
], overflow = [0, 0, 0, 0], // top, right, bottom, left
|
||||
dataLabelPositioners = series.dataLabelPositioners;
|
||||
var point, connectorWidth, connector, dataLabel, dataLabelWidth,
|
||||
// labelPos,
|
||||
labelPosition, labelHeight,
|
||||
// divide the points into right and left halves for anti collision
|
||||
x, y, visibility, j, pointDataLabelsOptions;
|
||||
// get out if not enabled
|
||||
if (!series.visible ||
|
||||
(!options.enabled &&
|
||||
!series._hasPointLabels)) {
|
||||
return;
|
||||
}
|
||||
// Reset all labels that have been shortened
|
||||
data.forEach(function (point) {
|
||||
if (point.dataLabel && point.visible && point.dataLabel.shortened) {
|
||||
point.dataLabel
|
||||
.attr({
|
||||
width: 'auto'
|
||||
}).css({
|
||||
width: 'auto',
|
||||
textOverflow: 'clip'
|
||||
});
|
||||
point.dataLabel.shortened = false;
|
||||
}
|
||||
});
|
||||
// run parent method
|
||||
Series.prototype.drawDataLabels.apply(series);
|
||||
data.forEach(function (point) {
|
||||
if (point.dataLabel) {
|
||||
if (point.visible) { // #407, #2510
|
||||
// Arrange points for detection collision
|
||||
halves[point.half].push(point);
|
||||
// Reset positions (#4905)
|
||||
point.dataLabel._pos = null;
|
||||
// Avoid long labels squeezing the pie size too far down
|
||||
if (!defined(options.style.width) &&
|
||||
!defined(point.options.dataLabels &&
|
||||
point.options.dataLabels.style &&
|
||||
point.options.dataLabels.style.width)) {
|
||||
if (point.dataLabel.getBBox().width > maxWidth) {
|
||||
point.dataLabel.css({
|
||||
// Use a fraction of the maxWidth to avoid
|
||||
// wrapping close to the end of the string.
|
||||
width: Math.round(maxWidth * 0.7) + 'px'
|
||||
});
|
||||
point.dataLabel.shortened = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
else {
|
||||
point.dataLabel = point.dataLabel.destroy();
|
||||
// Workaround to make pies destroy multiple datalabels
|
||||
// correctly. This logic needs rewriting to support multiple
|
||||
// datalabels fully.
|
||||
if (point.dataLabels && point.dataLabels.length === 1) {
|
||||
delete point.dataLabels;
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
/* Loop over the points in each half, starting from the top and bottom
|
||||
* of the pie to detect overlapping labels.
|
||||
*/
|
||||
halves.forEach(function (points, i) {
|
||||
var length = points.length, positions = [];
|
||||
var top, bottom, naturalY, sideOverflow, size, distributionLength;
|
||||
if (!length) {
|
||||
return;
|
||||
}
|
||||
// Sort by angle
|
||||
series.sortByAngle(points, i - 0.5);
|
||||
// Only do anti-collision when we have dataLabels outside the pie
|
||||
// and have connectors. (#856)
|
||||
if (series.maxLabelDistance > 0) {
|
||||
top = Math.max(0, centerY - radius - series.maxLabelDistance);
|
||||
bottom = Math.min(centerY + radius + series.maxLabelDistance, chart.plotHeight);
|
||||
points.forEach(function (point) {
|
||||
// check if specific points' label is outside the pie
|
||||
if (point.labelDistance > 0 && point.dataLabel) {
|
||||
// point.top depends on point.labelDistance value
|
||||
// Used for calculation of y value in getX method
|
||||
point.top = Math.max(0, centerY - radius - point.labelDistance);
|
||||
point.bottom = Math.min(centerY + radius + point.labelDistance, chart.plotHeight);
|
||||
size = point.dataLabel.getBBox().height || 21;
|
||||
// point.positionsIndex is needed for getting index of
|
||||
// parameter related to specific point inside positions
|
||||
// array - not every point is in positions array.
|
||||
point.distributeBox = {
|
||||
target: point.labelPosition.natural.y -
|
||||
point.top + size / 2,
|
||||
size: size,
|
||||
rank: point.y
|
||||
};
|
||||
positions.push(point.distributeBox);
|
||||
}
|
||||
});
|
||||
distributionLength = bottom + size - top;
|
||||
distribute(positions, distributionLength, distributionLength / 5);
|
||||
}
|
||||
// Now the used slots are sorted, fill them up sequentially
|
||||
for (j = 0; j < length; j++) {
|
||||
point = points[j];
|
||||
// labelPos = point.labelPos;
|
||||
labelPosition = point.labelPosition;
|
||||
dataLabel = point.dataLabel;
|
||||
visibility = point.visible === false ? 'hidden' : 'inherit';
|
||||
naturalY = labelPosition.natural.y;
|
||||
y = naturalY;
|
||||
if (positions && defined(point.distributeBox)) {
|
||||
if (typeof point.distributeBox.pos === 'undefined') {
|
||||
visibility = 'hidden';
|
||||
}
|
||||
else {
|
||||
labelHeight = point.distributeBox.size;
|
||||
// Find label's y position
|
||||
y = dataLabelPositioners
|
||||
.radialDistributionY(point);
|
||||
}
|
||||
}
|
||||
// It is needed to delete point.positionIndex for
|
||||
// dynamically added points etc.
|
||||
delete point.positionIndex; // @todo unused
|
||||
// Find label's x position
|
||||
// justify is undocumented in the API - preserve support for it
|
||||
if (options.justify) {
|
||||
x = dataLabelPositioners.justify(point, radius, seriesCenter);
|
||||
}
|
||||
else {
|
||||
switch (options.alignTo) {
|
||||
case 'connectors':
|
||||
x = dataLabelPositioners.alignToConnectors(points, i, plotWidth, plotLeft);
|
||||
break;
|
||||
case 'plotEdges':
|
||||
x = dataLabelPositioners.alignToPlotEdges(dataLabel, i, plotWidth, plotLeft);
|
||||
break;
|
||||
default:
|
||||
x = dataLabelPositioners.radialDistributionX(series, point, y, naturalY);
|
||||
}
|
||||
}
|
||||
// Record the placement and visibility
|
||||
dataLabel._attr = {
|
||||
visibility: visibility,
|
||||
align: labelPosition.alignment
|
||||
};
|
||||
pointDataLabelsOptions = point.options.dataLabels || {};
|
||||
dataLabel._pos = {
|
||||
x: (x +
|
||||
pick(pointDataLabelsOptions.x, options.x) + // (#12985)
|
||||
({
|
||||
left: connectorPadding,
|
||||
right: -connectorPadding
|
||||
}[labelPosition.alignment] || 0)),
|
||||
// 10 is for the baseline (label vs text)
|
||||
y: (y +
|
||||
pick(pointDataLabelsOptions.y, options.y) - // (#12985)
|
||||
10)
|
||||
};
|
||||
// labelPos.x = x;
|
||||
// labelPos.y = y;
|
||||
labelPosition.final.x = x;
|
||||
labelPosition.final.y = y;
|
||||
// Detect overflowing data labels
|
||||
if (pick(options.crop, true)) {
|
||||
dataLabelWidth = dataLabel.getBBox().width;
|
||||
sideOverflow = null;
|
||||
// Overflow left
|
||||
if (x - dataLabelWidth < connectorPadding &&
|
||||
i === 1 // left half
|
||||
) {
|
||||
sideOverflow = Math.round(dataLabelWidth - x + connectorPadding);
|
||||
overflow[3] = Math.max(sideOverflow, overflow[3]);
|
||||
// Overflow right
|
||||
}
|
||||
else if (x + dataLabelWidth > plotWidth - connectorPadding &&
|
||||
i === 0 // right half
|
||||
) {
|
||||
sideOverflow = Math.round(x + dataLabelWidth - plotWidth + connectorPadding);
|
||||
overflow[1] = Math.max(sideOverflow, overflow[1]);
|
||||
}
|
||||
// Overflow top
|
||||
if (y - labelHeight / 2 < 0) {
|
||||
overflow[0] = Math.max(Math.round(-y + labelHeight / 2), overflow[0]);
|
||||
// Overflow left
|
||||
}
|
||||
else if (y + labelHeight / 2 > plotHeight) {
|
||||
overflow[2] = Math.max(Math.round(y + labelHeight / 2 - plotHeight), overflow[2]);
|
||||
}
|
||||
dataLabel.sideOverflow = sideOverflow;
|
||||
}
|
||||
} // for each point
|
||||
}); // for each half
|
||||
// Do not apply the final placement and draw the connectors until we
|
||||
// have verified that labels are not spilling over.
|
||||
if (arrayMax(overflow) === 0 ||
|
||||
this.verifyDataLabelOverflow(overflow)) {
|
||||
// Place the labels in the final position
|
||||
this.placeDataLabels();
|
||||
this.points.forEach(function (point) {
|
||||
// #8864: every connector can have individual options
|
||||
pointDataLabelsOptions =
|
||||
merge(options, point.options.dataLabels);
|
||||
connectorWidth =
|
||||
pick(pointDataLabelsOptions.connectorWidth, 1);
|
||||
// Draw the connector
|
||||
if (connectorWidth) {
|
||||
var isNew = void 0;
|
||||
connector = point.connector;
|
||||
dataLabel = point.dataLabel;
|
||||
if (dataLabel &&
|
||||
dataLabel._pos &&
|
||||
point.visible &&
|
||||
point.labelDistance > 0) {
|
||||
visibility = dataLabel._attr.visibility;
|
||||
isNew = !connector;
|
||||
if (isNew) {
|
||||
point.connector = connector = chart.renderer
|
||||
.path()
|
||||
.addClass('highcharts-data-label-connector ' +
|
||||
' highcharts-color-' + point.colorIndex +
|
||||
(point.className ?
|
||||
' ' + point.className :
|
||||
''))
|
||||
.add(series.dataLabelsGroup);
|
||||
if (!chart.styledMode) {
|
||||
connector.attr({
|
||||
'stroke-width': connectorWidth,
|
||||
'stroke': (pointDataLabelsOptions.connectorColor ||
|
||||
point.color ||
|
||||
"#666666" /* Palette.neutralColor60 */)
|
||||
});
|
||||
}
|
||||
}
|
||||
connector[isNew ? 'attr' : 'animate']({
|
||||
d: point.getConnectorPath()
|
||||
});
|
||||
connector.attr('visibility', visibility);
|
||||
}
|
||||
else if (connector) {
|
||||
point.connector = connector.destroy();
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
/**
|
||||
* Perform the final placement of the data labels after we have verified
|
||||
* that they fall within the plot area.
|
||||
* @private
|
||||
*/
|
||||
function placeDataLabels() {
|
||||
this.points.forEach(function (point) {
|
||||
var dataLabel = point.dataLabel, _pos;
|
||||
if (dataLabel && point.visible) {
|
||||
_pos = dataLabel._pos;
|
||||
if (_pos) {
|
||||
// Shorten data labels with ellipsis if they still overflow
|
||||
// after the pie has reached minSize (#223).
|
||||
if (dataLabel.sideOverflow) {
|
||||
dataLabel._attr.width =
|
||||
Math.max(dataLabel.getBBox().width -
|
||||
dataLabel.sideOverflow, 0);
|
||||
dataLabel.css({
|
||||
width: dataLabel._attr.width + 'px',
|
||||
textOverflow: ((this.options.dataLabels.style || {})
|
||||
.textOverflow ||
|
||||
'ellipsis')
|
||||
});
|
||||
dataLabel.shortened = true;
|
||||
}
|
||||
dataLabel.attr(dataLabel._attr);
|
||||
dataLabel[dataLabel.moved ? 'animate' : 'attr'](_pos);
|
||||
dataLabel.moved = true;
|
||||
}
|
||||
else if (dataLabel) {
|
||||
dataLabel.attr({ y: -9999 });
|
||||
}
|
||||
}
|
||||
// Clear for update
|
||||
delete point.distributeBox;
|
||||
}, this);
|
||||
}
|
||||
/**
|
||||
* Verify whether the data labels are allowed to draw, or we should run more
|
||||
* translation and data label positioning to keep them inside the plot area.
|
||||
* Returns true when data labels are ready to draw.
|
||||
* @private
|
||||
*/
|
||||
function verifyDataLabelOverflow(overflow) {
|
||||
var center = this.center, options = this.options, centerOption = options.center, minSize = options.minSize || 80, newSize = minSize,
|
||||
// If a size is set, return true and don't try to shrink the pie
|
||||
// to fit the labels.
|
||||
ret = options.size !== null;
|
||||
if (!ret) {
|
||||
// Handle horizontal size and center
|
||||
if (centerOption[0] !== null) { // Fixed center
|
||||
newSize = Math.max(center[2] -
|
||||
Math.max(overflow[1], overflow[3]), minSize);
|
||||
}
|
||||
else { // Auto center
|
||||
newSize = Math.max(
|
||||
// horizontal overflow
|
||||
center[2] - overflow[1] - overflow[3], minSize);
|
||||
// horizontal center
|
||||
center[0] += (overflow[3] - overflow[1]) / 2;
|
||||
}
|
||||
// Handle vertical size and center
|
||||
if (centerOption[1] !== null) { // Fixed center
|
||||
newSize = clamp(newSize, minSize, center[2] - Math.max(overflow[0], overflow[2]));
|
||||
}
|
||||
else { // Auto center
|
||||
newSize = clamp(newSize, minSize,
|
||||
// vertical overflow
|
||||
center[2] - overflow[0] - overflow[2]);
|
||||
// vertical center
|
||||
center[1] += (overflow[0] - overflow[2]) / 2;
|
||||
}
|
||||
// If the size must be decreased, we need to run translate and
|
||||
// drawDataLabels again
|
||||
if (newSize < center[2]) {
|
||||
center[2] = newSize;
|
||||
center[3] = Math.min(// #3632
|
||||
options.thickness ?
|
||||
Math.max(0, newSize - options.thickness * 2) :
|
||||
Math.max(0, relativeLength(options.innerSize || 0, newSize)), newSize); // #6647
|
||||
this.translate(center);
|
||||
if (this.drawDataLabels) {
|
||||
this.drawDataLabels();
|
||||
}
|
||||
// Else, return true to indicate that the pie and its labels is
|
||||
// within the plot area
|
||||
}
|
||||
else {
|
||||
ret = true;
|
||||
}
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
})(ColumnDataLabel || (ColumnDataLabel = {}));
|
||||
/* *
|
||||
*
|
||||
* Default Export
|
||||
*
|
||||
* */
|
||||
export default ColumnDataLabel;
|
||||
Reference in New Issue
Block a user