Last active
October 6, 2016 21:55
-
-
Save seungjoolee/52bcb30305c0dcd08998cbfe9f21b545 to your computer and use it in GitHub Desktop.
drag drop
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import Ember from 'ember'; | |
import { computed } from 'ember'; | |
import { extend } from '../utils/keys'; | |
const DATA_TRANSFER_TYPE = 'text'; | |
/** | |
* Hello! I'm a basic wrapper component for HTML5 drag and drop behavior | |
*/ | |
export default Ember.Component.extend({ | |
// passed in attributes | |
isDraggable: false, // use this flag to turn on/off dragging behavior | |
isDroppable: false, // use this flag to enable/disable this element as a drop target | |
data: '', // a string to pass along with the element | |
dragHandleSelector: null, // (optional) a CSS selector of the part of this element from | |
// where a drag can be initiated | |
dragEffectAllowed: 'all', // HTML5 drag property that tells the drop area which kinds of actions | |
// are supported by this draggable element when it is dropped | |
dropEffect: 'move', // HTML5 drop property that indicates what kind of action will occur | |
// when an element is dropped on this element | |
// this component maintains this state | |
isHovered: false, // can't use :hover because of a webkit but with :hover being overly persistent | |
// with drag and drop: http://stackoverflow.com/questions/17946886/hover-sticks-to-element-on-drag-and-drop | |
isDragging: false, | |
isDraggedOver: false, | |
dragTarget: null, // the target element of the drag (which part of the element the mouse is on) | |
$dragGhost: null, // the JQuery element that we are moving around with the drag | |
// we do this because HTML5 drag and drop doesn't do a good job with that | |
classNames: ['sq-drag-drop'], | |
classNameBindings: [ | |
'isHovered:sq-drag-drop--hovered', | |
'isDraggable:sq-drag-drop--draggable', | |
'isDragging:sq-drag-drop--dragging', | |
'isDroppable:sq-drag-drop--droppable', | |
'isDraggedOver:sq-drag-drop--dragged-over' | |
], | |
attributeBindings: ['draggable'], | |
draggable: Ember.computed.readOnly('isDraggable'), | |
/* BEGIN REGULAR MOUSE EVENTS *******************/ | |
mouseEnter() { | |
this.set('isHovered', true); | |
}, | |
mouseLeave() { | |
this.set('isHovered', false); | |
}, | |
mouseDown(evt) { | |
this.set('dragTarget', evt.target); | |
}, | |
mouseUp() { | |
this.set('dragTarget', null); | |
}, | |
/* BEGIN DRAGGABLE EVENTS *******************/ | |
dragStart(evt) { | |
if (!this._shouldAllowDragStart()) { | |
return false; | |
} | |
const eventData = this._eventData(evt, { | |
dragData: this.get('data') | |
}); | |
const $dragGhost = this._createDragGhost(eventData); | |
this.set('isDragging', true); | |
evt.dataTransfer.setData(DATA_TRANSFER_TYPE, this.get('data')); | |
evt.dataTransfer.effectAllowed = this.get('dragEffectAllowed'); | |
evt.dataTransfer.setDragImage( | |
$dragGhost[0], | |
eventData.offsetX, | |
eventData.offsetY | |
); | |
this.sendAction('onDragStart', eventData); | |
return true; | |
}, | |
drag(evt) { | |
const eventData = this._eventData(evt, { | |
dragData: this.get('data') | |
}); | |
this.sendAction('onDrag', eventData); | |
}, | |
dragEnd(evt) { | |
this._clearDragGhost(); | |
this.set('isDragging', false); | |
this.sendAction('onDragEnd', this._eventData(evt, { | |
dragData: this.get('data') | |
})); | |
}, | |
/* BEGIN DROP TARGET EVENTS *******************/ | |
dragEnter(evt) { | |
this.set('isDraggedOver', true); | |
evt.dataTransfer.dropEffect = this.get('dropEffect'); | |
this.sendAction('onDragEnter', this._eventData(evt, { | |
// unfortunately HTML5 dnd doesn't let you know what is being dragged over this | |
dropData: this.get('data') | |
})); | |
evt.preventDefault(); | |
}, | |
dragOver(evt) { | |
this.sendAction('onDragOver', this._eventData(evt, { | |
// unfortunately HTML5 dnd doesn't let you know what is being dragged over this | |
dropData: this.get('data') | |
})); | |
evt.preventDefault(); | |
}, | |
dragLeave(evt) { | |
this.set('isDraggedOver', false); | |
this.sendAction('onDragLeave', this._eventData(evt, { | |
// unfortunately HTML5 dnd doesn't let you know what is being dragged over this | |
dropData: this.get('data') | |
})); | |
}, | |
drop(evt) { | |
this.set('isDraggedOver', false); | |
this.sendAction('onDrop', this._eventData(evt, { | |
dragData: evt.dataTransfer.getData(DATA_TRANSFER_TYPE), | |
dropData: this.get('data') | |
})); | |
evt.preventDefault(); | |
}, | |
/* BEGIN HELPERS *******************/ | |
_shouldAllowDragStart() { | |
const dragHandleSelector = this.get('dragHandleSelector'); | |
if (!dragHandleSelector) { | |
return true; // we don't care where the drag was initiated | |
} | |
// make sure the drag was initiated by the specified drag handle | |
const dragTarget = this.get('dragTarget'); | |
return Ember.$(dragTarget).is(dragHandleSelector); | |
}, | |
_createDragGhost() { | |
// notes: find a way to hide cloned div under empty spot | |
// dragging css not working | |
const $this = this.$('.sq-drag-drop__drop-content'); | |
const $dragGhost = $this.clone() | |
// .addClass('sq-drag-drop--dragging') // todo: figure out why classNameBindings isn't doing this for us | |
.css({ | |
// give the clone the right dimensions, and move it off screen | |
// we can't use display: none or anything because then HTML5 dnd doesn't | |
// draw the correct preview, so we use this hack to make it "invisible" | |
// See http://www.kryogenix.org/code/browser/custom-drag-image.html | |
position: 'absolute', | |
top: '0', | |
left: '0', | |
width: `${$this.width()}px`, | |
height: `${$this.height()}px`, | |
'z-index': -1 | |
}); | |
Ember.$('body').append($dragGhost); | |
this.set('$dragGhost', $dragGhost); | |
return $dragGhost; | |
}, | |
_clearDragGhost() { | |
const $dragGhost = this.get('$dragGhost'); | |
if ($dragGhost && $dragGhost.length > 0) { | |
$dragGhost.remove(); | |
} | |
this.set('$dragGhost', null); | |
}, | |
_eventData(evt, extraParams) { | |
const params = (evt && evt.originalEvent) || evt; | |
return extend(true, {}, { | |
// mouse position relative to page | |
pageX: params.pageX, | |
pageY: params.pageY, | |
// mouse position relative to target element | |
// (which is the dragged element for drag events, | |
// and the drop target element for drop events) | |
offsetX: params.offsetX, | |
offsetY: params.offsetY | |
}, extraParams); | |
} | |
}); |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import Ember from 'ember'; | |
export default Ember.Controller.extend({ | |
appName: 'Ember Twiddle', | |
actions: { | |
onWidgetDragStart() { | |
}, | |
onWidgetDragEnd() { | |
console.log('drag end') | |
}, | |
onWidgetDragOver() { | |
console.log('drag over') | |
}, | |
} | |
}); |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
body { | |
margin: 12px 16px; | |
font-family: 'Helvetica Neue', Helvetica, Arial, sans-serif; | |
font-size: 12pt; | |
} | |
.a { | |
height: 200px; | |
width: 200px; | |
background-color: teal; | |
} | |
.l-pulse-fluid { | |
position: relative; | |
} | |
/** | |
.sq-drag-drop__drop-area { | |
height: 100%; | |
display: none; | |
} | |
.sq-drag-drop--dragging { | |
cursor: grabbing; | |
cursor: -moz-grabbing; | |
cursor: -webkit-grabbing; | |
} | |
.sq-drag-drop--dragging .a { | |
display: none; | |
} | |
.sq-drag-drop--dragging .sq-drag-drop__drop-area { | |
display: block; | |
border: dashed 2px $color-light; | |
} | |
**/ |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
{ | |
"version": "0.10.5", | |
"EmberENV": { | |
"FEATURES": {} | |
}, | |
"options": { | |
"use_pods": false, | |
"enable-testing": false | |
}, | |
"dependencies": { | |
"jquery": "https://cdnjs.cloudflare.com/ajax/libs/jquery/1.11.3/jquery.js", | |
"ember": "2.8.0", | |
"ember-data": "2.8.0", | |
"ember-template-compiler": "2.8.0", | |
"ember-testing": "2.8.0" | |
}, | |
"addons": {} | |
} |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import Ember from 'ember'; | |
export function transformData(data, transform) { | |
switch (Ember.typeOf(data)) { | |
case 'array': | |
return data.map(item => transformData(item, transform)); | |
case 'object': { | |
const out = {}; | |
forOwnProperty(data, (key, value) => { | |
out[transform(key)] = transformData(value, transform); | |
}); | |
return out; | |
} | |
default: | |
return data; | |
} | |
} | |
export function camelize(data) { | |
return transformData(data, key => key.camelize()); | |
} | |
export function underscore(data) { | |
return transformData(data, key => { | |
if (key.match(/[A-Z].*\d+$/)) { | |
// Multiple words followed by a number | |
return key.replace(/([a-z])(\d+)/g, '$1_$2').underscore(); | |
} else { | |
return key.underscore(); | |
} | |
}); | |
} | |
export function extend(deep, target, ...sources) { | |
if (deep !== true && deep !== false) { | |
return extend(false, deep, target, ...sources); | |
} | |
for (let i = 0; i < sources.length; i++) { | |
const source = sources[i]; | |
if (source) { | |
forOwnProperty(source, (key, value) => { | |
if (deep && Ember.typeOf(value) === 'object') { | |
target[key] = extend(true, {}, target[key], value); | |
} else if (deep && Ember.typeOf(value) === 'array') { | |
target[key] = value.map(item => extend(true, {}, { item }).item); | |
} else { | |
target[key] = value; | |
} | |
}); | |
} | |
} | |
return target; | |
} | |
export function checker(...validKeys) { | |
if (validKeys.length === 1 && Ember.isArray(validKeys[0])) { | |
validKeys = validKeys[0]; | |
} | |
return { | |
check(object) { | |
forOwnProperty(object, key => { | |
if (validKeys.indexOf(key) === -1) { | |
throw new Error(`unexpected key: ${key}`); | |
} | |
}); | |
} | |
}; | |
} | |
function forOwnProperty(object, iterator) { | |
for (const key in object) { | |
if (Object.prototype.hasOwnProperty.call(object, key)) { | |
iterator(key, object[key]); | |
} | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment