From: Jukka Kurkela Date: Sat, 6 Mar 2021 22:18:49 +0000 (+0200) Subject: Add `grace` option for linear scale (#8581) X-Git-Tag: v3.0.0-beta.14~44 X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=7ccf9e2d4d2e052de0fb71da52d9d41f8eeda85c;p=thirdparty%2FChart.js.git Add `grace` option for linear scale (#8581) * Add `grace` option for linear scale * cc --- diff --git a/docs/docs/axes/cartesian/linear.mdx b/docs/docs/axes/cartesian/linear.mdx index ac1418389..da7a3b4dc 100644 --- a/docs/docs/axes/cartesian/linear.mdx +++ b/docs/docs/axes/cartesian/linear.mdx @@ -18,6 +18,7 @@ Namespace: `options.scales[scaleId]` | Name | Type | Description | ---- | ---- | ----------- | `beginAtZero` | `boolean` | if true, scale will include 0 if it is not already included. +| `grace` | `number`\|`string` | Percentage (string ending with `%`) or amount (number) for added room in the scale range above and below data. [more...](#grace) @@ -58,6 +59,45 @@ let options = { }; ``` +## Grace + +If the value is string ending with `%`, its treat as percentage. If number, its treat as value. +The value is added to the maximum data value and subtracted from the minumum data. This extends the scale range as if the data values were that much greater. + +import { useEffect, useRef } from 'react'; + +```jsx live +function example() { + const canvas = useRef(null); + useEffect(() => { + const cfg = { + type: 'bar', + data: { + labels: ['Positive', 'Negative'], + datasets: [{ + data: [100, -50], + backgroundColor: 'rgb(255, 99, 132)' + }], + }, + options: { + scales: { + y: { + type: 'linear', + grace: '5%' + } + }, + plugins: { + legend: false + } + } + }; + const chart = new Chart(canvas.current.getContext('2d'), cfg); + return () => chart.destroy(); + }); + return
; +} +``` + ## Internal data format Internally, the linear scale uses numeric data diff --git a/src/controllers/controller.doughnut.js b/src/controllers/controller.doughnut.js index a8a3fbcbd..427316c32 100644 --- a/src/controllers/controller.doughnut.js +++ b/src/controllers/controller.doughnut.js @@ -1,6 +1,6 @@ import DatasetController from '../core/core.datasetController'; import {formatNumber} from '../core/core.intl'; -import {isArray, toPercentage, toPixels, valueOrDefault} from '../helpers/helpers.core'; +import {isArray, toPercentage, toDimension, valueOrDefault} from '../helpers/helpers.core'; import {toRadians, PI, TAU, HALF_PI, _angleBetween} from '../helpers/helpers.math'; /** @@ -123,7 +123,7 @@ export default class DoughnutController extends DatasetController { const maxWidth = (chartArea.width - spacing) / ratioX; const maxHeight = (chartArea.height - spacing) / ratioY; const maxRadius = Math.max(Math.min(maxWidth, maxHeight) / 2, 0); - const outerRadius = toPixels(me.options.radius, maxRadius); + const outerRadius = toDimension(me.options.radius, maxRadius); const innerRadius = Math.max(outerRadius * cutout, 0); const radiusLength = (outerRadius - innerRadius) / me._getVisibleDatasetWeightTotal(); me.offsetX = offsetX * outerRadius; diff --git a/src/core/core.scale.js b/src/core/core.scale.js index ec8a42ae8..ca7b278dd 100644 --- a/src/core/core.scale.js +++ b/src/core/core.scale.js @@ -26,6 +26,12 @@ defaults.set('scale', { */ bounds: 'ticks', + /** + * Addition grace added to max and reduced from min data value. + * @since 3.0.0 + */ + grace: 0, + // grid line settings gridLines: { display: true, diff --git a/src/helpers/helpers.core.js b/src/helpers/helpers.core.js index 78ce47409..cf3170ea9 100644 --- a/src/helpers/helpers.core.js +++ b/src/helpers/helpers.core.js @@ -90,7 +90,7 @@ export const toPercentage = (value, dimension) => parseFloat(value) / 100 : value / dimension; -export const toPixels = (value, dimension) => +export const toDimension = (value, dimension) => typeof value === 'string' && value.endsWith('%') ? parseFloat(value) / 100 * dimension : +value; diff --git a/src/helpers/helpers.options.js b/src/helpers/helpers.options.js index 604c09603..25302133a 100644 --- a/src/helpers/helpers.options.js +++ b/src/helpers/helpers.options.js @@ -1,5 +1,5 @@ import defaults from '../core/core.defaults'; -import {isArray, isObject, valueOrDefault} from './helpers.core'; +import {isArray, isObject, toDimension, valueOrDefault} from './helpers.core'; import {toFontString} from './helpers.canvas'; const LINE_HEIGHT = new RegExp(/^(normal|(\d+(?:\.\d+)?)(px|em|%)?)$/); @@ -175,3 +175,16 @@ export function resolve(inputs, context, index, info) { } } } + +/** + * @param {{min: number, max: number}} minmax + * @param {number|string} grace + * @private + */ +export function _addGrace(minmax, grace) { + const {min, max} = minmax; + return { + min: min - Math.abs(toDimension(grace, min)), + max: max + toDimension(grace, max) + }; +} diff --git a/src/scales/scale.linearbase.js b/src/scales/scale.linearbase.js index fe954a43f..1dbaf285a 100644 --- a/src/scales/scale.linearbase.js +++ b/src/scales/scale.linearbase.js @@ -2,6 +2,7 @@ import {isNullOrUndef} from '../helpers/helpers.core'; import {almostEquals, almostWhole, niceNum, _decimalPlaces, _setMinAndMaxByKey, sign} from '../helpers/helpers.math'; import Scale from '../core/core.scale'; import {formatNumber} from '../core/core.intl'; +import {_addGrace} from '../helpers/helpers.options'; /** * Generate a set of linear ticks @@ -205,7 +206,7 @@ export default class LinearScaleBase extends Scale { precision: tickOpts.precision, stepSize: tickOpts.stepSize }; - const ticks = generateTicks(numericGeneratorOptions, me); + const ticks = generateTicks(numericGeneratorOptions, _addGrace(me, opts.grace)); // At this point, we need to update our max and min given the tick values, // since we probably have expanded the range of the scale diff --git a/test/fixtures/scale.linear/grace-neg.js b/test/fixtures/scale.linear/grace-neg.js new file mode 100644 index 000000000..f606202ae --- /dev/null +++ b/test/fixtures/scale.linear/grace-neg.js @@ -0,0 +1,30 @@ +module.exports = { + description: 'https://github.com/chartjs/Chart.js/issues/7734', + config: { + type: 'bar', + data: { + labels: ['a'], + datasets: [{ + data: [-0.18], + }], + }, + options: { + indexAxis: 'y', + scales: { + y: { + display: false + }, + x: { + grace: '5%' + } + } + } + }, + options: { + spriteText: true, + canvas: { + width: 512, + height: 128 + } + } +}; diff --git a/test/fixtures/scale.linear/grace-neg.png b/test/fixtures/scale.linear/grace-neg.png new file mode 100644 index 000000000..fbe444b38 Binary files /dev/null and b/test/fixtures/scale.linear/grace-neg.png differ diff --git a/test/fixtures/scale.linear/grace-pos.js b/test/fixtures/scale.linear/grace-pos.js new file mode 100644 index 000000000..76d6c693d --- /dev/null +++ b/test/fixtures/scale.linear/grace-pos.js @@ -0,0 +1,30 @@ +module.exports = { + description: 'https://github.com/chartjs/Chart.js/issues/7734', + config: { + type: 'bar', + data: { + labels: ['a'], + datasets: [{ + data: [0.18], + }], + }, + options: { + indexAxis: 'y', + scales: { + y: { + display: false + }, + x: { + grace: '5%' + } + } + } + }, + options: { + spriteText: true, + canvas: { + width: 512, + height: 128 + } + } +}; diff --git a/test/fixtures/scale.linear/grace-pos.png b/test/fixtures/scale.linear/grace-pos.png new file mode 100644 index 000000000..e6b654505 Binary files /dev/null and b/test/fixtures/scale.linear/grace-pos.png differ diff --git a/test/fixtures/scale.linear/grace.js b/test/fixtures/scale.linear/grace.js new file mode 100644 index 000000000..8f3f09f96 --- /dev/null +++ b/test/fixtures/scale.linear/grace.js @@ -0,0 +1,30 @@ +module.exports = { + description: 'https://github.com/chartjs/Chart.js/issues/7734', + config: { + type: 'bar', + data: { + labels: ['a', 'b'], + datasets: [{ + data: [1.2, -0.2], + }], + }, + options: { + indexAxis: 'y', + scales: { + y: { + display: false + }, + x: { + grace: 0.3 + } + } + } + }, + options: { + spriteText: true, + canvas: { + width: 512, + height: 128 + } + } +}; diff --git a/test/fixtures/scale.linear/grace.png b/test/fixtures/scale.linear/grace.png new file mode 100644 index 000000000..b7556e412 Binary files /dev/null and b/test/fixtures/scale.linear/grace.png differ