This commit is contained in:
Ilya Tregubov
2025-08-12 11:24:20 +03:00
4 changed files with 220 additions and 72 deletions
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
+217 -69
View File
@@ -1,5 +1,5 @@
/*!
* Chart.js v4.4.7
* Chart.js v4.5.0
* https://www.chartjs.org
* (c) 2024 Chart.js Contributors
* Released under the MIT License
@@ -379,8 +379,13 @@ function almostEquals(x, y, epsilon) {
result.sort((a, b)=>a - b).pop();
return result;
}
/**
* Verifies that attempting to coerce n to string or number won't throw a TypeError.
*/ function isNonPrimitive(n) {
return typeof n === 'symbol' || typeof n === 'object' && n !== null && !(Symbol.toPrimitive in n || 'toString' in n || 'valueOf' in n);
}
function isNumber(n) {
return !isNaN(parseFloat(n)) && isFinite(n);
return !isNonPrimitive(n) && !isNaN(parseFloat(n)) && isFinite(n);
}
function almostWhole(x, epsilon) {
const rounded = Math.round(x);
@@ -676,18 +681,29 @@ function fontString(pixelSize, fontStyle, fontFamily) {
let start = 0;
let count = pointCount;
if (meta._sorted) {
const { iScale , _parsed } = meta;
const { iScale , vScale , _parsed } = meta;
const spanGaps = meta.dataset ? meta.dataset.options ? meta.dataset.options.spanGaps : null : null;
const axis = iScale.axis;
const { min , max , minDefined , maxDefined } = iScale.getUserBounds();
if (minDefined) {
start = _limitValue(Math.min(// @ts-expect-error Need to type _parsed
start = Math.min(// @ts-expect-error Need to type _parsed
_lookupByKey(_parsed, axis, min).lo, // @ts-expect-error Need to fix types on _lookupByKey
animationsDisabled ? pointCount : _lookupByKey(points, axis, iScale.getPixelForValue(min)).lo), 0, pointCount - 1);
animationsDisabled ? pointCount : _lookupByKey(points, axis, iScale.getPixelForValue(min)).lo);
if (spanGaps) {
const distanceToDefinedLo = _parsed.slice(0, start + 1).reverse().findIndex((point)=>!isNullOrUndef(point[vScale.axis]));
start -= Math.max(0, distanceToDefinedLo);
}
start = _limitValue(start, 0, pointCount - 1);
}
if (maxDefined) {
count = _limitValue(Math.max(// @ts-expect-error Need to type _parsed
let end = Math.max(// @ts-expect-error Need to type _parsed
_lookupByKey(_parsed, iScale.axis, max, true).hi + 1, // @ts-expect-error Need to fix types on _lookupByKey
animationsDisabled ? 0 : _lookupByKey(points, axis, iScale.getPixelForValue(max), true).hi + 1), start, pointCount) - start;
animationsDisabled ? 0 : _lookupByKey(points, axis, iScale.getPixelForValue(max), true).hi + 1);
if (spanGaps) {
const distanceToDefinedHi = _parsed.slice(end - 1).findIndex((point)=>!isNullOrUndef(point[vScale.axis]));
end += Math.max(0, distanceToDefinedHi);
}
count = _limitValue(end, start, pointCount) - start;
} else {
count = pointCount - start;
}
@@ -1801,12 +1817,6 @@ var defaults = /* #__PURE__ */ new Defaults({
]);
/**
* Note: typedefs are auto-exported, so use a made-up `dom` namespace where
* necessary to avoid duplicates with `export * from './helpers`; see
* https://github.com/microsoft/TypeScript/issues/46011
* @typedef { import('../core/core.controller.js').default } dom.Chart
* @typedef { import('../../types').ChartEvent } ChartEvent
*/ /**
* @private
*/ function _isDomSupported() {
return typeof window !== 'undefined' && typeof document !== 'undefined';
@@ -3510,6 +3520,35 @@ function styleChanged(style, prevStyle) {
return JSON.stringify(style, replacer) !== JSON.stringify(prevStyle, replacer);
}
function getSizeForArea(scale, chartArea, field) {
return scale.options.clip ? scale[field] : chartArea[field];
}
function getDatasetArea(meta, chartArea) {
const { xScale , yScale } = meta;
if (xScale && yScale) {
return {
left: getSizeForArea(xScale, chartArea, 'left'),
right: getSizeForArea(xScale, chartArea, 'right'),
top: getSizeForArea(yScale, chartArea, 'top'),
bottom: getSizeForArea(yScale, chartArea, 'bottom')
};
}
return chartArea;
}
function getDatasetClipArea(chart, meta) {
const clip = meta._clip;
if (clip.disabled) {
return false;
}
const area = getDatasetArea(meta, chart.chartArea);
return {
left: clip.left === false ? 0 : area.left - (clip.left === true ? 0 : clip.left),
right: clip.right === false ? chart.width : area.right + (clip.right === true ? 0 : clip.right),
top: clip.top === false ? 0 : area.top - (clip.top === true ? 0 : clip.top),
bottom: clip.bottom === false ? chart.height : area.bottom + (clip.bottom === true ? 0 : clip.bottom)
};
}
var helpers = /*#__PURE__*/Object.freeze({
__proto__: null,
HALF_PI: HALF_PI,
@@ -3587,6 +3626,7 @@ finiteOrDefault: finiteOrDefault,
fontString: fontString,
formatNumber: formatNumber,
getAngleFromPoint: getAngleFromPoint,
getDatasetClipArea: getDatasetClipArea,
getHoverColor: getHoverColor,
getMaximumSize: getMaximumSize,
getRelativePosition: getRelativePosition,
@@ -3638,10 +3678,20 @@ valueOrDefault: valueOrDefault
function binarySearch(metaset, axis, value, intersect) {
const { controller , data , _sorted } = metaset;
const iScale = controller._cachedMeta.iScale;
const spanGaps = metaset.dataset ? metaset.dataset.options ? metaset.dataset.options.spanGaps : null : null;
if (iScale && axis === iScale.axis && axis !== 'r' && _sorted && data.length) {
const lookupMethod = iScale._reversePixels ? _rlookupByKey : _lookupByKey;
if (!intersect) {
return lookupMethod(data, axis, value);
const result = lookupMethod(data, axis, value);
if (spanGaps) {
const { vScale } = controller._cachedMeta;
const { _parsed } = metaset;
const distanceToDefinedLo = _parsed.slice(0, result.lo + 1).reverse().findIndex((point)=>!isNullOrUndef(point[vScale.axis]));
result.lo -= Math.max(0, distanceToDefinedLo);
const distanceToDefinedHi = _parsed.slice(result.hi).findIndex((point)=>!isNullOrUndef(point[vScale.axis]));
result.hi += Math.max(0, distanceToDefinedHi);
}
return result;
} else if (controller._sharedOptions) {
const el = data[0];
const range = typeof el.getRange === 'function' && el.getRange(axis);
@@ -7559,7 +7609,7 @@ function needContext(proxy, names) {
return false;
}
var version = "4.4.7";
var version = "4.5.0";
const KNOWN_POSITIONS = [
'top',
@@ -7629,21 +7679,6 @@ function moveNumericKeys(obj, start, move) {
}
return e;
}
function getSizeForArea(scale, chartArea, field) {
return scale.options.clip ? scale[field] : chartArea[field];
}
function getDatasetArea(meta, chartArea) {
const { xScale , yScale } = meta;
if (xScale && yScale) {
return {
left: getSizeForArea(xScale, chartArea, 'left'),
right: getSizeForArea(xScale, chartArea, 'right'),
top: getSizeForArea(yScale, chartArea, 'top'),
bottom: getSizeForArea(yScale, chartArea, 'bottom')
};
}
return chartArea;
}
class Chart {
static defaults = defaults;
static instances = instances;
@@ -8142,27 +8177,20 @@ class Chart {
}
_drawDataset(meta) {
const ctx = this.ctx;
const clip = meta._clip;
const useClip = !clip.disabled;
const area = getDatasetArea(meta, this.chartArea);
const args = {
meta,
index: meta.index,
cancelable: true
};
const clip = getDatasetClipArea(this, meta);
if (this.notifyPlugins('beforeDatasetDraw', args) === false) {
return;
}
if (useClip) {
clipArea(ctx, {
left: clip.left === false ? 0 : area.left - clip.left,
right: clip.right === false ? this.width : area.right + clip.right,
top: clip.top === false ? 0 : area.top - clip.top,
bottom: clip.bottom === false ? this.height : area.bottom + clip.bottom
});
if (clip) {
clipArea(ctx, clip);
}
meta.controller.draw();
if (useClip) {
if (clip) {
unclipArea(ctx);
}
args.cancelable = false;
@@ -8887,6 +8915,22 @@ class BarController extends DatasetController {
_getStackCount(index) {
return this._getStacks(undefined, index).length;
}
_getAxisCount() {
return this._getAxis().length;
}
getFirstScaleIdForIndexAxis() {
const scales = this.chart.scales;
const indexScaleId = this.chart.options.indexAxis;
return Object.keys(scales).filter((key)=>scales[key].axis === indexScaleId).shift();
}
_getAxis() {
const axis = {};
const firstScaleAxisId = this.getFirstScaleIdForIndexAxis();
for (const dataset of this.chart.data.datasets){
axis[valueOrDefault(this.chart.options.indexAxis === 'x' ? dataset.xAxisID : dataset.yAxisID, firstScaleAxisId)] = true;
}
return Object.keys(axis);
}
_getStackIndex(datasetIndex, name, dataIndex) {
const stacks = this._getStacks(datasetIndex, dataIndex);
const index = name !== undefined ? stacks.indexOf(name) : -1;
@@ -8977,10 +9021,13 @@ class BarController extends DatasetController {
const skipNull = options.skipNull;
const maxBarThickness = valueOrDefault(options.maxBarThickness, Infinity);
let center, size;
const axisCount = this._getAxisCount();
if (ruler.grouped) {
const stackCount = skipNull ? this._getStackCount(index) : ruler.stackCount;
const range = options.barThickness === 'flex' ? computeFlexCategoryTraits(index, ruler, options, stackCount) : computeFitCategoryTraits(index, ruler, options, stackCount);
const stackIndex = this._getStackIndex(this.index, this._cachedMeta.stack, skipNull ? index : undefined);
const range = options.barThickness === 'flex' ? computeFlexCategoryTraits(index, ruler, options, stackCount * axisCount) : computeFitCategoryTraits(index, ruler, options, stackCount * axisCount);
const axisID = this.chart.options.indexAxis === 'x' ? this.getDataset().xAxisID : this.getDataset().yAxisID;
const axisNumber = this._getAxis().indexOf(valueOrDefault(axisID, this.getFirstScaleIdForIndexAxis()));
const stackIndex = this._getStackIndex(this.index, this._cachedMeta.stack, skipNull ? index : undefined) + axisNumber;
center = range.start + range.chunk * stackIndex + range.chunk / 2;
size = Math.min(maxBarThickness, range.chunk * range.ratio);
} else {
@@ -9937,6 +9984,34 @@ RadarController: RadarController,
ScatterController: ScatterController
});
function clipSelf(ctx, element, endAngle) {
const { startAngle , x , y , outerRadius , innerRadius , options } = element;
const { borderWidth , borderJoinStyle } = options;
const outerAngleClip = Math.min(borderWidth / outerRadius, _normalizeAngle(startAngle - endAngle));
ctx.beginPath();
ctx.arc(x, y, outerRadius - borderWidth / 2, startAngle + outerAngleClip / 2, endAngle - outerAngleClip / 2);
if (innerRadius > 0) {
const innerAngleClip = Math.min(borderWidth / innerRadius, _normalizeAngle(startAngle - endAngle));
ctx.arc(x, y, innerRadius + borderWidth / 2, endAngle - innerAngleClip / 2, startAngle + innerAngleClip / 2, true);
} else {
const clipWidth = Math.min(borderWidth / 2, outerRadius * _normalizeAngle(startAngle - endAngle));
if (borderJoinStyle === 'round') {
ctx.arc(x, y, clipWidth, endAngle - PI / 2, startAngle + PI / 2, true);
} else if (borderJoinStyle === 'bevel') {
const r = 2 * clipWidth * clipWidth;
const endX = -r * Math.cos(endAngle + PI / 2) + x;
const endY = -r * Math.sin(endAngle + PI / 2) + y;
const startX = r * Math.cos(startAngle + PI / 2) + x;
const startY = r * Math.sin(startAngle + PI / 2) + y;
ctx.lineTo(endX, endY);
ctx.lineTo(startX, startY);
}
}
ctx.closePath();
ctx.moveTo(0, 0);
ctx.rect(0, 0, ctx.canvas.width, ctx.canvas.height);
ctx.clip('evenodd');
}
function clipArc(ctx, element, endAngle) {
const { startAngle , pixelMargin , x , y , outerRadius , innerRadius } = element;
let angleMargin = pixelMargin / outerRadius;
@@ -10100,7 +10175,7 @@ function drawArc(ctx, element, offset, spacing, circular) {
}
function drawBorder(ctx, element, offset, spacing, circular) {
const { fullCircles , startAngle , circumference , options } = element;
const { borderWidth , borderJoinStyle , borderDash , borderDashOffset } = options;
const { borderWidth , borderJoinStyle , borderDash , borderDashOffset , borderRadius } = options;
const inner = options.borderAlign === 'inner';
if (!borderWidth) {
return;
@@ -10127,6 +10202,9 @@ function drawBorder(ctx, element, offset, spacing, circular) {
if (inner) {
clipArc(ctx, element, endAngle);
}
if (options.selfJoin && endAngle - startAngle >= PI && borderRadius === 0 && borderJoinStyle !== 'miter') {
clipSelf(ctx, element, endAngle);
}
if (!fullCircles) {
pathArc(ctx, element, offset, spacing, endAngle, circular);
ctx.stroke();
@@ -10145,7 +10223,8 @@ class ArcElement extends Element {
offset: 0,
spacing: 0,
angle: undefined,
circular: true
circular: true,
selfJoin: false
};
static defaultRoutes = {
backgroundColor: 'backgroundColor'
@@ -11900,7 +11979,7 @@ const INTERVALS = {
value = parser(value);
}
if (!isNumberFinite(value)) {
value = typeof parser === 'string' ? adapter.parse(value, parser) : adapter.parse(value);
value = typeof parser === 'string' ? adapter.parse(value, parser) : adapter.parse(value);
}
if (value === null) {
return null;
@@ -13051,11 +13130,13 @@ function computeCircularBoundary(source) {
function _drawfill(ctx, source, area) {
const target = _getTarget(source);
const { line , scale , axis } = source;
const { chart , index , line , scale , axis } = source;
const lineOpts = line.options;
const fillOption = lineOpts.fill;
const color = lineOpts.backgroundColor;
const { above =color , below =color } = fillOption || {};
const meta = chart.getDatasetMeta(index);
const clip = getDatasetClipArea(chart, meta);
if (target && line.points.length) {
clipArea(ctx, area);
doFill(ctx, {
@@ -13065,34 +13146,54 @@ function _drawfill(ctx, source, area) {
below,
area,
scale,
axis
axis,
clip
});
unclipArea(ctx);
}
}
function doFill(ctx, cfg) {
const { line , target , above , below , area , scale } = cfg;
const { line , target , above , below , area , scale , clip } = cfg;
const property = line._loop ? 'angle' : cfg.axis;
ctx.save();
if (property === 'x' && below !== above) {
clipVertical(ctx, target, area.top);
fill(ctx, {
line,
target,
color: above,
scale,
property
});
ctx.restore();
ctx.save();
clipVertical(ctx, target, area.bottom);
let fillColor = below;
if (below !== above) {
if (property === 'x') {
clipVertical(ctx, target, area.top);
fill(ctx, {
line,
target,
color: above,
scale,
property,
clip
});
ctx.restore();
ctx.save();
clipVertical(ctx, target, area.bottom);
} else if (property === 'y') {
clipHorizontal(ctx, target, area.left);
fill(ctx, {
line,
target,
color: below,
scale,
property,
clip
});
ctx.restore();
ctx.save();
clipHorizontal(ctx, target, area.right);
fillColor = above;
}
}
fill(ctx, {
line,
target,
color: below,
color: fillColor,
scale,
property
property,
clip
});
ctx.restore();
}
@@ -13125,15 +13226,44 @@ function clipVertical(ctx, target, clipY) {
ctx.closePath();
ctx.clip();
}
function clipHorizontal(ctx, target, clipX) {
const { segments , points } = target;
let first = true;
let lineLoop = false;
ctx.beginPath();
for (const segment of segments){
const { start , end } = segment;
const firstPoint = points[start];
const lastPoint = points[_findSegmentEnd(start, end, points)];
if (first) {
ctx.moveTo(firstPoint.x, firstPoint.y);
first = false;
} else {
ctx.lineTo(clipX, firstPoint.y);
ctx.lineTo(firstPoint.x, firstPoint.y);
}
lineLoop = !!target.pathSegment(ctx, segment, {
move: lineLoop
});
if (lineLoop) {
ctx.closePath();
} else {
ctx.lineTo(clipX, lastPoint.y);
}
}
ctx.lineTo(clipX, target.first().y);
ctx.closePath();
ctx.clip();
}
function fill(ctx, cfg) {
const { line , target , property , color , scale } = cfg;
const { line , target , property , color , scale , clip } = cfg;
const segments = _segments(line, target, property);
for (const { source: src , target: tgt , start , end } of segments){
const { style: { backgroundColor =color } = {} } = src;
const notShape = target !== true;
ctx.save();
ctx.fillStyle = backgroundColor;
clipBounds(ctx, scale, notShape && _getBounds(property, start, end));
clipBounds(ctx, scale, clip, notShape && _getBounds(property, start, end));
ctx.beginPath();
const lineLoop = !!line.pathSegment(ctx, src);
let loop;
@@ -13157,12 +13287,30 @@ function fill(ctx, cfg) {
ctx.restore();
}
}
function clipBounds(ctx, scale, bounds) {
const { top , bottom } = scale.chart.chartArea;
function clipBounds(ctx, scale, clip, bounds) {
const chartArea = scale.chart.chartArea;
const { property , start , end } = bounds || {};
if (property === 'x') {
if (property === 'x' || property === 'y') {
let left, top, right, bottom;
if (property === 'x') {
left = start;
top = chartArea.top;
right = end;
bottom = chartArea.bottom;
} else {
left = chartArea.left;
top = start;
right = chartArea.right;
bottom = end;
}
ctx.beginPath();
ctx.rect(start, top, end - start, bottom - top);
if (clip) {
left = Math.max(left, clip.left);
right = Math.min(right, clip.right);
top = Math.max(top, clip.top);
bottom = Math.min(bottom, clip.bottom);
}
ctx.rect(left, top, right - left, bottom - top);
ctx.clip();
}
}
+1 -1
View File
@@ -404,7 +404,7 @@ All rights reserved.</copyright>
<location>amd/src/chartjs-lazy.js</location>
<name>Chart.js</name>
<description>Simple yet flexible JavaScript charting for designers &amp; developers.</description>
<version>4.4.7</version>
<version>4.5.0</version>
<license>MIT</license>
<repository>https://github.com/chartjs/Chart.js</repository>
<copyrights>