2024-02-07 01:37:07 +08:00
|
|
|
import {
|
|
|
|
getCurrentScope,
|
|
|
|
onEffectCleanup,
|
|
|
|
onScopeDispose,
|
|
|
|
} from '@vue/reactivity'
|
2024-03-14 15:50:58 +08:00
|
|
|
import {
|
|
|
|
MetadataKind,
|
|
|
|
getMetadata,
|
|
|
|
recordEventMetadata,
|
|
|
|
} from '../componentMetadata'
|
2024-02-07 01:37:07 +08:00
|
|
|
import { withKeys, withModifiers } from '@vue/runtime-dom'
|
2024-12-04 13:50:54 +08:00
|
|
|
import { queuePostFlushCb } from '@vue/runtime-dom'
|
2024-02-07 01:37:07 +08:00
|
|
|
|
|
|
|
export function addEventListener(
|
2024-03-14 14:25:54 +08:00
|
|
|
el: Element,
|
2024-02-07 01:37:07 +08:00
|
|
|
event: string,
|
|
|
|
handler: (...args: any) => any,
|
|
|
|
options?: AddEventListenerOptions,
|
|
|
|
) {
|
|
|
|
el.addEventListener(event, handler, options)
|
2024-08-09 16:56:59 +08:00
|
|
|
return (): void => el.removeEventListener(event, handler, options)
|
2024-02-07 01:37:07 +08:00
|
|
|
}
|
|
|
|
|
2024-02-25 00:40:00 +08:00
|
|
|
interface ModifierOptions {
|
|
|
|
modifiers?: string[]
|
|
|
|
keys?: string[]
|
|
|
|
}
|
|
|
|
|
2024-02-26 15:18:16 +08:00
|
|
|
export function on(
|
2024-03-14 14:25:54 +08:00
|
|
|
el: Element,
|
2024-02-26 15:18:16 +08:00
|
|
|
event: string,
|
|
|
|
handlerGetter: () => undefined | ((...args: any[]) => any),
|
2024-03-07 17:40:51 +08:00
|
|
|
options: AddEventListenerOptions &
|
|
|
|
ModifierOptions & { effect?: boolean } = {},
|
2024-08-09 16:56:59 +08:00
|
|
|
): void {
|
2024-02-26 15:18:16 +08:00
|
|
|
const handler: DelegatedHandler = eventHandler(handlerGetter, options)
|
2024-03-05 23:39:25 +08:00
|
|
|
let cleanupEvent: (() => void) | undefined
|
2024-04-19 16:19:56 +08:00
|
|
|
queuePostFlushCb(() => {
|
2024-03-05 23:39:25 +08:00
|
|
|
cleanupEvent = addEventListener(el, event, handler, options)
|
|
|
|
})
|
2024-03-01 18:15:00 +08:00
|
|
|
|
2024-03-07 17:40:51 +08:00
|
|
|
if (options.effect) {
|
2024-03-05 23:39:25 +08:00
|
|
|
onEffectCleanup(cleanup)
|
2024-03-07 17:40:51 +08:00
|
|
|
} else if (getCurrentScope()) {
|
2024-03-05 23:39:25 +08:00
|
|
|
onScopeDispose(cleanup)
|
|
|
|
}
|
2024-02-26 15:18:16 +08:00
|
|
|
|
2024-03-05 23:39:25 +08:00
|
|
|
function cleanup() {
|
|
|
|
cleanupEvent && cleanupEvent()
|
|
|
|
}
|
2024-02-25 15:11:25 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
export type DelegatedHandler = {
|
|
|
|
(...args: any[]): any
|
|
|
|
delegate?: boolean
|
|
|
|
}
|
|
|
|
|
2024-02-26 15:18:16 +08:00
|
|
|
export function delegate(
|
2024-02-07 01:37:07 +08:00
|
|
|
el: HTMLElement,
|
|
|
|
event: string,
|
|
|
|
handlerGetter: () => undefined | ((...args: any[]) => any),
|
2024-02-27 16:46:49 +08:00
|
|
|
options: ModifierOptions = {},
|
2024-08-09 16:56:59 +08:00
|
|
|
): void {
|
2024-02-25 15:11:25 +08:00
|
|
|
const handler: DelegatedHandler = eventHandler(handlerGetter, options)
|
2024-02-26 15:18:16 +08:00
|
|
|
handler.delegate = true
|
2024-03-01 18:15:00 +08:00
|
|
|
recordEventMetadata(el, event, handler)
|
2024-02-25 00:40:00 +08:00
|
|
|
}
|
|
|
|
|
2024-02-25 15:11:25 +08:00
|
|
|
function eventHandler(
|
2024-02-25 00:40:00 +08:00
|
|
|
getter: () => undefined | ((...args: any[]) => any),
|
|
|
|
{ modifiers, keys }: ModifierOptions = {},
|
2024-02-07 01:37:07 +08:00
|
|
|
) {
|
2024-02-25 00:40:00 +08:00
|
|
|
return (...args: any[]) => {
|
|
|
|
let handler = getter()
|
2024-02-23 13:21:04 +08:00
|
|
|
if (!handler) return
|
2024-02-07 01:37:07 +08:00
|
|
|
|
2024-04-29 16:49:04 +08:00
|
|
|
if (modifiers) handler = withModifiers(handler, modifiers as any[])
|
2024-02-25 15:11:25 +08:00
|
|
|
if (keys) handler = withKeys(handler, keys)
|
2024-02-23 13:21:04 +08:00
|
|
|
handler && handler(...args)
|
|
|
|
}
|
2024-02-25 00:40:00 +08:00
|
|
|
}
|
2024-02-07 01:37:07 +08:00
|
|
|
|
2024-02-25 00:40:00 +08:00
|
|
|
/**
|
|
|
|
* Event delegation borrowed from solid
|
|
|
|
*/
|
|
|
|
const delegatedEvents = Object.create(null)
|
2024-02-07 01:37:07 +08:00
|
|
|
|
2024-08-09 16:56:59 +08:00
|
|
|
export const delegateEvents = (...names: string[]): void => {
|
2024-02-25 00:40:00 +08:00
|
|
|
for (const name of names) {
|
|
|
|
if (!delegatedEvents[name]) {
|
|
|
|
delegatedEvents[name] = true
|
|
|
|
// eslint-disable-next-line no-restricted-globals
|
|
|
|
document.addEventListener(name, delegatedEventHandler)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
const delegatedEventHandler = (e: Event) => {
|
|
|
|
let node = ((e.composedPath && e.composedPath()[0]) || e.target) as any
|
|
|
|
if (e.target !== node) {
|
|
|
|
Object.defineProperty(e, 'target', {
|
|
|
|
configurable: true,
|
|
|
|
value: node,
|
|
|
|
})
|
|
|
|
}
|
|
|
|
Object.defineProperty(e, 'currentTarget', {
|
|
|
|
configurable: true,
|
|
|
|
get() {
|
|
|
|
// eslint-disable-next-line no-restricted-globals
|
|
|
|
return node || document
|
|
|
|
},
|
|
|
|
})
|
|
|
|
while (node !== null) {
|
2024-03-01 18:15:00 +08:00
|
|
|
const handlers = getMetadata(node)[MetadataKind.event][e.type]
|
|
|
|
if (handlers) {
|
|
|
|
for (const handler of handlers) {
|
|
|
|
if (handler.delegate && !node.disabled) {
|
|
|
|
handler(e)
|
|
|
|
if (e.cancelBubble) return
|
|
|
|
}
|
|
|
|
}
|
2024-02-25 00:40:00 +08:00
|
|
|
}
|
|
|
|
node =
|
|
|
|
node.host && node.host !== node && node.host instanceof Node
|
|
|
|
? node.host
|
|
|
|
: node.parentNode
|
2024-02-07 01:37:07 +08:00
|
|
|
}
|
|
|
|
}
|
2024-03-14 14:15:45 +08:00
|
|
|
|
|
|
|
export function setDynamicEvents(
|
|
|
|
el: HTMLElement,
|
|
|
|
events: Record<string, (...args: any[]) => any>,
|
2024-08-09 16:56:59 +08:00
|
|
|
): void {
|
2024-05-04 01:33:40 +08:00
|
|
|
for (const name in events) {
|
|
|
|
on(el, name, () => events[name], { effect: true })
|
2024-03-14 14:15:45 +08:00
|
|
|
}
|
|
|
|
}
|