Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Custom handle causing confusing error #23

Closed
Cowlephant opened this issue Jan 25, 2017 · 6 comments
Closed

Custom handle causing confusing error #23

Cowlephant opened this issue Jan 25, 2017 · 6 comments

Comments

@Cowlephant
Copy link

Cowlephant commented Jan 25, 2017

I have created a custom handle for my brush, following some of the V4 examples, but when I click on the handle to manipulate it when the selection has the same values, it gives an error.

EXCEPTION: Cannot read property '0' of null

If I go to the source, seems to be caused in this area of the d3 code, at the w0 variable area.

    if (type === "overlay") {
      state.selection = selection$$1 = [
        [w0 = dim === Y ? W : point0[0], n0 = dim === X ? N : point0[1]],
        [e0 = dim === Y ? E : w0, s0 = dim === X ? S : n0]
      ];
    } else {
      w0 = selection$$1[0][0];
      n0 = selection$$1[0][1];
      e0 = selection$$1[1][0];
      s0 = selection$$1[1][1];
    }

If I disable pointerevents for my custom handle, everything works fine with no error.

Maybe it's just an issue with the way I am trying to accomodate single date values for my selection, and to not have the selection area disappear.

Creating the brush items

this.brush = d3.brushX()
            .extent([[0, 0], [this.rootWidth, this.rootHeight / 2]])
            .on("start brush", this.brushmoved);

        this.brushGroup = this.root.append("g")
            .attr("transform", "translate(0," + this.rootHeight / 2 + ")")
            .attr("class", "brush")
            .call(this.brush);

        this.handle = this.brushGroup.selectAll(".handle-custom")
            .data([{ type: "w" }, { type: "e" }])
            .enter().append("path")
            .attr("class", function (d: any) {
                return "handle-custom " + d.type;
            })
            .attr("display", null)
            .attr("d", this.TREFOIL_HALF);

Updating my brush, on resizes or otherwise.

updateBrush = (ctx: DateSelectorComponent) => {
        this.brushGroup.call(this.brush.extent([[0, 0], [this.rootWidth, this.rootHeight / 2]]));
        this.brushGroup.call(this.brush.move,
            [this.selectedFromDate,
            this.selectedThroughDate].map(this.xScale));
    };

Updating my handles

    updateHandle = (ctx: DateSelectorComponent) => {
        this.handle
            .attr("transform", function (d, i) {
                let from = ctx.xScale(ctx.selectedFromDate);
                let through = ctx.xScale(ctx.selectedThroughDate);
                return "translate(" + (d.type === "w" ? from : through) + "," + ctx.rootHeight / 4 + ")" +
                    (d.type === "w" ? "scale(1.25, 1.25)" : "scale(-1.25, 1.25)");
            });
    };

brushmoved function

    brushmoved = () => {
        // make sure date range is updated and handles are moved to match the selection extent
        if (!d3.event.sourceEvent || d3.event.sourceEvent.type === "brush") return;
        this.updateDates(this);
        this.updateHandle(this);
    };

Everything works fine except clicking on my custom handle area again.
rangeselector

@mbostock
Copy link
Member

Did you look at this example?

https://bl.ocks.org/mbostock/4349545

@Cowlephant
Copy link
Author

I have yes. I'll pour through it again and see if the answer is in there. Probably an oversight on my part.

@Cowlephant
Copy link
Author

Cowlephant commented Jan 25, 2017

I think the major issue is of a zero-width selection, and my forcing the handles to be visible during that selection.

What is the best way to allow for a single width selection? That gif above represents 18 months. If I click on a value, I want to use that day that I clicked on as my only value. I don't want to set the extent to the start of the day and the end of the day, ideally... because of the visual representation I'm trying to go for, keeping the handles tight together when on a single date. All of the brush examples I've seen either snap to a wider date range, or just disappear the brush with that single selection.

@Cowlephant
Copy link
Author

Same thing happens in the example you provided, if I comment out the lines that hide the brush when there is a null selection. I account for the null selection and make sure I have some sort of selection, but not sure how to prevent this error from happening.

handle.attr("display", "none");
circle.classed("active", false);

Is it just complete bad practice to attempt this? I like the style of the handles directly together, especially for my example which completes the trefoil.

@ytatarynovich
Copy link

I have the same issue. Is it possible to have an empty selection? Since resize controls are required in my case and I cannot apply the same behavior as @mbostock in his great example .

@mbostock
Copy link
Member

mbostock commented Aug 2, 2019

Sorry for the delay. brush.move was too aggressive in converting a degenerate (trivial) selection to the null selection. This should only happen on interaction. Here is an example of preventing the brush from being cleared:

https://observablehq.com/@d3/empty-brush-selection

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Development

No branches or pull requests

3 participants