diff --git a/quicktests/quicktest-selection.html b/quicktests/quicktest-selection.html new file mode 100644 index 0000000000..a32845393c --- /dev/null +++ b/quicktests/quicktest-selection.html @@ -0,0 +1,79 @@ + + + Selection Quicktest + + + + + + + + + + + + + diff --git a/src/interactions/areaInteraction.ts b/src/interactions/areaInteraction.ts deleted file mode 100644 index 97440fa994..0000000000 --- a/src/interactions/areaInteraction.ts +++ /dev/null @@ -1,100 +0,0 @@ -/// - -module Plottable { - export class AreaInteraction extends Interaction { - private static CLASS_DRAG_BOX = "drag-box"; - private dragInitialized = false; - private dragBehavior: D3.Behavior.Drag; - private origin = [0,0]; - private location = [0,0]; - private constrainX: (n: number) => number; - private constrainY: (n: number) => number; - private dragBox: D3.Selection; - private callbackToCall: (area: SelectionArea) => any; - - /** - * Creates an AreaInteraction. - * - * @param {Component} componentToListenTo The component to listen for interactions on. - */ - constructor(componentToListenTo: Component) { - super(componentToListenTo); - this.dragBehavior = d3.behavior.drag(); - this.dragBehavior.on("dragstart", () => this.dragstart()); - this.dragBehavior.on("drag", () => this.drag ()); - this.dragBehavior.on("dragend", () => this.dragend ()); - } - - /** - * Adds a callback to be called when the AreaInteraction triggers. - * - * @param {(a: SelectionArea) => any} cb The function to be called. Takes in a SelectionArea in pixels. - * @returns {AreaInteraction} The calling AreaInteraction. - */ - public callback(cb?: (a: SelectionArea) => any): AreaInteraction { - this.callbackToCall = cb; - return this; - } - - private dragstart(){ - this.clearBox(); - var availableWidth = parseFloat(this.hitBox.attr("width")); - var availableHeight = parseFloat(this.hitBox.attr("height")); - // the constraint functions ensure that the selection rectangle will not exceed the hit box - var constraintFunction = (min: number, max: number) => (x: number) => Math.min(Math.max(x, min), max); - this.constrainX = constraintFunction(0, availableWidth); - this.constrainY = constraintFunction(0, availableHeight); - } - - private drag(){ - if (!this.dragInitialized) { - this.origin = [d3.event.x, d3.event.y]; - this.dragInitialized = true; - } - - this.location = [this.constrainX(d3.event.x), this.constrainY(d3.event.y)]; - var width = Math.abs(this.origin[0] - this.location[0]); - var height = Math.abs(this.origin[1] - this.location[1]); - var x = Math.min(this.origin[0], this.location[0]); - var y = Math.min(this.origin[1], this.location[1]); - this.dragBox.attr("x", x).attr("y", y).attr("height", height).attr("width", width); - } - - private dragend(){ - if (!this.dragInitialized) { - return; - } - - this.dragInitialized = false; - if (this.callbackToCall == null) { - return; - } - - var xMin = Math.min(this.origin[0], this.location[0]); - var xMax = Math.max(this.origin[0], this.location[0]); - var yMin = Math.min(this.origin[1], this.location[1]); - var yMax = Math.max(this.origin[1], this.location[1]); - var pixelArea = {xMin: xMin, xMax: xMax, yMin: yMin, yMax: yMax}; - this.callbackToCall(pixelArea); - } - - /** - * Clears the highlighted drag-selection box drawn by the AreaInteraction. - * - * @returns {AreaInteraction} The calling AreaInteraction. - */ - public clearBox(): AreaInteraction { - this.dragBox.attr("height", 0).attr("width", 0); - return this; - } - - public _anchor(hitBox: D3.Selection): AreaInteraction { - super._anchor(hitBox); - var cname = AreaInteraction.CLASS_DRAG_BOX; - var background = this.componentToListenTo.backgroundContainer; - this.dragBox = background.append("rect").classed(cname, true).attr("x", 0).attr("y", 0); - hitBox.call(this.dragBehavior); - return this; - } - } -} diff --git a/src/interactions/drag/dragBoxInteraction.ts b/src/interactions/drag/dragBoxInteraction.ts new file mode 100644 index 0000000000..3b4430a858 --- /dev/null +++ b/src/interactions/drag/dragBoxInteraction.ts @@ -0,0 +1,31 @@ +/// + +module Plottable { + export class DragBoxInteraction extends DragInteraction { + private static CLASS_DRAG_BOX = "drag-box"; + public dragBox: D3.Selection; + + public _dragstart() { + super._dragstart(); + this.clearBox(); + } + + /** + * Clears the highlighted drag-selection box drawn by the AreaInteraction. + * + * @returns {AreaInteraction} The calling AreaInteraction. + */ + public clearBox() { + this.dragBox.attr("height", 0).attr("width", 0); + return this; + } + + public _anchor(hitBox: D3.Selection) { + super._anchor(hitBox); + var cname = DragBoxInteraction.CLASS_DRAG_BOX; + var background = this.componentToListenTo.backgroundContainer; + this.dragBox = background.append("rect").classed(cname, true).attr("x", 0).attr("y", 0); + return this; + } + } +} diff --git a/src/interactions/drag/dragInteraction.ts b/src/interactions/drag/dragInteraction.ts new file mode 100644 index 0000000000..3f56e9e1f9 --- /dev/null +++ b/src/interactions/drag/dragInteraction.ts @@ -0,0 +1,77 @@ +/// + +module Plottable { + export class DragInteraction extends Interaction { + private dragInitialized = false; + private dragBehavior: D3.Behavior.Drag; + public origin = [0,0]; + public location = [0,0]; + private constrainX: (n: number) => number; + private constrainY: (n: number) => number; + public callbackToCall: (dragInfo: any) => any; + + /** + * Creates a DragInteraction. + * + * @param {Component} componentToListenTo The component to listen for interactions on. + */ + constructor(componentToListenTo: Component) { + super(componentToListenTo); + this.dragBehavior = d3.behavior.drag(); + this.dragBehavior.on("dragstart", () => this._dragstart()); + this.dragBehavior.on("drag", () => this._drag ()); + this.dragBehavior.on("dragend", () => this._dragend ()); + } + + /** + * Adds a callback to be called when the AreaInteraction triggers. + * + * @param {(a: SelectionArea) => any} cb The function to be called. Takes in a SelectionArea in pixels. + * @returns {AreaInteraction} The calling AreaInteraction. + */ + public callback(cb?: (a: any) => any) { + this.callbackToCall = cb; + return this; + } + + public _dragstart(){ + var availableWidth = this.componentToListenTo.availableWidth; + var availableHeight = this.componentToListenTo.availableHeight; + // the constraint functions ensure that the selection rectangle will not exceed the hit box + var constraintFunction = (min: number, max: number) => (x: number) => Math.min(Math.max(x, min), max); + this.constrainX = constraintFunction(0, availableWidth); + this.constrainY = constraintFunction(0, availableHeight); + } + + public _drag(){ + if (!this.dragInitialized) { + this.origin = [d3.event.x, d3.event.y]; + this.dragInitialized = true; + } + + this.location = [this.constrainX(d3.event.x), this.constrainY(d3.event.y)]; + } + + public _dragend(){ + if (!this.dragInitialized) { + return; + } + this.dragInitialized = false; + this._doDragend(); + } + + public _doDragend() { + // seperated out so it can be over-ridden by dragInteractions that want to pass out diff information + // eg just x values for an xSelectionInteraction + if (this.callbackToCall != null) { + this.callbackToCall([this.origin, this.location]); + } + } + + public _anchor(hitBox: D3.Selection) { + super._anchor(hitBox); + hitBox.call(this.dragBehavior); + return this; + } + } +} diff --git a/src/interactions/drag/xDragBoxInteraction.ts b/src/interactions/drag/xDragBoxInteraction.ts new file mode 100644 index 0000000000..91c6618b0e --- /dev/null +++ b/src/interactions/drag/xDragBoxInteraction.ts @@ -0,0 +1,24 @@ +/// + +module Plottable { + export class XDragBoxInteraction extends DragBoxInteraction { + public _drag(){ + super._drag(); + var width = Math.abs(this.origin[0] - this.location[0]); + var height = this.componentToListenTo.availableHeight; + var x = Math.min(this.origin[0] , this.location[0]); + var y = 0 + this.dragBox.attr({x: x, y: y, height: height, width: width}); + } + + public _doDragend(){ + if (this.callbackToCall == null) { + return; + } + var xMin = Math.min(this.origin[0], this.location[0]); + var xMax = Math.max(this.origin[0], this.location[0]); + var pixelArea = {xMin: xMin, xMax: xMax}; + this.callbackToCall(pixelArea); + } + } +} diff --git a/src/interactions/drag/xyDragBoxInteraction.ts b/src/interactions/drag/xyDragBoxInteraction.ts new file mode 100644 index 0000000000..f39b1d9381 --- /dev/null +++ b/src/interactions/drag/xyDragBoxInteraction.ts @@ -0,0 +1,27 @@ +/// + +module Plottable { + export class XYDragBoxInteraction extends DragBoxInteraction { + public _drag(){ + super._drag(); + var width = Math.abs(this.origin[0] - this.location[0]); + var height = Math.abs(this.origin[1] - this.location[1]); + var x = Math.min(this.origin[0] , this.location[0]); + var y = Math.min(this.origin[1] , this.location[1]); + this.dragBox.attr({x: x, y: y, height: height, width: width}); + } + + public _doDragend(){ + if (this.callbackToCall == null) { + return; + } + + var xMin = Math.min(this.origin[0], this.location[0]); + var xMax = Math.max(this.origin[0], this.location[0]); + var yMin = Math.min(this.origin[1], this.location[1]); + var yMax = Math.max(this.origin[1], this.location[1]); + var pixelArea = {xMin: xMin, xMax: xMax, yMin: yMin, yMax: yMax}; + this.callbackToCall(pixelArea); + } + } +} diff --git a/src/reference.ts b/src/reference.ts index 49675cebee..c0b9c624a6 100644 --- a/src/reference.ts +++ b/src/reference.ts @@ -33,10 +33,13 @@ /// /// -/// /// /// /// /// +/// +/// +/// +/// /// diff --git a/test/interactionTests.ts b/test/interactionTests.ts index e17b55c103..a6d623a62a 100644 --- a/test/interactionTests.ts +++ b/test/interactionTests.ts @@ -20,12 +20,12 @@ function makeFakeEvent(x: number, y: number): D3.Event { } function fakeDragSequence(anyedInteraction: any, startX: number, startY: number, endX: number, endY: number) { - anyedInteraction.dragstart(); + anyedInteraction._dragstart(); d3.event = makeFakeEvent(startX, startY); - anyedInteraction.drag(); + anyedInteraction._drag(); d3.event = makeFakeEvent(endX, endY); - anyedInteraction.drag(); - anyedInteraction.dragend(); + anyedInteraction._drag(); + anyedInteraction._dragend(); d3.event = null; } @@ -78,7 +78,7 @@ describe("Interactions", () => { }); }); - describe("AreaInteraction", () => { + describe("XYDragBoxInteraction", () => { var svgWidth = 400; var svgHeight = 400; var svg: D3.Selection; @@ -86,7 +86,7 @@ describe("Interactions", () => { var xScale: Plottable.QuantitiveScale; var yScale: Plottable.QuantitiveScale; var renderer: Plottable.XYRenderer; - var interaction: Plottable.AreaInteraction; + var interaction: Plottable.XYDragBoxInteraction; var dragstartX = 20; var dragstartY = svgHeight-100; @@ -100,12 +100,13 @@ describe("Interactions", () => { yScale = new Plottable.LinearScale(); renderer = new Plottable.CircleRenderer(dataset, xScale, yScale); renderer.renderTo(svg); - interaction = new Plottable.AreaInteraction(renderer); + interaction = new Plottable.XYDragBoxInteraction(renderer); interaction.registerWithComponent(); }); afterEach(() => { - interaction.callback().clearBox(); + interaction.callback(); + interaction.clearBox(); }); it("All callbacks are notified with appropriate data when a drag finishes", () => { @@ -132,7 +133,7 @@ describe("Interactions", () => { it("Highlights and un-highlights areas appropriately", () => { fakeDragSequence(( interaction), dragstartX, dragstartY, dragendX, dragendY); - var dragBoxClass = "." + ( Plottable.AreaInteraction).CLASS_DRAG_BOX; + var dragBoxClass = "." + ( Plottable.XYDragBoxInteraction).CLASS_DRAG_BOX; var dragBox = renderer.backgroundContainer.select(dragBoxClass); assert.isNotNull(dragBox, "the dragbox was created"); var actualStartPosition = {x: parseFloat(dragBox.attr("x")), y: parseFloat(dragBox.attr("y"))};