Comparing version 0.1.0 to 0.1.1
@@ -7,3 +7,3 @@ (function (global, factory) { | ||
var version = "0.1.0"; | ||
var version = "0.1.1"; | ||
@@ -22,9 +22,18 @@ function constant(x) { | ||
function nopropagation() { | ||
d3Selection.event.stopImmediatePropagation(); | ||
} | ||
function noevent() { | ||
d3Selection.event.preventDefault(); | ||
d3Selection.event.stopImmediatePropagation(); | ||
} | ||
var MODE_DRAG = {name: "drag"}; | ||
var MODE_SPACE = {name: "space"}; | ||
var MODE_RESIZE = {name: "resize"}; | ||
var MODE_HANDLE = {name: "handle"}; | ||
var MODE_CENTER = {name: "center"}; | ||
var X = { | ||
name: "x", | ||
resize: ["e", "w"].map(type), | ||
handles: ["e", "w"].map(type), | ||
input: function(x, e) { return x && [[x[0], e[0][1]], [x[1], e[1][1]]]; }, | ||
@@ -36,3 +45,3 @@ output: function(xy) { return xy && [xy[0][0], xy[1][0]]; } | ||
name: "y", | ||
resize: ["n", "s"].map(type), | ||
handles: ["n", "s"].map(type), | ||
input: function(y, e) { return y && [[e[0][0], y[0]], [e[1][0], y[1]]]; }, | ||
@@ -44,3 +53,3 @@ output: function(xy) { return xy && [xy[0][1], xy[1][1]]; } | ||
name: "xy", | ||
resize: ["n", "e", "s", "w", "nw", "ne", "se", "sw"].map(type), | ||
handles: ["n", "e", "s", "w", "nw", "ne", "se", "sw"].map(type), | ||
input: function(xy) { return xy; }, | ||
@@ -51,3 +60,3 @@ output: function(xy) { return xy; } | ||
var cursors = { | ||
background: "crosshair", | ||
overlay: "crosshair", | ||
selection: "move", | ||
@@ -83,3 +92,3 @@ n: "ns-resize", | ||
var signsX = { | ||
background: +1, | ||
overlay: +1, | ||
selection: +1, | ||
@@ -97,3 +106,3 @@ n: null, | ||
var signsY = { | ||
background: +1, | ||
overlay: +1, | ||
selection: +1, | ||
@@ -114,7 +123,10 @@ n: -1, | ||
// Ignore right-click, since that should open the context menu. | ||
function defaultFilter() { | ||
return !event.button; | ||
} | ||
function defaultExtent() { | ||
var svg = this.ownerSVGElement; | ||
return [[0, 0], svg | ||
? [svg.width.baseVal.value, svg.height.baseVal.value] | ||
: [this.clientWidth, this.clientHeight]]; | ||
return [[0, 0], [svg.width.baseVal.value, svg.height.baseVal.value]]; | ||
} | ||
@@ -128,2 +140,12 @@ | ||
function empty(extent) { | ||
return extent[0][0] === extent[1][0] | ||
|| extent[0][1] === extent[1][1]; | ||
} | ||
function brushSelection(node) { | ||
var state = node.__brush; | ||
return state ? state.dim.output(state.selection) : null; | ||
} | ||
function brushX() { | ||
@@ -143,16 +165,18 @@ return brush$1(X); | ||
var extent = defaultExtent, | ||
listeners = d3Dispatch.dispatch(brush, "start", "brush", "end"); | ||
filter = defaultFilter, | ||
listeners = d3Dispatch.dispatch(brush, "start", "brush", "end"), | ||
handleSize = 6, | ||
touchending; | ||
function brush(group) { | ||
var background = group | ||
var overlay = group | ||
.property("__brush", initialize) | ||
.selectAll(".background") | ||
.data([type("background")]); | ||
.selectAll(".overlay") | ||
.data([type("overlay")]); | ||
background.enter().append("rect") | ||
.attr("class", "background") | ||
.attr("fill", "none") | ||
overlay.enter().append("rect") | ||
.attr("class", "overlay") | ||
.attr("pointer-events", "all") | ||
.attr("cursor", cursors.background) | ||
.merge(background) | ||
.attr("cursor", cursors.overlay) | ||
.merge(overlay) | ||
.each(function() { | ||
@@ -174,17 +198,17 @@ var extent = local(this).extent; | ||
var resize = group.selectAll(".resize") | ||
.data(dim.resize, function(d) { return d.type; }); | ||
var handle = group.selectAll(".handle") | ||
.data(dim.handles, function(d) { return d.type; }); | ||
resize.exit().remove(); | ||
handle.exit().remove(); | ||
resize.enter().append("rect") | ||
.attr("class", function(d) { return "resize resize--" + d.type; }) | ||
.attr("cursor", function(d) { return cursors[d.type]; }) | ||
.attr("fill", "none"); | ||
handle.enter().append("rect") | ||
.attr("class", function(d) { return "handle handle--" + d.type; }) | ||
.attr("cursor", function(d) { return cursors[d.type]; }); | ||
group | ||
.each(redraw) | ||
.attr("fill", "none") | ||
.attr("pointer-events", "all") | ||
.style("-webkit-tap-highlight-color", "rgba(0,0,0,0)") | ||
.on("mousedown.brush", mousedowned); | ||
.on("mousedown.brush touchstart.brush", started); | ||
} | ||
@@ -206,3 +230,3 @@ | ||
function tween(t) { | ||
state.selection = i(t); | ||
state.selection = t === 1 && empty(selection1) ? null : i(t); | ||
redraw.call(that); | ||
@@ -219,2 +243,3 @@ emit.brush(); | ||
args = arguments, | ||
selection = dim.input(typeof selection === "function" ? selection.apply(that, args) : selection, state.extent), | ||
emit = emitter(that, args).beforestart(), | ||
@@ -224,3 +249,3 @@ state = that.__brush; | ||
d3Transition.interrupt(that); | ||
state.selection = dim.input(typeof selection === "function" ? selection.apply(that, args) : selection, state.extent); | ||
state.selection = empty(selection) ? null : selection; | ||
redraw.call(that); | ||
@@ -244,12 +269,12 @@ emit.start().brush().end(); | ||
group.selectAll(".resize") | ||
group.selectAll(".handle") | ||
.style("display", null) | ||
.attr("x", function(d) { return d.type[d.type.length - 1] === "e" ? selection[1][0] - 3 : selection[0][0] - 3; }) | ||
.attr("y", function(d) { return d.type[0] === "s" ? selection[1][1] - 3 : selection[0][1] - 3; }) | ||
.attr("width", function(d) { return d.type === "n" || d.type === "s" ? selection[1][0] - selection[0][0] + 6 : 6; }) | ||
.attr("height", function(d) { return d.type === "e" || d.type === "w" ? selection[1][1] - selection[0][1] + 6 : 6; }); | ||
.attr("x", function(d) { return d.type[d.type.length - 1] === "e" ? selection[1][0] - handleSize / 2 : selection[0][0] - handleSize / 2; }) | ||
.attr("y", function(d) { return d.type[0] === "s" ? selection[1][1] - handleSize / 2 : selection[0][1] - handleSize / 2; }) | ||
.attr("width", function(d) { return d.type === "n" || d.type === "s" ? selection[1][0] - selection[0][0] + handleSize : handleSize; }) | ||
.attr("height", function(d) { return d.type === "e" || d.type === "w" ? selection[1][1] - selection[0][1] + handleSize : handleSize; }); | ||
} | ||
else { | ||
group.selectAll(".selection,.resize") | ||
group.selectAll(".selection,.handle") | ||
.style("display", "none") | ||
@@ -296,6 +321,10 @@ .attr("x", null) | ||
function mousedowned() { | ||
function started() { | ||
if (event.touches) { if (event.changedTouches.length < event.touches.length) return noevent(); } | ||
else if (touchending) return; | ||
if (!filter.apply(this, arguments)) return; | ||
var that = this, | ||
type = event.target.__data__.type, | ||
mode = (event.metaKey ? type = "background" : type) === "selection" ? MODE_DRAG : (event.altKey ? MODE_CENTER : MODE_RESIZE), | ||
mode = (event.metaKey ? type = "overlay" : type) === "selection" ? MODE_DRAG : (event.altKey ? MODE_CENTER : MODE_HANDLE), | ||
signX = dim === Y ? null : signsX[type], | ||
@@ -311,2 +340,3 @@ signY = dim === X ? null : signsY[type], | ||
dx, dy, | ||
moving, | ||
point0 = d3Selection.mouse(that), | ||
@@ -316,12 +346,6 @@ point, | ||
if (type === "background") { | ||
if (type === "overlay") { | ||
state.selection = selection = [ | ||
[ | ||
w0 = dim === Y ? W : point0[0], | ||
n0 = dim === X ? N : point0[1] | ||
], | ||
[ | ||
e0 = dim === Y ? E : w0, | ||
s0 = dim === X ? S : n0 | ||
] | ||
[w0 = dim === Y ? W : point0[0], n0 = dim === X ? N : point0[1]], | ||
[e0 = dim === Y ? E : w0, s0 = dim === X ? S : n0] | ||
]; | ||
@@ -340,21 +364,31 @@ } else { | ||
var view = d3Selection.select(event.view) | ||
.on("keydown.brush", keydowned, true) | ||
.on("keyup.brush", keyupped, true) | ||
.on("mousemove.brush", mousemoved, true) | ||
.on("mouseup.brush", mouseupped, true); | ||
var group = d3Selection.select(that) | ||
.attr("pointer-events", "none"); | ||
var background = group.selectAll(".background") | ||
var overlay = group.selectAll(".overlay") | ||
.attr("cursor", cursors[type]); | ||
if (event.touches) { | ||
group | ||
.on("touchmove.brush", moved, true) | ||
.on("touchend.brush touchcancel.brush", ended, true); | ||
} else { | ||
var view = d3Selection.select(event.view) | ||
.on("keydown.brush", keydowned, true) | ||
.on("keyup.brush", keyupped, true) | ||
.on("mousemove.brush", moved, true) | ||
.on("mouseup.brush", ended, true); | ||
d3Drag.dragDisable(event.view); | ||
} | ||
nopropagation(); | ||
d3Transition.interrupt(that); | ||
d3Drag.dragDisable(event.view); | ||
redraw.call(that); | ||
emit.start(); | ||
function mousemoved() { | ||
function moved() { | ||
point = d3Selection.mouse(that); | ||
moving = true; | ||
noevent(); | ||
move(); | ||
@@ -376,3 +410,3 @@ } | ||
} | ||
case MODE_RESIZE: { | ||
case MODE_HANDLE: { | ||
if (signX < 0) dx = Math.max(W - w0, Math.min(E - w0, dx)), w1 = w0 + dx, e1 = e0; | ||
@@ -395,3 +429,3 @@ else if (signX > 0) dx = Math.max(W - e0, Math.min(E - e0, dx)), w1 = w0, e1 = e0 + dx; | ||
t = w1, w1 = e1, e1 = t; | ||
if (type in flipX) background.attr("cursor", cursors[type = flipX[type]]); | ||
if (type in flipX) overlay.attr("cursor", cursors[type = flipX[type]]); | ||
} | ||
@@ -403,3 +437,3 @@ | ||
t = n1, n1 = s1, s1 = t; | ||
if (type in flipY) background.attr("cursor", cursors[type = flipY[type]]); | ||
if (type in flipY) overlay.attr("cursor", cursors[type = flipY[type]]); | ||
} | ||
@@ -420,8 +454,16 @@ | ||
function mouseupped() { | ||
d3Drag.dragEnable(event.view); | ||
function ended() { | ||
nopropagation(); | ||
if (event.touches) { | ||
if (event.touches.length) return; | ||
if (touchending) clearTimeout(touchending); | ||
touchending = setTimeout(function() { touchending = null; }, 500); // Ghost clicks are delayed! | ||
group.on("touchmove.brush touchend.brush touchcancel.brush", null); | ||
} else { | ||
d3Drag.dragEnable(event.view, moving); | ||
view.on("keydown.brush keyup.brush mousemove.brush mouseup.brush", null); | ||
} | ||
group.attr("pointer-events", "all"); | ||
background.attr("cursor", cursors.background); | ||
view.on("keydown.brush keyup.brush mousemove.brush mouseup.brush", null); | ||
if (w1 === e1 || n1 === s1) state.selection = null, redraw.call(that); | ||
overlay.attr("cursor", cursors.overlay); | ||
if (empty(selection)) state.selection = null, redraw.call(that); | ||
emit.end(); | ||
@@ -433,3 +475,3 @@ } | ||
case 18: { // ALT | ||
if (mode === MODE_RESIZE) { | ||
if (mode === MODE_HANDLE) { | ||
if (signX) e0 = e1 - dx * signX, w0 = w1 + dx * signX; | ||
@@ -443,7 +485,7 @@ if (signY) s0 = s1 - dy * signY, n0 = n1 + dy * signY; | ||
case 32: { // SPACE; takes priority over ALT | ||
if (mode === MODE_RESIZE || mode === MODE_CENTER) { | ||
if (mode === MODE_HANDLE || mode === MODE_CENTER) { | ||
if (signX < 0) e0 = e1 - dx; else if (signX > 0) w0 = w1 - dx; | ||
if (signY < 0) s0 = s1 - dy; else if (signY > 0) n0 = n1 - dy; | ||
mode = MODE_SPACE; | ||
background.attr("cursor", cursors.selection); | ||
overlay.attr("cursor", cursors.selection); | ||
move(); | ||
@@ -453,9 +495,5 @@ } | ||
} | ||
case 16: { // SHIFT | ||
break; | ||
} | ||
default: return; | ||
} | ||
event.preventDefault(); | ||
event.stopImmediatePropagation(); | ||
noevent(); | ||
} | ||
@@ -469,3 +507,3 @@ | ||
if (signY < 0) s0 = s1; else if (signY > 0) n0 = n1; | ||
mode = MODE_RESIZE; | ||
mode = MODE_HANDLE; | ||
move(); | ||
@@ -484,5 +522,5 @@ } | ||
if (signY < 0) s0 = s1; else if (signY > 0) n0 = n1; | ||
mode = MODE_RESIZE; | ||
mode = MODE_HANDLE; | ||
} | ||
background.attr("cursor", cursors[type]); | ||
overlay.attr("cursor", cursors[type]); | ||
move(); | ||
@@ -492,9 +530,5 @@ } | ||
} | ||
case 16: { // SHIFT | ||
break; | ||
} | ||
default: return; | ||
} | ||
event.preventDefault(); | ||
event.stopImmediatePropagation(); | ||
noevent(); | ||
} | ||
@@ -506,2 +540,3 @@ } | ||
state.extent = extent.apply(this, arguments); | ||
state.dim = dim; | ||
return state; | ||
@@ -514,2 +549,10 @@ } | ||
brush.filter = function(_) { | ||
return arguments.length ? (filter = typeof _ === "function" ? _ : constant(!!_), brush) : filter; | ||
}; | ||
brush.handleSize = function(_) { | ||
return arguments.length ? (handleSize = +_, brush) : handleSize; | ||
}; | ||
brush.on = function() { | ||
@@ -527,3 +570,4 @@ var value = listeners.on.apply(listeners, arguments); | ||
exports.brushY = brushY; | ||
exports.brushSelection = brushSelection; | ||
})); |
@@ -1,1 +0,1 @@ | ||
!function(t,e){"object"==typeof exports&&"undefined"!=typeof module?e(exports,require("d3-dispatch"),require("d3-drag"),require("d3-interpolate"),require("d3-selection"),require("d3-transition")):"function"==typeof define&&define.amd?define(["exports","d3-dispatch","d3-drag","d3-interpolate","d3-selection","d3-transition"],e):e(t.d3_brush=t.d3_brush||{},t.d3_dispatch,t.d3_drag,t.d3_interpolate,t.d3_selection,t.d3_transition)}(this,function(t,e,n,r,s,i){"use strict";function a(t){return function(){return t}}function u(t,e,n){this.target=t,this.type=e,this.selection=n}function o(t){return{type:t}}function c(){var t=this.ownerSVGElement;return[[0,0],t?[t.width.baseVal.value,t.height.baseVal.value]:[this.clientWidth,this.clientHeight]]}function l(t){for(;!t.__brush;)if(!(t=t.parentNode))return;return t.__brush}function h(){return f(g)}function p(){return f(_)}function d(){return f(k)}function f(t){function h(e){var n=e.property("__brush",k).selectAll(".background").data([o("background")]);n.enter().append("rect").attr("class","background").attr("fill","none").attr("pointer-events","all").attr("cursor",x.background).merge(n).each(function(){var t=l(this).extent;s.select(this).attr("x",t[0][0]).attr("y",t[0][1]).attr("width",t[1][0]-t[0][0]).attr("height",t[1][1]-t[0][1])}),e.selectAll(".selection").data([o("selection")]).enter().append("rect").attr("class","selection").attr("cursor",x.selection).attr("fill","rgba(0,0,0,0.15)");var r=e.selectAll(".resize").data(t.resize,function(t){return t.type});r.exit().remove(),r.enter().append("rect").attr("class",function(t){return"resize resize--"+t.type}).attr("cursor",function(t){return x[t.type]}).attr("fill","none"),e.each(p).attr("pointer-events","all").style("-webkit-tap-highlight-color","rgba(0,0,0,0)").on("mousedown.brush",b)}function p(){var t=s.select(this),e=l(this).selection;e?(t.selectAll(".selection").style("display",null).attr("x",e[0][0]).attr("y",e[0][1]).attr("width",e[1][0]-e[0][0]).attr("height",e[1][1]-e[0][1]),t.selectAll(".resize").style("display",null).attr("x",function(t){return"e"===t.type[t.type.length-1]?e[1][0]-3:e[0][0]-3}).attr("y",function(t){return"s"===t.type[0]?e[1][1]-3:e[0][1]-3}).attr("width",function(t){return"n"===t.type||"s"===t.type?e[1][0]-e[0][0]+6:6}).attr("height",function(t){return"e"===t.type||"w"===t.type?e[1][1]-e[0][1]+6:6})):t.selectAll(".selection,.resize").style("display","none").attr("x",null).attr("y",null).attr("width",null).attr("height",null)}function d(t,e){return t.__brush.emitter||new f(t,e)}function f(t,e){this.that=t,this.args=e,this.state=t.__brush,this.active=0}function b(){function e(){I=s.mouse(P),r()}function r(){var t;switch(V=I[0]-L[0],C=I[1]-L[1],G){case w:case m:H&&(V=Math.max(Y-c,Math.min(F-k,V)),h=c+V,D=k+V),N&&(C=Math.max(B-f,Math.min(J-E,C)),b=f+C,K=E+C);break;case v:0>H?(V=Math.max(Y-c,Math.min(F-c,V)),h=c+V,D=k):H>0&&(V=Math.max(Y-k,Math.min(F-k,V)),h=c,D=k+V),0>N?(C=Math.max(B-f,Math.min(J-f,C)),b=f+C,K=E):N>0&&(C=Math.max(B-E,Math.min(J-E,C)),b=f,K=E+C);break;case y:H&&(h=Math.max(Y,Math.min(F,c-V*H)),D=Math.max(Y,Math.min(F,k+V*H))),N&&(b=Math.max(B,Math.min(J,f-C*N)),K=Math.max(B,Math.min(J,E+C*N)))}h>D&&(H*=-1,t=c,c=k,k=t,t=h,h=D,D=t,j in M&&T.attr("cursor",x[j=M[j]])),b>K&&(N*=-1,t=f,f=E,E=t,t=b,b=K,K=t,j in z&&T.attr("cursor",x[j=z[j]])),X[0][0]===h&&X[0][1]===b&&X[1][0]===D&&X[1][1]===K||(X[0][0]=h,X[0][1]=b,X[1][0]=D,X[1][1]=K,p.call(P),O.brush())}function a(){n.dragEnable(event.view),R.attr("pointer-events","all"),T.attr("cursor",x.background),Q.on("keydown.brush keyup.brush mousemove.brush mouseup.brush",null),h!==D&&b!==K||(S.selection=null,p.call(P)),O.end()}function u(){switch(event.keyCode){case 18:G===v&&(H&&(k=D-V*H,c=h+V*H),N&&(E=K-C*N,f=b+C*N),G=y,r());break;case 32:G!==v&&G!==y||(0>H?k=D-V:H>0&&(c=h-V),0>N?E=K-C:N>0&&(f=b-C),G=w,T.attr("cursor",x.selection),r());break;case 16:break;default:return}event.preventDefault(),event.stopImmediatePropagation()}function o(){switch(event.keyCode){case 18:G===y&&(0>H?k=D:H>0&&(c=h),0>N?E=K:N>0&&(f=b),G=v,r());break;case 32:G===w&&(event.altKey?(H&&(k=D-V*H,c=h+V*H),N&&(E=K-C*N,f=b+C*N),G=y):(0>H?k=D:H>0&&(c=h),0>N?E=K:N>0&&(f=b),G=v),T.attr("cursor",x[j]),r());break;case 16:break;default:return}event.preventDefault(),event.stopImmediatePropagation()}var c,h,f,b,k,D,E,K,V,C,I,P=this,j=event.target.__data__.type,G="selection"===(event.metaKey?j="background":j)?m:event.altKey?y:v,H=t===_?null:A[j],N=t===g?null:q[j],S=l(P),W=S.extent,X=S.selection,Y=W[0][0],B=W[0][1],F=W[1][0],J=W[1][1],L=s.mouse(P),O=d(P,arguments).beforestart();"background"===j?S.selection=X=[[c=t===_?Y:L[0],f=t===g?B:L[1]],[k=t===_?F:c,E=t===g?J:f]]:(c=X[0][0],f=X[0][1],k=X[1][0],E=X[1][1]),h=c,b=f,D=k,K=E;var Q=s.select(event.view).on("keydown.brush",u,!0).on("keyup.brush",o,!0).on("mousemove.brush",e,!0).on("mouseup.brush",a,!0),R=s.select(P).attr("pointer-events","none"),T=R.selectAll(".background").attr("cursor",x[j]);i.interrupt(P),n.dragDisable(event.view),p.call(P),O.start()}function k(){var t=this.__brush||{selection:null};return t.extent=D.apply(this,arguments),t}var D=c,E=e.dispatch(h,"start","brush","end");return h.move=function(e,n){e.selection?e.on("start.brush",function(){d(this,arguments).beforestart().start()}).on("interrupt.brush end.brush",function(){d(this,arguments).end()}).tween("brush",function(){function e(t){i.selection=c(t),p.call(s),a.brush()}var s=this,i=s.__brush,a=d(s,arguments),u=i.selection,o=t.input("function"==typeof n?n.apply(this,arguments):n,i.extent),c=r.interpolate(u,o);return u&&o?e:e(1)}):e.each(function(){var e=this,r=arguments,s=d(e,r).beforestart(),a=e.__brush;i.interrupt(e),a.selection=t.input("function"==typeof n?n.apply(e,r):n,a.extent),p.call(e),s.start().brush().end()})},f.prototype={beforestart:function(){return 1===++this.active&&(this.state.emitter=this,this.starting=!0),this},start:function(){return this.starting&&(this.starting=!1,this.emit("start")),this},brush:function(){return this.emit("brush"),this},end:function(){return 0===--this.active&&(delete this.state.emitter,this.emit("end")),this},emit:function(e){s.customEvent(new u(h,e,t.output(this.state.selection)),E.apply,E,[e,this.that,this.args])}},h.extent=function(t){return arguments.length?(D="function"==typeof t?t:a([[+t[0][0],+t[0][1]],[+t[1][0],+t[1][1]]]),h):D},h.on=function(){var t=E.on.apply(E,arguments);return t===E?h:t},h}var b="0.1.0",m={name:"drag"},w={name:"space"},v={name:"resize"},y={name:"center"},g={name:"x",resize:["e","w"].map(o),input:function(t,e){return t&&[[t[0],e[0][1]],[t[1],e[1][1]]]},output:function(t){return t&&[t[0][0],t[1][0]]}},_={name:"y",resize:["n","s"].map(o),input:function(t,e){return t&&[[e[0][0],t[0]],[e[1][0],t[1]]]},output:function(t){return t&&[t[0][1],t[1][1]]}},k={name:"xy",resize:["n","e","s","w","nw","ne","se","sw"].map(o),input:function(t){return t},output:function(t){return t}},x={background:"crosshair",selection:"move",n:"ns-resize",e:"ew-resize",s:"ns-resize",w:"ew-resize",nw:"nwse-resize",ne:"nesw-resize",se:"nwse-resize",sw:"nesw-resize"},M={e:"w",w:"e",nw:"ne",ne:"nw",se:"sw",sw:"se"},z={n:"s",s:"n",nw:"sw",ne:"se",se:"ne",sw:"nw"},A={background:1,selection:1,n:null,e:1,s:null,w:-1,nw:-1,ne:1,se:1,sw:-1},q={background:1,selection:1,n:-1,e:null,s:1,w:null,nw:-1,ne:-1,se:1,sw:1};t.version=b,t.brush=d,t.brushX=h,t.brushY=p}); | ||
!function(t,e){"object"==typeof exports&&"undefined"!=typeof module?e(exports,require("d3-dispatch"),require("d3-drag"),require("d3-interpolate"),require("d3-selection"),require("d3-transition")):"function"==typeof define&&define.amd?define(["exports","d3-dispatch","d3-drag","d3-interpolate","d3-selection","d3-transition"],e):e(t.d3_brush=t.d3_brush||{},t.d3_dispatch,t.d3_drag,t.d3_interpolate,t.d3_selection,t.d3_transition)}(this,function(t,e,n,r,s,i){"use strict";function a(t){return function(){return t}}function u(t,e,n){this.target=t,this.type=e,this.selection=n}function o(){s.event.stopImmediatePropagation()}function l(){s.event.preventDefault(),s.event.stopImmediatePropagation()}function c(t){return{type:t}}function h(){return!event.button}function f(){var t=this.ownerSVGElement;return[[0,0],[t.width.baseVal.value,t.height.baseVal.value]]}function p(t){for(;!t.__brush;)if(!(t=t.parentNode))return;return t.__brush}function d(t){return t[0][0]===t[1][0]||t[0][1]===t[1][1]}function v(t){var e=t.__brush;return e?e.dim.output(e.selection):null}function m(){return w(z)}function y(){return w(A)}function b(){return w(q)}function w(t){function v(e){var n=e.property("__brush",g).selectAll(".overlay").data([c("overlay")]);n.enter().append("rect").attr("class","overlay").attr("pointer-events","all").attr("cursor",E.overlay).merge(n).each(function(){var t=p(this).extent;s.select(this).attr("x",t[0][0]).attr("y",t[0][1]).attr("width",t[1][0]-t[0][0]).attr("height",t[1][1]-t[0][1])}),e.selectAll(".selection").data([c("selection")]).enter().append("rect").attr("class","selection").attr("cursor",E.selection).attr("fill","rgba(0,0,0,0.15)");var r=e.selectAll(".handle").data(t.handles,function(t){return t.type});r.exit().remove(),r.enter().append("rect").attr("class",function(t){return"handle handle--"+t.type}).attr("cursor",function(t){return E[t.type]}),e.each(m).attr("fill","none").attr("pointer-events","all").style("-webkit-tap-highlight-color","rgba(0,0,0,0)").on("mousedown.brush touchstart.brush",w)}function m(){var t=s.select(this),e=p(this).selection;e?(t.selectAll(".selection").style("display",null).attr("x",e[0][0]).attr("y",e[0][1]).attr("width",e[1][0]-e[0][0]).attr("height",e[1][1]-e[0][1]),t.selectAll(".handle").style("display",null).attr("x",function(t){return"e"===t.type[t.type.length-1]?e[1][0]-P/2:e[0][0]-P/2}).attr("y",function(t){return"s"===t.type[0]?e[1][1]-P/2:e[0][1]-P/2}).attr("width",function(t){return"n"===t.type||"s"===t.type?e[1][0]-e[0][0]+P:P}).attr("height",function(t){return"e"===t.type||"w"===t.type?e[1][1]-e[0][1]+P:P})):t.selectAll(".selection,.handle").style("display","none").attr("x",null).attr("y",null).attr("width",null).attr("height",null)}function y(t,e){return t.__brush.emitter||new b(t,e)}function b(t,e){this.that=t,this.args=e,this.state=t.__brush,this.active=0}function w(){function e(){N=s.mouse(X),G=!0,l(),r()}function r(){var t;switch(P=N[0]-Z[0],j=N[1]-Z[1],B){case x:case _:F&&(P=Math.max(Q-h,Math.min(U-w,P)),f=h+P,g=w+P),H&&(j=Math.max(R-v,Math.min(W-C,j)),b=v+j,I=C+j);break;case M:0>F?(P=Math.max(Q-h,Math.min(U-h,P)),f=h+P,g=w):F>0&&(P=Math.max(Q-w,Math.min(U-w,P)),f=h,g=w+P),0>H?(j=Math.max(R-v,Math.min(W-v,j)),b=v+j,I=C):H>0&&(j=Math.max(R-C,Math.min(W-C,j)),b=v,I=C+j);break;case k:F&&(f=Math.max(Q,Math.min(U,h-P*F)),g=Math.max(Q,Math.min(U,w+P*F))),H&&(b=Math.max(R,Math.min(W,v-j*H)),I=Math.max(R,Math.min(W,C+j*H)))}f>g&&(F*=-1,t=h,h=w,w=t,t=f,f=g,g=t,Y in K&&et.attr("cursor",E[Y=K[Y]])),b>I&&(H*=-1,t=v,v=C,C=t,t=b,b=I,I=t,Y in S&&et.attr("cursor",E[Y=S[Y]])),O[0][0]===f&&O[0][1]===b&&O[1][0]===g&&O[1][1]===I||(O[0][0]=f,O[0][1]=b,O[1][0]=g,O[1][1]=I,m.call(X),$.brush())}function a(){if(o(),event.touches){if(event.touches.length)return;q&&clearTimeout(q),q=setTimeout(function(){q=null},500),tt.on("touchmove.brush touchend.brush touchcancel.brush",null)}else n.dragEnable(event.view,G),nt.on("keydown.brush keyup.brush mousemove.brush mouseup.brush",null);tt.attr("pointer-events","all"),et.attr("cursor",E.overlay),d(O)&&(J.selection=null,m.call(X)),$.end()}function u(){switch(event.keyCode){case 18:B===M&&(F&&(w=g-P*F,h=f+P*F),H&&(C=I-j*H,v=b+j*H),B=k,r());break;case 32:B!==M&&B!==k||(0>F?w=g-P:F>0&&(h=f-P),0>H?C=I-j:H>0&&(v=b-j),B=x,et.attr("cursor",E.selection),r());break;default:return}l()}function c(){switch(event.keyCode){case 18:B===k&&(0>F?w=g:F>0&&(h=f),0>H?C=I:H>0&&(v=b),B=M,r());break;case 32:B===x&&(event.altKey?(F&&(w=g-P*F,h=f+P*F),H&&(C=I-j*H,v=b+j*H),B=k):(0>F?w=g:F>0&&(h=f),0>H?C=I:H>0&&(v=b),B=M),et.attr("cursor",E[Y]),r());break;default:return}l()}if(event.touches){if(event.changedTouches.length<event.touches.length)return l()}else if(q)return;if(D.apply(this,arguments)){var h,f,v,b,w,g,C,I,P,j,G,N,X=this,Y=event.target.__data__.type,B="selection"===(event.metaKey?Y="overlay":Y)?_:event.altKey?k:M,F=t===A?null:T[Y],H=t===z?null:V[Y],J=p(X),L=J.extent,O=J.selection,Q=L[0][0],R=L[0][1],U=L[1][0],W=L[1][1],Z=s.mouse(X),$=y(X,arguments).beforestart();"overlay"===Y?J.selection=O=[[h=t===A?Q:Z[0],v=t===z?R:Z[1]],[w=t===A?U:h,C=t===z?W:v]]:(h=O[0][0],v=O[0][1],w=O[1][0],C=O[1][1]),f=h,b=v,g=w,I=C;var tt=s.select(X).attr("pointer-events","none"),et=tt.selectAll(".overlay").attr("cursor",E[Y]);if(event.touches)tt.on("touchmove.brush",e,!0).on("touchend.brush touchcancel.brush",a,!0);else{var nt=s.select(event.view).on("keydown.brush",u,!0).on("keyup.brush",c,!0).on("mousemove.brush",e,!0).on("mouseup.brush",a,!0);n.dragDisable(event.view)}o(),i.interrupt(X),m.call(X),$.start()}}function g(){var e=this.__brush||{selection:null};return e.extent=C.apply(this,arguments),e.dim=t,e}var q,C=f,D=h,I=e.dispatch(v,"start","brush","end"),P=6;return v.move=function(e,n){e.selection?e.on("start.brush",function(){y(this,arguments).beforestart().start()}).on("interrupt.brush end.brush",function(){y(this,arguments).end()}).tween("brush",function(){function e(t){i.selection=1===t&&d(o)?null:l(t),m.call(s),a.brush()}var s=this,i=s.__brush,a=y(s,arguments),u=i.selection,o=t.input("function"==typeof n?n.apply(this,arguments):n,i.extent),l=r.interpolate(u,o);return u&&o?e:e(1)}):e.each(function(){var e=this,n=arguments,r=t.input("function"==typeof r?r.apply(e,n):r,a.extent),s=y(e,n).beforestart(),a=e.__brush;i.interrupt(e),a.selection=d(r)?null:r,m.call(e),s.start().brush().end()})},b.prototype={beforestart:function(){return 1===++this.active&&(this.state.emitter=this,this.starting=!0),this},start:function(){return this.starting&&(this.starting=!1,this.emit("start")),this},brush:function(){return this.emit("brush"),this},end:function(){return 0===--this.active&&(delete this.state.emitter,this.emit("end")),this},emit:function(e){s.customEvent(new u(v,e,t.output(this.state.selection)),I.apply,I,[e,this.that,this.args])}},v.extent=function(t){return arguments.length?(C="function"==typeof t?t:a([[+t[0][0],+t[0][1]],[+t[1][0],+t[1][1]]]),v):C},v.filter=function(t){return arguments.length?(D="function"==typeof t?t:a(!!t),v):D},v.handleSize=function(t){return arguments.length?(P=+t,v):P},v.on=function(){var t=I.on.apply(I,arguments);return t===I?v:t},v}var g="0.1.1",_={name:"drag"},x={name:"space"},M={name:"handle"},k={name:"center"},z={name:"x",handles:["e","w"].map(c),input:function(t,e){return t&&[[t[0],e[0][1]],[t[1],e[1][1]]]},output:function(t){return t&&[t[0][0],t[1][0]]}},A={name:"y",handles:["n","s"].map(c),input:function(t,e){return t&&[[e[0][0],t[0]],[e[1][0],t[1]]]},output:function(t){return t&&[t[0][1],t[1][1]]}},q={name:"xy",handles:["n","e","s","w","nw","ne","se","sw"].map(c),input:function(t){return t},output:function(t){return t}},E={overlay:"crosshair",selection:"move",n:"ns-resize",e:"ew-resize",s:"ns-resize",w:"ew-resize",nw:"nwse-resize",ne:"nesw-resize",se:"nwse-resize",sw:"nesw-resize"},K={e:"w",w:"e",nw:"ne",ne:"nw",se:"sw",sw:"se"},S={n:"s",s:"n",nw:"sw",ne:"se",se:"ne",sw:"nw"},T={overlay:1,selection:1,n:null,e:1,s:null,w:-1,nw:-1,ne:1,se:1,sw:-1},V={overlay:1,selection:1,n:-1,e:null,s:1,w:null,nw:-1,ne:-1,se:1,sw:1};t.version=g,t.brush=b,t.brushX=m,t.brushY=y,t.brushSelection=v}); |
export var name = "d3-brush"; | ||
export var version = "0.1.0"; | ||
export var description = ""; | ||
export var version = "0.1.1"; | ||
export var description = "Select a one- or two-dimensional region using the mouse or touch."; | ||
export var keywords = ["d3","brush","interaction"]; | ||
@@ -5,0 +5,0 @@ export var homepage = "https://github.com/d3/d3-brush"; |
export {version} from "./build/package"; | ||
export {default as brush, brushX, brushY} from "./src/brush"; | ||
export { | ||
default as brush, | ||
brushX, | ||
brushY, | ||
brushSelection | ||
} from "./src/brush"; |
{ | ||
"name": "d3-brush", | ||
"version": "0.1.0", | ||
"description": "", | ||
"version": "0.1.1", | ||
"description": "Select a one- or two-dimensional region using the mouse or touch.", | ||
"keywords": [ | ||
@@ -6,0 +6,0 @@ "d3", |
109
README.md
# d3-brush | ||
… | ||
Brushing is the interactive specification a one- or two-dimensional selected region using a pointing gesture, such as by clicking and dragging the mouse. Brushing is often used to select discrete elements, such as dots in a scatterplot or files on a desktop. It can also be used to zoom-in to a region of interest, or to select continuous regions for [cross-filtering data](http://square.github.io/crossfilter/) or live histograms: | ||
[<img alt="Mona Lisa Histogram" src="https://raw.githubusercontent.com/d3/d3-brush/master/img/mona-lisa.jpg" width="420" height="219">](http://bl.ocks.org/mbostock/0d20834e3d5a46138752f86b9b79727e) | ||
The d3-brush module implements brushing for mouse and touch events using [SVG](https://www.w3.org/TR/SVG/). Click and drag on the brush selection to translate the selection. Click and drag on one of the selection handles to move the corresponding edge (or edges) of the selection. Click and drag on the invisible overlay to define a new brush selection, or click anywhere within the brushable region while holding down the META (⌘) key. Holding down the ALT (⌥) key while moving the brush causes it to reposition around its center, while holding down SPACE locks the current brush size, allowing only translation. | ||
## Installing | ||
@@ -32,2 +36,103 @@ | ||
… | ||
Creates a new two-dimensional brush. | ||
<a href="#brushX" name="brushX">#</a> d3.<b>brushX</b>() | ||
Creates a new one-dimensional brush along the *x*-dimension. | ||
<a href="#brushY" name="brushY">#</a> d3.<b>brushY</b>() | ||
Creates a new one-dimensional brush along the *y*-dimension. | ||
<a href="#_brush" name="_brush">#</a> <i>brush</i>(<i>group</i>) | ||
Applies the brush to the specified *group*, which must be a [selection](https://github.com/d3/d3-selection) of SVG [G elements](https://www.w3.org/TR/SVG/struct.html#Groups). This function is typically not invoked directly, and is instead invoked via [*selection*.call](https://github.com/d3/d3-selection#selection_call). For example, to render a brush: | ||
```js | ||
svg.append("g") | ||
.attr("class", "brush") | ||
.call(d3.brush().on("brush", brushed)); | ||
``` | ||
Internally, the brush uses [*selection*.on](https://github.com/d3/d3-selection#selection_on) to bind the necessary event listeners for dragging. The listeners use the name `.brush`, so you can subsequently unbind the brush event listeners as follows: | ||
```js | ||
group.on(".brush", null); | ||
``` | ||
The brush also creates the SVG elements necessary to display the brush selection and to receive input events for interaction. You can add, remove or modify these elements as desired to change the brush appearance; you can also apply stylesheets to modify the brush appearance. The structure of a two-dimensional brush is as follows: | ||
```html | ||
<g class="brush" fill="none" pointer-events="all" style="-webkit-tap-highlight-color: rgba(0, 0, 0, 0);"> | ||
<rect class="overlay" pointer-events="all" cursor="crosshair" x="0" y="0" width="960" height="500"></rect> | ||
<rect class="selection" cursor="move" fill="rgba(0,0,0,0.15)" x="112" y="194" width="182" height="83"></rect> | ||
<rect class="handle handle--n" cursor="ns-resize" x="107" y="189" width="192" height="10"></rect> | ||
<rect class="handle handle--e" cursor="ew-resize" x="289" y="189" width="10" height="93"></rect> | ||
<rect class="handle handle--s" cursor="ns-resize" x="107" y="272" width="192" height="10"></rect> | ||
<rect class="handle handle--w" cursor="ew-resize" x="107" y="189" width="10" height="93"></rect> | ||
<rect class="handle handle--nw" cursor="nwse-resize" x="107" y="189" width="10" height="10"></rect> | ||
<rect class="handle handle--ne" cursor="nesw-resize" x="289" y="189" width="10" height="10"></rect> | ||
<rect class="handle handle--se" cursor="nwse-resize" x="289" y="272" width="10" height="10"></rect> | ||
<rect class="handle handle--sw" cursor="nesw-resize" x="107" y="272" width="10" height="10"></rect> | ||
</g> | ||
``` | ||
The overlay rect covers the brushable area defined by [*brush*.extent](#brush_extent). The selection rect covers the area defined by the current [brush selection](#brushSelection). The handle rects cover the edges and corners of the brush selection, allowing the corresponding value in the brush selection to be modified interactively. To modify the brush selection programmatically, use [*brush*.move](#brush_move). | ||
<a href="#brush_move" name="brush_move">#</a> <i>brush</i>.<b>move</b>(<i>group</i>, <i>selection</i>) | ||
Sets the active *selection* of the brush on the specified *group*, which must be a [selection](https://github.com/d3/d3-selection) or a [transition](https://github.com/d3/d3-transition) of SVG [G elements](https://www.w3.org/TR/SVG/struct.html#Groups). The *selection* must be defined as an array of numbers. For a [two-dimensional brush](#brush), it must be defined as [[*x0*, *y0*], [*x1*, *y1*]], where *x0* is the minimum *x*-value, *y0* is the minimum *y*-value, *x1* is the maximum *x*-value, and *y1* is the maximum *y*-value. For an [*x*-brush](#brushX), it must be defined as [*x0*, *x1*]; for a [*y*-brush](#brushY), it must be defined as [*y0*, *y1*]. The selection may also be specified as a function which returns such an array; if a function, it is invoked for each selected element, being passed the current datum `d` and index `i`, with the `this` context as the current DOM element. The returned array defines the brush selection for that element. | ||
<a href="#brush_extent" name="brush_extent">#</a> <i>brush</i>.<b>extent</b>([<i>extent</i>]) | ||
If *extent* is specified, sets the brushable extent to the specified array of points [[*x0*, *y0*], [*x1*, *y1*]], where [*x0*, *y0*] is the top-left corner and [*x1*, *y1*] is the bottom-right corner, and returns this brush. The *extent* may also be specified as a function which returns such an array; if a function, it is invoked for each selected element, being passed the current datum `d` and index `i`, with the `this` context as the current DOM element. If *extent* is not specified, returns the current extent accessor, which defaults to: | ||
```js | ||
function extent() { | ||
var svg = this.ownerSVGElement; | ||
return [[0, 0], [svg.width.baseVal.value, svg.height.baseVal.value]]; | ||
} | ||
``` | ||
The brush extent determines the size of the invisible overlay and also constrains the brush selection; the brush selection cannot go outside the brush extent. | ||
<a href="#brush_filter" name="brush_filter">#</a> <i>brush</i>.<b>filter</b>([<i>filter</i>]) | ||
If *filter* is specified, sets the filter to the specified function and returns the brush. If *filter* is not specified, returns the current filter, which defaults to: | ||
```js | ||
function filter() { | ||
return !event.button; | ||
} | ||
``` | ||
If the filter returns falsey, the initiating event is ignored and no brush gesture is started. Thus, the filter determines which input events are ignored. The default filter ignores mousedown events on secondary buttons, since those buttons are typically intended for other purposes, such as the context menu. | ||
<a href="#brush_handleSize" name="brush_handleSize">#</a> <i>brush</i>.<b>handleSize</b>([<i>size</i>]) | ||
If *size* is specified, sets the size of the brush handles to the specified number and returns the brush. If *size* is not specified, returns the current handle size, which defaults to six. This method must be called before [applying the brush](#_brush) to a selection; changing the handle size does not affect brushes that were previously rendered. | ||
<a href="#brush_on" name="brush_on">#</a> <i>brush</i>.<b>on</b>(<i>typenames</i>, [<i>listener</i>]) | ||
If *listener* is specified, sets the event *listener* for the specified *typenames* and returns the brush. If an event listener was already registered for the same type and name, the existing listener is removed before the new listener is added. If *listener* is null, removes the current event listeners for the specified *typenames*, if any. If *listener* is not specified, returns the first currently-assigned listener matching the specified *typenames*, if any. When a specified event is dispatched, each *listener* will be invoked with the same context and arguments as [*selection*.on](https://github.com/d3/d3-selection#selection_on) listeners: the current datum `d` and index `i`, with the `this` context as the current DOM element. | ||
The *typenames* is a string containing one or more *typename* separated by whitespace. Each *typename* is a *type*, optionally followed by a period (`.`) and a *name*, such as `brush.foo` and `brush.bar`; the name allows multiple listeners to be registered for the same *type*. The *type* must be one of the following: | ||
* `start` - at the start of a brush gesture, such as on mousedown. | ||
* `brush` - when the brush moves, such as on mousemove. | ||
* `end` - at the end of a brush gesture, such as on mouseup. | ||
See [*dispatch*.on](https://github.com/d3/d3-dispatch#dispatch_on) and [Brush Events](#brush-events) for more. | ||
<a href="#brushSelection" name="brushSelection">#</a> d3.<b>brushSelection</b>(<i>node</i>) | ||
Returns the current brush selection for the specified *node*. Internally, an element’s brush state is stored as *element*.\_\_brush; however, you should use this method rather than accessing it directly. If the given *node* has no selection, returns null. Otherwise, the *selection* is defined as an array of numbers. For a [two-dimensional brush](#brush), it is [[*x0*, *y0*], [*x1*, *y1*]], where *x0* is the minimum *x*-value, *y0* is the minimum *y*-value, *x1* is the maximum *x*-value, and *y1* is the maximum *y*-value. For an [*x*-brush](#brushX), it is [*x0*, *x1*]; for a [*y*-brush](#brushY), it is [*y0*, *y1*]. | ||
### Brush Events | ||
When a [brush event listener](#brush_on) is invoked, [d3.event](https://github.com/d3/d3-selection#event) is set to the current brush event. The *event* object exposes several fields: | ||
* `target` - the associated [brush behavior](#brush). | ||
* `type` - the string “start”, “brush” or “end”; see [*brush*.on](#brush_on). | ||
* `selection` - the current [brush selection](#brushSelection). | ||
* `sourceEvent` - the underlying input event, such as mousemove or touchmove. |
193
src/brush.js
@@ -8,6 +8,7 @@ import {dispatch} from "d3-dispatch"; | ||
import BrushEvent from "./event"; | ||
import noevent, {nopropagation} from "./noevent"; | ||
var MODE_DRAG = {name: "drag"}, | ||
MODE_SPACE = {name: "space"}, | ||
MODE_RESIZE = {name: "resize"}, | ||
MODE_HANDLE = {name: "handle"}, | ||
MODE_CENTER = {name: "center"}; | ||
@@ -17,3 +18,3 @@ | ||
name: "x", | ||
resize: ["e", "w"].map(type), | ||
handles: ["e", "w"].map(type), | ||
input: function(x, e) { return x && [[x[0], e[0][1]], [x[1], e[1][1]]]; }, | ||
@@ -25,3 +26,3 @@ output: function(xy) { return xy && [xy[0][0], xy[1][0]]; } | ||
name: "y", | ||
resize: ["n", "s"].map(type), | ||
handles: ["n", "s"].map(type), | ||
input: function(y, e) { return y && [[e[0][0], y[0]], [e[1][0], y[1]]]; }, | ||
@@ -33,3 +34,3 @@ output: function(xy) { return xy && [xy[0][1], xy[1][1]]; } | ||
name: "xy", | ||
resize: ["n", "e", "s", "w", "nw", "ne", "se", "sw"].map(type), | ||
handles: ["n", "e", "s", "w", "nw", "ne", "se", "sw"].map(type), | ||
input: function(xy) { return xy; }, | ||
@@ -40,3 +41,3 @@ output: function(xy) { return xy; } | ||
var cursors = { | ||
background: "crosshair", | ||
overlay: "crosshair", | ||
selection: "move", | ||
@@ -72,3 +73,3 @@ n: "ns-resize", | ||
var signsX = { | ||
background: +1, | ||
overlay: +1, | ||
selection: +1, | ||
@@ -86,3 +87,3 @@ n: null, | ||
var signsY = { | ||
background: +1, | ||
overlay: +1, | ||
selection: +1, | ||
@@ -103,7 +104,10 @@ n: -1, | ||
// Ignore right-click, since that should open the context menu. | ||
function defaultFilter() { | ||
return !event.button; | ||
} | ||
function defaultExtent() { | ||
var svg = this.ownerSVGElement; | ||
return [[0, 0], svg | ||
? [svg.width.baseVal.value, svg.height.baseVal.value] | ||
: [this.clientWidth, this.clientHeight]]; | ||
return [[0, 0], [svg.width.baseVal.value, svg.height.baseVal.value]]; | ||
} | ||
@@ -117,2 +121,12 @@ | ||
function empty(extent) { | ||
return extent[0][0] === extent[1][0] | ||
|| extent[0][1] === extent[1][1]; | ||
} | ||
export function brushSelection(node) { | ||
var state = node.__brush; | ||
return state ? state.dim.output(state.selection) : null; | ||
} | ||
export function brushX() { | ||
@@ -132,16 +146,18 @@ return brush(X); | ||
var extent = defaultExtent, | ||
listeners = dispatch(brush, "start", "brush", "end"); | ||
filter = defaultFilter, | ||
listeners = dispatch(brush, "start", "brush", "end"), | ||
handleSize = 6, | ||
touchending; | ||
function brush(group) { | ||
var background = group | ||
var overlay = group | ||
.property("__brush", initialize) | ||
.selectAll(".background") | ||
.data([type("background")]); | ||
.selectAll(".overlay") | ||
.data([type("overlay")]); | ||
background.enter().append("rect") | ||
.attr("class", "background") | ||
.attr("fill", "none") | ||
overlay.enter().append("rect") | ||
.attr("class", "overlay") | ||
.attr("pointer-events", "all") | ||
.attr("cursor", cursors.background) | ||
.merge(background) | ||
.attr("cursor", cursors.overlay) | ||
.merge(overlay) | ||
.each(function() { | ||
@@ -163,17 +179,17 @@ var extent = local(this).extent; | ||
var resize = group.selectAll(".resize") | ||
.data(dim.resize, function(d) { return d.type; }); | ||
var handle = group.selectAll(".handle") | ||
.data(dim.handles, function(d) { return d.type; }); | ||
resize.exit().remove(); | ||
handle.exit().remove(); | ||
resize.enter().append("rect") | ||
.attr("class", function(d) { return "resize resize--" + d.type; }) | ||
.attr("cursor", function(d) { return cursors[d.type]; }) | ||
.attr("fill", "none"); | ||
handle.enter().append("rect") | ||
.attr("class", function(d) { return "handle handle--" + d.type; }) | ||
.attr("cursor", function(d) { return cursors[d.type]; }); | ||
group | ||
.each(redraw) | ||
.attr("fill", "none") | ||
.attr("pointer-events", "all") | ||
.style("-webkit-tap-highlight-color", "rgba(0,0,0,0)") | ||
.on("mousedown.brush", mousedowned); | ||
.on("mousedown.brush touchstart.brush", started); | ||
} | ||
@@ -195,3 +211,3 @@ | ||
function tween(t) { | ||
state.selection = i(t); | ||
state.selection = t === 1 && empty(selection1) ? null : i(t); | ||
redraw.call(that); | ||
@@ -208,2 +224,3 @@ emit.brush(); | ||
args = arguments, | ||
selection = dim.input(typeof selection === "function" ? selection.apply(that, args) : selection, state.extent), | ||
emit = emitter(that, args).beforestart(), | ||
@@ -213,3 +230,3 @@ state = that.__brush; | ||
interrupt(that); | ||
state.selection = dim.input(typeof selection === "function" ? selection.apply(that, args) : selection, state.extent); | ||
state.selection = empty(selection) ? null : selection; | ||
redraw.call(that); | ||
@@ -233,12 +250,12 @@ emit.start().brush().end(); | ||
group.selectAll(".resize") | ||
group.selectAll(".handle") | ||
.style("display", null) | ||
.attr("x", function(d) { return d.type[d.type.length - 1] === "e" ? selection[1][0] - 3 : selection[0][0] - 3; }) | ||
.attr("y", function(d) { return d.type[0] === "s" ? selection[1][1] - 3 : selection[0][1] - 3; }) | ||
.attr("width", function(d) { return d.type === "n" || d.type === "s" ? selection[1][0] - selection[0][0] + 6 : 6; }) | ||
.attr("height", function(d) { return d.type === "e" || d.type === "w" ? selection[1][1] - selection[0][1] + 6 : 6; }); | ||
.attr("x", function(d) { return d.type[d.type.length - 1] === "e" ? selection[1][0] - handleSize / 2 : selection[0][0] - handleSize / 2; }) | ||
.attr("y", function(d) { return d.type[0] === "s" ? selection[1][1] - handleSize / 2 : selection[0][1] - handleSize / 2; }) | ||
.attr("width", function(d) { return d.type === "n" || d.type === "s" ? selection[1][0] - selection[0][0] + handleSize : handleSize; }) | ||
.attr("height", function(d) { return d.type === "e" || d.type === "w" ? selection[1][1] - selection[0][1] + handleSize : handleSize; }); | ||
} | ||
else { | ||
group.selectAll(".selection,.resize") | ||
group.selectAll(".selection,.handle") | ||
.style("display", "none") | ||
@@ -285,6 +302,10 @@ .attr("x", null) | ||
function mousedowned() { | ||
function started() { | ||
if (event.touches) { if (event.changedTouches.length < event.touches.length) return noevent(); } | ||
else if (touchending) return; | ||
if (!filter.apply(this, arguments)) return; | ||
var that = this, | ||
type = event.target.__data__.type, | ||
mode = (event.metaKey ? type = "background" : type) === "selection" ? MODE_DRAG : (event.altKey ? MODE_CENTER : MODE_RESIZE), | ||
mode = (event.metaKey ? type = "overlay" : type) === "selection" ? MODE_DRAG : (event.altKey ? MODE_CENTER : MODE_HANDLE), | ||
signX = dim === Y ? null : signsX[type], | ||
@@ -300,2 +321,3 @@ signY = dim === X ? null : signsY[type], | ||
dx, dy, | ||
moving, | ||
point0 = mouse(that), | ||
@@ -305,12 +327,6 @@ point, | ||
if (type === "background") { | ||
if (type === "overlay") { | ||
state.selection = selection = [ | ||
[ | ||
w0 = dim === Y ? W : point0[0], | ||
n0 = dim === X ? N : point0[1] | ||
], | ||
[ | ||
e0 = dim === Y ? E : w0, | ||
s0 = dim === X ? S : n0 | ||
] | ||
[w0 = dim === Y ? W : point0[0], n0 = dim === X ? N : point0[1]], | ||
[e0 = dim === Y ? E : w0, s0 = dim === X ? S : n0] | ||
]; | ||
@@ -329,21 +345,31 @@ } else { | ||
var view = select(event.view) | ||
.on("keydown.brush", keydowned, true) | ||
.on("keyup.brush", keyupped, true) | ||
.on("mousemove.brush", mousemoved, true) | ||
.on("mouseup.brush", mouseupped, true); | ||
var group = select(that) | ||
.attr("pointer-events", "none"); | ||
var background = group.selectAll(".background") | ||
var overlay = group.selectAll(".overlay") | ||
.attr("cursor", cursors[type]); | ||
if (event.touches) { | ||
group | ||
.on("touchmove.brush", moved, true) | ||
.on("touchend.brush touchcancel.brush", ended, true); | ||
} else { | ||
var view = select(event.view) | ||
.on("keydown.brush", keydowned, true) | ||
.on("keyup.brush", keyupped, true) | ||
.on("mousemove.brush", moved, true) | ||
.on("mouseup.brush", ended, true); | ||
dragDisable(event.view); | ||
} | ||
nopropagation(); | ||
interrupt(that); | ||
dragDisable(event.view); | ||
redraw.call(that); | ||
emit.start(); | ||
function mousemoved() { | ||
function moved() { | ||
point = mouse(that); | ||
moving = true; | ||
noevent(); | ||
move(); | ||
@@ -365,3 +391,3 @@ } | ||
} | ||
case MODE_RESIZE: { | ||
case MODE_HANDLE: { | ||
if (signX < 0) dx = Math.max(W - w0, Math.min(E - w0, dx)), w1 = w0 + dx, e1 = e0; | ||
@@ -384,3 +410,3 @@ else if (signX > 0) dx = Math.max(W - e0, Math.min(E - e0, dx)), w1 = w0, e1 = e0 + dx; | ||
t = w1, w1 = e1, e1 = t; | ||
if (type in flipX) background.attr("cursor", cursors[type = flipX[type]]); | ||
if (type in flipX) overlay.attr("cursor", cursors[type = flipX[type]]); | ||
} | ||
@@ -392,3 +418,3 @@ | ||
t = n1, n1 = s1, s1 = t; | ||
if (type in flipY) background.attr("cursor", cursors[type = flipY[type]]); | ||
if (type in flipY) overlay.attr("cursor", cursors[type = flipY[type]]); | ||
} | ||
@@ -409,8 +435,16 @@ | ||
function mouseupped() { | ||
dragEnable(event.view); | ||
function ended() { | ||
nopropagation(); | ||
if (event.touches) { | ||
if (event.touches.length) return; | ||
if (touchending) clearTimeout(touchending); | ||
touchending = setTimeout(function() { touchending = null; }, 500); // Ghost clicks are delayed! | ||
group.on("touchmove.brush touchend.brush touchcancel.brush", null); | ||
} else { | ||
dragEnable(event.view, moving); | ||
view.on("keydown.brush keyup.brush mousemove.brush mouseup.brush", null); | ||
} | ||
group.attr("pointer-events", "all"); | ||
background.attr("cursor", cursors.background); | ||
view.on("keydown.brush keyup.brush mousemove.brush mouseup.brush", null); | ||
if (w1 === e1 || n1 === s1) state.selection = null, redraw.call(that); | ||
overlay.attr("cursor", cursors.overlay); | ||
if (empty(selection)) state.selection = null, redraw.call(that); | ||
emit.end(); | ||
@@ -422,3 +456,3 @@ } | ||
case 18: { // ALT | ||
if (mode === MODE_RESIZE) { | ||
if (mode === MODE_HANDLE) { | ||
if (signX) e0 = e1 - dx * signX, w0 = w1 + dx * signX; | ||
@@ -432,7 +466,7 @@ if (signY) s0 = s1 - dy * signY, n0 = n1 + dy * signY; | ||
case 32: { // SPACE; takes priority over ALT | ||
if (mode === MODE_RESIZE || mode === MODE_CENTER) { | ||
if (mode === MODE_HANDLE || mode === MODE_CENTER) { | ||
if (signX < 0) e0 = e1 - dx; else if (signX > 0) w0 = w1 - dx; | ||
if (signY < 0) s0 = s1 - dy; else if (signY > 0) n0 = n1 - dy; | ||
mode = MODE_SPACE; | ||
background.attr("cursor", cursors.selection); | ||
overlay.attr("cursor", cursors.selection); | ||
move(); | ||
@@ -442,9 +476,5 @@ } | ||
} | ||
case 16: { // SHIFT | ||
break; | ||
} | ||
default: return; | ||
} | ||
event.preventDefault(); | ||
event.stopImmediatePropagation(); | ||
noevent(); | ||
} | ||
@@ -458,3 +488,3 @@ | ||
if (signY < 0) s0 = s1; else if (signY > 0) n0 = n1; | ||
mode = MODE_RESIZE; | ||
mode = MODE_HANDLE; | ||
move(); | ||
@@ -473,5 +503,5 @@ } | ||
if (signY < 0) s0 = s1; else if (signY > 0) n0 = n1; | ||
mode = MODE_RESIZE; | ||
mode = MODE_HANDLE; | ||
} | ||
background.attr("cursor", cursors[type]); | ||
overlay.attr("cursor", cursors[type]); | ||
move(); | ||
@@ -481,9 +511,5 @@ } | ||
} | ||
case 16: { // SHIFT | ||
break; | ||
} | ||
default: return; | ||
} | ||
event.preventDefault(); | ||
event.stopImmediatePropagation(); | ||
noevent(); | ||
} | ||
@@ -495,2 +521,3 @@ } | ||
state.extent = extent.apply(this, arguments); | ||
state.dim = dim; | ||
return state; | ||
@@ -503,2 +530,10 @@ } | ||
brush.filter = function(_) { | ||
return arguments.length ? (filter = typeof _ === "function" ? _ : constant(!!_), brush) : filter; | ||
}; | ||
brush.handleSize = function(_) { | ||
return arguments.length ? (handleSize = +_, brush) : handleSize; | ||
}; | ||
brush.on = function() { | ||
@@ -505,0 +540,0 @@ var value = listeners.on.apply(listeners, arguments); |
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
Major refactor
Supply chain riskPackage has recently undergone a major refactor. It may be unstable or indicate significant internal changes. Use caution when updating to versions that include significant changes.
Found 1 instance in 1 package
New author
Supply chain riskA new npm collaborator published a version of the package for the first time. New collaborators are usually benign additions to a project, but do indicate a change to the security surface area of a package.
Found 1 instance in 1 package
173497
14
964
138
0