Last active
October 3, 2016 19:25
-
-
Save geleto/2345046bb06f9f3c3fe10c5027768d1d to your computer and use it in GitHub Desktop.
aurelia-dragula test
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
.group{ | |
display:block; | |
width:300px; | |
border: 2px dashed blue; | |
padding:10px; | |
} | |
.item{ | |
width:260px; | |
margin:10px; | |
border: 2px dashed blue; | |
background:#fff6e0; | |
height:20px; | |
padding:10px; | |
} |
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
<template> | |
<require from="./app.css"></require> | |
<require from="./dragula-and-drop"></require> | |
<dragula-and-drop drop-fn.call="itemDropped(item, target, source, sibling, itemVM, siblingVM)"> | |
</dragula-and-drop> | |
<div repeat.for="group of groups" class="group drag-source drop-target"> | |
<div repeat.for="item of group.items" class="item"> | |
${item} | |
</div> | |
</div> | |
</template> |
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
import {Dragula} from './dragula'; | |
export class App { | |
message = 'Audelia Dragula Test!'; | |
dragula = new Dragula(); | |
groups = [ | |
{ | |
title:"Group 1", | |
items:["Item 1","Item 2","Item 3"] | |
}, | |
{ | |
title:"Group 2", | |
items:["Item 4","Item 5","Item 6"] | |
}, | |
{ | |
title:"Group 3", | |
items:["Item 7","Item 8","Item 9"] | |
} | |
]; | |
itemDropped(item, target, source, sibling, itemVM, siblingVM){ | |
console.log('dropped item'); | |
} | |
} |
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 is purportedly necessary to support Internet Explorer (not Edge) properly (it doesn't support classList on SVG elements!) */ | |
let cache = {}; | |
const start = '(?:^|\\s)'; | |
const end = '(?:\\s|$)'; | |
function lookupClass(className) { | |
var cached = cache[className]; | |
if (cached) { | |
cached.lastIndex = 0; | |
} else { | |
cache[className] = cached = new RegExp(start + className + end, 'g'); | |
} | |
return cached; | |
} | |
export function add(el, className) { | |
if (el.classList) { | |
el.classList.add(className); | |
return; | |
} | |
var current = el.className; | |
if (!current.length) { | |
el.className = className; | |
} else if (!lookupClass(className).test(current)) { | |
el.className += ' ' + className; | |
} | |
} | |
export function rm(el, className) { | |
if (el.classList) { | |
el.classList.remove(className); | |
return; | |
} | |
el.className = el.className.replace(lookupClass(className), ' ').trim(); | |
} |
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
import {customElement, bindable, inlineView} from 'aurelia-templating'; | |
import {bindingMode} from 'aurelia-binding'; | |
import {inject} from 'aurelia-dependency-injection'; | |
import {Options, GLOBAL_OPTIONS} from './options'; | |
import {Dragula} from './dragula'; | |
@bindable({ name: 'moves', defaultBindingMode: bindingMode.oneTime }) | |
@bindable({ name: 'accepts', defaultBindingMode: bindingMode.oneTime }) | |
@bindable({ name: 'invalid', defaultBindingMode: bindingMode.oneTime }) | |
@bindable({ name: 'containers', defaultBindingMode: bindingMode.oneTime }) | |
@bindable({ name: 'isContainer', attribute: 'is-container', defaultBindingMode: bindingMode.oneTime }) | |
@bindable({ name: 'copy', defaultBindingMode: bindingMode.oneTime }) | |
@bindable({ name: 'copySortSource', defaultBindingMode: bindingMode.oneTime }) | |
@bindable({ name: 'revertOnSpill', attribute: 'revert-on-spill', defaultBindingMode: bindingMode.oneTime, defaultValue: true }) | |
@bindable({ name: 'removeOnSpill', attribute: 'remove-on-spill', defaultBindingMode: bindingMode.oneTime }) | |
@bindable({ name: 'direction', defaultBindingMode: bindingMode.oneTime }) | |
@bindable({ name: 'ignoreInputTextSelection', attribute: 'ingore-input-text-selection', defaultBindingMode: bindingMode.oneTime }) | |
@bindable({ name: 'mirrorContainer', attribute: 'mirror-container', defaultBindingMode: bindingMode.oneTime }) | |
@bindable({ name: 'targetClass', attribute: 'target-class', defaultBindingMode: bindingMode.oneTime, defaultValue: 'drop-target' }) | |
@bindable({ name: 'sourceClass', attribute: 'source-class', defaultBindingMode: bindingMode.oneTime, defaultValue: 'drag-source' }) | |
@bindable({ name: 'dragFn', attribute: 'drag-fn', defaultBindingMode: bindingMode.oneTime }) | |
@bindable({ name: 'dropFn', attribute: 'drop-fn', defaultBindingMode: bindingMode.oneTime }) | |
@bindable({ name: 'dragEndFn', attribute: 'drag-end-fn', defaultBindingMode: bindingMode.oneTime }) | |
@customElement('dragula-and-drop') | |
@inlineView('<template><require from="./dragula.css"></require></template>') | |
@inject(GLOBAL_OPTIONS) | |
export class DragulaAndDrop { | |
constructor(globalOptions) { | |
this.dragula = {}; | |
this.globalOptions = globalOptions; | |
} | |
bind() { | |
let boundOptions = this._setupOptions(); | |
let aureliaOptions = { | |
isContainer: this._isContainer.bind(this), | |
moves: this._moves.bind(this), | |
accepts: this._accepts.bind(this), | |
invalid: this._invalid.bind(this) | |
}; | |
let options = Object.assign(aureliaOptions, boundOptions); | |
this.dragula = new Dragula(options); | |
this.dragula.on('drop', this._dropFunction.bind(this)); | |
this.dragula.on('drag', (item, source, itemVM) => { | |
if (typeof this.dragFn === 'function') | |
this.dragFn({ item: item, source: source, itemVM: itemVM }); | |
}); | |
this.dragula.on('dragend', (item, itemVM) => { | |
if (typeof this.dragEndFn === 'function') | |
this.dragEndFn({ item: item, itemVM: itemVM }); | |
}) | |
} | |
unbind() { | |
this.dragula.destroy(); | |
} | |
_dropFunction(item, target, source, sibling, itemVM, siblingVM) { | |
this.dragula.cancel(); | |
if (typeof this.dropFn === 'function') | |
this.dropFn({ item: item, target: target, source: source, sibling: sibling, itemVM: itemVM, siblingVM: siblingVM }); | |
} | |
_isContainer(el) { | |
if (!el) { | |
return false; | |
} | |
if (typeof this.isContainer === 'function') { | |
return this.isContainer({ item: el }); | |
} | |
if (this.dragula.dragging) { | |
return el.classList.contains(this.targetClass); | |
} | |
return el.classList.contains(this.sourceClass); | |
} | |
_moves(item, source, handle, sibling) { | |
if (typeof this.moves === 'function') { | |
return this.moves({ item: item, source: source, handle: handle, sibling: sibling }); | |
} | |
else { | |
return this.globalOptions.moves(item, source, handle, sibling); | |
} | |
} | |
_accepts(item, target, source, sibling) { | |
if (typeof this.accepts === 'function') { | |
return this.accepts({ item: item, target: target, source: source, sibling: sibling }); | |
} | |
else { | |
return this.globalOptions.accepts(item, target, source, sibling); | |
} | |
} | |
_invalid(item, handle) { | |
if (typeof this.invalid === 'function') { | |
return this.invalid({ item: item, handle: handle }); | |
} | |
else { | |
return this.globalOptions.invalid(item, handle); | |
} | |
} | |
_setupOptions() { | |
let result = { | |
containers: this._getOption('containers'), | |
copy: this._convertToBooleanIfRequired(this._getOption('copy')), | |
copySortSource: this._convertToBooleanIfRequired(this._getOption('copySortSource')), | |
revertOnSpill: this._convertToBooleanIfRequired(this._getOption('revertOnSpill')), | |
removeOnSpill: this._convertToBooleanIfRequired(this._getOption('removeOnSpill')), | |
direction: this._getOption('direction'), | |
ignoreInputTextSelection: this._convertToBooleanIfRequired(this._getOption('ignoreInputTextSelection')), | |
mirrorContainer: this._getOption('mirrorContainer') | |
}; | |
return result; | |
} | |
_getOption(option) { | |
if (this[option] == null) { | |
return this.globalOptions[option]; | |
} | |
return this[option]; | |
} | |
_convertToBooleanIfRequired(option) { | |
if (typeof option === 'function') { | |
return option; | |
} | |
if (typeof option === 'string') { | |
return option.toLowerCase() === 'true'; | |
} | |
return new Boolean(option).valueOf(); | |
} | |
} |
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
.gu-mirror { | |
position: fixed !important; | |
margin: 0 !important; | |
z-index: 9999 !important; | |
opacity: 0.8; | |
-ms-filter: "progid:DXImageTransform.Microsoft.Alpha(Opacity=80)"; | |
filter: alpha(opacity=80); | |
} | |
.gu-hide { | |
display: none !important; | |
} | |
.gu-unselectable { | |
-webkit-touch-callout: none; /* iOS Safari */ | |
-webkit-user-select: none !important; | |
-moz-user-select: none !important; | |
-ms-user-select: none !important; | |
user-select: none !important; | |
-khtml-user-select: none !important; /* Konqueror */ | |
} | |
.gu-transit { | |
opacity: 0.2; | |
-ms-filter: "progid:DXImageTransform.Microsoft.Alpha(Opacity=20)"; | |
filter: alpha(opacity=20); | |
} |
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
import {inject, Container} from 'aurelia-dependency-injection'; | |
import {touchy} from './touchy'; | |
import {GLOBAL_OPTIONS, Options} from './options'; | |
import {Util} from './util'; | |
import {Emitter} from './emitter'; | |
import * as classes from './classes'; | |
const MIN_TIME_BETWEEN_REDRAWS_MS = 20; | |
export class Dragula { | |
constructor(options) { | |
let len = arguments.length; | |
let globalOptions = new Options();//Container.instance.get(GLOBAL_OPTIONS); | |
this.options = Object.assign({}, globalOptions/*, options*/); | |
this._emitter = new Emitter(); | |
this.dragging = false; | |
if (this.options.removeOnSpill === true) { | |
this._emitter.on('over', this.spillOver.bind(this)); | |
this._emitter.on('out', this.spillOut.bind(this)); | |
} | |
this.boundStart = this._startBecauseMouseMoved.bind(this); | |
this.boundGrab = this._grab.bind(this); | |
this.boundRelease = this._release.bind(this); | |
this.boundPreventGrabbed = this._preventGrabbed.bind(this); | |
this.boundDrag = this.drag.bind(this); | |
this._events(); | |
this._mirror; // mirror image | |
this._source; // source container | |
this._item; // item being dragged | |
this._offsetX; // reference x | |
this._offsetY; // reference y | |
this._moveX; // reference move x | |
this._moveY; // reference move y | |
this._initialSibling; // reference sibling when grabbed | |
this._currentSibling; // reference sibling now | |
this._copy; // item used for copying | |
this._lastRenderTime = null; // last time we rendered the mirror | |
this._lastDropTarget = null; // last container item was over | |
this._grabbed; // holds mousedown context until first mousemove | |
} | |
on(eventName, callback) { | |
this._emitter.on(eventName, callback); | |
} | |
once(eventName, callback) { | |
this._emitter.once(eventName, callback); | |
} | |
off(eventName, fn) { | |
this._emitter.off(eventName, fn); | |
} | |
get containers() { | |
return this.options.containers; | |
} | |
set containers(value) { | |
this.options.containers = value; | |
} | |
isContainer(el) { | |
return this.options.containers.indexOf(el) !== -1 || this.options.isContainer(el); | |
} | |
_events(remove) { | |
let op = remove ? 'removeEventListener' : 'addEventListener'; | |
touchy(document.body, op, 'mousedown', this.boundGrab); | |
touchy(document.body, op, 'mouseup', this.boundRelease); | |
} | |
_eventualMovements(remove) { | |
let op = remove ? 'removeEventListener' : 'addEventListener'; | |
touchy(document.body, op, 'mousemove', this.boundStart); | |
} | |
_movements(remove) { | |
let op = remove ? 'removeEventListener' : 'addEventListener'; | |
touchy(document.body, op, 'click', this.boundPreventGrabbed); | |
} | |
destroy() { | |
this._events(true); | |
this._release({}); | |
this._emitter.destroy(); | |
} | |
_preventGrabbed(e) { | |
if (this._grabbed) { | |
e.preventDefault(); | |
} | |
} | |
_grab(e) { | |
console.log("Grab") | |
this._moveX = e.clientX; | |
this._moveY = e.clientY; | |
let ignore = Util.whichMouseButton(e) !== 1 || e.metaKey || e.ctrlKey; | |
if (ignore) { | |
return; // we only care about honest-to-god left clicks and touch events | |
} | |
let item = e.target; | |
let context = this._canStart(item); | |
if (!context) { | |
return; | |
} | |
this._grabbed = context; | |
this._eventualMovements(); | |
if (Util.isInput(item)) { // see also: https://github.com/bevacqua/dragula/issues/208 | |
item.focus(); // fixes https://github.com/bevacqua/dragula/issues/176 | |
} else { | |
e.preventDefault(); // fixes https://github.com/bevacqua/dragula/issues/155 | |
} | |
} | |
_startBecauseMouseMoved (e) { | |
if (!this._grabbed || this.dragging) { | |
return; | |
} | |
if (Util.whichMouseButton(e) === 0) { | |
this._release({}); | |
return; // when text is selected on an input and then dragged, mouseup doesn't fire. this is our only hope | |
} | |
// truthy check fixes #239, equality fixes #207 | |
if (e.clientX !== void 0 && e.clientX === this._moveX && e.clientY !== void 0 && e.clientY === this._moveY) { | |
return; | |
} | |
if (this.options.ignoreInputTextSelection) { | |
let clientX = Util.getCoord('clientX', e); | |
let clientY = Util.getCoord('clientY', e); | |
let elementBehindCursor = document.elementFromPoint(clientX, clientY); | |
if (Util.isInput(elementBehindCursor)) { | |
return; | |
} | |
} | |
let grabbed = this._grabbed; // call to end() unsets _grabbed | |
this._eventualMovements(true); | |
this._movements(); | |
this.end(); | |
this.start(grabbed); | |
let offset = Util.getOffset(this._item); | |
this._offsetX = Util.getCoord('pageX', e) - offset.left; | |
this._offsetY = Util.getCoord('pageY', e) - offset.top; | |
classes.add(this._copy || this._item, 'gu-transit'); | |
this.renderMirrorImage(); | |
this.drag(e); | |
} | |
_canStart(item) { | |
if (this.dragging && this._mirror) { | |
return; | |
} | |
console.log("Check is container") | |
if (this.isContainer(item)) { | |
return; // don't drag container itself | |
} | |
let handle = item; | |
while (Util.getParent(item) && this.isContainer(Util.getParent(item)) === false) { | |
if (this.options.invalid(item, handle)) { | |
return; | |
} | |
item = Util.getParent(item); // drag target should be a top element | |
if (!item) { | |
return; | |
} | |
} | |
console.log("It is container 1") | |
let source = Util.getParent(item); | |
if (!source) { | |
return; | |
} | |
console.log("It is container 2") | |
if (this.options.invalid(item, handle)) { | |
return; | |
} | |
console.log("It is container 3") | |
let movable = this.options.moves(item, source, handle, Util.nextEl(item)); | |
if (!movable) { | |
return; | |
} | |
return { | |
item: item, | |
source: source | |
}; | |
} | |
manualStart(item) { | |
let context = this._canStart(item); | |
if (context) { | |
this.start(context); | |
} | |
} | |
start(context) { | |
if (this._isCopy(context.item, context.source)) { | |
this._copy = context.item.cloneNode(true); | |
this._emitter.emit('cloned', this._copy, context.item, 'copy', Util.getViewModel(context.item)); | |
} | |
this._source = context.source; | |
this._item = context.item; | |
this._initialSibling = context.item.nextSibling; | |
this._currentSibling = Util.nextEl(context.item); | |
this.dragging = true; | |
this._emitter.emit('drag', this._item, this._source, Util.getViewModel(this._item)); | |
} | |
end() { | |
if (!this.dragging) { | |
return; | |
} | |
let item = this._copy || this._item; | |
this.drop(item, Util.getParent(item)); | |
} | |
_ungrab() { | |
this._grabbed = false; | |
this._eventualMovements(true); | |
this._movements(true); | |
} | |
_release(e) { | |
this._ungrab(); | |
if (!this.dragging) { | |
return; | |
} | |
let item = this._copy || this._item; | |
let clientX = Util.getCoord('clientX', e); | |
let clientY = Util.getCoord('clientY', e); | |
let elementBehindCursor = Util.getElementBehindPoint(this._mirror, clientX, clientY); | |
let dropTarget = this._findDropTarget(elementBehindCursor, clientX, clientY); | |
if (dropTarget && ((this._copy && this.options.copySortSource) || (!this._copy || dropTarget !== this._source))) { | |
this.drop(item, dropTarget); | |
} else if (this.options.removeOnSpill) { | |
this.remove(); | |
} else { | |
this.cancel(); | |
} | |
} | |
drop(item, target) { | |
if (this._copy && this.options.copySortSource && target === this._source) { | |
let parent = Util.getParent(this._item); | |
if (parent) | |
parent.removeChild(this._item); | |
} | |
if (this._isInitialPlacement(target)) { | |
this._emitter.emit('cancel', item, this._source, this._source, Util.getViewModel(this._item)); | |
} else { | |
this._emitter.emit('drop', item, target, this._source, this._currentSibling, | |
Util.getViewModel(this._item), Util.getViewModel(this._currentSibling)); | |
} | |
this._cleanup(); | |
} | |
remove() { | |
if (!this.dragging) { | |
return; | |
} | |
let item = this._copy || this._item; | |
let parent = Util.getParent(item); | |
if (parent) { | |
parent.removeChild(item); | |
} | |
this._emitter.emit(this._copy ? 'cancel' : 'remove', item, parent, this._source, Util.getViewModel(this._item)); | |
this._cleanup(); | |
} | |
cancel(revert) { | |
if (!this.dragging) { | |
return; | |
} | |
let reverts = arguments.length > 0 ? revert : this.options.revertOnSpill; | |
let item = this._copy || this._item; | |
let parent = Util.getParent(item); | |
if (this._copy && parent) { | |
parent.removeChild(this._copy); | |
} | |
let initial = this._isInitialPlacement(parent); | |
if (initial === false && !this._copy && reverts) { | |
this._source.insertBefore(item, this._initialSibling); | |
} | |
if (initial || reverts) { | |
this._emitter.emit('cancel', item, this._source, this._source, Util.getViewModel(this._item)); | |
} else { | |
this._emitter.emit('drop', item, parent, this._source, this._currentSibling, | |
Util.getViewModel(this._item), Util.getViewModel(this._currentSibling)); | |
} | |
this._cleanup(); | |
} | |
_cleanup () { | |
let item = this._copy || this._item; | |
this._ungrab(); | |
this.removeMirrorImage(); | |
if (item) { | |
classes.rm(item, 'gu-transit'); | |
} | |
this.dragging = false; | |
if (this._lastDropTarget) { | |
this._emitter.emit('out', item, this._lastDropTarget, this._source, Util.getViewModel(item)); | |
} | |
this._emitter.emit('dragend', item, Util.getViewModel(item)); | |
this._source = this._item = this._copy = this._initialSibling = this._currentSibling = this._lastRenderTime = this._lastDropTarget = null; | |
} | |
_isInitialPlacement(target, s) { | |
let sibling; | |
if (s !== void 0) { | |
sibling = s; | |
} else if (this._mirror) { | |
sibling = this._currentSibling; | |
} else { | |
sibling = (this._copy || this._item).nextSibling; | |
} | |
return target === this._source && sibling === this._initialSibling; | |
} | |
_findDropTarget(elementBehindCursor, clientX, clientY) { | |
let accepted = () => { | |
let droppable = this.isContainer(target); | |
if (droppable === false) { | |
return false; | |
} | |
let immediate = Util.getImmediateChild(target, elementBehindCursor); | |
let reference = this.getReference(target, immediate, clientX, clientY); | |
let initial = this._isInitialPlacement(target, reference); | |
if (initial) { | |
return true; // should always be able to drop it right back where it was | |
} | |
return this.options.accepts(this._item, target, this._source, reference); | |
} | |
let target = elementBehindCursor; | |
while (target && !accepted()) { | |
target = Util.getParent(target); | |
} | |
return target; | |
} | |
drag(e) { | |
e.preventDefault(); | |
if (!this._mirror) { | |
return; | |
} | |
if (this._lastRenderTime !== null && Date.now() - this._lastRenderTime < MIN_TIME_BETWEEN_REDRAWS_MS) { | |
return; | |
} | |
this._lastRenderTime = Date.now(); | |
let item = this._copy || this._item; | |
let moved = (type) => { this._emitter.emit(type, item, this._lastDropTarget, this._source, Util.getViewModel(item)); } | |
let over = () => { if (changed) { moved('over'); } } | |
let out = () => { if (this._lastDropTarget) { moved('out'); } } | |
let clientX = Util.getCoord('clientX', e); | |
let clientY = Util.getCoord('clientY', e); | |
let x = clientX - this._offsetX; | |
let y = clientY - this._offsetY; | |
this._mirror.style.left = x + 'px'; | |
this._mirror.style.top = y + 'px'; | |
let elementBehindCursor = Util.getElementBehindPoint(this._mirror, clientX, clientY); | |
let dropTarget = this._findDropTarget(elementBehindCursor, clientX, clientY); | |
let changed = dropTarget !== null && dropTarget !== this._lastDropTarget; | |
if (changed || dropTarget === null) { | |
out(); | |
this._lastDropTarget = dropTarget; | |
over(); | |
} | |
let parent = Util.getParent(item); | |
if (dropTarget === this._source && this._copy && !this.options.copySortSource) { | |
if (parent) { | |
parent.removeChild(item); | |
} | |
return; | |
} | |
let reference; | |
let immediate = Util.getImmediateChild(dropTarget, elementBehindCursor); | |
if (immediate !== null) { | |
reference = this.getReference(dropTarget, immediate, clientX, clientY); | |
} else if (this.options.revertOnSpill === true && !this._copy) { | |
reference = this._initialSibling; | |
dropTarget = this._source; | |
} else { | |
if (this._copy && parent) { | |
parent.removeChild(item); | |
} | |
return; | |
} | |
if ( | |
(reference === null && changed) || | |
reference !== item && | |
reference !== Util.nextEl(item) | |
) { | |
this._currentSibling = reference; | |
dropTarget.insertBefore(item, reference); | |
this._emitter.emit('shadow', item, dropTarget, this._source, Util.getViewModel(item)); | |
} | |
} | |
spillOver(el) { | |
classes.rm(el, 'gu-hide'); | |
} | |
spillOut(el) { | |
if (this.dragging) { classes.add(el, 'gu-hide'); } | |
} | |
renderMirrorImage() { | |
if (this._mirror) { | |
return; | |
} | |
let rect = this._item.getBoundingClientRect(); | |
this._mirror = this._item.cloneNode(true); | |
this._mirror.style.width = Util.getRectWidth(rect) + 'px'; | |
this._mirror.style.height = Util.getRectHeight(rect) + 'px'; | |
classes.rm(this._mirror, 'gu-transit'); | |
classes.add(this._mirror, 'gu-mirror'); | |
this.options.mirrorContainer.appendChild(this._mirror); | |
touchy(document.documentElement, 'addEventListener', 'mousemove', this.boundDrag); | |
classes.add(this.options.mirrorContainer, 'gu-unselectable'); | |
this._emitter.emit('cloned', this._mirror, this._item, 'mirror', Util.getViewModel(this._item)); | |
} | |
removeMirrorImage() { | |
if (this._mirror) { | |
classes.rm(this.options.mirrorContainer, 'gu-unselectable'); | |
touchy(document.documentElement, 'removeEventListener', 'mousemove', this.boundDrag); | |
Util.getParent(this._mirror).removeChild(this._mirror); | |
this._mirror = null; | |
} | |
} | |
getReference(dropTarget, target, x, y) { | |
let outside = () => { // slower, but able to figure out any position | |
let len = dropTarget.children.length; | |
let i; | |
let el; | |
let rect; | |
for (i = 0; i < len; i++) { | |
el = dropTarget.children[i]; | |
rect = el.getBoundingClientRect(); | |
if (horizontal && (rect.left + rect.width / 2) > x) { return el; } | |
if (!horizontal && (rect.top + rect.height / 2) > y) { return el; } | |
} | |
return null; | |
} | |
let resolve = (after) => { | |
return after ? Util.nextEl(target) : target; | |
} | |
let inside = () => { // faster, but only available if dropped inside a child element | |
let rect = target.getBoundingClientRect(); | |
if (horizontal) { | |
return resolve(x > rect.left + Util.getRectWidth(rect) / 2); | |
} | |
return resolve(y > rect.top + Util.getRectHeight(rect) / 2); | |
} | |
let horizontal = this.options.direction === 'horizontal'; | |
let reference = target !== dropTarget ? inside() : outside(); | |
return reference; | |
} | |
_isCopy(item, container) { | |
let isBoolean = typeof this.options.copy === 'boolean' || | |
(typeof this.options.copy === 'object' && typeof this.options.copy.valueOf() === 'boolean'); | |
return isBoolean ? this.options.copy : this.options.copy(item, container); | |
} | |
} |
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
//let debounce = require('./debounce'); | |
class EventListener { | |
constructor(func, once = false) { | |
this.func = func; | |
this.once = once; | |
} | |
} | |
export class Emitter { | |
constructor() { | |
this.events = {}; | |
} | |
on(type, fn, once = false) { | |
let newEvent = new EventListener(fn, once); | |
if (this.events[type] === undefined) { | |
this.events[type] = []; | |
} | |
this.events[type].push(newEvent); | |
} | |
once(type, fn) { | |
this.on(type, fn, true); | |
} | |
off(type, fn) { | |
if (arguments.length === 1) { | |
delete this.events[type]; | |
} | |
else if (arguments.length === 0) { | |
this.events = {}; | |
} | |
else { | |
let eventList = this.events[type]; | |
if (eventList) { | |
let index = eventList.findIndex(x => x.func === fn); | |
if (index >= 0) | |
eventList.splice(index, 1); | |
} | |
} | |
} | |
destroy() { | |
this.events = {}; | |
} | |
emit() { | |
let args = arguments ? [...arguments] : []; | |
let type = args.shift(); | |
let et = (this.events[type] || []).slice(0); | |
if (type === 'error' && !et.length) { throw args.length === 1 ? args[0] : args; } | |
let toDeregister = []; | |
et.forEach(listener => { | |
listener.func(...args); | |
if (listener.once) { | |
toDeregister.push(listener); | |
} | |
}); | |
toDeregister.forEach(listener => { | |
this.off(type, listener.func); | |
}); | |
} | |
} |
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
<!doctype html> | |
<html> | |
<head> | |
<title>Aurelia</title> | |
<meta name="viewport" content="width=device-width, initial-scale=1"> | |
</head> | |
<body aurelia-app> | |
<h1>Loading...</h1> | |
<script src="https://jdanyow.github.io/rjs-bundle/node_modules/requirejs/require.js"></script> | |
<script src="https://jdanyow.github.io/rjs-bundle/config.js"></script> | |
<script src="https://jdanyow.github.io/rjs-bundle/bundles/aurelia.js"></script> | |
<script src="https://jdanyow.github.io/rjs-bundle/bundles/babel.js"></script> | |
<script> | |
require(['aurelia-bootstrapper']); | |
</script> | |
</body> | |
</html> |
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
import {Options, GLOBAL_OPTIONS, DIRECTION} from './options'; | |
import {Dragula} from './dragula'; | |
import {moveBefore} from './move-before'; | |
export {Dragula, Options, DIRECTION, moveBefore}; | |
export function configure(config, callback) { | |
let defaults = new Options(); | |
config.container.registerInstance(GLOBAL_OPTIONS, defaults); | |
if (callback !== undefined && typeof callback === 'function') { | |
callback(defaults); | |
} | |
config.globalResources(['./dragula-and-drop']); | |
} |
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
export function moveBefore(array, itemMatcherFn, siblingMatcherFn) { | |
let removedItem = remove(array, itemMatcherFn); | |
let nextIndex = array.findIndex(siblingMatcherFn); | |
array.splice(nextIndex >= 0 ? nextIndex : array.length, 0, removedItem); | |
} | |
function remove(array, matcherFn) { | |
let index = array.findIndex(matcherFn); | |
if (index >= 0) { | |
return array.splice(index, 1)[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
export const GLOBAL_OPTIONS = 'GlobalOptions'; | |
export const DIRECTION = { | |
VERTICAL: 'vertical', | |
HORIZONTAL: 'horizontal' | |
}; | |
export class Options { | |
constructor() { | |
this.moves = Options.always; | |
this.accepts = Options.always; | |
this.invalid = Options.invalidTarget; | |
this.containers = []; | |
this.isContainer = Options.never; | |
this.copy = false; | |
this.copySortSource = false; | |
this.revertOnSpill = false; | |
this.removeOnSpill = false; | |
this.direction = DIRECTION.VERTICAL, | |
this.ignoreInputTextSelection = true; | |
this.mirrorContainer = document.body; | |
} | |
static always() { | |
return true; | |
} | |
static never() { | |
return false; | |
} | |
static invalidTarget() { | |
return false; | |
} | |
} |
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
const touch = { | |
mouseup: 'touchend', | |
mousedown: 'touchstart', | |
mousemove: 'touchmove' | |
}; | |
const pointers = { | |
mouseup: 'pointerup', | |
mousedown: 'pointerdown', | |
mousemove: 'pointermove' | |
}; | |
const microsoft = { | |
mouseup: 'MSPointerUp', | |
mousedown: 'MSPointerDown', | |
mousemove: 'MSPointerMove' | |
}; | |
export function touchy(el, op, type, fn) { | |
if (window.navigator.pointerEnabled) { | |
el[op](pointers[type], fn); | |
} else if (window.navigator.msPointerEnabled) { | |
el[op](microsoft[type], fn); | |
} else { | |
el[op](touch[type], fn); | |
el[op](type, fn); | |
} | |
} |
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
class _Util { | |
nextEl(el) { | |
return el.nextElementSibling || manually(); | |
function manually () { | |
let sibling = el; | |
do { | |
sibling = sibling.nextSibling; | |
} while (sibling && sibling.nodeType !== 1); | |
return sibling; | |
} | |
} | |
whichMouseButton(e) { | |
if (e.touches !== void 0) { return e.touches.length; } | |
if (e.which !== void 0 && e.which !== 0) { return e.which; } // see https://github.com/bevacqua/dragula/issues/261 | |
if (e.buttons !== void 0) { return e.buttons; } | |
let button = e.button; | |
if (button !== void 0) { // see https://github.com/jquery/jquery/blob/99e8ff1baa7ae341e94bb89c3e84570c7c3ad9ea/src/event.js#L573-L575 | |
return button & 1 ? 1 : button & 2 ? 3 : (button & 4 ? 2 : 0); | |
} | |
} | |
getElementBehindPoint(point, x, y) { | |
let p = point || {}; | |
let state = p.className; | |
let el; | |
p.className += ' gu-hide'; | |
el = document.elementFromPoint(x, y); | |
p.className = state; | |
return el; | |
} | |
getParent(el) { return el.parentNode === document ? null : el.parentNode; } | |
getRectWidth(rect) { return rect.width || (rect.right - rect.left); } | |
getRectHeight(rect) { return rect.height || (rect.bottom - rect.top); } | |
isInput(el) { return el.tagName === 'INPUT' || el.tagName === 'TEXTAREA' || el.tagName === 'SELECT' || Util.isEditable(el); } | |
isEditable(el) { | |
if (!el) { return false; } // no parents were editable | |
if (el.contentEditable === 'false') { return false; } // stop the lookup | |
if (el.contentEditable === 'true') { return true; } // found a contentEditable element in the chain | |
return this.isEditable(this.getParent(el)); // contentEditable is set to 'inherit' | |
} | |
getOffset(el) { | |
let rect = el.getBoundingClientRect(); | |
return { | |
left: rect.left + this.getScroll('scrollLeft', 'pageXOffset'), | |
top: rect.top + this.getScroll('scrollTop', 'pageYOffset') | |
}; | |
} | |
getScroll(scrollProp, offsetProp) { | |
if (typeof window[offsetProp] !== 'undefined') { | |
return window[offsetProp]; | |
} | |
if (document.documentElement.clientHeight) { | |
return document.documentElement[scrollProp]; | |
} | |
return document.body[scrollProp]; | |
} | |
getElementBehindPoint(point, x, y) { | |
if (point) | |
point.classList.add('gu-hide'); | |
let el = document.elementFromPoint(x, y); | |
if (point) | |
point.classList.remove('gu-hide'); | |
return el; | |
} | |
getEventHost(e) { | |
// on touchend event, we have to use `e.changedTouches` | |
// see http://stackoverflow.com/questions/7192563/touchend-event-properties | |
// see https://github.com/bevacqua/dragula/issues/34 | |
if (e.targetTouches && e.targetTouches.length) { | |
return e.targetTouches[0]; | |
} | |
if (e.changedTouches && e.changedTouches.length) { | |
return e.changedTouches[0]; | |
} | |
return e; | |
} | |
getCoord(coord, e) { | |
let host = this.getEventHost(e); | |
return host[coord]; | |
} | |
getImmediateChild(dropTarget, target) { | |
let immediate = target; | |
while (immediate !== dropTarget && this.getParent(immediate) !== dropTarget) { | |
immediate = this.getParent(immediate); | |
} | |
if (immediate === document.documentElement) { | |
return null; | |
} | |
return immediate; | |
} | |
getViewModel(element) { | |
if (element && element.au && element.au.controller) { | |
if (element.au.controller.viewModel.currentViewModel) | |
return element.au.controller.viewModel.currentViewModel; | |
else | |
return element.au.controller.viewModel; | |
} | |
return null; | |
} | |
} | |
let Util = new _Util(); | |
export { Util }; |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment