-
-
Notifications
You must be signed in to change notification settings - Fork 10
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
Showing
4 changed files
with
365 additions
and
9 deletions.
There are no files selected for viewing
This file contains 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
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,4 +1,3 @@ | ||
/*eslint-env node, mocha */ | ||
/*global expect */ | ||
/*eslint no-console: 0*/ | ||
|
||
|
This file contains 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
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,16 +1,25 @@ | ||
import { appWrapper } from 'helpers/fullRenderHelper' | ||
import 'helpers/jquery.simulate.drag-sortable.js' | ||
|
||
describe('slides > well', () => { | ||
it('should add a slide when clicking top insert slide button', () => { | ||
expect(appWrapper.state().deck.getActiveSlide().components.length).to.equal( | ||
1 | ||
) | ||
expect(appWrapper.state().deck.components.length).to.equal(4) | ||
appWrapper | ||
.find( | ||
'.sp-well-slide-creator > .btn-success' | ||
) | ||
.first().simulate('click') | ||
.find('.sp-well-slide-creator > .btn-success') | ||
.first() | ||
.simulate('click') | ||
expect(appWrapper.state().deck.components.length).to.equal(5) | ||
}) | ||
it('should move the 1st slide to 3rd when performing DnD', done => { | ||
expect(appWrapper.state().deck.components[2].components.length).to.equal(0) | ||
$('.sp-well-slide-wrapper') | ||
.first() | ||
.simulateDragSortable({ move: 3 }) | ||
setTimeout(() => { | ||
expect( | ||
appWrapper.state().deck.components[2].components[0].text | ||
).to.contain('Welcome to ShowPreper!') | ||
done() | ||
}, 10) | ||
}) | ||
}) |
This file contains 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
This file contains 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
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,348 @@ | ||
/*eslint no-console: 0*/ | ||
;(function($) { | ||
/* | ||
* Simulate drag of a JQuery UI sortable list | ||
* Repository: https://github.com/mattheworiordan/jquery.simulate.drag-sortable.js | ||
* Author: http://mattheworiordan.com | ||
* | ||
* options are: | ||
* - move: move item up (positive) or down (negative) by Integer amount | ||
* - dropOn: move item to a new linked list, move option now represents position in the new list (zero indexed) | ||
* - handle: selector for the draggable handle element (optional) | ||
* - listItem: selector to limit which sibling items can be used for reordering | ||
* - placeHolder: if a placeholder is used during dragging, we need to consider it's height | ||
* - tolerance: (optional) number of pixels to overlap by instead of the default 50% of the element height | ||
* | ||
*/ | ||
$.fn.simulateDragSortable = function(options) { | ||
// build main options before element iteration | ||
var opts = $.extend({}, $.fn.simulateDragSortable.defaults, options) | ||
|
||
let applyDrag = function(options) { | ||
// allow for a drag handle if item is not draggable | ||
var that = this, | ||
options = options || opts, // default to plugin opts unless options explicitly provided | ||
handle = options.handle ? $(this).find(options.handle)[0] : $(this)[0], | ||
listItem = options.listItem, | ||
placeHolder = options.placeHolder, | ||
sibling = $(this), | ||
moveCounter = Math.floor(options.move), | ||
direction = moveCounter > 0 ? 'down' : 'up', | ||
moveVerticalAmount = 0, | ||
initialVerticalPosition = 0, | ||
extraDrag = !isNaN(parseInt(options.tolerance, 10)) | ||
? function() { | ||
return Number(options.tolerance) | ||
} | ||
: function(obj) { | ||
return $(obj).outerHeight() / 2 + 5 | ||
}, | ||
dragPastBy = 0, // represents the additional amount one drags past an element and bounce back | ||
dropOn = options.dropOn ? $(options.dropOn) : false, | ||
center = findCenter(handle), | ||
x = Math.floor(center.x), | ||
y = Math.floor(center.y), | ||
mouseUpAfter = opts.debug ? 2500 : 10 | ||
|
||
if (dropOn) { | ||
if (dropOn.length === 0) { | ||
if (console && console.log) { | ||
console.log( | ||
'simulate.drag-sortable.js ERROR: Drop on target could not be found' | ||
) | ||
console.log(options.dropOn) | ||
} | ||
return | ||
} | ||
sibling = dropOn.find('>*:last') | ||
moveCounter = -(dropOn.find('>*').length + 1) + (moveCounter + 1) // calculate length of list after this move, use moveCounter as a positive index position in list to reverse back up | ||
if (dropOn.offset().top - $(this).offset().top < 0) { | ||
// moving to a list above this list, so move to just above top of last item (tried moving to top but JQuery UI wouldn't bite) | ||
initialVerticalPosition = | ||
sibling.offset().top - $(this).offset().top - extraDrag(this) | ||
} else { | ||
// moving to a list below this list, so move to bottom and work up (JQuery UI does not trigger new list below unless you move past top item first) | ||
initialVerticalPosition = | ||
sibling.offset().top - $(this).offset().top - $(this).height() | ||
} | ||
} else if (moveCounter === 0) { | ||
if (console && console.log) { | ||
console.log( | ||
'simulate.drag-sortable.js WARNING: Drag with move set to zero has no effect' | ||
) | ||
} | ||
return | ||
} else { | ||
while (moveCounter !== 0) { | ||
if (direction === 'down') { | ||
if (sibling.next(listItem).length) { | ||
sibling = sibling.next(listItem) | ||
moveVerticalAmount += sibling.outerHeight() | ||
} | ||
moveCounter -= 1 | ||
} else { | ||
if (sibling.prev(listItem).length) { | ||
sibling = sibling.prev(listItem) | ||
moveVerticalAmount -= sibling.outerHeight() | ||
} | ||
moveCounter += 1 | ||
} | ||
} | ||
} | ||
|
||
dispatchEvent( | ||
handle, | ||
'mousedown', | ||
createEvent('mousedown', handle, { clientX: x, clientY: y }) | ||
) | ||
// simulate drag start | ||
dispatchEvent( | ||
document, | ||
'mousemove', | ||
createEvent('mousemove', document, { clientX: x + 1, clientY: y + 1 }) | ||
) | ||
|
||
if (dropOn) { | ||
// jump to top or bottom of new list but do it in increments so that JQuery UI registers the drag events | ||
slideUpTo(x, y, initialVerticalPosition) | ||
|
||
// reset y position to top or bottom of list and move from there | ||
y += initialVerticalPosition | ||
|
||
// now call regular shift/down in a list | ||
options = jQuery.extend(options, { move: moveCounter }) | ||
delete options.dropOn | ||
|
||
// add some delays to allow JQuery UI to catch up | ||
setTimeout(function() { | ||
dispatchEvent( | ||
document, | ||
'mousemove', | ||
createEvent('mousemove', document, { clientX: x, clientY: y }) | ||
) | ||
}, 5) | ||
setTimeout(function() { | ||
dispatchEvent( | ||
handle, | ||
'mouseup', | ||
createEvent('mouseup', handle, { clientX: x, clientY: y }) | ||
) | ||
setTimeout(function() { | ||
if (options.move) { | ||
applyDrag.call(that, options) | ||
} | ||
}, 5) | ||
}, mouseUpAfter) | ||
|
||
// stop execution as applyDrag has been called again | ||
return | ||
} | ||
|
||
// Sortable is using a fixed height placeholder meaning items jump up and down as you drag variable height items into fixed height placeholder | ||
placeHolder = | ||
placeHolder && | ||
$(this) | ||
.parent() | ||
.find(placeHolder) | ||
|
||
if (!placeHolder && direction === 'down') { | ||
// need to move at least as far as this item and or the last sibling | ||
if ($(this).outerHeight() > $(sibling).outerHeight()) { | ||
moveVerticalAmount += $(this).outerHeight() - $(sibling).outerHeight() | ||
} | ||
moveVerticalAmount += extraDrag(sibling) | ||
dragPastBy += extraDrag(sibling) | ||
} else if (direction === 'up') { | ||
// move a little extra to ensure item clips into next position | ||
moveVerticalAmount -= Math.max(extraDrag(this), 5) | ||
} else if (direction === 'down') { | ||
// moving down with a place holder | ||
if (placeHolder.height() < $(this).height()) { | ||
moveVerticalAmount += Math.max(placeHolder.height(), 5) | ||
} else { | ||
moveVerticalAmount += extraDrag(sibling) | ||
} | ||
} | ||
|
||
if (sibling[0] !== $(this)[0]) { | ||
// step through so that the UI controller can determine when to show the placeHolder | ||
slideUpTo(x, y, moveVerticalAmount, dragPastBy) | ||
} else { | ||
if (window.console) { | ||
console.log( | ||
'simulate.drag-sortable.js WARNING: Could not move as at top or bottom already' | ||
) | ||
} | ||
} | ||
|
||
setTimeout(function() { | ||
dispatchEvent( | ||
document, | ||
'mousemove', | ||
createEvent('mousemove', document, { | ||
clientX: x, | ||
clientY: y + moveVerticalAmount | ||
}) | ||
) | ||
}, 5) | ||
setTimeout(function() { | ||
dispatchEvent( | ||
handle, | ||
'mouseup', | ||
createEvent('mouseup', handle, { | ||
clientX: x, | ||
clientY: y + moveVerticalAmount | ||
}) | ||
) | ||
}, mouseUpAfter) | ||
} | ||
|
||
// iterate and move each matched element | ||
return this.each(applyDrag) | ||
} | ||
|
||
// fire mouse events, go half way, then the next half, so small mouse movements near target and big at the start | ||
function slideUpTo(x, y, targetOffset, goPastBy) { | ||
var offset | ||
|
||
if (!goPastBy) { | ||
goPastBy = 0 | ||
} | ||
if (targetOffset < 0 && goPastBy > 0) { | ||
goPastBy = -goPastBy | ||
} // ensure go past is in the direction as often passed in from object height so always positive | ||
|
||
// go forwards including goPastBy | ||
for ( | ||
offset = 0; | ||
Math.abs(offset) + 1 < Math.abs(targetOffset + goPastBy); | ||
offset += (targetOffset + goPastBy - offset) / 2 | ||
) { | ||
dispatchEvent( | ||
document, | ||
'mousemove', | ||
createEvent('mousemove', document, { | ||
clientX: x, | ||
clientY: y + Math.ceil(offset) | ||
}) | ||
) | ||
} | ||
offset = targetOffset + goPastBy | ||
dispatchEvent( | ||
document, | ||
'mousemove', | ||
createEvent('mousemove', document, { clientX: x, clientY: y + offset }) | ||
) | ||
|
||
// now bounce back | ||
for ( | ||
; | ||
Math.abs(offset) - 1 >= Math.abs(targetOffset); | ||
offset += (targetOffset - offset) / 2 | ||
) { | ||
dispatchEvent( | ||
document, | ||
'mousemove', | ||
createEvent('mousemove', document, { | ||
clientX: x, | ||
clientY: y + Math.ceil(offset) | ||
}) | ||
) | ||
} | ||
dispatchEvent( | ||
document, | ||
'mousemove', | ||
createEvent('mousemove', document, { | ||
clientX: x, | ||
clientY: y + targetOffset | ||
}) | ||
) | ||
} | ||
|
||
function createEvent(type, target, options) { | ||
var evt | ||
var e = $.extend( | ||
{ | ||
target: target, | ||
preventDefault: function() {}, | ||
stopImmediatePropagation: function() {}, | ||
stopPropagation: function() {}, | ||
isPropagationStopped: function() { | ||
return true | ||
}, | ||
isImmediatePropagationStopped: function() { | ||
return true | ||
}, | ||
isDefaultPrevented: function() { | ||
return true | ||
}, | ||
bubbles: true, | ||
cancelable: type != 'mousemove', | ||
view: window, | ||
detail: 0, | ||
screenX: 0, | ||
screenY: 0, | ||
clientX: 0, | ||
clientY: 0, | ||
ctrlKey: false, | ||
altKey: false, | ||
shiftKey: false, | ||
metaKey: false, | ||
button: 0, | ||
relatedTarget: undefined | ||
}, | ||
options || {} | ||
) | ||
|
||
if ($.isFunction(document.createEvent)) { | ||
evt = document.createEvent('MouseEvents') | ||
evt.initMouseEvent( | ||
type, | ||
e.bubbles, | ||
e.cancelable, | ||
e.view, | ||
e.detail, | ||
e.screenX, | ||
e.screenY, | ||
e.clientX, | ||
e.clientY, | ||
e.ctrlKey, | ||
e.altKey, | ||
e.shiftKey, | ||
e.metaKey, | ||
e.button, | ||
e.relatedTarget || document.body.parentNode | ||
) | ||
} else if (document.createEventObject) { | ||
evt = document.createEventObject() | ||
$.extend(evt, e) | ||
evt.button = { 0: 1, 1: 4, 2: 2 }[evt.button] || evt.button | ||
} | ||
return evt | ||
} | ||
|
||
function dispatchEvent(el, type, evt) { | ||
if (el.dispatchEvent) { | ||
el.dispatchEvent(evt) | ||
} else if (el.fireEvent) { | ||
el.fireEvent('on' + type, evt) | ||
} | ||
return evt | ||
} | ||
|
||
function findCenter(el) { | ||
var elm = $(el), | ||
o = elm.offset() | ||
return { | ||
x: o.left + elm.outerWidth() / 2, | ||
y: o.top + elm.outerHeight() / 2 | ||
} | ||
} | ||
|
||
// | ||
// plugin defaults | ||
// | ||
$.fn.simulateDragSortable.defaults = { | ||
move: 0 | ||
} | ||
})(jQuery) |