Skip to content

Commit

Permalink
refactor: use vue-directive-normalizer and refactor drag, with extra …
Browse files Browse the repository at this point in the history
…test cases
  • Loading branch information
Justineo committed May 16, 2019
1 parent a6bcd20 commit 9be76da
Show file tree
Hide file tree
Showing 3 changed files with 129 additions and 83 deletions.
136 changes: 53 additions & 83 deletions packages/veui/src/directives/drag.js
Original file line number Diff line number Diff line change
@@ -1,8 +1,7 @@
import { normalize } from 'vue-directive-normalizer'
import {
noop,
isObject,
isFunction,
find,
noop,
get,
keys,
isString,
Expand All @@ -21,6 +20,21 @@ config.defaults({

const HANDLERS = {}

const OPTIONS_SCHEMA = {
arg: 'targets[]',
modifiers: () => ({
type: keys(HANDLERS),
axis: [null, 'x', 'y']
}),
defaults: {
draggable: true,
dragstart: noop,
drag: noop,
dragend: noop,
ready: noop
}
}

export function registerHandler (name, Handler) {
if (!(Handler.prototype instanceof BaseHandler)) {
throw new TypeError('The handler class must derive from `BaseHandler`.')
Expand All @@ -41,106 +55,48 @@ function clear (el) {
el.dragData = null
}

function parseParams (el, { arg, value, modifiers }, vnode) {
// 解析 target
let targets = []
if (arg) {
targets = arg.split(',')
} else {
targets = get(value, 'targets', [])
}

// 解析 type
let type = find(keys(HANDLERS), t => modifiers[t])
// 如果 modifiers 里面没有 type,就到 value 里面去找
if (!type) {
type = get(value, 'type')
}
function getOptions (binding, vnode) {
let options = normalize(binding, OPTIONS_SCHEMA)

// 解析 draggable
let draggable = get(value, 'draggable', true)
let { containment } = options

// 解析 containment
let containment = get(value, 'containment')
// 如果 containment 不是特殊配置,也不是 object ,或者是 object ,
// 但是没有完整的 top 、 left 、 width 、 height 属性,
// 就要看看用 containment 能不能选出 DOM Element 了。
function isSpecialSyntax (value) {
return isString(value) && value.indexOf(config.get('drag.prefix')) === 0
}
function isRect (value) {
return (
isObject(containment) &&
containment.hasOwnProperty('top') &&
containment.hasOwnProperty('left') &&
containment.hasOwnProperty('width') &&
containment.hasOwnProperty('height')
)
}
if (!isSpecialSyntax(containment) && !isRect(containment)) {
containment = getNodes(containment, vnode.context)
containment = get(containment, '[0]', null)
options.containment = get(getNodes(containment, vnode.context), '[0]', null)
}

// 解析 axis
let axis = find(['x', 'y'], item => modifiers[item])
if (!axis) {
axis = get(value, 'axis')
}

function parseFn (name) {
let fn = get(value, name, noop)
return isFunction(fn) ? fn : noop
}

// 解析 drag 系列回调函数
let dragstart = parseFn('dragstart')
let drag = parseFn('drag')
let dragend = parseFn('dragend')

// ready 回调
let ready = parseFn('ready')

return {
targets,
type,
draggable,
containment,
axis,
dragstart,
drag,
dragend,
ready
}
return options
}

function refresh (el, { modifiers, value, arg }, vnode) {
const params = parseParams(el, { arg, value, modifiers }, vnode)
function refresh (el, binding, vnode) {
const options = getOptions(binding, vnode)

const oldParams = el.dragOldParams
const oldOptions = el.dragOldParams
// 如果参数没发生变化,就不要刷新了
if (
difference(get(params, 'targets', []), get(oldParams, 'targets', []))
difference(get(options, 'targets', []), get(oldOptions, 'targets', []))
.length === 0 &&
isEqual(omit(params, 'targets'), omit(oldParams, 'targets'))
isEqual(omit(options, 'targets'), omit(oldOptions, 'targets'))
) {
return
}
el.dragOldParams = params
el.dragOldParams = options

if (el.dragData) {
el.dragData.handler.setOptions(params)
el.dragData.handler.setOptions(options)
} else {
let contextComponent = vnode.context
let handler = null
if (HANDLERS[params.type]) {
let Handler = HANDLERS[params.type]
handler = new Handler(params, contextComponent)
if (HANDLERS[options.type]) {
let Handler = HANDLERS[options.type]
handler = new Handler(options, contextComponent)
} else {
handler = new BaseHandler(params, contextComponent)
handler = new BaseHandler(options, contextComponent)
}

params.ready({ reset: () => handler.reset() })
options.ready({ reset: () => handler.reset() })

let dragData = {
dragging: false,
Expand All @@ -149,7 +105,7 @@ function refresh (el, { modifiers, value, arg }, vnode) {
handler,

mousedownHandler (event) {
if (!params.draggable || dragData.dragging) {
if (!options.draggable || dragData.dragging) {
return
}

Expand All @@ -159,7 +115,7 @@ function refresh (el, { modifiers, value, arg }, vnode) {
dragData.initY = clientY
contextComponent.$emit('dragstart', { event })
handler.start({ event })
params.dragstart({ event })
options.dragstart({ event })

function selectStartHandler (e) {
e.preventDefault()
Expand All @@ -178,7 +134,7 @@ function refresh (el, { modifiers, value, arg }, vnode) {
}
contextComponent.$emit('drag', dragParams)
handler.drag(dragParams)
params.drag(dragParams)
options.drag(dragParams)
}

function mouseupHandler (event) {
Expand All @@ -193,15 +149,15 @@ function refresh (el, { modifiers, value, arg }, vnode) {
}
contextComponent.$emit('dragend', dragParams)
handler.end(dragParams)
params.dragend(dragParams)
options.dragend(dragParams)

window.removeEventListener('mousemove', mouseMoveHandler)
window.removeEventListener('mouseup', mouseupHandler)
window.removeEventListener('selectstart', selectStartHandler)
}

// TODO: 非IE下面不用移除选区
document.getSelection().removeAllRanges()
window.getSelection().removeAllRanges()
window.addEventListener('selectstart', selectStartHandler)

window.addEventListener('mousemove', mouseMoveHandler)
Expand All @@ -214,6 +170,20 @@ function refresh (el, { modifiers, value, arg }, vnode) {
}
}

function isSpecialSyntax (value) {
return isString(value) && value.indexOf(config.get('drag.prefix')) === 0
}

function isRect (containment) {
return (
isObject(containment) &&
containment.hasOwnProperty('top') &&
containment.hasOwnProperty('left') &&
containment.hasOwnProperty('width') &&
containment.hasOwnProperty('height')
)
}

/**
* drag 指令基本使用方式如下:
*
Expand Down
4 changes: 4 additions & 0 deletions packages/veui/test/unit/env.js
Original file line number Diff line number Diff line change
Expand Up @@ -6,3 +6,7 @@ window.getComputedStyle = () => {
animationDuration: ''
}
}

window.getSelection = () => ({
removeAllRanges: () => {}
})
72 changes: 72 additions & 0 deletions packages/veui/test/unit/specs/directives/drag.spec.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,72 @@
import { mount } from '@vue/test-utils'
import drag from '@/directives/drag'

describe('directives/drag', () => {
it(`should receive correct params inside callbacks`, done => {
let results = []
const wrapper = mount({
directives: { drag },
template: `<div v-drag="{
dragstart: handleStart,
drag: handleMove,
dragend: handleEnd
}">foo</div>`,
methods: {
handleStart ({ event }) {
results.push({
x: event.clientX,
y: event.clientY
})
},
handleMove ({ event, distanceX, distanceY }) {
results.push({
x: event.clientX,
y: event.clientY,
dx: distanceX,
dy: distanceY
})
},
handleEnd ({ event, distanceX, distanceY }) {
results.push({
x: event.clientX,
y: event.clientY,
dx: distanceX,
dy: distanceY
})

expect(results).toEqual([
{ x: 5, y: 5 },
{ x: 105, y: 105, dx: 100, dy: 100 },
{ x: 205, y: 205, dx: 200, dy: 200 },
{ x: 205, y: 205, dx: 200, dy: 200 }
])

done()
}
}
})

wrapper.find('div').trigger('mousedown', {
clientX: 5,
clientY: 5
})
window.dispatchEvent(
new MouseEvent('mousemove', {
clientX: 105,
clientY: 105
})
)
window.dispatchEvent(
new MouseEvent('mousemove', {
clientX: 205,
clientY: 205
})
)
window.dispatchEvent(
new MouseEvent('mouseup', {
clientX: 205,
clientY: 205
})
)
})
})

0 comments on commit 9be76da

Please sign in to comment.