@eflexsystems/ember-drag-drop
v2.0.0
Published
Addon for Ember CLI to do drag and drop
Downloads
48
Keywords
Readme
Ember Drag Drop
Simple drag and drop addon for your Ember CLI app.
The goal is to allow you to add drag and drop to your app without having to become an expert in the browser's low level D&D API.
To use this addon, you don't need to:
- Know anything about how the browser implements drag and drop.
- Ever deal with a browser drag and drop event, or even know that they exist.
When using this addon, you get to work with objects in your domain layer, just like everywhere else in Ember. The only two things you need to use are (as you might expect) Draggable Object and Draggable Object Target
Requirements
- As of version 0.9 and up it works with Ember 3.12 and higher.
- Use 0.8.2 if you need to support a Ember 2.X or version less than Ember 3.12
Installation
ember install ember-drag-drop
Thanks
Huge thanks to ic-droppable, from which I shamelessly stole as promised.
Usage
Primitives
Examples
Mobile and touch events
As of version 0.4.4 you can install the ember-drag-drop-polyfill to enable drag and drop actions on mobile devices. It is my intention to make mobile a first class citizen in this addon, but hopefully this can fill the gaps for now.
Primitives
Draggable Object
The draggable-object
component represents an object you want to drag onto a target.
The two things to provide to the component are:
- The content - Represents the object to be dragged. Will be passed to the target after a completed drag.
- The template code to render for the draggable object
<DraggableObject @content={{this}}>
{{name}}
</DraggableObject>
// represents the controller backing the above template
Ember.Controller.extend({
// your regular controller code
actions: {
myStartAction: function(content) {
//Content is the same as the content parameter set above
},
myEndAction: function(content) {
//Content is the same as the content parameter set above
},
}
}
});
Draggable Object Target
The draggable-object-target
represents a place to drag objects. This will trigger an action which accepts the dragged object as an argument.
The two things to provide to the component are:
- The action - Represents the action to be called with the dragged object.
- The template code to render for the target.
The action is called with two arguments:
- The dragged object.
- An options hash. Currently the only key is
target
, which is the draggable-object-target component.
... your regular template code
<DraggableObjectTarget @action={{fn this.increaseRating}} amount={{"5"}}>
Drag here to increase rating
</DraggableObjectTarget>
Optionally you can also get an action fired when an object is being dragged over and out of the drop target. No parameter is currently sent with these actions.
<DraggableObjectTarget @action={{fn this.increaseRating}} @amount={{"5"}} @dragOverAction={{fn this.myOverAction}} @onDragOut={{this.myDragOutAction}}>
Drag here to increase rating
</DraggableObjectTarget>
// represents the controller backing the above template
Ember.Controller.extend({
// your regular controller code
actions: {
increaseRating: function(obj,ops) {
const amount = parseInt(ops.target.amount);
obj.incrementProperty("rating",amount);
obj.save();
},
myOverAction: function() {
//will notify you when an object is being dragged over the drop target
},
onDragOut: function() {
//will notify you when an object has left the drop target area
},
}
}
});
You can check out an example of this is action here
Sorting of objects
We now have a basic sorting capabilities in this library. If you wrap the <SortableObjects>
component around your <DraggableObject>
components you can get an array of sorted elements returned.
**Important Note on Ember Versions: If you use Ember version 1.13.2 and above you must user at least addon version 0.3 if you use sorting If you use Ember version 1.12.1 and below you must use 0.2.3 if you use sorting This only applies if you use the sort capabilities, regular dragging is not version specific.
An Example:
<SortableObjects @sortableObjectList={{this.sortableObjectList}} @onSortEnd={{fn this.onSortEnd}} @useSwap={{true}} @sortingScope={{"sortingGroup"}}>
{{#each sortableObjectList as |item|}}
<DraggableObject content=item sortingScope="sortingGroup">
{{item.name}}
</DraggableObject>
{{/each}}
</SortableObjects>
On drop of an item in the list, the sortableObjectList is re-ordered and onSortEnd is fired unless the optional parameter 'enableSort' is false. You can check out an example of this is action here
useSwap
defaults to true and is optional. If you set it to false, then the sort algorithm will cascade the swap of items, pushing the values down the list. See Demo
sortingScope
is optional and only needed if you have multiple lists on the screen that you want to share dragging between. See Demo
Test Helpers
When writing tests, there is a drag
helper you can use to help facilitate dragging and dropping.
drag helper
As of v0.4.5 you can use this helper in integration tests without booting up the entire application.
- Is an async aware helper ( use await to wait for drop to finish )
Can be used to test sortable elements as well as plain draggable
Has one argument
- the drag start selector
- Example:
.draggable-object.drag-handle
And many options:
- dragStartOptions
- options for the drag-start event
- can be used to set a cursor position for the drag start event
- Example:
{ pageX: 0, pageY: 0 }
- dragOverMoves
- array of moves used to simulate dragging over.
- it's an array of [position, selector] arrays where the selector is optional and will use the 'drop' selector ( from drop options ) as default
- Example:
[ [{ clientX: 1, clientY: 500 }, '.drag-move-div'], [{ clientX: 1, clientY: 600 }, '.drag-move-div'] ] or [ [{ clientX: 1, clientY: 500 }], // moves drop selector [{ clientX: 1, clientY: 600 }] // moves drop selector ]
- dropEndOptions
- options for the drag-end event
- can be used to set a cursor position for the drag end event
- Example:
{ pageX: 0, pageY: 0 }
- afterDrag
- a function to call after dragging actions are complete
- gives you a chance to inspect state after dragging
- Example:
afterDrag() { // check on state of things }
- beforeDrop
- a function to call before drop action is called
- gives you a chance to inspect state before dropping
- Example:
beforeDrop() { // check on state of things }
- drop
- selector for the element to drop onto
- Example:
.drop-target-div
- dragStartOptions
You import it like this:
// new async helper
import { drag } from 'your-app/tests/helpers/drag-drop';
You can pass the CSS selector for the draggable-object-target
and pass a beforeDrop
callback.
Async test Example:
test('drag stuff', async function(assert) {
// setup component
await drag('.draggable-object.drag-handle', {
drop: '.draggable-container .draggable-object-target:nth-child(1)'
});
assert.equal("things happened", true);
});
In this example,
- we're dragging the draggable-object element with CSS selector
.draggable-object.drag-handle
- and dropping on a draggable-object-target with the CSS selector
draggable-object-target:eq(1)
.
For a fuller example check out this integration test
Note #1 In order to use async / await style tests you need to tell ember-cli-babel to include a polyfill in ember-cli-build.js
Note #2 You don't have to use the new async/await helper. You can simply keep using the older drag helper ( which makes your tests far slower because you have to start the application for each test. ) This older helper only has one option ( beforeDrop )
// old drag helper
import { drag } from 'your-app/tests/helpers/ember-drag-drop';