mirror of
https://gitee.com/element-plus/element-plus.git
synced 2024-12-15 18:01:24 +08:00
d0b37cdf5f
* feat/implementing-trigger-for-popper - Implementing trigger for popper - Refactors against click-outside * feat(popper): implemented trigger for popper
97 lines
2.5 KiB
TypeScript
97 lines
2.5 KiB
TypeScript
import isServer from '@element-plus/utils/isServer'
|
|
import { on } from '@element-plus/utils/dom'
|
|
|
|
import type { DirectiveBinding, ObjectDirective, ComponentPublicInstance } from 'vue'
|
|
|
|
type DocumentHandler = <T extends MouseEvent>(mouseup: T, mousedown: T) => void;
|
|
|
|
type FlushList = Map<
|
|
HTMLElement,
|
|
{
|
|
documentHandler: DocumentHandler
|
|
bindingFn: (...args: unknown[]) => unknown
|
|
}
|
|
>;
|
|
|
|
const nodeList: FlushList = new Map()
|
|
|
|
let startClick: MouseEvent
|
|
|
|
if (!isServer) {
|
|
on(document, 'mousedown', (e: MouseEvent) => (startClick = e))
|
|
on(document, 'mouseup', (e: MouseEvent) => {
|
|
for (const { documentHandler } of nodeList.values()) {
|
|
documentHandler(e, startClick)
|
|
}
|
|
})
|
|
}
|
|
|
|
function createDocumentHandler(
|
|
el: HTMLElement,
|
|
binding: DirectiveBinding,
|
|
): DocumentHandler {
|
|
let excludes: HTMLElement[] = []
|
|
if (Array.isArray(binding.arg)) {
|
|
excludes = binding.arg
|
|
} else {
|
|
// due to current implementation on binding type is wrong the type casting is necessary here
|
|
excludes.push(binding.arg as unknown as HTMLElement)
|
|
}
|
|
return function(mouseup, mousedown) {
|
|
const popperRef = (binding.instance as ComponentPublicInstance<{
|
|
popperRef: Nullable<HTMLElement>
|
|
}>).popperRef
|
|
const mouseUpTarget = mouseup.target as Node
|
|
const mouseDownTarget = mousedown.target as Node
|
|
const isBound = !binding || !binding.instance
|
|
const isTargetExists = !mouseUpTarget || !mouseDownTarget
|
|
const isContainedByEl = el.contains(mouseUpTarget) || el.contains(mouseDownTarget)
|
|
const isSelf = el === mouseUpTarget
|
|
|
|
const isTargetExcluded =
|
|
( excludes.length &&
|
|
excludes.some(item => item?.contains(mouseUpTarget))
|
|
) || (
|
|
excludes.length && excludes.includes(mouseDownTarget as HTMLElement)
|
|
)
|
|
const isContainedByPopper = (
|
|
popperRef &&
|
|
(
|
|
popperRef.contains(mouseUpTarget) ||
|
|
popperRef.contains(mouseDownTarget)
|
|
)
|
|
)
|
|
if (
|
|
isBound ||
|
|
isTargetExists ||
|
|
isContainedByEl ||
|
|
isSelf ||
|
|
isTargetExcluded ||
|
|
isContainedByPopper
|
|
) {
|
|
return
|
|
}
|
|
binding.value()
|
|
}
|
|
}
|
|
|
|
const ClickOutside: ObjectDirective = {
|
|
beforeMount(el, binding) {
|
|
nodeList.set(el, {
|
|
documentHandler: createDocumentHandler(el, binding),
|
|
bindingFn: binding.value,
|
|
})
|
|
},
|
|
updated(el, binding) {
|
|
nodeList.set(el, {
|
|
documentHandler: createDocumentHandler(el, binding),
|
|
bindingFn: binding.value,
|
|
})
|
|
},
|
|
unmounted(el) {
|
|
nodeList.delete(el)
|
|
},
|
|
}
|
|
|
|
export default ClickOutside
|