Skip to content

Enable stacked scales and with reverse option support #655

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 18 commits into from
Apr 4, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions docs/guide/migrationV2.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,3 +8,4 @@ A number of changes were made to the configuration options passed to the plugin

* `xScaleID` option default has been changed, now set to `undefined`. If the option is missing, the plugin will try to use the first scale of the chart, configured as `'x'` axis. If more than one scale has been defined in the chart as `'x'` axis, the option is mandatory to select the right scale.
* `yScaleID` option default has been changed, now set to `undefined`. If the option is missing, the plugin will try to use the first scale of the chart, configured as `'y'` axis. If more than one scale has been defined in the chart as `'y'` axis, the option is mandatory to select the right scale.
* When [stacked scales](https://www.chartjs.org/docs/latest/axes/cartesian/#common-options-to-all-cartesian-axes) are used, instead of the whole chart area, the designated scale area is used as fallback for `xMin`, `xMax`, `yMin`, `yMax`, `xValue` or `yValue` options.
2 changes: 1 addition & 1 deletion docs/guide/types/label.md
Original file line number Diff line number Diff line change
Expand Up @@ -94,7 +94,7 @@ The following options are available for label annotations.

### General

If one of the axes does not match an axis in the chart, the content will be rendered in the center of the chart. The 2 coordinates, xValue, yValue are optional. If not specified, the content will be rendered in the center of the chart.
If one of the axes does not match an axis in the chart, the content will be rendered in the center of the chart. The 2 coordinates, xValue, yValue are optional. If not specified, the content will be rendered in the center of the scale dimension.

The 4 coordinates, xMin, xMax, yMin, yMax are optional. If not specified, the box is expanded out to the edges in the respective direction and the box size is used to calculated the center of the point. To enable to use the box positioning, the `radius` must be set to `0` or `NaN`.

Expand Down
2 changes: 1 addition & 1 deletion docs/guide/types/point.md
Original file line number Diff line number Diff line change
Expand Up @@ -78,7 +78,7 @@ The following options are available for point annotations.

### General

If one of the axes does not match an axis in the chart, the point annotation will take the center of the chart as point. The 2 coordinates, xValue, yValue are optional. If not specified, the point annotation will take the center of the chart as point.
If one of the axes does not match an axis in the chart, the point annotation will take the center of the chart as point. The 2 coordinates, xValue, yValue are optional. If not specified, the point annotation will take the center of the scale dimension as point.

The 4 coordinates, xMin, xMax, yMin, yMax are optional. If not specified, the box is expanded out to the edges in the respective direction and the box size is used to calculated the center of the point. To enable to use the box positioning, the `radius` must be set to `0` or `NaN`.

Expand Down
2 changes: 1 addition & 1 deletion docs/guide/types/polygon.md
Original file line number Diff line number Diff line change
Expand Up @@ -83,7 +83,7 @@ The following options are available for polygon annotations.

### General

If one of the axes does not match an axis in the chart, the polygon annotation will take the center of the chart as point. The 2 coordinates, xValue, yValue are optional. If not specified, the polygon annotation will take the center of the chart.
If one of the axes does not match an axis in the chart, the polygon annotation will take the center of the chart as point. The 2 coordinates, xValue, yValue are optional. If not specified, the polygon annotation will take the center of the scale dimension.

The 4 coordinates, xMin, xMax, yMin, yMax are optional. If not specified, the box is expanded out to the edges in the respective direction and the box size is used to calculated the center of the point. To enable to use the box positioning, the `radius` must be set to `0` or `NaN`.

Expand Down
37 changes: 24 additions & 13 deletions src/helpers/helpers.chart.js
Original file line number Diff line number Diff line change
Expand Up @@ -42,21 +42,31 @@ export function retrieveScaleID(scales, options, key) {

/**
* @param {Scale} scale
* @param {{start: number, end: number}} options
* @returns {{start: number, end: number}}
* @param {{min: number, max: number, start: number, end: number}} options
* @returns {{start: number, end: number}|undefined}
*/
function getChartDimensionByScale(scale, options) {
export function getDimensionByScale(scale, options) {
if (scale) {
const min = scaleValue(scale, options.min, options.start);
const max = scaleValue(scale, options.max, options.end);
const reverse = scale.options.reverse;
const start = scaleValue(scale, options.min, reverse ? options.end : options.start);
const end = scaleValue(scale, options.max, reverse ? options.start : options.end);
return {
start: Math.min(min, max),
end: Math.max(min, max)
start,
end
};
}
}

/**
* @param {Scale} scale
* @param {{min: number, max: number, start: number, end: number}} options
* @returns {{start: number, end: number}}
*/
function getChartDimensionByScale(scale, options) {
const result = getDimensionByScale(scale, options) || options;
return {
start: options.start,
end: options.end
start: Math.min(result.start, result.end),
end: Math.max(result.start, result.end)
};
}

Expand All @@ -73,11 +83,11 @@ export function getChartPoint(chart, options) {
let y = chartArea.height / 2;

if (xScale) {
x = scaleValue(xScale, options.xValue, x);
x = scaleValue(xScale, options.xValue, xScale.left + xScale.width / 2);
}

if (yScale) {
y = scaleValue(yScale, options.yValue, y);
y = scaleValue(yScale, options.yValue, yScale.top + yScale.height / 2);
}
return {x, y};
}
Expand All @@ -91,16 +101,17 @@ export function getChartRect(chart, options) {
const scales = chart.scales;
const xScale = scales[retrieveScaleID(scales, options, 'xScaleID')];
const yScale = scales[retrieveScaleID(scales, options, 'yScaleID')];
let {top: y, left: x, bottom: y2, right: x2} = chart.chartArea;

if (!xScale && !yScale) {
return {};
}

let {left: x, right: x2} = xScale || chart.chartArea;
let {top: y, bottom: y2} = yScale || chart.chartArea;
const xDim = getChartDimensionByScale(xScale, {min: options.xMin, max: options.xMax, start: x, end: x2});
x = xDim.start;
x2 = xDim.end;
const yDim = getChartDimensionByScale(yScale, {min: options.yMin, max: options.yMax, start: y, end: y2});
const yDim = getChartDimensionByScale(yScale, {min: options.yMin, max: options.yMax, start: y2, end: y});
y = yDim.start;
y2 = yDim.end;

Expand Down
39 changes: 27 additions & 12 deletions src/types/line.js
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import {Element} from 'chart.js';
import {PI, toRadians, toPadding} from 'chart.js/helpers';
import {clamp, scaleValue, rotated, drawBox, drawLabel, measureLabelSize, getRelativePosition, setBorderStyle, setShadowStyle, retrieveScaleID} from '../helpers';
import {PI, toRadians, toPadding, valueOrDefault} from 'chart.js/helpers';
import {clamp, scaleValue, rotated, drawBox, drawLabel, measureLabelSize, getRelativePosition, setBorderStyle, setShadowStyle, retrieveScaleID, getDimensionByScale} from '../helpers';

const pointInLine = (p1, p2, t) => ({x: p1.x + t * (p2.x - p1.x), y: p1.y + t * (p2.y - p1.y)});
const interpolateX = (y, p1, p2) => pointInLine(p1, p2, Math.abs((y - p1.y) / (p2.y - p1.y))).x;
Expand Down Expand Up @@ -162,34 +162,33 @@ export default class LineAnnotation extends Element {

resolveElementProperties(chart, options) {
const scale = chart.scales[options.scaleID];
let {top: y, left: x, bottom: y2, right: x2} = chart.chartArea;
const area = translate(chart.chartArea, {y: 'top', x: 'left', y2: 'bottom', x2: 'right'});
let min, max;

if (scale) {
min = scaleValue(scale, options.value, NaN);
max = scaleValue(scale, options.endValue, min);
if (scale.isHorizontal()) {
x = min;
x2 = max;
area.x = min;
area.x2 = max;
} else {
y = min;
y2 = max;
area.y = min;
area.y2 = max;
}
} else {
const xScale = chart.scales[retrieveScaleID(chart.scales, options, 'xScaleID')];
const yScale = chart.scales[retrieveScaleID(chart.scales, options, 'yScaleID')];

if (xScale) {
x = scaleValue(xScale, options.xMin, x);
x2 = scaleValue(xScale, options.xMax, x2);
applyScaleValueToDimension(area, xScale, {min: options.xMin, max: options.xMax, start: xScale.left, end: xScale.right, startProp: 'x', endProp: 'x2'});
}

if (yScale) {
y = scaleValue(yScale, options.yMin, y);
y2 = scaleValue(yScale, options.yMax, y2);
applyScaleValueToDimension(area, yScale, {min: options.yMin, max: options.yMax, start: yScale.bottom, end: yScale.top, startProp: 'y', endProp: 'y2'});
}
}
const inside = isLineInArea({x, y, x2, y2}, chart.chartArea);
const {x, y, x2, y2} = area;
const inside = isLineInArea(area, chart.chartArea);
const properties = inside
? limitLineToArea({x, y}, {x: x2, y: y2}, chart.chartArea)
: {x, y, x2, y2, width: Math.abs(x2 - x), height: Math.abs(y2 - y)};
Expand Down Expand Up @@ -305,6 +304,22 @@ LineAnnotation.defaultRoutes = {
borderColor: 'color'
};

function translate(source, mapping) {
const ret = {};
const keys = Object.keys(mapping);
const read = prop => valueOrDefault(source[prop], source[mapping[prop]]);
for (const prop of keys) {
ret[prop] = read(prop);
}
return ret;
}

function applyScaleValueToDimension(area, scale, options) {
const dim = getDimensionByScale(scale, options);
area[options.startProp] = dim.start;
area[options.endProp] = dim.end;
}

function loadLabelRect(line, chart, options) {
// TODO: v2 remove support for xPadding and yPadding
const {padding: lblPadding, xPadding, yPadding, borderWidth} = options;
Expand Down
45 changes: 45 additions & 0 deletions test/fixtures/box/missingXMinMax.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
module.exports = {
tolerance: 0.0055,
config: {
type: 'scatter',
options: {
scales: {
x: {
display: true,
min: -10,
max: 50
},
y: {
display: false,
min: -10,
max: 50
}
},
plugins: {
annotation: {
annotations: {
first: {
type: 'box',
xMax: 1,
backgroundColor: 'rgba(159, 226, 191, 0.5)',
},
second: {
type: 'box',
xMax: 30,
xMin: 1,
backgroundColor: 'rgba(255, 191, 0, 0.5)',
},
third: {
type: 'box',
xMin: 30,
backgroundColor: 'rgba(222, 49, 99, 0.5)',
}
}
}
}
}
},
options: {
spriteText: true
}
};
Binary file added test/fixtures/box/missingXMinMax.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
47 changes: 47 additions & 0 deletions test/fixtures/box/missingXMinMaxCategory.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
module.exports = {
tolerance: 0.0055,
config: {
type: 'bar',
data: {
datasets: [{
data: [0, 0, 0, 0, 0, 0]
}]
},
options: {
scales: {
x: {
labels: ['January', 'February', 'March', 'April', 'May', 'June', 'July'],
},
y: {
display: false
}
},
plugins: {
legend: false,
annotation: {
annotations: {
first: {
type: 'box',
xMax: 'February',
backgroundColor: 'rgba(159, 226, 191, 0.5)',
},
second: {
type: 'box',
xMax: 'April',
xMin: 'February',
backgroundColor: 'rgba(255, 191, 0, 0.5)',
},
third: {
type: 'box',
xMin: 'April',
backgroundColor: 'rgba(222, 49, 99, 0.5)',
}
}
}
}
}
},
options: {
spriteText: true
}
};
Binary file added test/fixtures/box/missingXMinMaxCategory.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
48 changes: 48 additions & 0 deletions test/fixtures/box/missingXMinMaxCategoryReverse.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
module.exports = {
tolerance: 0.0055,
config: {
type: 'bar',
data: {
datasets: [{
data: [0, 0, 0, 0, 0, 0]
}]
},
options: {
scales: {
x: {
labels: ['January', 'February', 'March', 'April', 'May', 'June', 'July'],
reverse: true
},
y: {
display: false
}
},
plugins: {
legend: false,
annotation: {
annotations: {
first: {
type: 'box',
xMax: 'February',
backgroundColor: 'rgba(159, 226, 191, 0.5)',
},
second: {
type: 'box',
xMax: 'April',
xMin: 'February',
backgroundColor: 'rgba(255, 191, 0, 0.5)',
},
third: {
type: 'box',
xMin: 'April',
backgroundColor: 'rgba(222, 49, 99, 0.5)',
}
}
}
}
}
},
options: {
spriteText: true
}
};
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
57 changes: 57 additions & 0 deletions test/fixtures/box/missingY2MinMaxStacked.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
module.exports = {
tolerance: 0.0055,
config: {
type: 'bar',
data: {
datasets: [{
data: [0, 0, 0, 0, 0, 0]
}]
},
options: {
scales: {
x: {
display: false,
labels: ['January', 'February', 'March', 'April', 'May', 'June', 'July'],
},
y: {
display: true,
stack: 'demo',
stackWeight: 2,
min: -10,
max: 50
},
y2: {
type: 'category',
display: true,
labels: ['ON', 'OFF'],
offset: true,
position: 'left',
stack: 'demo',
stackWeight: 1,
}
},
plugins: {
legend: false,
annotation: {
annotations: {
first: {
type: 'box',
yScaleID: 'y2',
yMax: 'OFF',
backgroundColor: 'rgba(159, 226, 191, 0.5)',
},
second: {
type: 'box',
yScaleID: 'y2',
yMin: 'OFF',
backgroundColor: 'rgba(255, 191, 0, 0.5)',
},
}
}
}
}
},
options: {
spriteText: true
}
};
Binary file added test/fixtures/box/missingY2MinMaxStacked.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading