diff --git a/src/controllers/controller.line.js b/src/controllers/controller.line.js index 970cc837924..a62d9be895a 100644 --- a/src/controllers/controller.line.js +++ b/src/controllers/controller.line.js @@ -26,6 +26,7 @@ export default class LineController extends DatasetController { } // Update Line + line._chart = this.chart; line._datasetIndex = this.index; line._decimated = !!_dataset._decimated; line.points = points; @@ -46,13 +47,13 @@ export default class LineController extends DatasetController { updateElements(points, start, count, mode) { const reset = mode === 'reset'; - const {iScale, vScale, _stacked} = this._cachedMeta; + const {iScale, vScale, _stacked, _dataset} = this._cachedMeta; const firstOpts = this.resolveDataElementOptions(start, mode); const sharedOptions = this.getSharedOptions(firstOpts); const includeOptions = this.includeOptions(mode, sharedOptions); const iAxis = iScale.axis; const vAxis = vScale.axis; - const spanGaps = this.options.spanGaps; + const {spanGaps, segment} = this.options; const maxGapLength = isNumber(spanGaps) ? spanGaps : Number.POSITIVE_INFINITY; const directUpdate = this.chart._animationsDisabled || reset || mode === 'none'; let prevParsed = start > 0 && this.getParsed(start - 1); @@ -67,7 +68,10 @@ export default class LineController extends DatasetController { properties.skip = isNaN(iPixel) || isNaN(vPixel) || nullData; properties.stop = i > 0 && (parsed[iAxis] - prevParsed[iAxis]) > maxGapLength; - properties.parsed = parsed; + if (segment) { + properties.parsed = parsed; + properties.raw = _dataset.data[i]; + } if (includeOptions) { properties.options = sharedOptions || this.resolveDataElementOptions(i, point.active ? 'active' : mode); diff --git a/src/core/core.controller.js b/src/core/core.controller.js index 3ba3b2fd748..8e288578d6a 100644 --- a/src/core/core.controller.js +++ b/src/core/core.controller.js @@ -8,7 +8,7 @@ import registry from './core.registry'; import Config, {determineAxis, getIndexAxis} from './core.config'; import {retinaScale, _isDomSupported} from '../helpers/helpers.dom'; import {each, callback as callCallback, uid, valueOrDefault, _elementsEqual, isNullOrUndef, setsEqual, defined, isFunction} from '../helpers/helpers.core'; -import {clearCanvas, clipArea, unclipArea, _isPointInArea} from '../helpers/helpers.canvas'; +import {clearCanvas, clipArea, createContext, unclipArea, _isPointInArea} from '../helpers'; // @ts-ignore import {version} from '../../package.json'; import {debounce} from '../helpers/helpers.extras'; @@ -735,7 +735,7 @@ class Chart { } getContext() { - return this.$context || (this.$context = {chart: this, type: 'chart'}); + return this.$context || (this.$context = createContext(null, {chart: this, type: 'chart'})); } getVisibleDatasetCount() { diff --git a/src/core/core.datasetController.js b/src/core/core.datasetController.js index 63aea1f33ad..1794c7031b9 100644 --- a/src/core/core.datasetController.js +++ b/src/core/core.datasetController.js @@ -2,7 +2,7 @@ import Animations from './core.animations'; import defaults from './core.defaults'; import {isArray, isFinite, isObject, valueOrDefault, resolveObjectKey, defined} from '../helpers/helpers.core'; import {listenArrayEvents, unlistenArrayEvents} from '../helpers/helpers.collection'; -import {sign} from '../helpers/helpers.math'; +import {createContext, sign} from '../helpers'; /** * @typedef { import("./core.controller").default } Chart @@ -167,7 +167,7 @@ function getFirstScaleId(chart, axis) { } function createDatasetContext(parent, index) { - return Object.assign(Object.create(parent), + return createContext(parent, { active: false, dataset: undefined, @@ -180,7 +180,7 @@ function createDatasetContext(parent, index) { } function createDataContext(parent, index, element) { - return Object.assign(Object.create(parent), { + return createContext(parent, { active: false, dataIndex: index, parsed: undefined, diff --git a/src/core/core.scale.js b/src/core/core.scale.js index 4549cadb9d9..97529e591a9 100644 --- a/src/core/core.scale.js +++ b/src/core/core.scale.js @@ -3,7 +3,7 @@ import {_alignPixel, _measureText, renderText, clipArea, unclipArea} from '../he import {callback as call, each, finiteOrDefault, isArray, isFinite, isNullOrUndef, isObject, valueOrDefault} from '../helpers/helpers.core'; import {toDegrees, toRadians, _int16Range, _limitValue, HALF_PI} from '../helpers/helpers.math'; import {_alignStartEnd, _toLeftRightCenter} from '../helpers/helpers.extras'; -import {toFont, toPadding, _addGrace} from '../helpers/helpers.options'; +import {createContext, toFont, toPadding, _addGrace} from '../helpers/helpers.options'; import './core.scale.defaults'; @@ -109,14 +109,14 @@ function getTitleHeight(options, fallback) { } function createScaleContext(parent, scale) { - return Object.assign(Object.create(parent), { + return createContext(parent, { scale, type: 'scale' }); } function createTickContext(parent, index, tick) { - return Object.assign(Object.create(parent), { + return createContext(parent, { tick, index, type: 'tick' diff --git a/src/elements/element.line.js b/src/elements/element.line.js index 9ad022dcb1f..64070628cfb 100644 --- a/src/elements/element.line.js +++ b/src/elements/element.line.js @@ -244,6 +244,7 @@ export default class LineElement extends Element { this.animated = true; this.options = undefined; + this._chart = undefined; this._loop = undefined; this._fullLoop = undefined; this._path = undefined; diff --git a/src/helpers/helpers.options.js b/src/helpers/helpers.options.js index d42b3503b8e..4c901c85d9e 100644 --- a/src/helpers/helpers.options.js +++ b/src/helpers/helpers.options.js @@ -184,3 +184,13 @@ export function _addGrace(minmax, grace, beginAtZero) { max: keepZero(max, change) }; } + +/** + * Create a context inheriting parentContext + * @param {object|null} parentContext + * @param {object} context + * @returns {object} + */ +export function createContext(parentContext, context) { + return Object.assign(Object.create(parentContext), context); +} diff --git a/src/helpers/helpers.segment.js b/src/helpers/helpers.segment.js index 3524558e7b9..3bbfebb0dfa 100644 --- a/src/helpers/helpers.segment.js +++ b/src/helpers/helpers.segment.js @@ -1,4 +1,5 @@ import {_angleBetween, _angleDiff, _normalizeAngle} from './helpers.math'; +import {createContext} from './helpers.options'; /** * @typedef { import("../elements/element.line").default } LineElement @@ -275,6 +276,7 @@ function splitByStyles(line, segments, points, segmentOptions) { * @return {Segment[]} */ function doSplitByStyles(line, segments, points, segmentOptions) { + const chartContext = line._chart.getContext(); const baseStyle = readStyle(line.options); const {_datasetIndex: datasetIndex, options: {spanGaps}} = line; const count = points.length; @@ -309,14 +311,14 @@ function doSplitByStyles(line, segments, points, segmentOptions) { let style; for (i = start + 1; i <= segment.end; i++) { const pt = points[i % count]; - style = readStyle(segmentOptions.setContext({ + style = readStyle(segmentOptions.setContext(createContext(chartContext, { type: 'segment', p0: prev, p1: pt, p0DataIndex: (i - 1) % count, p1DataIndex: i % count, datasetIndex - })); + }))); if (styleChanged(style, prevStyle)) { addStyle(start, i - 1, segment.loop, prevStyle); } diff --git a/src/plugins/plugin.tooltip.js b/src/plugins/plugin.tooltip.js index cdf4734ec3c..fd068e7aa7b 100644 --- a/src/plugins/plugin.tooltip.js +++ b/src/plugins/plugin.tooltip.js @@ -5,7 +5,7 @@ import {each, noop, isNullOrUndef, isArray, _elementsEqual} from '../helpers/hel import {toFont, toPadding, toTRBLCorners} from '../helpers/helpers.options'; import {getRtlAdapter, overrideTextDirection, restoreTextDirection} from '../helpers/helpers.rtl'; import {distanceBetweenPoints, _limitValue} from '../helpers/helpers.math'; -import {drawPoint} from '../helpers'; +import {createContext, drawPoint} from '../helpers'; /** * @typedef { import("../platform/platform.base").ChartEvent } ChartEvent @@ -335,7 +335,7 @@ function getBeforeAfterBodyLines(callback) { } function createTooltipContext(parent, tooltip, tooltipItems) { - return Object.assign(Object.create(parent), { + return createContext(parent, { tooltip, tooltipItems, type: 'tooltip' diff --git a/src/scales/scale.radialLinear.js b/src/scales/scale.radialLinear.js index 8ad7e8b23c2..cbe37238e66 100644 --- a/src/scales/scale.radialLinear.js +++ b/src/scales/scale.radialLinear.js @@ -4,7 +4,7 @@ import {HALF_PI, isNumber, TAU, toDegrees, toRadians, _normalizeAngle} from '../ import LinearScaleBase from './scale.linearbase'; import Ticks from '../core/core.ticks'; import {valueOrDefault, isArray, isFinite, callback as callCallback, isNullOrUndef} from '../helpers/helpers.core'; -import {toFont, toPadding} from '../helpers/helpers.options'; +import {createContext, toFont, toPadding} from '../helpers/helpers.options'; function getTickBackdropHeight(opts) { const tickOpts = opts.ticks; @@ -265,7 +265,7 @@ function numberOrZero(param) { } function createPointLabelContext(parent, index, label) { - return Object.assign(Object.create(parent), { + return createContext(parent, { label, index, type: 'pointLabel' diff --git a/types/helpers/helpers.options.d.ts b/types/helpers/helpers.options.d.ts index 28ad773d3ae..a6ff051b524 100644 --- a/types/helpers/helpers.options.d.ts +++ b/types/helpers/helpers.options.d.ts @@ -52,3 +52,10 @@ export function resolve( index?: number, info?: { cacheable?: boolean } ): T | undefined; + + +/** + * Create a context inheriting parentContext + * @since 3.6.0 + */ +export function createContext(parentContext: P, context: T): P extends null ? T : P & T; diff --git a/types/tests/helpers/options.ts b/types/tests/helpers/options.ts new file mode 100644 index 00000000000..9fa22225ca6 --- /dev/null +++ b/types/tests/helpers/options.ts @@ -0,0 +1,10 @@ +import { createContext } from '../../helpers/helpers.options'; + +const context1 = createContext(null, { type: 'test1', parent: true }); +const context2 = createContext(context1, { type: 'test2' }); + +const sSest: string = context1.type + context2.type; +const bTest: boolean = context1.parent && context2.parent; + +// @ts-expect-error Property 'notThere' does not exist on type '{ type: string; parent: boolean; } & { type: string; }' +context2.notThere = '';