Skip to content

Commit

Permalink
Replace overScaleMode with more flexible scaleMode (#658)
Browse files Browse the repository at this point in the history
* Spelling

* Implement new scaleMode property

* Add tests and a deprecation warning

* Fix wording
  • Loading branch information
joshkel committed Sep 1, 2022
1 parent 18e87a1 commit 7ec113a
Show file tree
Hide file tree
Showing 10 changed files with 149 additions and 31 deletions.
6 changes: 4 additions & 2 deletions docs/guide/options.md
Expand Up @@ -35,7 +35,8 @@ const chart = new Chart('id', {
| `enabled` | `boolean` | `false` | Enable panning
| `mode` | `'x'`\|`'y'`\|`'xy'` | `'xy'` | Allowed panning directions
| `modifierKey` | `'ctrl'`\|`'alt'`\|`'shift'`\|`'meta'` | `null` | Modifier key required for panning with mouse
| `overScaleMode` | `'x'`\|`'y'`\|`'xy'` | `undefined` | Which of the enabled panning directions should only be available when the mouse cursor is over a scale for that axis
| `scaleMode` | `'x'`\|`'y'`\|`'xy'` | `undefined` | Enable panning over a scale for that axis (regardless of mode)
| `overScaleMode` | `'x'`\|`'y'`\|`'xy'` | `undefined` | Enable panning over a scale for that axis (but only if mode is also enabled), and disables panning along that axis otherwise. Deprecated.
| `threshold` | `number` | `10` | Minimal pan distance required before actually applying pan

### Pan Events
Expand All @@ -57,7 +58,8 @@ const chart = new Chart('id', {
| `drag` | [`DragOptions`](#drag-options) | | Options of the drag-to-zoom behavior
| `pinch` | [`PinchOptions`](#pinch-options) | | Options of the pinch behavior
| `mode` | `'x'`\|`'y'`\|`'xy'` | `'xy'` | Allowed zoom directions
| `overScaleMode` | `'x'`\|`'y'`\|`'xy'` | `undefined` | Which of the enabled zooming directions should only be available when the mouse cursor is over a scale for that axis
| `scaleMode` | `'x'`\|`'y'`\|`'xy'` | `undefined` | Which of the enabled zooming directions should only be available when the mouse cursor is over a scale for that axis
| `overScaleMode` | `'x'`\|`'y'`\|`'xy'` | `undefined` | Allowed zoom directions when the mouse cursor is over a scale for that axis (but only if mode is also enabled), and disables zooming along that axis otherwise. Deprecated; use `scaleMode` instead.

#### Wheel options

Expand Down
4 changes: 2 additions & 2 deletions docs/samples/wheel/over-scale-mode.md
Expand Up @@ -62,12 +62,12 @@ const zoomOptions = {
enabled: true,
},
mode: 'xy',
overScaleMode: 'xy',
scaleMode: 'xy',
},
pan: {
enabled: true,
mode: 'xy',
overScaleMode: 'xy',
scaleMode: 'xy',
}
};
// </block>
Expand Down
8 changes: 4 additions & 4 deletions samples/zoom-separately.html
Expand Up @@ -100,8 +100,8 @@
zoom: {
pan: {
enabled: true,
mode: 'xy',
overScaleMode: 'y'
mode: 'x',
scaleMode: 'y'
},
zoom: {
wheel: {
Expand All @@ -110,8 +110,8 @@
pinch: {
enabled: true,
},
mode: 'xy',
overScaleMode: 'y'
mode: 'x',
scaleMode: 'y'
}
}
}
Expand Down
13 changes: 6 additions & 7 deletions src/core.js
Expand Up @@ -60,13 +60,12 @@ export function zoom(chart, amount, transition = 'none') {
const {x = 1, y = 1, focalPoint = getCenter(chart)} = typeof amount === 'number' ? {x: amount, y: amount} : amount;
const state = getState(chart);
const {options: {limits, zoom: zoomOptions}} = state;
const {mode = 'xy', overScaleMode} = zoomOptions || {};

storeOriginalScaleLimits(chart, state);

const xEnabled = x !== 1 && directionEnabled(mode, 'x', chart);
const yEnabled = y !== 1 && directionEnabled(mode, 'y', chart);
const enabledScales = overScaleMode && getEnabledScalesByPoint(overScaleMode, focalPoint, chart);
const xEnabled = x !== 1;
const yEnabled = y !== 1;
const enabledScales = getEnabledScalesByPoint(zoomOptions, focalPoint, chart);

each(enabledScales || chart.scales, function(scale) {
if (scale.isHorizontal() && xEnabled) {
Expand Down Expand Up @@ -182,12 +181,12 @@ export function pan(chart, delta, enabledScales, transition = 'none') {
const {x = 0, y = 0} = typeof delta === 'number' ? {x: delta, y: delta} : delta;
const state = getState(chart);
const {options: {pan: panOptions, limits}} = state;
const {mode = 'xy', onPan} = panOptions || {};
const {onPan} = panOptions || {};

storeOriginalScaleLimits(chart, state);

const xEnabled = x !== 0 && directionEnabled(mode, 'x', chart);
const yEnabled = y !== 0 && directionEnabled(mode, 'y', chart);
const xEnabled = x !== 0;
const yEnabled = y !== 0;

each(enabledScales || chart.scales, function(scale) {
if (scale.isHorizontal() && xEnabled) {
Expand Down
4 changes: 2 additions & 2 deletions src/hammer.js
Expand Up @@ -90,7 +90,7 @@ function handlePan(chart, state, e) {
}

function startPan(chart, state, event) {
const {enabled, overScaleMode, onPanStart, onPanRejected} = state.options.pan;
const {enabled, onPanStart, onPanRejected} = state.options.pan;
if (!enabled) {
return;
}
Expand All @@ -104,7 +104,7 @@ function startPan(chart, state, event) {
return call(onPanRejected, [{chart, event}]);
}

state.panScales = overScaleMode && getEnabledScalesByPoint(overScaleMode, point, chart);
state.panScales = getEnabledScalesByPoint(state.options.pan, point, chart);
state.delta = {x: 0, y: 0};
clearTimeout(state.panEndTimeout);
handlePan(chart, state, event);
Expand Down
2 changes: 1 addition & 1 deletion src/handlers.js
Expand Up @@ -133,7 +133,7 @@ function wheelPreconditions(chart, event, zoomOptions) {
return;
}

// Prevent the event from triggering the default behavior (eg. Content scrolling).
// Prevent the event from triggering the default behavior (e.g. content scrolling).
if (event.cancelable) {
event.preventDefault();
}
Expand Down
4 changes: 4 additions & 0 deletions src/plugin.js
Expand Up @@ -42,6 +42,10 @@ export default {
if (Object.prototype.hasOwnProperty.call(options.zoom, 'enabled')) {
console.warn('The option `zoom.enabled` is no longer supported. Please use `zoom.wheel.enabled`, `zoom.drag.enabled`, or `zoom.pinch.enabled`.');
}
if (Object.prototype.hasOwnProperty.call(options.zoom, 'overScaleMode')
|| Object.prototype.hasOwnProperty.call(options.pan, 'overScaleMode')) {
console.warn('The option `overScaleMode` is deprecated. Please use `scaleMode` instead (and update `mode` as desired).');
}

if (Hammer) {
startHammer(chart, options);
Expand Down
45 changes: 35 additions & 10 deletions src/utils.js
Expand Up @@ -22,6 +22,17 @@ export function directionEnabled(mode, dir, chart) {
return false;
}

function directionsEnabled(mode, chart) {
if (typeof mode === 'function') {
mode = mode({chart});
}
if (typeof mode === 'string') {
return {x: mode.indexOf('x') !== -1, y: mode.indexOf('y') !== -1};
}

return {x: false, y: false};
}

/**
* Debounces calling `fn` for `delay` ms
* @param {function} fn - Function to call. No arguments are passed.
Expand All @@ -37,7 +48,8 @@ export function debounce(fn, delay) {
};
}

/** This function use for check what axis now under mouse cursor.
/**
* Checks which axis is under the mouse cursor.
* @param {{x: number, y: number}} point - the mouse location
* @param {import('chart.js').Chart} [chart] instance of the chart in question
* @return {import('chart.js').Scale}
Expand All @@ -54,27 +66,40 @@ function getScaleUnderPoint({x, y}, chart) {
return null;
}

/** This function return only one scale whose position is under mouse cursor and which direction is enabled.
* If under mouse hasn't scale, then return all other scales which 'mode' is diffrent with overScaleMode.
* So 'overScaleMode' works as a limiter to scale the user-selected scale (in 'mode') only when the cursor is under the scale,
* and other directions in 'mode' works as before.
* Example: mode = 'xy', overScaleMode = 'y' -> it's means 'x' - works as before, and 'y' only works for one scale when cursor is under it.
/**
* Evaluate the chart's mode, scaleMode, and overScaleMode properties to
* determine which axes are eligible for scaling.
* options.overScaleMode can be a function if user want zoom only one scale of many for example.
* @param {string} mode - 'xy', 'x' or 'y'
* @param options - Zoom or pan options
* @param {{x: number, y: number}} point - the mouse location
* @param {import('chart.js').Chart} [chart] instance of the chart in question
* @return {import('chart.js').Scale[]}
*/
export function getEnabledScalesByPoint(mode, point, chart) {
export function getEnabledScalesByPoint(options, point, chart) {
const {mode = 'xy', scaleMode, overScaleMode} = options || {};
const scale = getScaleUnderPoint(point, chart);

if (scale && directionEnabled(mode, scale.axis, chart)) {
const enabled = directionsEnabled(mode, chart);
const scaleEnabled = directionsEnabled(scaleMode, chart);

// Convert deprecated overScaleEnabled to new scaleEnabled.
if (overScaleMode) {
const overScaleEnabled = directionsEnabled(overScaleMode, chart);
for (const axis of ['x', 'y']) {
if (overScaleEnabled[axis]) {
scaleEnabled[axis] = enabled[axis];
enabled[axis] = false;
}
}
}

if (scale && scaleEnabled[scale.axis]) {
return [scale];
}

const enabledScales = [];
each(chart.scales, function(scaleItem) {
if (!directionEnabled(mode, scaleItem.axis, chart)) {
if (enabled[scaleItem.axis]) {
enabledScales.push(scaleItem);
}
});
Expand Down
84 changes: 84 additions & 0 deletions test/specs/zoom.spec.js
Expand Up @@ -550,6 +550,90 @@ describe('zoom', function() {
});
});

describe('with scaleMode = y and mode = xy', function() {
const config = {
type: 'line',
data,
options: {
scales: {
x: {
type: 'linear',
min: 1,
max: 10
},
y: {
type: 'linear'
}
},
plugins: {
zoom: {
zoom: {
wheel: {
enabled: true,
},
mode: 'xy',
scaleMode: 'y'
}
}
}
}
};

describe('Wheel under Y scale', function() {
it('should be applied on Y, but not on X scales.', async function() {
const chart = window.acquireChart(config);

const scaleX = chart.scales.x;
const scaleY = chart.scales.y;

const oldMinX = scaleX.options.min;
const oldMaxX = scaleX.options.max;
const oldMinY = scaleY.options.min;
const oldMaxY = scaleY.options.max;

const wheelEv = {
x: scaleY.left + (scaleY.right - scaleY.left) / 2,
y: scaleY.top + (scaleY.bottom - scaleY.top) / 2,
deltaY: 1
};

await jasmine.triggerWheelEvent(chart, wheelEv);

expect(scaleX.options.min).toEqual(oldMinX);
expect(scaleX.options.max).toEqual(oldMaxX);
expect(scaleY.options.min).not.toEqual(oldMinY);
expect(scaleY.options.max).not.toEqual(oldMaxY);
});
});

describe('Wheel not under Y scale', function() {
it('should be applied on X and Y scales.', async function() {
const chart = window.acquireChart(config);

const scaleX = chart.scales.x;
const scaleY = chart.scales.y;

const oldMinX = scaleX.options.min;
const oldMaxX = scaleX.options.max;
const oldMinY = scaleY.options.min;
const oldMaxY = scaleY.options.max;

const wheelEv = {
x: scaleX.getPixelForValue(1.5),
y: scaleY.getPixelForValue(1.1),
deltaY: 1
};

await jasmine.triggerWheelEvent(chart, wheelEv);

expect(scaleX.options.min).not.toEqual(oldMinX);
expect(scaleX.options.max).not.toEqual(oldMaxX);
expect(scaleY.options.min).not.toEqual(oldMinY);
expect(scaleY.options.max).not.toEqual(oldMaxY);
});
});
});

describe('events', function() {
describe('wheel', function() {
it('should call onZoomStart', function() {
Expand Down
10 changes: 7 additions & 3 deletions types/options.d.ts
Expand Up @@ -67,7 +67,7 @@ export interface PinchOptions {
export interface ZoomOptions {
/**
* Zooming directions. Remove the appropriate direction to disable
* Eg. 'y' would only allow zooming in the y direction
* E.g. 'y' would only allow zooming in the y direction
* A function that is called as the user is zooming and returns the
* available directions can also be used:
* mode: function({ chart }) {
Expand All @@ -91,6 +91,8 @@ export interface ZoomOptions {
*/
pinch?: PinchOptions;

scaleMode?: Mode | { (chart: Chart): Mode };
/** @deprecated Use scaleMode instead */
overScaleMode?: Mode | { (chart: Chart): Mode };

/**
Expand Down Expand Up @@ -122,7 +124,7 @@ export interface PanOptions {

/**
* Panning directions. Remove the appropriate direction to disable
* Eg. 'y' would only allow panning in the y direction
* E.g. 'y' would only allow panning in the y direction
* A function that is called as the user is panning and returns the
* available directions can also be used:
* mode: function({ chart }) {
Expand All @@ -136,7 +138,9 @@ export interface PanOptions {
*/
modifierKey?: Key;

overScaleMode?: Mode | { (char: Chart): Mode };
scaleMode?: Mode | { (chart: Chart): Mode };
/** @deprecated Use scaleMode instead */
overScaleMode?: Mode | { (chart: Chart): Mode };

/**
* Minimal pan distance required before actually applying pan
Expand Down

0 comments on commit 7ec113a

Please sign in to comment.