Skip to content

Commit

Permalink
fix(axis): Fix x axis extent to work
Browse files Browse the repository at this point in the history
Make axis.x.extent option to limit dragging area
for zoom drag

Ref #3768
  • Loading branch information
netil authored and netil committed Nov 26, 2024
1 parent 582feb4 commit 5642216
Show file tree
Hide file tree
Showing 6 changed files with 148 additions and 28 deletions.
23 changes: 23 additions & 0 deletions src/ChartInternal/Axis/Axis.ts
Original file line number Diff line number Diff line change
Expand Up @@ -106,6 +106,29 @@ class Axis {
return type;
}

/**
* Get extent value
* @returns {Array} default extent
* @private
*/
public getExtent(): number[] {
const $$ = this.owner;
const {config, scale} = $$;
let extent = config.axis_x_extent;

if (extent) {
if (isFunction(extent)) {
extent = extent.bind($$.api)($$.getXDomain($$.data.targets), scale.subX);
} else if (this.isTimeSeries() && extent.every(isNaN)) {
const fn = parseDate.bind($$);

extent = extent.map(v => scale.subX(fn(v)));
}
}

return extent;
}

init() {
const $$ = this.owner;
const {config, $el: {main, axis}, state: {clip}} = $$;
Expand Down
27 changes: 2 additions & 25 deletions src/ChartInternal/interactions/subchart.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@
import {brushSelection as d3BrushSelection, brushX as d3BrushX, brushY as d3BrushY} from "d3-brush";
import {select as d3Select} from "d3-selection";
import CLASS from "../../config/classes";
import {brushEmpty, capitalize, isArray, isFunction, parseDate} from "../../module/util";
import {brushEmpty, capitalize, isArray} from "../../module/util";

export default {
/**
Expand Down Expand Up @@ -92,7 +92,7 @@ export default {
// set the brush extent
$$.brush.scale = function(scale) {
const h = config.subchart_size_height;
let extent = $$.getExtent();
let extent = $$.axis.getExtent();

if (!extent && scale.range) {
extent = [[0, 0], [scale.range()[1], h]];
Expand Down Expand Up @@ -373,28 +373,5 @@ export default {

subchart.main.attr("transform", $$.getTranslate("context"));
subXAxis.attr("transform", $$.getTranslate("subX"));
},

/**
* Get extent value
* @returns {Array} default extent
* @private
*/
getExtent(): number[] {
const $$ = this;
const {config, scale} = $$;
let extent = config.axis_x_extent;

if (extent) {
if (isFunction(extent)) {
extent = extent.bind($$.api)($$.getXDomain($$.data.targets), scale.subX);
} else if ($$.axis.isTimeSeries() && extent.every(isNaN)) {
const fn = parseDate.bind($$);

extent = extent.map(v => scale.subX(fn(v)));
}
}

return extent;
}
};
21 changes: 21 additions & 0 deletions src/ChartInternal/interactions/zoom.ts
Original file line number Diff line number Diff line change
Expand Up @@ -367,6 +367,7 @@ export default {
let start = 0;
let end = 0;
let zoomRect;
let extent;

const prop = {
axis: isRotated ? "y" : "x",
Expand All @@ -377,6 +378,9 @@ export default {
$$.zoomBehaviour = d3Drag()
.clickDistance(4)
.on("start", function(event) {
// get extent at first zooming, when is zoomed do not consider
extent = $$.scale.zoom ? null : $$.axis.getExtent();

state.event = event;
$$.setDragStatus(true);
$$.unselectRect();
Expand All @@ -390,6 +394,15 @@ export default {
}

start = getPointer(event, this as SVGAElement)[prop.index];

if (extent) {
if (start < extent[0]) {
start = extent[0];
} else if (start > extent[1]) {
start = extent[1];
}
}

end = start;

zoomRect
Expand All @@ -401,6 +414,14 @@ export default {
.on("drag", function(event) {
end = getPointer(event, this as SVGAElement)[prop.index];

if (extent) {
if (end > extent[1]) {
end = extent[1];
} else if (end < extent[0]) {
end = extent[0];
}
}

zoomRect
.attr(prop.axis, Math.min(start, end))
.attr(prop.attr, Math.abs(end - start));
Expand Down
3 changes: 2 additions & 1 deletion src/config/Options/axis/x.ts
Original file line number Diff line number Diff line change
Expand Up @@ -635,7 +635,8 @@ export default {
axis_x_height: <number | undefined>undefined,

/**
* Set default extent for subchart and zoom. This can be an array or function that returns an array.
* Set extent for subchart and zoom(drag only). This can be an array or function that returns an array.
* - **NOTE:** Specifying value, will limit the zoom scope selection within.
* @name axis․x․extent
* @memberof Options
* @type {Array|Function}
Expand Down
98 changes: 98 additions & 0 deletions test/interactions/drag-spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
import {beforeEach, beforeAll, afterEach, describe, expect, it} from "vitest";
import {$DRAG, $SELECT} from "../../src/config/classes";
import util from "../assets/util";
import { transition } from "d3-transition";

describe("DRAG", function() {
let chart;
Expand Down Expand Up @@ -170,4 +171,101 @@ describe("DRAG", function() {
}, 300);
}));
});

describe("axis.x.extent", () => {
beforeAll(() => {
args = {
data: {
x: "x",
json: {
Temperature: [
"23.43",
"23.16",
"27.48",
"26.78",
"26.62",
"26.64",
"26.29",
"26.01",
"25.84",
"25.07",
"24.85",
"24.01",
],
x: [
"2018-01-01",
"2018-02-01",
"2018-03-01",
"2018-04-01",
"2018-05-01",
"2018-06-01",
"2018-07-01",
"2018-08-01",
"2018-09-01",
"2018-10-01",
"2018-11-01",
"2018-12-01",
],
},
type: "area",
},
axis: {
x: {
tick: {
fit: false,
count: 5
},
type: "timeseries",
extent: ["2018-04-01", "2018-07-01"],
}
},
zoom: {
enabled: true,
type: "drag"
},
transition: {
duration: 0
}
};
});

it("drag zoom can't surpass the extent", () => new Promise(done => {
const {internal: {$el, scale}} = chart;

util.doDrag($el.eventRect.node(), {
clientX: 50,
clientY: 50
}, {
clientX: 550,
clientY: 50,
});

setTimeout(() => {
const zoomed = chart.zoom().map(v => Number(v).toString());

expect(/^15225.*/.test(zoomed[0])).to.be.true;
expect(/^1530.*/.test(zoomed[1])).to.be.true;

done(1);
}, 300);
}));

it("set options: subchart.show=true", () => {
args.subchart = {
show: true
};

args.zoom.enabled = false;
});

it("subchart selection width should match with extent", () => {
const {internal: {brush, scale}} = chart;
const overlay = brush.getSelection().select(".overlay");
const start = scale.x(new Date(`${args.axis.x.extent[0]} 00:00:00`));
const end = scale.x(new Date(`${args.axis.x.extent[1]} 00:00:00`));

expect(+overlay.attr("x")).to.be.equal(start);
expect(+overlay.attr("x") + +overlay.attr("width")).to.be.equal(end);
});
});
});
4 changes: 2 additions & 2 deletions types/axis.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -121,8 +121,8 @@ export interface xAxisConfiguration extends AxisConfigurationBase {
height?: number;

/**
* Set default extent for subchart and zoom.
* This can be an array or function that returns an array.
* Set extent for subchart and zoom(drag only). This can be an array or function that returns an array.
* - **NOTE:** Specifying value, will limit the zoom scope selection within.
*/
extent?: Array<number|string> | (
(
Expand Down

0 comments on commit 5642216

Please sign in to comment.