diff --git a/packages/veui/jest.config.js b/packages/veui/jest.config.js index 601082b1c..87625bae8 100644 --- a/packages/veui/jest.config.js +++ b/packages/veui/jest.config.js @@ -6,7 +6,7 @@ module.exports = { '^veui\\/(.*)': '/src/$1', '^veui-theme-one-icons\\/(.*)': '/../veui-theme-one-icons/icons/$1', - '^vue$': '/node_modules/vue/dist/vue.common.js' + '^vue$': '/node_modules/vue/dist/vue.common.prod.js' }, setupFiles: ['./test/unit/env.js'], snapshotSerializers: ['jest-serializer-vue'], diff --git a/packages/veui/src/directives/drag.js b/packages/veui/src/directives/drag.js index 74840da62..99887d4c7 100644 --- a/packages/veui/src/directives/drag.js +++ b/packages/veui/src/directives/drag.js @@ -1,8 +1,7 @@ +import { normalize } from 'vue-directive-normalizer' import { - noop, isObject, - isFunction, - find, + noop, get, keys, isString, @@ -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`.') @@ -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, @@ -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 } @@ -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() @@ -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) { @@ -193,7 +149,7 @@ 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) @@ -201,7 +157,7 @@ function refresh (el, { modifiers, value, arg }, vnode) { } // TODO: 非IE下面不用移除选区 - document.getSelection().removeAllRanges() + window.getSelection().removeAllRanges() window.addEventListener('selectstart', selectStartHandler) window.addEventListener('mousemove', mouseMoveHandler) @@ -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 指令基本使用方式如下: * diff --git a/packages/veui/test/unit/env.js b/packages/veui/test/unit/env.js index f4d21d834..de8cdbb50 100644 --- a/packages/veui/test/unit/env.js +++ b/packages/veui/test/unit/env.js @@ -6,3 +6,7 @@ window.getComputedStyle = () => { animationDuration: '' } } + +window.getSelection = () => ({ + removeAllRanges: () => {} +}) diff --git a/packages/veui/test/unit/specs/directives/drag.spec.js b/packages/veui/test/unit/specs/directives/drag.spec.js new file mode 100644 index 000000000..e1fe2a7fd --- /dev/null +++ b/packages/veui/test/unit/specs/directives/drag.spec.js @@ -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: `
foo
`, + 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 + }) + ) + }) +})