diff --git a/platform/features/layout/res/templates/fixed.html b/platform/features/layout/res/templates/fixed.html index 502b5e2579..dbe03ef22a 100644 --- a/platform/features/layout/res/templates/fixed.html +++ b/platform/features/layout/res/templates/fixed.html @@ -24,13 +24,15 @@
diff --git a/platform/features/layout/src/FixedDragHandle.js b/platform/features/layout/src/FixedDragHandle.js index d01af84223..a57e6e48d3 100644 --- a/platform/features/layout/src/FixedDragHandle.js +++ b/platform/features/layout/src/FixedDragHandle.js @@ -15,8 +15,10 @@ define( * @constructor */ function FixedDragHandle(elementHandle, gridSize, commit) { - var self = {}; + var self = {}, + dragging; + // Generate ng-style-appropriate style for positioning function getStyle() { // Adjust from grid to pixel coordinates var x = elementHandle.x() * gridSize[0], @@ -25,14 +27,39 @@ define( // Convert to a CSS style centered on that point return { left: (x - DRAG_HANDLE_SIZE[0] / 2) + 'px', - right: (x - DRAG_HANDLE_SIZE[1] / 2) + 'px', + top: (y - DRAG_HANDLE_SIZE[1] / 2) + 'px', width: DRAG_HANDLE_SIZE[0] + 'px', height: DRAG_HANDLE_SIZE[1] + 'px' }; } - function noop() { + // Begin a drag gesture + function startDrag() { + // Cache initial x/y positions + dragging = { x: elementHandle.x(), y: elementHandle.y() }; + } + // Reposition during drag + function continueDrag(delta) { + if (dragging) { + // Update x/y positions (snapping to grid) + elementHandle.x( + dragging.x + Math.round(delta[0] / gridSize[0]) + ); + elementHandle.y( + dragging.y + Math.round(delta[1] / gridSize[1]) + ); + } + } + + // Conclude a drag gesture + function endDrag() { + // Clear cached state + dragging = undefined; + // Mark change as complete + if (commit) { + commit("Dragged handle."); + } } return { @@ -41,9 +68,22 @@ define( * @returns CSS style object (for `ng-style`) */ style: getStyle, - startDrag: noop, - continueDrag: noop, - endDrag: noop + /** + * Start a drag gesture. This should be called when a drag + * begins to track initial state. + */ + startDrag: startDrag, + /** + * Continue a drag gesture; update x/y positions. + * @param {number[]} delta x/y pixel difference since drag + * started + */ + continueDrag: continueDrag, + /** + * End a drag gesture. This should be callled when a drag + * concludes to trigger commit of changes. + */ + endDrag: endDrag }; } diff --git a/platform/features/layout/test/FixedDragHandleSpec.js b/platform/features/layout/test/FixedDragHandleSpec.js new file mode 100644 index 0000000000..ae0b8a42d6 --- /dev/null +++ b/platform/features/layout/test/FixedDragHandleSpec.js @@ -0,0 +1,62 @@ +/*global define,describe,it,expect,beforeEach,jasmine,xit*/ + +define( + ['../src/FixedDragHandle'], + function (FixedDragHandle) { + "use strict"; + + var TEST_GRID_SIZE = [ 13, 33 ]; + + describe("A fixed position drag handle", function () { + var mockElementHandle, + mockCommit, + handle; + + beforeEach(function () { + mockElementHandle = jasmine.createSpyObj( + 'elementHandle', + [ 'x', 'y' ] + ); + mockCommit = jasmine.createSpy('commit'); + + mockElementHandle.x.andReturn(6); + mockElementHandle.y.andReturn(8); + + handle = new FixedDragHandle( + mockElementHandle, + TEST_GRID_SIZE, + mockCommit + ); + }); + + it("provides a style for positioning", function () { + var style = handle.style(); + // 6 grid coords * 13 pixels - 4 pixels for centering + expect(style.left).toEqual('74px'); + // 8 grid coords * 33 pixels - 4 pixels for centering + expect(style.top).toEqual('260px'); + }); + + it("allows handles to be dragged", function () { + handle.startDrag(); + handle.continueDrag([ 16, 8 ]); + + // Should update x/y, snapped to grid + expect(mockElementHandle.x).toHaveBeenCalledWith(7); + expect(mockElementHandle.y).toHaveBeenCalledWith(8); + + handle.continueDrag([ -16, -35 ]); + + // Should have interpreted relative to initial state + expect(mockElementHandle.x).toHaveBeenCalledWith(5); + expect(mockElementHandle.y).toHaveBeenCalledWith(7); + + // Finally, ending drag should commit + expect(mockCommit).not.toHaveBeenCalled(); + handle.endDrag(); + expect(mockCommit).toHaveBeenCalled(); + }); + + }); + } +); \ No newline at end of file diff --git a/platform/features/layout/test/elements/ResizeHandleSpec.js b/platform/features/layout/test/elements/ResizeHandleSpec.js new file mode 100644 index 0000000000..49168aa5cd --- /dev/null +++ b/platform/features/layout/test/elements/ResizeHandleSpec.js @@ -0,0 +1,59 @@ +/*global define,describe,it,expect,beforeEach,jasmine,xit*/ + +define( + ['../../src/elements/ResizeHandle'], + function (ResizeHandle) { + "use strict"; + + var TEST_MIN_WIDTH = 4, TEST_MIN_HEIGHT = 2; + + describe("A fixed position drag handle", function () { + var testElement, + handle; + + beforeEach(function () { + testElement = { + x: 3, + y: 42, + width: 30, + height: 36 + }; + + handle = new ResizeHandle( + testElement, + TEST_MIN_WIDTH, + TEST_MIN_HEIGHT + ); + }); + + it("provides x/y grid coordinates for lower-right corner", function () { + expect(handle.x()).toEqual(33); + expect(handle.y()).toEqual(78); + }); + + it("changes width of an element", function () { + handle.x(30); + // Should change width, not x + expect(testElement.x).toEqual(3); + expect(testElement.width).toEqual(27); + }); + + it("changes height of an element", function () { + handle.y(60); + // Should change height, not y + expect(testElement.y).toEqual(42); + expect(testElement.height).toEqual(18); + }); + + it("enforces minimum width/height", function () { + handle.x(testElement.x); + handle.y(testElement.y); + expect(testElement.x).toEqual(3); + expect(testElement.y).toEqual(42); + expect(testElement.width).toEqual(TEST_MIN_WIDTH); + expect(testElement.height).toEqual(TEST_MIN_HEIGHT); + }); + + }); + } +); \ No newline at end of file diff --git a/platform/features/layout/test/suite.json b/platform/features/layout/test/suite.json index 8f0eec06c3..684c8fda12 100644 --- a/platform/features/layout/test/suite.json +++ b/platform/features/layout/test/suite.json @@ -1,5 +1,6 @@ [ "FixedController", + "FixedDragHandle", "FixedProxy", "LayoutController", "LayoutDrag", @@ -10,6 +11,7 @@ "elements/ElementProxies", "elements/ElementProxy", "elements/LineProxy", + "elements/ResizeHandle", "elements/TelemetryProxy", "elements/TextProxy" ] \ No newline at end of file