313 lines
8.8 KiB
313 lines
8.8 KiB
class DragData {
|
|
constructor () {
|
|
this.data = {}
|
|
}
|
|
|
|
new (key) {
|
|
if (!this.data[key]) {
|
|
this.data[key] = {
|
|
className: '',
|
|
List: [],
|
|
KEY_MAP: {}
|
|
}
|
|
}
|
|
return this.data[key]
|
|
}
|
|
|
|
get (key) {
|
|
return this.data[key]
|
|
}
|
|
}
|
|
|
|
const $dragging = {
|
|
listeners: {
|
|
},
|
|
$on (event, func) {
|
|
const events = this.listeners[event]
|
|
if (!events) {
|
|
this.listeners[event] = []
|
|
}
|
|
this.listeners[event].push(func)
|
|
},
|
|
$once (event, func) {
|
|
const vm = this
|
|
function on (...args) {
|
|
vm.$off(event, on)
|
|
func.apply(vm, args)
|
|
}
|
|
this.$on(event, on)
|
|
},
|
|
$off (event, func) {
|
|
const events = this.listeners[event]
|
|
if (!func || !events) {
|
|
this.listeners[event] = []
|
|
return
|
|
}
|
|
this.listeners[event] = this.listeners[event].filter(i => i !== func)
|
|
},
|
|
$emit (event, context) {
|
|
const events = this.listeners[event]
|
|
if (events && events.length > 0) {
|
|
events.forEach(func => {
|
|
func(context)
|
|
})
|
|
}
|
|
}
|
|
}
|
|
const _ = {
|
|
on (el, type, fn) {
|
|
el.addEventListener(type, fn)
|
|
},
|
|
off (el, type, fn) {
|
|
el.removeEventListener(type, fn)
|
|
},
|
|
addClass (el, cls) {
|
|
if(arguments.length < 2) {
|
|
el.classList.add(cls)
|
|
} else {
|
|
for (let i = 1, len = arguments.length; i < len; i++) {
|
|
el.classList.add(arguments[i])
|
|
}
|
|
}
|
|
},
|
|
removeClass (el, cls) {
|
|
if(arguments.length < 2) {
|
|
el.classList.remove(cls)
|
|
} else {
|
|
for (let i = 1, len = arguments.length; i < len; i++) {
|
|
el.classList.remove(arguments[i])
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
export default function (Vue, options) {
|
|
const isPreVue = Vue.version.split('.')[0] === '1'
|
|
const dragData = new DragData()
|
|
let isSwap = false
|
|
let Current = null
|
|
|
|
function handleDragStart(e) {
|
|
const el = getBlockEl(e.target)
|
|
const key = el.getAttribute('drag_group')
|
|
const drag_key = el.getAttribute('drag_key')
|
|
const comb = el.getAttribute('comb')
|
|
const DDD = dragData.new(key)
|
|
const item = DDD.KEY_MAP[drag_key]
|
|
const index = DDD.List.indexOf(item)
|
|
const groupArr = DDD.List.filter(item => item[comb])
|
|
_.addClass(el, 'dragging')
|
|
|
|
if (e.dataTransfer) {
|
|
e.dataTransfer.effectAllowed = 'move'
|
|
e.dataTransfer.setData('text', JSON.stringify(item))
|
|
}
|
|
|
|
Current = {
|
|
index,
|
|
item,
|
|
el,
|
|
group: key,
|
|
groupArr
|
|
}
|
|
}
|
|
|
|
function handleDragOver(e) {
|
|
if (e.preventDefault) {
|
|
e.preventDefault()
|
|
}
|
|
return false
|
|
}
|
|
|
|
function handleDragEnter(e) {
|
|
let el
|
|
if (e.type === 'touchmove') {
|
|
e.stopPropagation()
|
|
e.preventDefault()
|
|
el = getOverElementFromTouch(e)
|
|
el = getBlockEl(el)
|
|
} else {
|
|
el = getBlockEl(e.target)
|
|
}
|
|
|
|
if (!el || !Current) return
|
|
|
|
const key = el.getAttribute('drag_group')
|
|
if (key !== Current.group || !Current.el || !Current.item || el === Current.el) return
|
|
const drag_key = el.getAttribute('drag_key')
|
|
const DDD = dragData.new(key)
|
|
const item = DDD.KEY_MAP[drag_key]
|
|
|
|
if (item === Current.item) return
|
|
|
|
const indexTo = DDD.List.indexOf(item)
|
|
const indexFrom = DDD.List.indexOf(Current.item)
|
|
|
|
swapArrayElements(DDD.List, indexFrom, indexTo)
|
|
|
|
Current.groupArr.forEach(item => {
|
|
if (item != Current.item) {
|
|
DDD.List.splice(DDD.List.indexOf(item), 1)
|
|
}
|
|
})
|
|
|
|
let targetIndex = DDD.List.indexOf(Current.item)
|
|
if (Current.groupArr.length) {
|
|
DDD.List.splice(targetIndex, 1, ...Current.groupArr)
|
|
}
|
|
|
|
Current.index = indexTo
|
|
isSwap = true
|
|
$dragging.$emit('dragged', {
|
|
draged: Current.item,
|
|
to: item,
|
|
value: DDD.value,
|
|
group: key
|
|
})
|
|
}
|
|
|
|
function handleDragLeave(e) {
|
|
_.removeClass(getBlockEl(e.target), 'drag-over', 'drag-enter')
|
|
}
|
|
|
|
function handleDrag (e) {
|
|
}
|
|
|
|
function handleDragEnd (e) {
|
|
const el = getBlockEl(e.target)
|
|
_.removeClass(el, 'dragging', 'drag-over', 'drag-enter')
|
|
Current = null
|
|
// if (isSwap) {
|
|
isSwap = false
|
|
const group = el.getAttribute('drag_group')
|
|
$dragging.$emit('dragend', { group })
|
|
// }
|
|
}
|
|
|
|
function handleDrop(e) {
|
|
e.preventDefault()
|
|
if (e.stopPropagation) {
|
|
e.stopPropagation()
|
|
}
|
|
return false
|
|
}
|
|
|
|
function getBlockEl (el) {
|
|
if (!el) return
|
|
while (el.parentNode) {
|
|
if (el.getAttribute && el.getAttribute('drag_block')) {
|
|
return el
|
|
break
|
|
} else {
|
|
el = el.parentNode
|
|
}
|
|
}
|
|
}
|
|
|
|
function swapArrayElements (items, indexFrom, indexTo) {
|
|
let item = items[indexTo]
|
|
if (isPreVue) {
|
|
items.$set(indexTo, items[indexFrom])
|
|
items.$set(indexFrom, item)
|
|
} else {
|
|
Vue.set(items, indexTo, items[indexFrom])
|
|
Vue.set(items, indexFrom, item)
|
|
}
|
|
return items
|
|
}
|
|
|
|
function getOverElementFromTouch (e) {
|
|
const touch = e.touches[0]
|
|
const el = document.elementFromPoint(touch.clientX, touch.clientY)
|
|
return el
|
|
}
|
|
|
|
function addDragItem (el, binding, vnode) {
|
|
const item = binding.value.item
|
|
const list = binding.value.list
|
|
const DDD = dragData.new(binding.value.group)
|
|
|
|
const drag_key = isPreVue? binding.value.key : vnode.key
|
|
DDD.value = binding.value
|
|
DDD.className = binding.value.className
|
|
DDD.KEY_MAP[drag_key] = item
|
|
if (list && DDD.List !== list) {
|
|
DDD.List = list
|
|
}
|
|
el.setAttribute('draggable', 'true')
|
|
el.setAttribute('drag_group', binding.value.group)
|
|
el.setAttribute('drag_block', binding.value.group)
|
|
el.setAttribute('drag_key', drag_key)
|
|
el.setAttribute('comb', binding.value.comb)
|
|
|
|
_.on(el, 'dragstart', handleDragStart)
|
|
_.on(el, 'dragenter', handleDragEnter)
|
|
_.on(el, 'dragover', handleDragOver)
|
|
_.on(el, 'drag', handleDrag)
|
|
_.on(el, 'dragleave', handleDragLeave)
|
|
_.on(el, 'dragend', handleDragEnd)
|
|
_.on(el, 'drop', handleDrop)
|
|
|
|
_.on(el, 'touchstart', handleDragStart)
|
|
_.on(el, 'touchmove', handleDragEnter)
|
|
_.on(el, 'touchend', handleDragEnd)
|
|
}
|
|
|
|
function removeDragItem (el, binding, vnode) {
|
|
const DDD = dragData.new(binding.value.group)
|
|
const drag_key = isPreVue? binding.value.key : vnode.key
|
|
DDD.KEY_MAP[drag_key] = undefined
|
|
_.off(el, 'dragstart', handleDragStart)
|
|
_.off(el, 'dragenter', handleDragEnter)
|
|
_.off(el, 'dragover', handleDragOver)
|
|
_.off(el, 'drag', handleDrag)
|
|
_.off(el, 'dragleave', handleDragLeave)
|
|
_.off(el, 'dragend', handleDragEnd)
|
|
_.off(el, 'drop', handleDrop)
|
|
|
|
_.off(el, 'touchstart', handleDragStart)
|
|
_.off(el, 'touchmove', handleDragEnter)
|
|
_.off(el, 'touchend', handleDragEnd)
|
|
}
|
|
|
|
Vue.prototype.$dragging = $dragging
|
|
if (!isPreVue) {
|
|
Vue.directive('dragging', {
|
|
bind: addDragItem,
|
|
update(el, binding, vnode) {
|
|
const DDD = dragData.new(binding.value.group)
|
|
const item = binding.value.item
|
|
const list = binding.value.list
|
|
|
|
const drag_key = vnode.key
|
|
const old_item = DDD.KEY_MAP[drag_key]
|
|
if (item && old_item !== item) {
|
|
DDD.KEY_MAP[drag_key] = item
|
|
}
|
|
if (list && DDD.List !== list) {
|
|
DDD.List = list
|
|
}
|
|
},
|
|
unbind : removeDragItem
|
|
})
|
|
} else {
|
|
Vue.directive('dragging', {
|
|
update (newValue, oldValue) {
|
|
addDragItem(this.el, {
|
|
modifiers: this.modifiers,
|
|
arg: this.arg,
|
|
value: newValue,
|
|
oldValue: oldValue
|
|
})
|
|
},
|
|
unbind (newValue, oldValue) {
|
|
removeDragItem(this.el, {
|
|
modifiers: this.modifiers,
|
|
arg: this.arg,
|
|
value: newValue?newValue:{group: this.el.getAttribute('drag_group')},
|
|
oldValue: oldValue
|
|
})
|
|
}
|
|
})
|
|
}
|
|
}
|
|
|