Implement routing of defaults (#7453)

Implement routing of defaults
This commit is contained in:
Jukka Kurkela
2020-06-09 00:49:17 +03:00
committed by Evert Timberg
parent 956149b498
commit d0a1d689fc
7 changed files with 135 additions and 55 deletions

View File

@@ -21,11 +21,11 @@ Global point options: `Chart.defaults.elements.point`.
| Name | Type | Default | Description
| ---- | ---- | ------- | -----------
| `radius` | `number` | `3` | Point radius.
| [`pointStyle`](#point-styles) | <code>string&#124;Image</code> | `'circle'` | Point style.
| [`pointStyle`](#point-styles) | `string`\|`Image` | `'circle'` | Point style.
| `rotation` | `number` | `0` | Point rotation (in degrees).
| `backgroundColor` | `Color` | `'rgba(0, 0, 0, 0.1)'` | Point fill color.
| `backgroundColor` | `Color` | `Chart.defaults.color` | Point fill color.
| `borderWidth` | `number` | `1` | Point stroke width.
| `borderColor` | `Color` | `'rgba(0, 0, 0, 0.1)'` | Point stroke color.
| `borderColor` | `Color` | `Chart.defaults.color` | Point stroke color.
| `hitRadius` | `number` | `1` | Extra radius added to point radius for hit detection.
| `hoverRadius` | `number` | `4` | Point radius when hovered.
| `hoverBorderWidth` | `number` | `1` | Stroke width when hovered.
@@ -56,16 +56,16 @@ Global line options: `Chart.defaults.elements.line`.
| Name | Type | Default | Description
| ---- | ---- | ------- | -----------
| `tension` | `number` | `0.4` | Bézier curve tension (`0` for no Bézier curves).
| `backgroundColor` | `Color` | `'rgba(0, 0, 0, 0.1)'` | Line fill color.
| `backgroundColor` | `Color` | `Chart.defaults.color` | Line fill color.
| `borderWidth` | `number` | `3` | Line stroke width.
| `borderColor` | `Color` | `'rgba(0, 0, 0, 0.1)'` | Line stroke color.
| `borderColor` | `Color` | `Chart.defaults.color` | Line stroke color.
| `borderCapStyle` | `string` | `'butt'` | Line cap style. See [MDN](https://developer.mozilla.org/en/docs/Web/API/CanvasRenderingContext2D/lineCap).
| `borderDash` | `number[]` | `[]` | Line dash. See [MDN](https://developer.mozilla.org/en-US/docs/Web/API/CanvasRenderingContext2D/setLineDash).
| `borderDashOffset` | `number` | `0.0` | Line dash offset. See [MDN](https://developer.mozilla.org/en-US/docs/Web/API/CanvasRenderingContext2D/lineDashOffset).
| `borderJoinStyle` | `string` | `'miter'` | Line join style. See [MDN](https://developer.mozilla.org/en-US/docs/Web/API/CanvasRenderingContext2D/lineJoin).
| `capBezierPoints` | `boolean` | `true` | `true` to keep Bézier control inside the chart, `false` for no restriction.
| `cubicInterpolationMode` | `string` | `'default'` | Interpolation mode to apply. [See more...](../charts/line.md#cubicinterpolationmode)
| `fill` | <code>boolean&#124;string</code> | `true` | How to fill the area under the line. See [area charts](../charts/area.md#filling-modes).
| `fill` | `boolean`\|`string` | `true` | How to fill the area under the line. See [area charts](../charts/area.md#filling-modes).
| `stepped` | `boolean` | `false` | `true` to show the line as a stepped line (`tension` will be ignored).
## Rectangle Configuration
@@ -76,9 +76,9 @@ Global rectangle options: `Chart.defaults.elements.rectangle`.
| Name | Type | Default | Description
| ---- | ---- | ------- | -----------
| `backgroundColor` | `Color` | `'rgba(0, 0, 0, 0.1)'` | Bar fill color.
| `backgroundColor` | `Color` | `Chart.defaults.color` | Bar fill color.
| `borderWidth` | `number` | `0` | Bar stroke width.
| `borderColor` | `Color` | `'rgba(0, 0, 0, 0.1)'` | Bar stroke color.
| `borderColor` | `Color` | `Chart.defaults.color` | Bar stroke color.
| `borderSkipped` | `string` | `'bottom'` | Skipped (excluded) border: `'bottom'`, `'left'`, `'top'` or `'right'`.
## Arc Configuration
@@ -90,7 +90,7 @@ Global arc options: `Chart.defaults.elements.arc`.
| Name | Type | Default | Description
| ---- | ---- | ------- | -----------
| `angle` - for polar only | `number` | `circumference / (arc count)` | Arc angle to cover.
| `backgroundColor` | `Color` | `'rgba(0, 0, 0, 0.1)'` | Arc fill color.
| `backgroundColor` | `Color` | `Chart.defaults.color` | Arc fill color.
| `borderAlign` | `string` | `'center'` | Arc stroke alignment.
| `borderColor` | `Color` | `'#fff'` | Arc stroke color.
| `borderWidth`| `number` | `2` | Arc stroke width.

View File

@@ -1,7 +1,25 @@
import {merge} from '../helpers/helpers.core';
import {merge, isArray, valueOrDefault} from '../helpers/helpers.core';
/**
* @param {object} node
* @param {string} key
* @return {object}
*/
function getScope(node, key) {
if (!key) {
return node;
}
const keys = key.split('.');
for (let i = 0, n = keys.length; i < n; ++i) {
const k = keys[i];
node = node[k] || (node[k] = {});
}
return node;
}
/**
* Please use the module's default export which provides a singleton instance
* Note: class is exported for typedoc
*/
export class Defaults {
constructor() {
@@ -39,13 +57,63 @@ export class Defaults {
this.title = undefined;
this.tooltips = undefined;
this.doughnut = undefined;
this._routes = {};
}
/**
* @param {string} scope
* @param {*} values
*/
set(scope, values) {
return merge(this[scope] || (this[scope] = {}), values);
return merge(getScope(this, scope), values);
}
/**
* Routes the named defaults to fallback to another scope/name.
* This routing is useful when those target values, like defaults.color, are changed runtime.
* If the values would be copied, the runtime change would not take effect. By routing, the
* fallback is evaluated at each access, so its always up to date.
*
* Examples:
*
* defaults.route('elements.arc', 'backgroundColor', '', 'color')
* - reads the backgroundColor from defaults.color when undefined locally
*
* defaults.route('elements.line', ['backgroundColor', 'borderColor'], '', 'color')
* - reads the backgroundColor and borderColor from defaults.color when undefined locally
*
* defaults.route('elements.customLine', ['borderWidth', 'tension'], 'elements.line', ['borderWidth', 'tension'])
* - reads the borderWidth and tension from elements.line when those are not defined in elements.customLine
*
* @param {string} scope Scope this route applies to.
* @param {string[]} names Names of the properties that should be routed to different namespace when not defined here.
* @param {string} targetScope The namespace where those properties should be routed to. Empty string ('') is the root of defaults.
* @param {string|string[]} targetNames The target name/names in the target scope the properties should be routed to.
*/
route(scope, names, targetScope, targetNames) {
const scopeObject = getScope(this, scope);
const targetScopeObject = getScope(this, targetScope);
const targetNamesIsArray = isArray(targetNames);
names.forEach((name, index) => {
const privateName = '_' + name;
const targetName = targetNamesIsArray ? targetNames[index] : targetNames;
Object.defineProperties(scopeObject, {
// A private property is defined to hold the actual value, when this property is set in its scope (set in the setter)
[privateName]: {
writable: true
},
// The actual property is defined as getter/setter so we can do the routing when value is not locally set.
[name]: {
enumerable: true,
get() {
// @ts-ignore
return valueOrDefault(this[privateName], targetScopeObject[targetName]);
},
set(value) {
this[privateName] = value;
}
}
});
});
}
}

View File

@@ -3,15 +3,15 @@ import Element from '../core/core.element';
import {_angleBetween, getAngleFromPoint} from '../helpers/helpers.math';
const TAU = Math.PI * 2;
defaults.set('elements', {
arc: {
backgroundColor: defaults.color,
borderAlign: 'center',
borderColor: '#fff',
borderWidth: 2
}
const scope = 'elements.arc';
defaults.set(scope, {
borderAlign: 'center',
borderColor: '#fff',
borderWidth: 2
});
defaults.route(scope, ['backgroundColor'], '', ['color']);
function clipArc(ctx, model) {
const {startAngle, endAngle, pixelMargin, x, y} = model;
let angleMargin = pixelMargin / model.outerRadius;

View File

@@ -9,23 +9,20 @@ import {_updateBezierControlPoints} from '../helpers/helpers.curve';
* @typedef { import("./element.point").default } Point
*/
const defaultColor = defaults.color;
defaults.set('elements', {
line: {
backgroundColor: defaultColor,
borderCapStyle: 'butt',
borderColor: defaultColor,
borderDash: [],
borderDashOffset: 0,
borderJoinStyle: 'miter',
borderWidth: 3,
capBezierPoints: true,
fill: true,
tension: 0.4
}
const scope = 'elements.line';
defaults.set(scope, {
borderCapStyle: 'butt',
borderDash: [],
borderDashOffset: 0,
borderJoinStyle: 'miter',
borderWidth: 3,
capBezierPoints: true,
fill: true,
tension: 0.4
});
defaults.route(scope, ['backgroundColor', 'borderColor'], '', 'color');
function setStyle(ctx, vm) {
ctx.lineCap = vm.borderCapStyle;
ctx.setLineDash(vm.borderDash);

View File

@@ -2,21 +2,18 @@ import defaults from '../core/core.defaults';
import Element from '../core/core.element';
import {_isPointInArea, drawPoint} from '../helpers/helpers.canvas';
const defaultColor = defaults.color;
defaults.set('elements', {
point: {
backgroundColor: defaultColor,
borderColor: defaultColor,
borderWidth: 1,
hitRadius: 1,
hoverBorderWidth: 1,
hoverRadius: 4,
pointStyle: 'circle',
radius: 3
}
const scope = 'elements.point';
defaults.set(scope, {
borderWidth: 1,
hitRadius: 1,
hoverBorderWidth: 1,
hoverRadius: 4,
pointStyle: 'circle',
radius: 3
});
defaults.route(scope, ['backgroundColor', 'borderColor'], '', 'color');
class Point extends Element {
constructor(cfg) {

View File

@@ -2,17 +2,14 @@ import defaults from '../core/core.defaults';
import Element from '../core/core.element';
import {isObject} from '../helpers/helpers.core';
const defaultColor = defaults.color;
defaults.set('elements', {
rectangle: {
backgroundColor: defaultColor,
borderColor: defaultColor,
borderSkipped: 'bottom',
borderWidth: 0
}
const scope = 'elements.rectangle';
defaults.set(scope, {
borderSkipped: 'bottom',
borderWidth: 0
});
defaults.route(scope, ['backgroundColor', 'borderColor'], '', 'color');
/**
* Helper function to get the bounds of the bar regardless of the orientation
* @param {Rectangle} bar the bar

View File

@@ -502,4 +502,25 @@ describe('Chart.DatasetController', function() {
expect(data1[hook]).toBe(Array.prototype[hook]);
});
});
it('should resolve data element options to the default color', function() {
var data0 = [0, 1, 2, 3, 4, 5];
var oldColor = Chart.defaults.color;
Chart.defaults.color = 'red';
var chart = acquireChart({
type: 'line',
data: {
datasets: [{
data: data0
}]
}
});
var meta = chart.getDatasetMeta(0);
expect(meta.dataset.options.borderColor).toBe('red');
expect(meta.data[0].options.borderColor).toBe('red');
// Reset old shared state
Chart.defaults.color = oldColor;
});
});