Files
element-plus/packages/directives/repeat-click/index.ts
qiang a038e00d48 fix(directives): [vRepeatClick] remove event listener during unmounted (#22439)
* fix(directives): [vRepeatClick] remove event listener during unmounted

* Update packages/directives/repeat-click/index.ts

Co-authored-by: btea <2356281422@qq.com>

* fix: type error

---------

Co-authored-by: btea <2356281422@qq.com>
2025-10-11 13:27:25 +02:00

81 lines
1.8 KiB
TypeScript

import { isFunction } from '@element-plus/utils'
import type { ObjectDirective } from 'vue'
export const REPEAT_INTERVAL = 100
export const REPEAT_DELAY = 600
const SCOPE = '_RepeatClick'
interface RepeatClickEl extends HTMLElement {
[SCOPE]: null | {
start?: (evt: MouseEvent) => void
clear?: () => void
}
}
export interface RepeatClickOptions {
interval?: number
delay?: number
handler: (...args: unknown[]) => unknown
}
export const vRepeatClick: ObjectDirective<
RepeatClickEl,
RepeatClickOptions | RepeatClickOptions['handler']
> = {
beforeMount(el, binding) {
const value = binding.value
const { interval = REPEAT_INTERVAL, delay = REPEAT_DELAY } = isFunction(
value
)
? {}
: value
let intervalId: ReturnType<typeof setInterval> | undefined
let delayId: ReturnType<typeof setTimeout> | undefined
const handler = () => (isFunction(value) ? value() : value.handler())
const clear = () => {
if (delayId) {
clearTimeout(delayId)
delayId = undefined
}
if (intervalId) {
clearInterval(intervalId)
intervalId = undefined
}
}
const start = (evt: MouseEvent) => {
if (evt.button !== 0) return
clear()
handler()
document.addEventListener('mouseup', clear, { once: true })
delayId = setTimeout(() => {
intervalId = setInterval(() => {
handler()
}, interval)
}, delay)
}
el[SCOPE] = { start, clear }
el.addEventListener('mousedown', start)
},
unmounted(el) {
if (!el[SCOPE]) return
const { start, clear } = el[SCOPE]
if (start) {
el.removeEventListener('mousedown', start)
}
if (clear) {
clear()
document.removeEventListener('mouseup', clear)
}
el[SCOPE] = null
},
}