]> git.ipfire.org Git - thirdparty/Chart.js.git/commitdiff
Fix: maximum size and mouse position with styling (#7816)
authorJukka Kurkela <jukka.kurkela@gmail.com>
Sat, 26 Sep 2020 17:18:35 +0000 (20:18 +0300)
committerGitHub <noreply@github.com>
Sat, 26 Sep 2020 17:18:35 +0000 (13:18 -0400)
Fix: maximum size and mouse position with styling

docs/docs/getting-started/v3-migration.md
src/core/core.controller.js
src/helpers/helpers.dom.js
src/platform/platform.base.js
src/platform/platform.dom.js
test/specs/helpers.dom.tests.js
types/helpers/helpers.dom.d.ts
types/platform/index.d.ts

index de875bd8cb50c9c4353a0ff84f48d375276f6165..5622fb5916c2f2a871ef2770edcdd68e20cd9c21 100644 (file)
@@ -380,8 +380,6 @@ The following properties were renamed during v3 development:
 * `helpers.distanceBetweenPoints` was renamed to `helpers.math.distanceBetweenPoints`
 * `helpers.drawRoundedRectangle` was renamed to `helpers.canvas.roundedRect`
 * `helpers.getAngleFromPoint` was renamed to `helpers.math.getAngleFromPoint`
-* `helpers.getMaximumHeight` was renamed to `helpers.dom.getMaximumHeight`
-* `helpers.getMaximumWidth` was renamed to `helpers.dom.getMaximumWidth`
 * `helpers.getRelativePosition` was renamed to `helpers.dom.getRelativePosition`
 * `helpers.getStyle` was renamed to `helpers.dom.getStyle`
 * `helpers.getValueOrDefault` was renamed to `helpers.valueOrDefault`
@@ -477,6 +475,8 @@ The APIs listed in this section have changed in signature or behaviour from vers
 ##### Canvas Helper
 
 * The second parameter to `drawPoint` is now the full options object, so `style`, `rotation`, and `radius` are no longer passed explicitly
+* `helpers.getMaximumHeight` was replaced by `helpers.dom.getMaximumSize`
+* `helpers.getMaximumWidth` was replaced by `helpers.dom.getMaximumSize`
 
 #### Platform
 
index 1bc6c896d20c58fe4e76b18d63f1bb4330da50a3..5f0834b87f38389602702c84cf2c0b282672e47a 100644 (file)
@@ -6,7 +6,7 @@ import layouts from './core.layouts';
 import {BasicPlatform, DomPlatform} from '../platform';
 import PluginService from './core.plugins';
 import registry from './core.registry';
-import {getMaximumWidth, getMaximumHeight, retinaScale} from '../helpers/helpers.dom';
+import {retinaScale} from '../helpers/helpers.dom';
 import {mergeIf, merge, _merger, each, callback as callCallback, uid, valueOrDefault, _elementsEqual} from '../helpers/helpers.core';
 import {clear as canvasClear, clipArea, unclipArea, _isPointInArea} from '../helpers/helpers.canvas';
 // @ts-ignore
@@ -214,22 +214,6 @@ function getCanvas(item) {
        return item;
 }
 
-function computeNewSize(canvas, width, height, aspectRatio) {
-       if (width === undefined || height === undefined) {
-               width = getMaximumWidth(canvas);
-               height = getMaximumHeight(canvas);
-       }
-       // the canvas render width and height will be casted to integers so make sure that
-       // the canvas display style uses the same integer values to avoid blurring effect.
-
-       // Minimum values set to 0 instead of canvas.size because the size defaults to 300x150 if the element is collapsed
-       width = Math.max(0, Math.floor(width));
-       return {
-               width,
-               height: Math.max(0, Math.floor(aspectRatio ? width / aspectRatio : height))
-       };
-}
-
 class Chart {
 
        // eslint-disable-next-line max-statements
@@ -355,7 +339,7 @@ class Chart {
                const options = me.options;
                const canvas = me.canvas;
                const aspectRatio = options.maintainAspectRatio && me.aspectRatio;
-               const newSize = computeNewSize(canvas, width, height, aspectRatio);
+               const newSize = me.platform.getMaximumSize(canvas, width, height, aspectRatio);
 
                // detect devicePixelRation changes
                const oldRatio = me.currentDevicePixelRatio;
index 1539af9fbe22a16e17e3538eb1fe6c2a677f3386..e813079ef106d7917c54381f02389bb264dcc88f 100644 (file)
@@ -1,11 +1,3 @@
-/**
- * Returns if the given value contains an effective constraint.
- * @private
- */
-function isConstrainedValue(value) {
-       return value !== undefined && value !== null && value !== 'none';
-}
-
 /**
  * @private
  */
@@ -17,7 +9,10 @@ export function _getParentNode(domNode) {
        return parent;
 }
 
-// Private helper function to convert max-width/max-height values that may be percentages into a number
+/**
+ * convert max-width/max-height values that may be percentages into a number
+ * @private
+ */
 function parseMaxStyle(styleValue, node, parentProperty) {
        let valueInPixels;
        if (typeof styleValue === 'string') {
@@ -34,112 +29,118 @@ function parseMaxStyle(styleValue, node, parentProperty) {
        return valueInPixels;
 }
 
-/**
- * Returns the max width or height of the given DOM node in a cross-browser compatible fashion
- * @param {HTMLElement} domNode - the node to check the constraint on
- * @param {string} maxStyle - the style that defines the maximum for the direction we are using ('max-width' / 'max-height')
- * @param {string} percentageProperty - property of parent to use when calculating width as a percentage
- * @return {number=} number or undefined if no constraint
- * @see {@link https://www.nathanaeljones.com/blog/2013/reading-max-width-cross-browser}
- */
-function getConstraintDimension(domNode, maxStyle, percentageProperty) {
-       const view = document.defaultView;
-       const parentNode = _getParentNode(domNode);
-       const constrainedNode = view.getComputedStyle(domNode)[maxStyle];
-       const constrainedContainer = view.getComputedStyle(parentNode)[maxStyle];
-       const hasCNode = isConstrainedValue(constrainedNode);
-       const hasCContainer = isConstrainedValue(constrainedContainer);
-       const infinity = Number.POSITIVE_INFINITY;
-
-       if (hasCNode || hasCContainer) {
-               return Math.min(
-                       hasCNode ? parseMaxStyle(constrainedNode, domNode, percentageProperty) : infinity,
-                       hasCContainer ? parseMaxStyle(constrainedContainer, parentNode, percentageProperty) : infinity);
-       }
-}
+const getComputedStyle = (element) => window.getComputedStyle(element, null);
 
 export function getStyle(el, property) {
        return el.currentStyle ?
                el.currentStyle[property] :
-               document.defaultView.getComputedStyle(el, null).getPropertyValue(property);
+               getComputedStyle(el).getPropertyValue(property);
 }
 
-/**
- * @private
- */
-function _calculatePadding(container, padding, parentDimension) {
-       padding = getStyle(container, padding);
-
-       // If the padding is not set at all and the node is not in the DOM, this can be an empty string
-       // In that case, we need to handle it as no padding
-       if (padding === '') {
-               return 0;
+const positions = ['top', 'right', 'bottom', 'left'];
+function getPositionedStyle(styles, style, suffix) {
+       const result = {};
+       suffix = suffix ? '-' + suffix : '';
+       for (let i = 0; i < 4; i++) {
+               const pos = positions[i];
+               result[pos] = parseFloat(styles[style + '-' + pos + suffix]) || 0;
        }
-
-       return padding.indexOf('%') > -1 ? parentDimension * parseInt(padding, 10) / 100 : parseInt(padding, 10);
+       result.width = result.left + result.right;
+       result.height = result.top + result.bottom;
+       return result;
 }
 
-export function getRelativePosition(evt, chart) {
+function getCanvasPosition(evt, canvas) {
        const e = evt.originalEvent || evt;
        const touches = e.touches;
        const source = touches && touches.length ? touches[0] : e;
        const {offsetX, offsetY} = source;
-
+       let box = false;
+       let x, y;
        if (offsetX > 0 || offsetY > 0) {
-               return {
-                       x: offsetX,
-                       y: offsetY
-               };
+               x = offsetX;
+               y = offsetY;
+       } else {
+               const rect = canvas.getBoundingClientRect();
+               x = source.clientX - rect.left;
+               y = source.clientY - rect.top;
+               box = true;
        }
-
-       return calculateRelativePositionFromClientXY(source, chart);
+       return {x, y, box};
 }
 
-function calculateRelativePositionFromClientXY(source, chart) {
-       const {clientX: x, clientY: y} = source;
-
-       const canvasElement = chart.canvas;
-       const devicePixelRatio = chart.currentDevicePixelRatio;
-       const boundingRect = canvasElement.getBoundingClientRect();
-       // Scale mouse coordinates into canvas coordinates
-       // by following the pattern laid out by 'jerryj' in the comments of
-       // https://www.html5canvastutorials.com/advanced/html5-canvas-mouse-coordinates/
-       const paddingLeft = parseFloat(getStyle(canvasElement, 'padding-left'));
-       const paddingTop = parseFloat(getStyle(canvasElement, 'padding-top'));
-       const paddingRight = parseFloat(getStyle(canvasElement, 'padding-right'));
-       const paddingBottom = parseFloat(getStyle(canvasElement, 'padding-bottom'));
-       const width = boundingRect.right - boundingRect.left - paddingLeft - paddingRight;
-       const height = boundingRect.bottom - boundingRect.top - paddingTop - paddingBottom;
-
-       // We divide by the current device pixel ratio, because the canvas is scaled up by that amount in each direction. However
-       // the backend model is in unscaled coordinates. Since we are going to deal with our model coordinates, we go back here
+export function getRelativePosition(evt, chart) {
+       const {canvas, currentDevicePixelRatio} = chart;
+       const style = getComputedStyle(canvas);
+       const borderBox = style.boxSizing === 'border-box';
+       const paddings = getPositionedStyle(style, 'padding');
+       const borders = getPositionedStyle(style, 'border', 'width');
+       const {x, y, box} = getCanvasPosition(evt, canvas);
+       const xOffset = paddings.left + (box && borders.left);
+       const yOffset = paddings.top + (box && borders.top);
+
+       let {width, height} = chart;
+       if (borderBox) {
+               width -= paddings.width + borders.width;
+               height -= paddings.height + borders.height;
+       }
        return {
-               x: Math.round((x - boundingRect.left - paddingLeft) / (width) * canvasElement.width / devicePixelRatio),
-               y: Math.round((y - boundingRect.top - paddingTop) / (height) * canvasElement.height / devicePixelRatio)
+               x: Math.round((x - xOffset) / width * canvas.width / currentDevicePixelRatio),
+               y: Math.round((y - yOffset) / height * canvas.height / currentDevicePixelRatio)
        };
 }
 
-function fallbackIfNotValid(measure, fallback) {
-       return typeof measure === 'number' ? measure : fallback;
+const infinity = Number.POSITIVE_INFINITY;
+
+function getContainerSize(canvas, width, height) {
+       let maxWidth, maxHeight;
+
+       if (width === undefined || height === undefined) {
+               const container = _getParentNode(canvas);
+               if (!container) {
+                       width = canvas.clientWidth;
+                       height = canvas.clientHeight;
+               } else {
+                       const rect = container.getBoundingClientRect(); // this is the border box of the container
+                       const containerStyle = getComputedStyle(container);
+                       const containerBorder = getPositionedStyle(containerStyle, 'border', 'width');
+                       const contarinerPadding = getPositionedStyle(containerStyle, 'padding');
+                       width = rect.width - contarinerPadding.width - containerBorder.width;
+                       height = rect.height - contarinerPadding.height - containerBorder.height;
+                       maxWidth = parseMaxStyle(containerStyle.maxWidth, container, 'clientWidth');
+                       maxHeight = parseMaxStyle(containerStyle.maxHeight, container, 'clientHeight');
+               }
+       }
+       return {
+               width,
+               height,
+               maxWidth: maxWidth || infinity,
+               maxHeight: maxHeight || infinity
+       };
 }
 
-function getMax(domNode, prop, fallback, paddings) {
-       const container = _getParentNode(domNode);
-       if (!container) {
-               return fallbackIfNotValid(domNode[prop], domNode[fallback]);
+export function getMaximumSize(canvas, bbWidth, bbHeight, aspectRatio) {
+       const style = getComputedStyle(canvas);
+       const margins = getPositionedStyle(style, 'margin');
+       const maxWidth = parseMaxStyle(style.maxWidth, canvas, 'clientWidth') || infinity;
+       const maxHeight = parseMaxStyle(style.maxHeight, canvas, 'clientHeight') || infinity;
+       const containerSize = getContainerSize(canvas, bbWidth, bbHeight);
+       let {width, height} = containerSize;
+
+       if (style.boxSizing === 'content-box') {
+               const borders = getPositionedStyle(style, 'border', 'width');
+               const paddings = getPositionedStyle(style, 'padding');
+               width -= paddings.width + borders.width;
+               height -= paddings.height + borders.height;
        }
-
-       const value = container[prop];
-       const padding = paddings.reduce((acc, cur) => acc + _calculatePadding(container, 'padding-' + cur, value), 0);
-
-       const v = value - padding;
-       const cv = getConstraintDimension(domNode, 'max-' + fallback, prop);
-       return isNaN(cv) ? v : Math.min(v, cv);
+       width = Math.max(0, width - margins.width);
+       height = Math.max(0, aspectRatio ? Math.floor(width / aspectRatio) : height - margins.height);
+       return {
+               width: Math.min(width, maxWidth, containerSize.maxWidth),
+               height: Math.min(height, maxHeight, containerSize.maxHeight)
+       };
 }
 
-export const getMaximumWidth = (domNode) => getMax(domNode, 'clientWidth', 'width', ['left', 'right']);
-export const getMaximumHeight = (domNode) => getMax(domNode, 'clientHeight', 'height', ['top', 'bottom']);
-
 export function retinaScale(chart, forceRatio) {
        const pixelRatio = chart.currentDevicePixelRatio = forceRatio || (typeof window !== 'undefined' && window.devicePixelRatio) || 1;
        const {canvas, width, height} = chart;
index 434ede1c7803e5a613a6e11aa6b06c378895f3f8..9baea172f2501d0bdb2342220bb0c473b10e74e6 100644 (file)
@@ -49,6 +49,22 @@ export default class BasePlatform {
                return 1;
        }
 
+       /**
+        * Returns the maximum size in pixels of given canvas element.
+        * @param {HTMLCanvasElement} element
+        * @param {number} [width] - content width of parent element
+        * @param {number} [height] - content height of parent element
+        * @param {number} [aspectRatio] - aspect ratio to maintain
+        */
+       getMaximumSize(element, width, height, aspectRatio) {
+               width = Math.max(0, width || element.width);
+               height = height || element.height;
+               return {
+                       width,
+                       height: Math.max(0, aspectRatio ? Math.floor(width / aspectRatio) : height)
+               };
+       }
+
        /**
         * @param {HTMLCanvasElement} canvas
         * @returns {boolean} true if the canvas is attached to the platform, false if not.
index 4a31ebf542233c0f0a006826665b6bbf2a195b78..24068924fefa259baf91315c0ae6bea088090692 100644 (file)
@@ -3,7 +3,7 @@
  */
 
 import BasePlatform from './platform.base';
-import {_getParentNode, getRelativePosition, supportsEventListenerOptions, readUsedSize} from '../helpers/helpers.dom';
+import {_getParentNode, getRelativePosition, supportsEventListenerOptions, readUsedSize, getMaximumSize} from '../helpers/helpers.dom';
 import {throttled} from '../helpers/helpers.extras';
 import {isNullOrUndef} from '../helpers/helpers.core';
 
@@ -102,22 +102,18 @@ function removeListener(chart, type, listener) {
        chart.canvas.removeEventListener(type, listener, eventListenerOptions);
 }
 
-function createEvent(type, chart, x, y, nativeEvent) {
+function fromNativeEvent(event, chart) {
+       const type = EVENT_TYPES[event.type] || event.type;
+       const {x, y} = getRelativePosition(event, chart);
        return {
                type,
                chart,
-               native: nativeEvent || null,
+               native: event,
                x: x !== undefined ? x : null,
                y: y !== undefined ? y : null,
        };
 }
 
-function fromNativeEvent(event, chart) {
-       const type = EVENT_TYPES[event.type] || event.type;
-       const pos = getRelativePosition(event, chart);
-       return createEvent(type, chart, pos.x, pos.y, event);
-}
-
 function createAttachObserver(chart, type, listener) {
        const canvas = chart.canvas;
        const container = canvas && _getParentNode(canvas);
@@ -371,6 +367,15 @@ export default class DomPlatform extends BasePlatform {
                return window.devicePixelRatio;
        }
 
+       /**
+        * @param {HTMLCanvasElement} canvas
+        * @param {number} [width] - content width of parent element
+        * @param {number} [height] - content height of parent element
+        * @param {number} [aspectRatio] - aspect ratio to maintain
+        */
+       getMaximumSize(canvas, width, height, aspectRatio) {
+               return getMaximumSize(canvas, width, height, aspectRatio);
+       }
 
        /**
         * @param {HTMLCanvasElement} canvas
index 3a2f95724ac5584c4242a4dcf09b49fbcdb9b4e6..04c4e183241136ef140d43e629c82b4e5b36011e 100644 (file)
@@ -5,7 +5,7 @@ describe('DOM helpers tests', function() {
                helpers = window.Chart.helpers.dom;
        });
 
-       it ('should get the maximum width and height for a node', function() {
+       it ('should get the maximum size for a node', function() {
                // Create div with fixed size as a test bed
                var div = document.createElement('div');
                div.style.width = '200px';
@@ -17,8 +17,7 @@ describe('DOM helpers tests', function() {
                var innerDiv = document.createElement('div');
                div.appendChild(innerDiv);
 
-               expect(helpers.getMaximumWidth(innerDiv)).toBe(200);
-               expect(helpers.getMaximumHeight(innerDiv)).toBe(300);
+               expect(helpers.getMaximumSize(innerDiv)).toEqual(jasmine.objectContaining({width: 200, height: 300}));
 
                document.body.removeChild(div);
        });
@@ -42,8 +41,7 @@ describe('DOM helpers tests', function() {
                var innerDiv = document.createElement('div');
                shadow.appendChild(innerDiv);
 
-               expect(helpers.getMaximumWidth(innerDiv)).toBe(200);
-               expect(helpers.getMaximumHeight(innerDiv)).toBe(300);
+               expect(helpers.getMaximumSize(innerDiv)).toEqual(jasmine.objectContaining({width: 200, height: 300}));
 
                document.body.removeChild(div);
        });
@@ -61,7 +59,7 @@ describe('DOM helpers tests', function() {
                innerDiv.style.maxWidth = '150px';
                div.appendChild(innerDiv);
 
-               expect(helpers.getMaximumWidth(innerDiv)).toBe(150);
+               expect(helpers.getMaximumSize(innerDiv)).toEqual(jasmine.objectContaining({width: 150}));
 
                document.body.removeChild(div);
        });
@@ -79,7 +77,7 @@ describe('DOM helpers tests', function() {
                innerDiv.style.maxHeight = '150px';
                div.appendChild(innerDiv);
 
-               expect(helpers.getMaximumHeight(innerDiv)).toBe(150);
+               expect(helpers.getMaximumSize(innerDiv)).toEqual(jasmine.objectContaining({height: 150}));
 
                document.body.removeChild(div);
        });
@@ -101,7 +99,7 @@ describe('DOM helpers tests', function() {
                var innerDiv = document.createElement('div');
                parentDiv.appendChild(innerDiv);
 
-               expect(helpers.getMaximumWidth(innerDiv)).toBe(150);
+               expect(helpers.getMaximumSize(innerDiv)).toEqual(jasmine.objectContaining({width: 150}));
 
                document.body.removeChild(div);
        });
@@ -124,7 +122,7 @@ describe('DOM helpers tests', function() {
                innerDiv.style.height = '300px'; // make it large
                parentDiv.appendChild(innerDiv);
 
-               expect(helpers.getMaximumHeight(innerDiv)).toBe(150);
+               expect(helpers.getMaximumSize(innerDiv)).toEqual(jasmine.objectContaining({height: 150}));
 
                document.body.removeChild(div);
        });
@@ -142,12 +140,12 @@ describe('DOM helpers tests', function() {
                innerDiv.style.maxWidth = '50%';
                div.appendChild(innerDiv);
 
-               expect(helpers.getMaximumWidth(innerDiv)).toBe(100);
+               expect(helpers.getMaximumSize(innerDiv)).toEqual(jasmine.objectContaining({width: 100}));
 
                document.body.removeChild(div);
        });
 
-       it ('should get the maximum height of a node that has a percentage max-height style', function() {
+       it('should get the maximum height of a node that has a percentage max-height style', function() {
                // Create div with fixed size as a test bed
                var div = document.createElement('div');
                div.style.width = '200px';
@@ -160,7 +158,7 @@ describe('DOM helpers tests', function() {
                innerDiv.style.maxHeight = '50%';
                div.appendChild(innerDiv);
 
-               expect(helpers.getMaximumHeight(innerDiv)).toBe(150);
+               expect(helpers.getMaximumSize(innerDiv)).toEqual(jasmine.objectContaining({height: 150}));
 
                document.body.removeChild(div);
        });
@@ -182,7 +180,7 @@ describe('DOM helpers tests', function() {
                var innerDiv = document.createElement('div');
                parentDiv.appendChild(innerDiv);
 
-               expect(helpers.getMaximumWidth(innerDiv)).toBe(100);
+               expect(helpers.getMaximumSize(innerDiv)).toEqual(jasmine.objectContaining({width: 100}));
 
                document.body.removeChild(div);
        });
@@ -204,7 +202,7 @@ describe('DOM helpers tests', function() {
                innerDiv.style.height = '300px'; // make it large
                parentDiv.appendChild(innerDiv);
 
-               expect(helpers.getMaximumHeight(innerDiv)).toBe(150);
+               expect(helpers.getMaximumSize(innerDiv)).toEqual(jasmine.objectContaining({height: 150}));
 
                document.body.removeChild(div);
        });
@@ -226,15 +224,15 @@ describe('DOM helpers tests', function() {
                innerDiv.appendChild(canvas);
 
                // No padding
-               expect(helpers.getMaximumWidth(canvas)).toBe(300);
+               expect(helpers.getMaximumSize(canvas)).toEqual(jasmine.objectContaining({width: 300}));
 
                // test with percentage
                innerDiv.style.padding = '5%';
-               expect(helpers.getMaximumWidth(canvas)).toBe(270);
+               expect(helpers.getMaximumSize(canvas)).toEqual(jasmine.objectContaining({width: 270}));
 
                // test with pixels
                innerDiv.style.padding = '10px';
-               expect(helpers.getMaximumWidth(canvas)).toBe(280);
+               expect(helpers.getMaximumSize(canvas)).toEqual(jasmine.objectContaining({width: 280}));
 
                document.body.removeChild(div);
        });
@@ -258,9 +256,51 @@ describe('DOM helpers tests', function() {
 
        describe('getRelativePosition', function() {
                it('should use offsetX/Y when available', function() {
-                       const event = {offsetX: 0, offsetY: 10};
-                       const chart = undefined;
-                       expect(helpers.getRelativePosition(event, chart)).toEqual({x: 0, y: 10});
+                       const event = {offsetX: 50, offsetY: 100};
+                       const chart = window.acquireChart({}, {
+                               canvas: {
+                                       height: 200,
+                                       width: 200,
+                               }
+                       });
+                       expect(helpers.getRelativePosition(event, chart)).toEqual({x: 50, y: 100});
+
+                       const chart2 = window.acquireChart({}, {
+                               canvas: {
+                                       height: 200,
+                                       width: 200,
+                                       style: 'padding: 10px'
+                               }
+                       });
+                       expect(helpers.getRelativePosition(event, chart2)).toEqual({
+                               x: Math.round((event.offsetX - 10) / 180 * 200),
+                               y: Math.round((event.offsetY - 10) / 180 * 200)
+                       });
+
+                       const chart3 = window.acquireChart({}, {
+                               canvas: {
+                                       height: 200,
+                                       width: 200,
+                                       style: 'width: 400px, height: 400px; padding: 10px'
+                               }
+                       });
+                       expect(helpers.getRelativePosition(event, chart3)).toEqual({
+                               x: Math.round((event.offsetX - 10) / 360 * 400),
+                               y: Math.round((event.offsetY - 10) / 360 * 400)
+                       });
+
+                       const chart4 = window.acquireChart({}, {
+                               canvas: {
+                                       height: 200,
+                                       width: 200,
+                                       style: 'width: 400px, height: 400px; padding: 10px; position: absolute; left: 20, top: 20'
+                               }
+                       });
+                       expect(helpers.getRelativePosition(event, chart4)).toEqual({
+                               x: Math.round((event.offsetX - 10) / 360 * 400),
+                               y: Math.round((event.offsetY - 10) / 360 * 400)
+                       });
+
                });
 
                it('should calculate from clientX/Y as fallback', function() {
index dbb722243a87ea11b7242b1ff0ef8a0e99b063d3..32ad47783f4f0c294cd7b15fbec2465776f42ac3 100644 (file)
@@ -1,5 +1,4 @@
-export function getMaximumHeight(node: HTMLElement): number;
-export function getMaximumWidth(node: HTMLElement): number;
+export function getMaximumSize(node: HTMLElement, width?: number, height?: number, aspectRatio?: number): { width: number, height: number };
 export function getRelativePosition(
   evt: MouseEvent,
   chart: { readonly canvas: HTMLCanvasElement }
index 1980b57d060cbb1c6bdd1031e669a72104458c15..64423db100b581052aef555d5e7475e2cbc1fe47 100644 (file)
@@ -38,6 +38,14 @@ export class BasePlatform {
    * @returns {number} the current devicePixelRatio of the device this platform is connected to.
    */
   getDevicePixelRatio(): number;
+  /**
+   * @param {HTMLCanvasElement} canvas - The canvas for which to calculate the maximum size
+   * @param {number} [width] - Parent element's content width
+   * @param {number} [height] - Parent element's content height
+   * @param {number} [aspectRatio] - The aspect ratio to maintain
+   * @returns { width: number, height: number } the maximum size available.
+   */
+  getMaximumSize(canvas: HTMLCanvasElement, width?: number, height?: number, aspectRatio?: number): { width: number, height: number };
   /**
    * @param {HTMLCanvasElement} canvas
    * @returns {boolean} true if the canvas is attached to the platform, false if not.