]> git.ipfire.org Git - thirdparty/Chart.js.git/commitdiff
refactor: rewrite canvas helpers to ts (#11100)
authorDan Onoshko <danon0404@gmail.com>
Wed, 15 Feb 2023 14:26:49 +0000 (18:26 +0400)
committerGitHub <noreply@github.com>
Wed, 15 Feb 2023 14:26:49 +0000 (09:26 -0500)
* refactor: rewrite canvas helpers to ts

* refactor: review fixes

* refactor: rm src/helpers/types.ts temporary entry point

12 files changed:
src/core/core.scale.js
src/elements/element.line.js
src/helpers/helpers.canvas.ts [moved from src/helpers/helpers.canvas.js with 54% similarity]
src/helpers/helpers.curve.ts
src/helpers/helpers.interpolation.ts
src/helpers/helpers.options.ts
src/helpers/types.ts [deleted file]
src/types/geometric.d.ts
src/types/helpers/helpers.canvas.d.ts [deleted file]
src/types/helpers/helpers.segment.d.ts [deleted file]
src/types/helpers/index.d.ts [deleted file]
src/types/index.d.ts

index 50ddca8724c5c3bf70a8932c8f880de53210997e..8b8bf34729253f3f2a39bf61659bef4ca47e1345 100644 (file)
@@ -11,7 +11,7 @@ const offsetFromEdge = (scale, edge, offset) => edge === 'top' || edge === 'left
 const getTicksLimit = (ticksLength, maxTicksLimit) => Math.min(maxTicksLimit || ticksLength, ticksLength);
 
 /**
- * @typedef { import('./core.controller.js').default } Chart
+ * @typedef { import('../types/index.js').Chart } Chart
  * @typedef {{value:number | string, label?:string, major?:boolean, $context?:any}} Tick
  */
 
@@ -120,6 +120,7 @@ function createTickContext(parent, index, tick) {
 }
 
 function titleAlign(align, position, reverse) {
+  /** @type {CanvasTextAlign} */
   let ret = _toLeftRightCenter(align);
   if ((reverse && position !== 'right') || (!reverse && position === 'right')) {
     ret = reverseAlign(ret);
@@ -839,7 +840,7 @@ export default class Scale extends Element {
       } else if (isArray(label)) {
         // if it is an array let's measure each element
         for (j = 0, jlen = label.length; j < jlen; ++j) {
-          nestedLabel = label[j];
+          nestedLabel = /** @type {string} */ (label[j]);
           // Undefined labels and arrays should not be measured
           if (!isNullOrUndef(nestedLabel) && !isArray(nestedLabel)) {
             width = _measureText(ctx, cache.data, cache.gc, width, nestedLabel);
index 3305393dc0b94dfab32ebaa593e2a1761a917c84..4384e4d7b8480547291ed5c46b7fd94d04d29d6e 100644 (file)
@@ -22,6 +22,9 @@ function lineTo(ctx, previous, target) {
   ctx.lineTo(target.x, target.y);
 }
 
+/**
+ * @returns {any}
+ */
 function getLineMethod(options) {
   if (options.stepped) {
     return _steppedLineTo;
similarity index 54%
rename from src/helpers/helpers.canvas.js
rename to src/helpers/helpers.canvas.ts
index 217c3f1edce14772d8f19a7de0ad0416a2185241..5221c438c263932e73433638d4a29af6e6fc9596 100644 (file)
@@ -1,25 +1,28 @@
+import type {
+  Chart,
+  Point,
+  FontSpec,
+  CanvasFontSpec,
+  PointStyle,
+  RenderTextOpts,
+  BackdropOptions
+} from '../types/index.js';
+import type {
+  TRBL,
+  SplinePoint,
+  RoundedRect,
+  TRBLCorners
+} from '../types/geometric.js';
 import {isArray, isNullOrUndef} from './helpers.core.js';
 import {PI, TAU, HALF_PI, QUARTER_PI, TWO_THIRDS_PI, RAD_PER_DEG} from './helpers.math.js';
 
-/**
- * Note: typedefs are auto-exported, so use a made-up `canvas` 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 } canvas.Chart
- * @typedef { import('../types/index.js').Point } Point
- */
-
-/**
- * @namespace Chart.helpers.canvas
- */
-
 /**
  * Converts the given font object into a CSS font string.
- * @param {object} font - A font object.
- * @return {string|null} The CSS font string. See https://developer.mozilla.org/en-US/docs/Web/CSS/font
+ * @param font - A font object.
+ * @return The CSS font string. See https://developer.mozilla.org/en-US/docs/Web/CSS/font
  * @private
  */
-export function toFontString(font) {
+export function toFontString(font: FontSpec) {
   if (!font || isNullOrUndef(font.size) || isNullOrUndef(font.family)) {
     return null;
   }
@@ -33,7 +36,13 @@ export function toFontString(font) {
 /**
  * @private
  */
-export function _measureText(ctx, data, gc, longest, string) {
+export function _measureText(
+  ctx: CanvasRenderingContext2D,
+  data: Record<string, number>,
+  gc: string[],
+  longest: number,
+  string: string
+) {
   let textWidth = data[string];
   if (!textWidth) {
     textWidth = data[string] = ctx.measureText(string).width;
@@ -45,10 +54,19 @@ export function _measureText(ctx, data, gc, longest, string) {
   return longest;
 }
 
+type Thing = string | undefined | null
+type Things = (Thing | Thing[])[]
+
 /**
  * @private
  */
-export function _longestText(ctx, font, arrayOfThings, cache) {
+// eslint-disable-next-line complexity
+export function _longestText(
+  ctx: CanvasRenderingContext2D,
+  font: string,
+  arrayOfThings: Things,
+  cache?: {data?: Record<string, number>, garbageCollect?: string[], font?: string}
+) {
   cache = cache || {};
   let data = cache.data = cache.data || {};
   let gc = cache.garbageCollect = cache.garbageCollect || [];
@@ -64,12 +82,12 @@ export function _longestText(ctx, font, arrayOfThings, cache) {
   ctx.font = font;
   let longest = 0;
   const ilen = arrayOfThings.length;
-  let i, j, jlen, thing, nestedThing;
+  let i: number, j: number, jlen: number, thing: Thing | Thing[], nestedThing: Thing | Thing[];
   for (i = 0; i < ilen; i++) {
     thing = arrayOfThings[i];
 
     // Undefined strings and arrays should not be measured
-    if (thing !== undefined && thing !== null && isArray(thing) !== true) {
+    if (thing !== undefined && thing !== null && !isArray(thing)) {
       longest = _measureText(ctx, data, gc, longest, thing);
     } else if (isArray(thing)) {
       // if it is an array lets measure each element
@@ -98,13 +116,13 @@ export function _longestText(ctx, font, arrayOfThings, cache) {
 
 /**
  * Returns the aligned pixel value to avoid anti-aliasing blur
- * @param {canvas.Chart} chart - The chart instance.
- * @param {number} pixel - A pixel value.
- * @param {number} width - The width of the element.
- * @returns {number} The aligned pixel value.
+ * @param chart - The chart instance.
+ * @param pixel - A pixel value.
+ * @param width - The width of the element.
+ * @returns The aligned pixel value.
  * @private
  */
-export function _alignPixel(chart, pixel, width) {
+export function _alignPixel(chart: Chart, pixel: number, width: number) {
   const devicePixelRatio = chart.currentDevicePixelRatio;
   const halfWidth = width !== 0 ? Math.max(width / 2, 0.5) : 0;
   return Math.round((pixel - halfWidth) * devicePixelRatio) / devicePixelRatio + halfWidth;
@@ -112,10 +130,8 @@ export function _alignPixel(chart, pixel, width) {
 
 /**
  * Clears the entire canvas.
- * @param {HTMLCanvasElement} canvas
- * @param {CanvasRenderingContext2D} [ctx]
  */
-export function clearCanvas(canvas, ctx) {
+export function clearCanvas(canvas: HTMLCanvasElement, ctx?: CanvasRenderingContext2D) {
   ctx = ctx || canvas.getContext('2d');
 
   ctx.save();
@@ -126,12 +142,32 @@ export function clearCanvas(canvas, ctx) {
   ctx.restore();
 }
 
-export function drawPoint(ctx, options, x, y) {
+export interface DrawPointOptions {
+  pointStyle: PointStyle;
+  rotation?: number;
+  radius: number;
+  borderWidth: number;
+}
+
+export function drawPoint(
+  ctx: CanvasRenderingContext2D,
+  options: DrawPointOptions,
+  x: number,
+  y: number
+) {
+  // eslint-disable-next-line @typescript-eslint/no-use-before-define
   drawPointLegend(ctx, options, x, y, null);
 }
 
-export function drawPointLegend(ctx, options, x, y, w) {
-  let type, xOffset, yOffset, size, cornerRadius, width, xOffsetW, yOffsetW;
+// eslint-disable-next-line complexity
+export function drawPointLegend(
+  ctx: CanvasRenderingContext2D,
+  options: DrawPointOptions,
+  x: number,
+  y: number,
+  w: number
+) {
+  let type: string, xOffset: number, yOffset: number, size: number, cornerRadius: number, width: number, xOffsetW: number, yOffsetW: number;
   const style = options.pointStyle;
   const rotation = options.rotation;
   const radius = options.radius;
@@ -157,24 +193,24 @@ export function drawPointLegend(ctx, options, x, y, w) {
 
   switch (style) {
   // Default includes circle
-  default:
-    if (w) {
-      ctx.ellipse(x, y, w / 2, radius, 0, 0, TAU);
-    } else {
-      ctx.arc(x, y, radius, 0, TAU);
-    }
-    ctx.closePath();
-    break;
-  case 'triangle':
-    width = w ? w / 2 : radius;
-    ctx.moveTo(x + Math.sin(rad) * width, y - Math.cos(rad) * radius);
-    rad += TWO_THIRDS_PI;
-    ctx.lineTo(x + Math.sin(rad) * width, y - Math.cos(rad) * radius);
-    rad += TWO_THIRDS_PI;
-    ctx.lineTo(x + Math.sin(rad) * width, y - Math.cos(rad) * radius);
-    ctx.closePath();
-    break;
-  case 'rectRounded':
+    default:
+      if (w) {
+        ctx.ellipse(x, y, w / 2, radius, 0, 0, TAU);
+      } else {
+        ctx.arc(x, y, radius, 0, TAU);
+      }
+      ctx.closePath();
+      break;
+    case 'triangle':
+      width = w ? w / 2 : radius;
+      ctx.moveTo(x + Math.sin(rad) * width, y - Math.cos(rad) * radius);
+      rad += TWO_THIRDS_PI;
+      ctx.lineTo(x + Math.sin(rad) * width, y - Math.cos(rad) * radius);
+      rad += TWO_THIRDS_PI;
+      ctx.lineTo(x + Math.sin(rad) * width, y - Math.cos(rad) * radius);
+      ctx.closePath();
+      break;
+    case 'rectRounded':
     // NOTE: the rounded rect implementation changed to use `arc` instead of
     // `quadraticCurveTo` since it generates better results when rect is
     // almost a circle. 0.516 (instead of 0.5) produces results with visually
@@ -182,83 +218,83 @@ export function drawPointLegend(ctx, options, x, y, w) {
     // circle with `radius`. For more details, see the following PRs:
     // https://github.com/chartjs/Chart.js/issues/5597
     // https://github.com/chartjs/Chart.js/issues/5858
-    cornerRadius = radius * 0.516;
-    size = radius - cornerRadius;
-    xOffset = Math.cos(rad + QUARTER_PI) * size;
-    xOffsetW = Math.cos(rad + QUARTER_PI) * (w ? w / 2 - cornerRadius : size);
-    yOffset = Math.sin(rad + QUARTER_PI) * size;
-    yOffsetW = Math.sin(rad + QUARTER_PI) * (w ? w / 2 - cornerRadius : size);
-    ctx.arc(x - xOffsetW, y - yOffset, cornerRadius, rad - PI, rad - HALF_PI);
-    ctx.arc(x + yOffsetW, y - xOffset, cornerRadius, rad - HALF_PI, rad);
-    ctx.arc(x + xOffsetW, y + yOffset, cornerRadius, rad, rad + HALF_PI);
-    ctx.arc(x - yOffsetW, y + xOffset, cornerRadius, rad + HALF_PI, rad + PI);
-    ctx.closePath();
-    break;
-  case 'rect':
-    if (!rotation) {
-      size = Math.SQRT1_2 * radius;
-      width = w ? w / 2 : size;
-      ctx.rect(x - width, y - size, 2 * width, 2 * size);
+      cornerRadius = radius * 0.516;
+      size = radius - cornerRadius;
+      xOffset = Math.cos(rad + QUARTER_PI) * size;
+      xOffsetW = Math.cos(rad + QUARTER_PI) * (w ? w / 2 - cornerRadius : size);
+      yOffset = Math.sin(rad + QUARTER_PI) * size;
+      yOffsetW = Math.sin(rad + QUARTER_PI) * (w ? w / 2 - cornerRadius : size);
+      ctx.arc(x - xOffsetW, y - yOffset, cornerRadius, rad - PI, rad - HALF_PI);
+      ctx.arc(x + yOffsetW, y - xOffset, cornerRadius, rad - HALF_PI, rad);
+      ctx.arc(x + xOffsetW, y + yOffset, cornerRadius, rad, rad + HALF_PI);
+      ctx.arc(x - yOffsetW, y + xOffset, cornerRadius, rad + HALF_PI, rad + PI);
+      ctx.closePath();
       break;
-    }
-    rad += QUARTER_PI;
+    case 'rect':
+      if (!rotation) {
+        size = Math.SQRT1_2 * radius;
+        width = w ? w / 2 : size;
+        ctx.rect(x - width, y - size, 2 * width, 2 * size);
+        break;
+      }
+      rad += QUARTER_PI;
     /* falls through */
-  case 'rectRot':
-    xOffsetW = Math.cos(rad) * (w ? w / 2 : radius);
-    xOffset = Math.cos(rad) * radius;
-    yOffset = Math.sin(rad) * radius;
-    yOffsetW = Math.sin(rad) * (w ? w / 2 : radius);
-    ctx.moveTo(x - xOffsetW, y - yOffset);
-    ctx.lineTo(x + yOffsetW, y - xOffset);
-    ctx.lineTo(x + xOffsetW, y + yOffset);
-    ctx.lineTo(x - yOffsetW, y + xOffset);
-    ctx.closePath();
-    break;
-  case 'crossRot':
-    rad += QUARTER_PI;
+    case 'rectRot':
+      xOffsetW = Math.cos(rad) * (w ? w / 2 : radius);
+      xOffset = Math.cos(rad) * radius;
+      yOffset = Math.sin(rad) * radius;
+      yOffsetW = Math.sin(rad) * (w ? w / 2 : radius);
+      ctx.moveTo(x - xOffsetW, y - yOffset);
+      ctx.lineTo(x + yOffsetW, y - xOffset);
+      ctx.lineTo(x + xOffsetW, y + yOffset);
+      ctx.lineTo(x - yOffsetW, y + xOffset);
+      ctx.closePath();
+      break;
+    case 'crossRot':
+      rad += QUARTER_PI;
     /* falls through */
-  case 'cross':
-    xOffsetW = Math.cos(rad) * (w ? w / 2 : radius);
-    xOffset = Math.cos(rad) * radius;
-    yOffset = Math.sin(rad) * radius;
-    yOffsetW = Math.sin(rad) * (w ? w / 2 : radius);
-    ctx.moveTo(x - xOffsetW, y - yOffset);
-    ctx.lineTo(x + xOffsetW, y + yOffset);
-    ctx.moveTo(x + yOffsetW, y - xOffset);
-    ctx.lineTo(x - yOffsetW, y + xOffset);
-    break;
-  case 'star':
-    xOffsetW = Math.cos(rad) * (w ? w / 2 : radius);
-    xOffset = Math.cos(rad) * radius;
-    yOffset = Math.sin(rad) * radius;
-    yOffsetW = Math.sin(rad) * (w ? w / 2 : radius);
-    ctx.moveTo(x - xOffsetW, y - yOffset);
-    ctx.lineTo(x + xOffsetW, y + yOffset);
-    ctx.moveTo(x + yOffsetW, y - xOffset);
-    ctx.lineTo(x - yOffsetW, y + xOffset);
-    rad += QUARTER_PI;
-    xOffsetW = Math.cos(rad) * (w ? w / 2 : radius);
-    xOffset = Math.cos(rad) * radius;
-    yOffset = Math.sin(rad) * radius;
-    yOffsetW = Math.sin(rad) * (w ? w / 2 : radius);
-    ctx.moveTo(x - xOffsetW, y - yOffset);
-    ctx.lineTo(x + xOffsetW, y + yOffset);
-    ctx.moveTo(x + yOffsetW, y - xOffset);
-    ctx.lineTo(x - yOffsetW, y + xOffset);
-    break;
-  case 'line':
-    xOffset = w ? w / 2 : Math.cos(rad) * radius;
-    yOffset = Math.sin(rad) * radius;
-    ctx.moveTo(x - xOffset, y - yOffset);
-    ctx.lineTo(x + xOffset, y + yOffset);
-    break;
-  case 'dash':
-    ctx.moveTo(x, y);
-    ctx.lineTo(x + Math.cos(rad) * (w ? w / 2 : radius), y + Math.sin(rad) * radius);
-    break;
-  case false:
-    ctx.closePath();
-    break;
+    case 'cross':
+      xOffsetW = Math.cos(rad) * (w ? w / 2 : radius);
+      xOffset = Math.cos(rad) * radius;
+      yOffset = Math.sin(rad) * radius;
+      yOffsetW = Math.sin(rad) * (w ? w / 2 : radius);
+      ctx.moveTo(x - xOffsetW, y - yOffset);
+      ctx.lineTo(x + xOffsetW, y + yOffset);
+      ctx.moveTo(x + yOffsetW, y - xOffset);
+      ctx.lineTo(x - yOffsetW, y + xOffset);
+      break;
+    case 'star':
+      xOffsetW = Math.cos(rad) * (w ? w / 2 : radius);
+      xOffset = Math.cos(rad) * radius;
+      yOffset = Math.sin(rad) * radius;
+      yOffsetW = Math.sin(rad) * (w ? w / 2 : radius);
+      ctx.moveTo(x - xOffsetW, y - yOffset);
+      ctx.lineTo(x + xOffsetW, y + yOffset);
+      ctx.moveTo(x + yOffsetW, y - xOffset);
+      ctx.lineTo(x - yOffsetW, y + xOffset);
+      rad += QUARTER_PI;
+      xOffsetW = Math.cos(rad) * (w ? w / 2 : radius);
+      xOffset = Math.cos(rad) * radius;
+      yOffset = Math.sin(rad) * radius;
+      yOffsetW = Math.sin(rad) * (w ? w / 2 : radius);
+      ctx.moveTo(x - xOffsetW, y - yOffset);
+      ctx.lineTo(x + xOffsetW, y + yOffset);
+      ctx.moveTo(x + yOffsetW, y - xOffset);
+      ctx.lineTo(x - yOffsetW, y + xOffset);
+      break;
+    case 'line':
+      xOffset = w ? w / 2 : Math.cos(rad) * radius;
+      yOffset = Math.sin(rad) * radius;
+      ctx.moveTo(x - xOffset, y - yOffset);
+      ctx.lineTo(x + xOffset, y + yOffset);
+      break;
+    case 'dash':
+      ctx.moveTo(x, y);
+      ctx.lineTo(x + Math.cos(rad) * (w ? w / 2 : radius), y + Math.sin(rad) * radius);
+      break;
+    case false:
+      ctx.closePath();
+      break;
   }
 
   ctx.fill();
@@ -269,34 +305,43 @@ export function drawPointLegend(ctx, options, x, y, w) {
 
 /**
  * Returns true if the point is inside the rectangle
- * @param {Point} point - The point to test
- * @param {object} area - The rectangle
- * @param {number} [margin] - allowed margin
- * @returns {boolean}
+ * @param point - The point to test
+ * @param area - The rectangle
+ * @param margin - allowed margin
  * @private
  */
-export function _isPointInArea(point, area, margin) {
+export function _isPointInArea(
+  point: Point,
+  area: TRBL,
+  margin?: number
+) {
   margin = margin || 0.5; // margin - default is to match rounded decimals
 
   return !area || (point && point.x > area.left - margin && point.x < area.right + margin &&
                point.y > area.top - margin && point.y < area.bottom + margin);
 }
 
-export function clipArea(ctx, area) {
+export function clipArea(ctx: CanvasRenderingContext2D, area: TRBL) {
   ctx.save();
   ctx.beginPath();
   ctx.rect(area.left, area.top, area.right - area.left, area.bottom - area.top);
   ctx.clip();
 }
 
-export function unclipArea(ctx) {
+export function unclipArea(ctx: CanvasRenderingContext2D) {
   ctx.restore();
 }
 
 /**
  * @private
  */
-export function _steppedLineTo(ctx, previous, target, flip, mode) {
+export function _steppedLineTo(
+  ctx: CanvasRenderingContext2D,
+  previous: Point,
+  target: Point,
+  flip?: boolean,
+  mode?: string
+) {
   if (!previous) {
     return ctx.lineTo(target.x, target.y);
   }
@@ -315,7 +360,12 @@ export function _steppedLineTo(ctx, previous, target, flip, mode) {
 /**
  * @private
  */
-export function _bezierCurveTo(ctx, previous, target, flip) {
+export function _bezierCurveTo(
+  ctx: CanvasRenderingContext2D,
+  previous: SplinePoint,
+  target: SplinePoint,
+  flip?: boolean
+) {
   if (!previous) {
     return ctx.lineTo(target.x, target.y);
   }
@@ -328,47 +378,7 @@ export function _bezierCurveTo(ctx, previous, target, flip) {
     target.y);
 }
 
-/**
- * Render text onto the canvas
- */
-export function renderText(ctx, text, x, y, font, opts = {}) {
-  const lines = isArray(text) ? text : [text];
-  const stroke = opts.strokeWidth > 0 && opts.strokeColor !== '';
-  let i, line;
-
-  ctx.save();
-  ctx.font = font.string;
-  setRenderOpts(ctx, opts);
-
-  for (i = 0; i < lines.length; ++i) {
-    line = lines[i];
-
-    if (opts.backdrop) {
-      drawBackdrop(ctx, opts.backdrop);
-    }
-
-    if (stroke) {
-      if (opts.strokeColor) {
-        ctx.strokeStyle = opts.strokeColor;
-      }
-
-      if (!isNullOrUndef(opts.strokeWidth)) {
-        ctx.lineWidth = opts.strokeWidth;
-      }
-
-      ctx.strokeText(line, x, y, opts.maxWidth);
-    }
-
-    ctx.fillText(line, x, y, opts.maxWidth);
-    decorateText(ctx, x, y, line, opts);
-
-    y += font.lineHeight;
-  }
-
-  ctx.restore();
-}
-
-function setRenderOpts(ctx, opts) {
+function setRenderOpts(ctx: CanvasRenderingContext2D, opts: RenderTextOpts) {
   if (opts.translation) {
     ctx.translate(opts.translation[0], opts.translation[1]);
   }
@@ -390,7 +400,13 @@ function setRenderOpts(ctx, opts) {
   }
 }
 
-function decorateText(ctx, x, y, line, opts) {
+function decorateText(
+  ctx: CanvasRenderingContext2D,
+  x: number,
+  y: number,
+  line: string,
+  opts: RenderTextOpts
+) {
   if (opts.strikethrough || opts.underline) {
     /**
      * Now that IE11 support has been dropped, we can use more
@@ -415,20 +431,70 @@ function decorateText(ctx, x, y, line, opts) {
   }
 }
 
-function drawBackdrop(ctx, opts) {
+function drawBackdrop(ctx: CanvasRenderingContext2D, opts: BackdropOptions) {
   const oldColor = ctx.fillStyle;
 
-  ctx.fillStyle = opts.color;
+  ctx.fillStyle = opts.color as string;
   ctx.fillRect(opts.left, opts.top, opts.width, opts.height);
   ctx.fillStyle = oldColor;
 }
 
+/**
+ * Render text onto the canvas
+ */
+export function renderText(
+  ctx: CanvasRenderingContext2D,
+  text: string | string[],
+  x: number,
+  y: number,
+  font: CanvasFontSpec,
+  opts: RenderTextOpts = {}
+) {
+  const lines = isArray(text) ? text : [text];
+  const stroke = opts.strokeWidth > 0 && opts.strokeColor !== '';
+  let i: number, line: string;
+
+  ctx.save();
+  ctx.font = font.string;
+  setRenderOpts(ctx, opts);
+
+  for (i = 0; i < lines.length; ++i) {
+    line = lines[i];
+
+    if (opts.backdrop) {
+      drawBackdrop(ctx, opts.backdrop);
+    }
+
+    if (stroke) {
+      if (opts.strokeColor) {
+        ctx.strokeStyle = opts.strokeColor;
+      }
+
+      if (!isNullOrUndef(opts.strokeWidth)) {
+        ctx.lineWidth = opts.strokeWidth;
+      }
+
+      ctx.strokeText(line, x, y, opts.maxWidth);
+    }
+
+    ctx.fillText(line, x, y, opts.maxWidth);
+    decorateText(ctx, x, y, line, opts);
+
+    y += Number(font.lineHeight);
+  }
+
+  ctx.restore();
+}
+
 /**
  * Add a path of a rectangle with rounded corners to the current sub-path
- * @param {CanvasRenderingContext2D} ctx Context
- * @param {*} rect Bounding rect
+ * @param ctx - Context
+ * @param rect - Bounding rect
  */
-export function addRoundedRectPath(ctx, rect) {
+export function addRoundedRectPath(
+  ctx: CanvasRenderingContext2D,
+  rect: RoundedRect & { radius: TRBLCorners }
+) {
   const {x, y, w, h, radius} = rect;
 
   // top left arc
index 97b76993e645b7c442ebdd7e273b838c2cbcf022..7dd0b7c346570ef145fa82c0bffc5f79af53f0f5 100644 (file)
@@ -1,19 +1,7 @@
 import {almostEquals, distanceBetweenPoints, sign} from './helpers.math.js';
 import {_isPointInArea} from './helpers.canvas.js';
 import type {ChartArea} from '../types/index.js';
-
-export interface SplinePoint {
-  x: number;
-  y: number;
-  skip?: boolean;
-
-  // Both Bezier and monotone interpolations have these fields
-  // but they are added in different spots
-  cp1x?: number;
-  cp1y?: number;
-  cp2x?: number;
-  cp2y?: number;
-}
+import type {SplinePoint} from '../types/geometric.js';
 
 const EPSILON = Number.EPSILON || 1e-14;
 
index 202575629d1e4c6ad1f48d98da4fa46678dbc6c9..d3427b5592d2eb24eeea8c2c6633ad7baa5d0fc2 100644 (file)
@@ -1,5 +1,4 @@
-import type {Point} from '../types/geometric.js';
-import type {SplinePoint} from './helpers.curve.js';
+import type {Point, SplinePoint} from '../types/geometric.js';
 
 /**
  * @private
index c476ccb5e1ab2d6b5c418ecc8d315f473cf372b1..a69d52f063209732246e9a0c931ccbae05ded8db 100644 (file)
@@ -1,7 +1,7 @@
 import defaults from '../core/core.defaults.js';
 import {isArray, isObject, toDimension, valueOrDefault} from './helpers.core.js';
-import {Point, toFontString} from './helpers.canvas.js';
-import type {ChartArea, FontSpec} from '../types/index.js';
+import {toFontString} from './helpers.canvas.js';
+import type {ChartArea, FontSpec, Point} from '../types/index.js';
 import type {TRBL, TRBLCorners} from '../types/geometric.js';
 
 const LINE_HEIGHT = /^(normal|(\d+(?:\.\d+)?)(px|em|%)?)$/;
@@ -104,10 +104,6 @@ export function toPadding(value?: number | TRBL): ChartArea {
   return obj;
 }
 
-export interface CanvasFontSpec extends FontSpec {
-  string: string;
-}
-
 /**
  * Parses font options and returns the font object.
  * @param options - A object that contains font options to be parsed.
diff --git a/src/helpers/types.ts b/src/helpers/types.ts
deleted file mode 100644 (file)
index 3c3badb..0000000
+++ /dev/null
@@ -1,19 +0,0 @@
-/**
- * Temporary entry point of the types at the time of the transition.
- * After transition done need to remove it in favor of index.ts
- */
-
-// export * from '..js';
-export * from './helpers.color.js';
-export * from './helpers.collection.js';
-export * from './helpers.core.js';
-export * from './helpers.curve.js';
-export * from './helpers.dom.js';
-export * from './helpers.easing.js';
-export * from './helpers.extras.js';
-export * from './helpers.interpolation.js';
-export * from './helpers.intl.js';
-export * from './helpers.math.js';
-export * from './helpers.options.js';
-export * from './helpers.rtl.js';
-export * from '../types/helpers/index.js';
index e8e4f27b2856e4543465eb794b4a001434e87c7d..9df01f16487a88a021b789cf932eee4b67e41e38 100644 (file)
@@ -37,3 +37,16 @@ export type RoundedRect = {
 }
 
 export type Padding = Partial<TRBL> | number | Point;
+
+export interface SplinePoint {
+  x: number;
+  y: number;
+  skip?: boolean;
+
+  // Both Bezier and monotone interpolations have these fields
+  // but they are added in different spots
+  cp1x?: number;
+  cp1y?: number;
+  cp2x?: number;
+  cp2y?: number;
+}
diff --git a/src/types/helpers/helpers.canvas.d.ts b/src/types/helpers/helpers.canvas.d.ts
deleted file mode 100644 (file)
index 41f99b2..0000000
+++ /dev/null
@@ -1,135 +0,0 @@
-import {PointStyle, Scriptable, ScriptableScaleContext} from '../index.js';
-import {Color} from '../color.js';
-import {ChartArea, RoundedRect} from '../geometric.js';
-import {CanvasFontSpec} from '../../helpers/helpers.options.js';
-
-export function clearCanvas(canvas: HTMLCanvasElement, ctx?: CanvasRenderingContext2D): void;
-
-export function clipArea(ctx: CanvasRenderingContext2D, area: ChartArea): void;
-
-export function unclipArea(ctx: CanvasRenderingContext2D): void;
-
-export interface DrawPointOptions {
-  pointStyle: PointStyle;
-  rotation?: number;
-  radius: number;
-  borderWidth: number;
-}
-
-export function drawPoint(ctx: CanvasRenderingContext2D, options: DrawPointOptions, x: number, y: number): void;
-
-export function drawPointLegend(ctx: CanvasRenderingContext2D, options: DrawPointOptions, x: number, y: number, w: number): void;
-
-/**
- * Converts the given font object into a CSS font string.
- * @param font a font object
- * @return The CSS font string. See https://developer.mozilla.org/en-US/docs/Web/CSS/font
- */
-export function toFontString(font: { size: number; family: string; style?: string; weight?: string }): string | null;
-
-export interface RenderTextOpts {
-  /**
-   * The fill color of the text. If unset, the existing
-   * fillStyle property of the canvas is unchanged.
-   */
-  color?: Color;
-
-  /**
-   * The width of the strikethrough / underline
-   * @default 2
-   */
-  decorationWidth?: number;
-
-  /**
-   * The max width of the text in pixels
-   */
-  maxWidth?: number;
-
-  /**
-   * A rotation to be applied to the canvas
-   * This is applied after the translation is applied
-   */
-  rotation?: number;
-
-  /**
-   * Apply a strikethrough effect to the text
-   */
-  strikethrough?: boolean;
-
-  /**
-   * The color of the text stroke. If unset, the existing
-   * strokeStyle property of the context is unchanged
-   */
-  strokeColor?: Color;
-
-  /**
-   * The text stroke width. If unset, the existing
-   * lineWidth property of the context is unchanged
-   */
-  strokeWidth?: number;
-
-  /**
-   * The text alignment to use. If unset, the existing
-   * textAlign property of the context is unchanged
-   */
-  textAlign?: CanvasTextAlign;
-
-  /**
-   * The text baseline to use. If unset, the existing
-   * textBaseline property of the context is unchanged
-   */
-  textBaseline?: CanvasTextBaseline;
-
-  /**
-   * If specified, a translation to apply to the context
-   */
-  translation?: [number, number];
-
-  /**
-   * Underline the text
-   */
-  underline?: boolean;
-
-  /**
-   * Dimensions for drawing the label backdrop
-   */
-  backdrop?: BackdropOptions;
-}
-
-export interface BackdropOptions {
-  /**
-   * Left position of backdrop as pixel
-   */
-  left: number;
-
-  /**
-   * Top position of backdrop as pixel
-   */
-  top: number;
-
-  /**
-   * Width of backdrop in pixels
-   */
-  width: number;
-
-  /**
-   * Height of backdrop in pixels
-   */
-  height: number;
-
-  /**
-   * Color of label backdrops.
-   */
-  color: Scriptable<Color, ScriptableScaleContext>;
-}
-
-export function renderText(
-  ctx: CanvasRenderingContext2D,
-  text: string | string[],
-  x: number,
-  y: number,
-  font: CanvasFontSpec,
-  opts?: RenderTextOpts
-): void;
-
-export function addRoundedRectPath(ctx: CanvasRenderingContext2D, rect: RoundedRect): void;
diff --git a/src/types/helpers/helpers.segment.d.ts b/src/types/helpers/helpers.segment.d.ts
deleted file mode 100644 (file)
index cb0ff5c..0000000
+++ /dev/null
@@ -1 +0,0 @@
-export {};
diff --git a/src/types/helpers/index.d.ts b/src/types/helpers/index.d.ts
deleted file mode 100644 (file)
index 9aa9cf2..0000000
+++ /dev/null
@@ -1,3 +0,0 @@
-export * from './helpers.canvas.js';
-export * from './helpers.canvas.js';
-export * from './helpers.segment.js';
index 6caf2174d458a384432be64a9cf87e85d4d69fc9..c4f042ec16e9d4b398cd95f4341efdf05033c655 100644 (file)
@@ -10,9 +10,7 @@ import {Color} from './color.js';
 import Element from '../core/core.element.js';
 import {ChartArea, Padding, Point} from './geometric.js';
 import {LayoutItem, LayoutPosition} from './layout.js';
-import {RenderTextOpts} from './helpers/helpers.canvas.js';
-import {CanvasFontSpec} from '../helpers/helpers.options.js';
-import type {ColorsPluginOptions} from '../plugins/plugin.colors.js';
+import {ColorsPluginOptions} from '../plugins/plugin.colors.js';
 
 export {EasingFunction} from '../helpers/helpers.easing.js';
 export {default as ArcElement, ArcProps} from '../elements/element.arc.js';
@@ -548,6 +546,8 @@ export declare class Chart<
 
   isPluginEnabled(pluginId: string): boolean;
 
+  getContext(): { chart: Chart, type: string };
+
   static readonly defaults: Defaults;
   static readonly overrides: Overrides;
   static readonly version: string;
@@ -1359,6 +1359,102 @@ export interface ScriptableScalePointLabelContext {
   type: string;
 }
 
+export interface RenderTextOpts {
+  /**
+   * The fill color of the text. If unset, the existing
+   * fillStyle property of the canvas is unchanged.
+   */
+  color?: Color;
+
+  /**
+   * The width of the strikethrough / underline
+   * @default 2
+   */
+  decorationWidth?: number;
+
+  /**
+   * The max width of the text in pixels
+   */
+  maxWidth?: number;
+
+  /**
+   * A rotation to be applied to the canvas
+   * This is applied after the translation is applied
+   */
+  rotation?: number;
+
+  /**
+   * Apply a strikethrough effect to the text
+   */
+  strikethrough?: boolean;
+
+  /**
+   * The color of the text stroke. If unset, the existing
+   * strokeStyle property of the context is unchanged
+   */
+  strokeColor?: Color;
+
+  /**
+   * The text stroke width. If unset, the existing
+   * lineWidth property of the context is unchanged
+   */
+  strokeWidth?: number;
+
+  /**
+   * The text alignment to use. If unset, the existing
+   * textAlign property of the context is unchanged
+   */
+  textAlign?: CanvasTextAlign;
+
+  /**
+   * The text baseline to use. If unset, the existing
+   * textBaseline property of the context is unchanged
+   */
+  textBaseline?: CanvasTextBaseline;
+
+  /**
+   * If specified, a translation to apply to the context
+   */
+  translation?: [number, number];
+
+  /**
+   * Underline the text
+   */
+  underline?: boolean;
+
+  /**
+   * Dimensions for drawing the label backdrop
+   */
+  backdrop?: BackdropOptions;
+}
+
+export interface BackdropOptions {
+  /**
+   * Left position of backdrop as pixel
+   */
+  left: number;
+
+  /**
+   * Top position of backdrop as pixel
+   */
+  top: number;
+
+  /**
+   * Width of backdrop in pixels
+   */
+  width: number;
+
+  /**
+   * Height of backdrop in pixels
+   */
+  height: number;
+
+  /**
+   * Color of label backdrops.
+   */
+  color: Scriptable<Color, ScriptableScaleContext>;
+}
+
 export interface LabelItem {
   label: string | string[];
   font: CanvasFontSpec;
@@ -1658,6 +1754,10 @@ export interface FontSpec {
   lineHeight: number | string;
 }
 
+export interface CanvasFontSpec extends FontSpec {
+  string: string;
+}
+
 export type TextAlign = 'left' | 'center' | 'right';
 export type Align = 'start' | 'center' | 'end';
 
@@ -3655,6 +3755,8 @@ export interface ChartData<
   TLabel = unknown
 > {
   labels?: TLabel[];
+  xLabels?: TLabel[];
+  yLabels?: TLabel[];
   datasets: ChartDataset<TType, TData>[];
 }
 
@@ -3664,6 +3766,8 @@ export interface ChartDataCustomTypesPerDataset<
   TLabel = unknown
 > {
   labels?: TLabel[];
+  xLabels?: TLabel[];
+  yLabels?: TLabel[];
   datasets: ChartDatasetCustomTypesPerDataset<TType, TData>[];
 }