Skip to content

Commit

Permalink
move directives to there own files
Browse files Browse the repository at this point in the history
  • Loading branch information
nruffing committed Jan 6, 2024
1 parent c3e23cf commit 8462b8b
Show file tree
Hide file tree
Showing 9 changed files with 521 additions and 120 deletions.
116 changes: 6 additions & 110 deletions lib/DragonDropVue.ts
Original file line number Diff line number Diff line change
@@ -1,10 +1,9 @@
import { type App, type Directive, type DirectiveBinding, type Plugin, type VNode } from 'vue'
import { addClasses, addEventHandler, removeEventHandler } from './htmlHelpers'
import type { DragonDropVueDragOptions, DragonDropVueOptions } from './options'
import { type App } from 'vue'
import type { DragonDropVueOptions } from './options'
import constants from './constants'
import { onDragEnd, onDragEnter, onDragLeave, onDragOver, onDragStart, onDrop } from './eventHandlers'
import { log } from './logger'
import { NativeEventVue } from 'native-event-vue'
import { useDragDirective } from './drag'
import { useDropDirective } from './drop'

export default {
install: (app: App, options: DragonDropVueOptions = {}) => {
Expand All @@ -21,114 +20,11 @@ export default {
/*
* v-drag
*/
app.directive(opts.dragDirectiveName!, {
beforeMount: (
el: HTMLElement,
binding: DirectiveBinding<DragonDropVueDragOptions | false>,
vnode: VNode<any, HTMLElement>,
prevVnode: VNode<any, HTMLElement> | null,
) => {
if (binding.value === false) {
return
}
setupDrag(el, binding.value)
log({ eventName: 'drag | beforeMount', domEl: el, dragOpts: binding.value, opts })
},
updated: (
el: HTMLElement,
binding: DirectiveBinding<DragonDropVueDragOptions | false>,
vnode: VNode<any, HTMLElement>,
prevVnode: VNode<any, HTMLElement> | null,
) => {
if (binding.value === false) {
el.setAttribute('draggable', 'false')
} else if (el.getAttribute('draggable') !== 'true') {
setupDrag(el, binding.value)
}
log({ eventName: 'drag | updated', domEl: el, dragOpts: binding.value, opts })
},
beforeUnmount: (
el: HTMLElement,
binding: DirectiveBinding<DragonDropVueDragOptions | false>,
vnode: VNode<any, HTMLElement>,
prevVnode: VNode<any, HTMLElement> | null,
) => {
// remove drag events
removeEventHandler(el, 'dragstart', opts)
removeEventHandler(el, 'dragend', opts)
log({ eventName: 'drag | beforeUnmount', domEl: el, dragOpts: binding.value, opts })
},
} as Directive<HTMLElement, DragonDropVueDragOptions | false>)

function setupDrag(el: HTMLElement, dragOpts: DragonDropVueDragOptions) {
// add css classes
addClasses(el, [constants.dragClass, opts.dragClass])

// add draggable attribute
el.setAttribute('draggable', 'true')

// wire drag events
addEventHandler(el, 'dragstart', ev => onDragStart(ev, dragOpts, opts), opts)
addEventHandler(el, 'dragend', ev => onDragEnd(ev, dragOpts, opts), opts)
}
app.directive(opts.dragDirectiveName!, useDragDirective(opts))

/*
* v-drop
*/
app.directive(opts.dropDirectiveName!, {
beforeMount: (
el: HTMLElement,
binding: DirectiveBinding<DragonDropVueDragOptions | false>,
vnode: VNode<any, HTMLElement>,
prevVnode: VNode<any, HTMLElement> | null,
) => {
if (binding.value === false) {
return
}

setupDrop(el, binding.value)
log({ eventName: 'drop | beforeMount', domEl: el, dragOpts: binding.value, opts })
},
updated: (
el: HTMLElement,
binding: DirectiveBinding<DragonDropVueDragOptions | false>,
vnode: VNode<any, HTMLElement>,
prevVnode: VNode<any, HTMLElement> | null,
) => {
if (binding.value === false) {
return
}

const domEl = el as HTMLElement
if (!domEl.classList.contains(constants.dropClass)) {
setupDrop(el, binding.value)
}
log({ eventName: 'drop | updated', domEl: el, dragOpts: binding.value, opts })
},
beforeUnmount: (
el: HTMLElement,
binding: DirectiveBinding<DragonDropVueDragOptions | false>,
vnode: VNode<any, HTMLElement>,
prevVnode: VNode<any, HTMLElement> | null,
) => {
// remove drag events
removeEventHandler(el, 'dragover', opts)
removeEventHandler(el, 'dragenter', opts)
removeEventHandler(el, 'dragleave', opts)
removeEventHandler(el, 'drop', opts)
log({ eventName: 'drop | beforeUnmount', domEl: el, dragOpts: binding.value, opts })
},
} as Directive<HTMLElement, DragonDropVueDragOptions | false>)

function setupDrop(el: HTMLElement, dragOpts: DragonDropVueDragOptions) {
// add css classes
addClasses(el, [constants.dropClass, opts.dropClass])

// wire drag events
addEventHandler(el, 'dragover', ev => onDragOver(ev, dragOpts, opts), opts)
addEventHandler(el, 'dragenter', ev => onDragEnter(ev, dragOpts, opts), opts)
addEventHandler(el, 'dragleave', ev => onDragLeave(ev, dragOpts, opts), opts)
addEventHandler(el, 'drop', ev => onDrop(ev, dragOpts, opts), opts)
}
app.directive(opts.dropDirectiveName!, useDropDirective(opts))
},
}
2 changes: 2 additions & 0 deletions lib/constants.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
import { DebounceMode } from 'native-event-vue'
import type { DragonDropVueOptions } from './options'

export default {
Expand All @@ -10,5 +11,6 @@ export default {
dragDirectiveName: 'drag',
dropDirectiveName: 'drop',
dragOverDebounceMs: 300,
dragOverDebounceMode: DebounceMode.MaximumFrequency,
} as DragonDropVueOptions,
}
59 changes: 59 additions & 0 deletions lib/drag.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
import type { Directive, DirectiveBinding, VNode } from 'vue'
import type { DragonDropVueDragOptions, DragonDropVueOptions } from './options'
import { log } from './logger'
import { addClasses, addEventHandler, removeEventHandler } from './htmlHelpers'
import constants from './constants'
import { onDragEnd, onDragStart } from './eventHandlers'

export function useDragDirective(opts: DragonDropVueOptions) {
return {
beforeMount: (
el: HTMLElement,
binding: DirectiveBinding<DragonDropVueDragOptions | false>,
vnode: VNode<any, HTMLElement>,
prevVnode: VNode<any, HTMLElement> | null,
) => {
if (binding.value === false) {
return
}
setupDrag(el, binding.value, opts)
log({ eventName: 'drag | beforeMount', domEl: el, dragOpts: binding.value, opts })
},
updated: (
el: HTMLElement,
binding: DirectiveBinding<DragonDropVueDragOptions | false>,
vnode: VNode<any, HTMLElement>,
prevVnode: VNode<any, HTMLElement> | null,
) => {
if (binding.value === false) {
el.setAttribute('draggable', 'false')
} else if (el.getAttribute('draggable') !== 'true') {
setupDrag(el, binding.value, opts)
}
log({ eventName: 'drag | updated', domEl: el, dragOpts: binding.value, opts })
},
beforeUnmount: (
el: HTMLElement,
binding: DirectiveBinding<DragonDropVueDragOptions | false>,
vnode: VNode<any, HTMLElement>,
prevVnode: VNode<any, HTMLElement> | null,
) => {
// remove drag events
removeEventHandler(el, 'dragstart', opts)
removeEventHandler(el, 'dragend', opts)
log({ eventName: 'drag | beforeUnmount', domEl: el, dragOpts: binding.value, opts })
},
} as Directive<HTMLElement, DragonDropVueDragOptions | false>
}

function setupDrag(el: HTMLElement, dragOpts: DragonDropVueDragOptions, opts: DragonDropVueOptions) {
// add css classes
addClasses(el, [constants.dragClass, opts.dragClass])

// add draggable attribute
el.setAttribute('draggable', 'true')

// wire drag events
addEventHandler(el, 'dragstart', ev => onDragStart(ev, dragOpts, opts), dragOpts, opts)
addEventHandler(el, 'dragend', ev => onDragEnd(ev, dragOpts, opts), dragOpts, opts)
}
64 changes: 64 additions & 0 deletions lib/drop.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,64 @@
import type { Directive, DirectiveBinding, VNode } from 'vue'
import constants from './constants'
import { onDragEnter, onDragLeave, onDragOver, onDrop } from './eventHandlers'
import { addClasses, addEventHandler, removeEventHandler } from './htmlHelpers'
import type { DragonDropVueDragOptions, DragonDropVueOptions } from './options'
import { log } from './logger'

export function useDropDirective(opts: DragonDropVueOptions) {
return {
beforeMount: (
el: HTMLElement,
binding: DirectiveBinding<DragonDropVueDragOptions | false>,
vnode: VNode<any, HTMLElement>,
prevVnode: VNode<any, HTMLElement> | null,
) => {
if (binding.value === false) {
return
}

setupDrop(el, binding.value, opts)
log({ eventName: 'drop | beforeMount', domEl: el, dragOpts: binding.value, opts })
},
updated: (
el: HTMLElement,
binding: DirectiveBinding<DragonDropVueDragOptions | false>,
vnode: VNode<any, HTMLElement>,
prevVnode: VNode<any, HTMLElement> | null,
) => {
if (binding.value === false) {
return
}

const domEl = el as HTMLElement
if (!domEl.classList.contains(constants.dropClass)) {
setupDrop(el, binding.value, opts)
}
log({ eventName: 'drop | updated', domEl: el, dragOpts: binding.value, opts })
},
beforeUnmount: (
el: HTMLElement,
binding: DirectiveBinding<DragonDropVueDragOptions | false>,
vnode: VNode<any, HTMLElement>,
prevVnode: VNode<any, HTMLElement> | null,
) => {
// remove drag events
removeEventHandler(el, 'dragover', opts)
removeEventHandler(el, 'dragenter', opts)
removeEventHandler(el, 'dragleave', opts)
removeEventHandler(el, 'drop', opts)
log({ eventName: 'drop | beforeUnmount', domEl: el, dragOpts: binding.value, opts })
},
} as Directive<HTMLElement, DragonDropVueDragOptions | false>
}

function setupDrop(el: HTMLElement, dragOpts: DragonDropVueDragOptions, opts: DragonDropVueOptions) {
// add css classes
addClasses(el, [constants.dropClass, opts.dropClass])

// wire drag events
addEventHandler(el, 'dragover', ev => onDragOver(ev, dragOpts, opts), dragOpts, opts)
addEventHandler(el, 'dragenter', ev => onDragEnter(ev, dragOpts, opts), dragOpts, opts)
addEventHandler(el, 'dragleave', ev => onDragLeave(ev, dragOpts, opts), dragOpts, opts)
addEventHandler(el, 'drop', ev => onDrop(ev, dragOpts, opts), dragOpts, opts)
}
15 changes: 11 additions & 4 deletions lib/htmlHelpers.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { log } from './logger'
import type { DragonDropVueOptions } from './options'
import type { DragonDropVueDragOptions, DragonDropVueOptions } from './options'
import { useNativeEvent, resolveEventPropNamePrefix } from 'native-event-vue'

export function addClasses(domEl: HTMLElement, classes: (string | undefined)[]) {
Expand All @@ -14,9 +14,16 @@ export function removeClasses(domEl: HTMLElement, classes: (string | undefined)[
}
}

export function addEventHandler(domEl: HTMLElement, eventName: string, listener: (ev: DragEvent) => any, opts: DragonDropVueOptions) {
const debounceMs = eventName === 'dragover' ? opts.dragOverDebounceMs : undefined
useNativeEvent(domEl, eventName, listener as (ev: Event) => any, undefined, debounceMs)
export function addEventHandler(
domEl: HTMLElement,
eventName: string,
listener: (ev: DragEvent) => any,
dragOpts: DragonDropVueDragOptions,
opts: DragonDropVueOptions,
) {
const debounceMs = eventName === 'dragover' ? dragOpts.dragOverDebounceMs ?? opts.dragOverDebounceMs : undefined
const debounceMode = eventName === 'dragover' ? dragOpts.dragOverDebounceMode ?? opts.dragOverDebounceMode : undefined
useNativeEvent(domEl, eventName, listener as (ev: Event) => any, undefined, debounceMs, debounceMode)
log({ eventName: `addEventHandler | ${eventName}`, domEl, opts })
}

Expand Down
3 changes: 2 additions & 1 deletion lib/main.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import DragonDropVue from './DragonDropVue'
import type { DragonDropVueOptions, DragonDropVueDragOptions, DragonDropVueDragImageOptions } from './options'
import { DropEffect } from './htmlHelpers'
import { DebounceMode } from 'native-event-vue'

export { DragonDropVue, type DragonDropVueOptions, type DragonDropVueDragOptions, type DragonDropVueDragImageOptions, DropEffect }
export { DragonDropVue, type DragonDropVueOptions, type DragonDropVueDragOptions, type DragonDropVueDragImageOptions, DropEffect, DebounceMode }
4 changes: 4 additions & 0 deletions lib/options.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import type { Component } from 'vue'
import type { DropEffect } from './htmlHelpers'
import type { DebounceMode } from 'native-event-vue'

export interface DragonDropVueOptions {
dragDirectiveName?: string
Expand All @@ -10,6 +11,7 @@ export interface DragonDropVueOptions {
dropClass?: string
dragOverClass?: string
dragOverDebounceMs?: number
dragOverDebounceMode?: DebounceMode
}

export interface DragonDropVueDragOptions<T = any> {
Expand Down Expand Up @@ -47,6 +49,8 @@ export interface DragonDropVueDragOptions<T = any> {
onDrop?: (domEl: HTMLElement, dragEvent: DragEvent, dragOptions: DragonDropVueDragOptions<T>, options: DragonDropVueOptions) => boolean | undefined
dropEffect?: DropEffect
dragImage?: DragonDropVueDragImageOptions
dragOverDebounceMs?: number
dragOverDebounceMode?: DebounceMode
}

export interface DragonDropVueDragImageOptions {
Expand Down
4 changes: 3 additions & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -84,6 +84,7 @@
"@types/debounce": "^1.2.4",
"@types/node": "^18.11.9",
"@vitejs/plugin-vue": "^4.5.2",
"@vitest/browser": "^1.1.1",
"@vue/tsconfig": "^0.5.1",
"husky": "^8.0.3",
"lint-staged": "^15.2.0",
Expand All @@ -94,6 +95,7 @@
"tslib": "^2.6.2",
"typescript": "~5.3.3",
"vite": "^5.0.10",
"vitest": "^1.1.1",
"vue": "^3.3.13",
"vue-router": "^4.2.5",
"vue-tsc": "^1.8.25"
Expand All @@ -102,6 +104,6 @@
"*.{ts,json,vue,css}": "prettier --write"
},
"dependencies": {
"native-event-vue": "^1.0.1"
"native-event-vue": "^1.1.0"
}
}

0 comments on commit 8462b8b

Please sign in to comment.