@antv/x6
Version:
JavaScript diagramming library that uses SVG and HTML for rendering
183 lines (166 loc) • 4.33 kB
text/typescript
import { type AsyncBoolean, toAsyncBoolean } from '../function'
import type {
EventArgs,
EventNames,
Handler,
NamesWithArrayArgs,
OptionalNormalNames,
OtherNames,
RequiredNormalNames,
UnknownNames,
} from './types'
import { call } from './util'
export class Events<Args extends EventArgs = any> {
private listeners: { [name: string]: any[] } = {}
on<Name extends EventNames<Args>>(
name: Name,
handler: Handler<Args[Name]>,
context?: any,
): this
on<Name extends UnknownNames<Args>>(
name: Name,
handler: Handler<any>,
context?: any,
): this
on<Name extends EventNames<Args>>(
name: Name,
handler: Handler<Args[Name]>,
context?: any,
) {
if (handler == null) {
return this
}
if (!this.listeners[name]) {
this.listeners[name] = []
}
const cache = this.listeners[name]
cache.push(handler, context)
return this
}
once<Name extends EventNames<Args>>(
name: Name,
handler: Handler<Args[Name]>,
context?: any,
): this
once<Name extends UnknownNames<Args>>(
name: Name,
handler: Handler<any>,
context?: any,
): this
once<Name extends EventNames<Args>>(
name: Name,
handler: Handler<Args[Name]>,
context?: any,
) {
const cb = (...args: any) => {
this.off(name, cb as any)
return call([handler, context], args)
}
return this.on(name, cb as any, this)
}
off(): this
off(name: null, handler: Handler<any>): this
off(name: null, handler: null, context: any): this
off<Name extends EventNames<Args>>(
name: Name,
handler?: Handler<Args[Name]>,
context?: any,
): this
off<Name extends UnknownNames<Args>>(
name: Name,
handler?: Handler<any>,
context?: any,
): this
off(name?: string | null, handler?: Handler<any> | null, context?: any) {
// remove all events.
if (!(name || handler || context)) {
this.listeners = {}
return this
}
const listeners = this.listeners
const names = name ? [name] : Object.keys(listeners)
names.forEach((n) => {
const cache = listeners[n]
if (!cache) {
return
}
// remove all events with specified name.
if (!(handler || context)) {
delete listeners[n]
return
}
for (let i = cache.length - 2; i >= 0; i -= 2) {
if (
!(
(handler && cache[i] !== handler) ||
(context && cache[i + 1] !== context)
)
) {
cache.splice(i, 2)
}
}
})
return this
}
trigger<Name extends OptionalNormalNames<Args>>(name: Name): AsyncBoolean
trigger<Name extends RequiredNormalNames<Args>>(
name: Name,
args: Args[Name],
): AsyncBoolean
trigger<Name extends NamesWithArrayArgs<Args>>(
name: Name,
...args: Args[Name]
): AsyncBoolean
trigger<Name extends OtherNames<Args>>(
name: Name,
args?: Args[Name],
): AsyncBoolean
trigger<Name extends OtherNames<Args>>(
name: Name,
...args: Args[Name]
): AsyncBoolean
trigger<Name extends UnknownNames<Args>>(
name: Name,
...args: any[]
): AsyncBoolean
trigger<Name extends EventNames<Args>>(name: Name, ...args: any[]) {
let returned: AsyncBoolean = true
if (name !== '*') {
const list = this.listeners[name]
if (list != null) {
returned = call([...list], args)
}
}
const list = this.listeners['*']
if (list != null) {
return toAsyncBoolean([returned, call([...list], [name, ...args])])
}
return returned
}
/**
* Triggers event with specified event name. Unknown names
* will cause a typescript type error.
*/
protected emit<Name extends OptionalNormalNames<Args>>(
name: Name,
): AsyncBoolean
protected emit<Name extends RequiredNormalNames<Args>>(
name: Name,
args: Args[Name],
): AsyncBoolean
protected emit<Name extends NamesWithArrayArgs<Args>>(
name: Name,
...args: Args[Name]
): AsyncBoolean
protected emit<Name extends OtherNames<Args>>(
name: Name,
args?: Args[Name],
): AsyncBoolean
protected emit<Name extends OtherNames<Args>>(
name: Name,
...args: Args[Name]
): AsyncBoolean
protected emit(name: any, ...args: any[]) {
return this.trigger(name, ...args)
}
}