diff --git a/demo/demo.js b/demo/demo.js index 18139de69..38540cb25 100644 --- a/demo/demo.js +++ b/demo/demo.js @@ -1703,27 +1703,53 @@ var demos = { ]; } }, - DataLabel: { - options: { - data: { - columns: [ - ["data1", 30, -200, -100, 400, 150, 250], - ["data2", -50, 150, -150, 150, -50, -150], - ["data3", -100, 100, -40, 100, -150, -50] - ], - groups: [ - ["data1", "data2"] - ], - type: "bar", - labels: true - }, - grid: { - y: { - lines: [{value: 0}] + DataLabel: [ + { + options: { + data: { + columns: [ + ["data1", 30, -200, -100, 400, 150, 250], + ["data2", -50, 150, -150, 150, -50, -150], + ["data3", -100, 100, -40, 100, -150, -50] + ], + groups: [ + ["data1", "data2"] + ], + type: "bar", + labels: true + }, + grid: { + y: { + lines: [{value: 0}] + } + } + } + }, + { + options: { + data: { + columns: [ + ["data1", 230, -200, 400], + ["data2", -250, 350, -170], + ["data3", -123, 100, -240] + ], + groups: [ + ["data1", "data2"] + ], + type: "bar", + labels: { + colors: "white", + centered: true + } + }, + grid: { + y: { + lines: [{value: 0}] + } } } } - }, + ], DataLabelColors: [ { options: { @@ -2532,7 +2558,7 @@ d3.select(".chart_area") "01-10-2019 01:00", "01-10-2019 01:30", "01-10-2019 02:00", - "01-10-2019 02:30", + "01-10-2019 02:30", "01-10-2019 03:00", "01-10-2019 03:30", "01-10-2019 04:00", diff --git a/spec/internals/data-spec.js b/spec/internals/data-spec.js index 5a5601f3c..c0a966ff0 100644 --- a/spec/internals/data-spec.js +++ b/spec/internals/data-spec.js @@ -729,6 +729,51 @@ describe("DATA", () => { .each(checkXY(expectedTextX[key], expectedTextY[key], "", 4)); }); }); + + it("set options data.labels.centered=true", () => { + args.data.labels = { + centered: true, + colors: "white" + }; + }); + + it("check for data label text position", () => { + const index = 1; + let j = 0; + const bars = chart.$.bar.bars.filter(d => d.index === index); + const texts = chart.$.text.texts.filter(d => d.index === index).nodes(); + + bars.each(function(d) { + const barRect = this.getBoundingClientRect(); + const textRect = texts[j++].getBoundingClientRect(); + + expect( + (barRect.height / 2) - (textRect.y - barRect.y) + ).to.be.closeTo(textRect.height / 2, 3); + }); + }); + + it("set options axis.rotated=true", () => { + args.axis = { + rotated: true + } + }); + + it("check for data label text position when is rotated", () => { + const index = 1; + let j = 0; + const bars = chart.$.bar.bars.filter(d => d.index === index); + const texts = chart.$.text.texts.filter(d => d.index === index).nodes(); + + bars.each(function(d) { + const barRect = this.getBoundingClientRect(); + const textRect = texts[j++].getBoundingClientRect(); + + expect( + (barRect.width / 2) - (textRect.x - barRect.x) + ).to.be.closeTo(textRect.width / 2, 3); + }); + }); }); describe("for all targets", () => { diff --git a/src/config/Options.js b/src/config/Options.js index 56322faed..742c8463c 100644 --- a/src/config/Options.js +++ b/src/config/Options.js @@ -560,7 +560,8 @@ export default class Options { * @memberof Options * @type {Object} * @property {Boolean} [data.labels=false] Show or hide labels on each data points - * @property {Function} [data.labels.format={}] Set formatter function for data labels.
+ * @property {Boolean} [data.labels.centered=false] Centerize labels on `bar` shape. (**NOTE:** works only for 'bar' type) + * @property {Function} [data.labels.format] Set formatter function for data labels.
* The formatter function receives 4 arguments such as v, id, i, j and it must return a string that will be shown as the label. The arguments are:
* - `v` is the value of the data point where the label is shown. * - `id` is the id of the data where the label is shown. @@ -591,6 +592,9 @@ export default class Options { * ... * }, * + * // align text to center of the 'bar' shape (works only for 'bar' type) + * centered: true, + * * // apply for all label texts * colors: "red", * @@ -1457,7 +1461,7 @@ export default class Options { axis_x_categories: [], /** - * Centerise ticks on category axis. + * centerize ticks on category axis. * @name axis․x․tick․centered * @memberof Options * @type {Boolean} diff --git a/src/internals/text.js b/src/internals/text.js index 1723fb2f8..5cbf94e2d 100644 --- a/src/internals/text.js +++ b/src/internals/text.js @@ -181,6 +181,45 @@ extend(ChartInternal.prototype, { }; }, + /** + * Get centerized text position for bar type data.label.text + * @private + * @param {Object} d Data object + * @param {Array} points Data points position + * @param {HTMLElement} textElement Data label text element + * @returns {Number} Position value + */ + getCenteredTextPos(d, points, textElement) { + const $$ = this; + const config = $$.config; + const isRotated = config.axis_rotated; + + if (config.data_labels.centered && $$.isBarType(d)) { + const rect = textElement.getBoundingClientRect(); + const isPositive = d.value >= 0; + + if (isRotated) { + const w = ( + isPositive ? + points[1][1] - points[0][1] : + points[0][1] - points[1][1] + ) / 2 + (rect.width / 2); + + return isPositive ? -w - 3 : w + 4; + } else { + const h = ( + isPositive ? + points[0][1] - points[1][1] : + points[1][1] - points[0][1] + ) / 2 + (rect.height / 2); + + return isPositive ? h : -h - 4; + } + } + + return 0; + }, + /** * Gets the x coordinate of the text * @private @@ -192,10 +231,11 @@ extend(ChartInternal.prototype, { getXForText(points, d, textElement) { const $$ = this; const config = $$.config; + const isRotated = config.axis_rotated; let xPos; let padding; - if (config.axis_rotated) { + if (isRotated) { padding = $$.isBarType(d) ? 4 : 6; xPos = points[2][1] + padding * (d.value < 0 ? -1 : 1); } else { @@ -210,6 +250,10 @@ extend(ChartInternal.prototype, { } } + if (isRotated) { + xPos += $$.getCenteredTextPos(d, points, textElement); + } + return xPos + (config.data_labels_position.x || 0); }, @@ -224,12 +268,14 @@ extend(ChartInternal.prototype, { getYForText(points, d, textElement) { const $$ = this; const config = $$.config; + const isRotated = config.axis_rotated; const r = config.point_r; + const rect = textElement.getBoundingClientRect(); let baseY = 3; let yPos; - if (config.axis_rotated) { - yPos = (points[0][0] + points[2][0] + textElement.getBoundingClientRect().height * 0.6) / 2; + if (isRotated) { + yPos = (points[0][0] + points[2][0] + rect.height * 0.6) / 2; } else { yPos = points[2][1]; @@ -238,7 +284,7 @@ extend(ChartInternal.prototype, { } if (d.value < 0 || (d.value === 0 && !$$.hasPositiveValue)) { - yPos += textElement.getBoundingClientRect().height; + yPos += rect.height; if ($$.isBarType(d) && $$.isSafari()) { yPos -= baseY; @@ -259,8 +305,8 @@ extend(ChartInternal.prototype, { } // show labels regardless of the domain if value is null - if (d.value === null && !config.axis_rotated) { - const boxHeight = textElement.getBoundingClientRect().height; + if (d.value === null && !isRotated) { + const boxHeight = rect.height; if (yPos < boxHeight) { yPos = boxHeight; @@ -269,6 +315,10 @@ extend(ChartInternal.prototype, { } } + if (!isRotated) { + yPos += $$.getCenteredTextPos(d, points, textElement); + } + return yPos + (config.data_labels_position.y || 0); } }); diff --git a/types/options.d.ts b/types/options.d.ts index c8a3bea11..cfe1f0889 100644 --- a/types/options.d.ts +++ b/types/options.d.ts @@ -1174,6 +1174,11 @@ export interface Data { * - j is the sub index of the data point where the label is shown. */ labels?: boolean | { + /** + * Centerize labels on `bar` shape. (**NOTE:** works only for 'bar' type) + */ + centered?: boolean; + /** * Set label text colors. */