UNPKG

golden-layout

Version:
276 lines (240 loc) 10.1 kB
import { BrowserPopout } from '../controls/browser-popout'; import { Tab } from '../controls/tab'; import { ComponentItem } from '../items/component-item'; /** * A generic and very fast EventEmitter implementation. On top of emitting the actual event it emits an * {@link (EventEmitter:namespace).ALL_EVENT} event for every event triggered. This allows to hook into it and proxy events forwards * @public */ export class EventEmitter { /** @internal */ private _allEventSubscriptions: EventEmitter.UnknownCallback[] = []; /** @internal */ private _subscriptionsMap = new Map<string, EventEmitter.UnknownCallback[]>(); // eslint-disable-next-line @typescript-eslint/no-unused-vars tryBubbleEvent(name: string, args: unknown[]): void { // overridden by ContentItem } /** * Emit an event and notify listeners * * @param eventName - The name of the event * @param args - Additional arguments that will be passed to the listener */ emit<K extends keyof EventEmitter.EventParamsMap>(eventName: K, ...args: EventEmitter.EventParamsMap[K]): void { let subcriptions = this._subscriptionsMap.get(eventName); if (subcriptions !== undefined) { subcriptions = subcriptions.slice(); for (let i = 0; i < subcriptions.length; i++) { const subscription = subcriptions[i]; subscription(...args); } } this.emitAllEvent(eventName, args); this.tryBubbleEvent(eventName, args); } /** @internal */ emitUnknown(eventName: string, ...args: EventEmitter.UnknownParams): void { let subs = this._subscriptionsMap.get(eventName); if (subs !== undefined) { subs = subs.slice(); for (let i = 0; i < subs.length; i++) { subs[i](...args); } } this.emitAllEvent(eventName, args); this.tryBubbleEvent(eventName, args); } /* @internal **/ emitBaseBubblingEvent<K extends keyof EventEmitter.EventParamsMap>(eventName: K): void { const event = new EventEmitter.BubblingEvent(eventName, this); this.emitUnknown(eventName, event); } /** @internal */ emitUnknownBubblingEvent(eventName: string): void { const event = new EventEmitter.BubblingEvent(eventName, this); this.emitUnknown(eventName, event); } /** * Removes a listener for an event. * @param eventName - The name of the event * @param callback - The previously registered callback method (optional) */ removeEventListener<K extends keyof EventEmitter.EventParamsMap>(eventName: K, callback: EventEmitter.Callback<K>): void { const unknownCallback = callback as EventEmitter.UnknownCallback; this.removeUnknownEventListener(eventName, unknownCallback); } off<K extends keyof EventEmitter.EventParamsMap>(eventName: K, callback: EventEmitter.Callback<K>): void { this.removeEventListener(eventName, callback); } /** * Alias for off */ unbind = this.removeEventListener; /** * Alias for emit */ trigger = this.emit; /** * Listen for events * * @param eventName - The name of the event to listen to * @param callback - The callback to execute when the event occurs */ addEventListener<K extends keyof EventEmitter.EventParamsMap>(eventName: K, callback: EventEmitter.Callback<K>): void { const unknownCallback = callback as EventEmitter.UnknownCallback; this.addUnknownEventListener(eventName, unknownCallback); } on<K extends keyof EventEmitter.EventParamsMap>(eventName: K, callback: EventEmitter.Callback<K>): void { this.addEventListener(eventName, callback); } /** @internal */ private addUnknownEventListener(eventName: string, callback: EventEmitter.UnknownCallback): void { if (eventName === EventEmitter.ALL_EVENT) { this._allEventSubscriptions.push(callback); } else { let subscriptions = this._subscriptionsMap.get(eventName); if (subscriptions !== undefined) { subscriptions.push(callback); } else { subscriptions = [callback]; this._subscriptionsMap.set(eventName, subscriptions); } } } /** @internal */ private removeUnknownEventListener(eventName: string, callback: EventEmitter.UnknownCallback): void { if (eventName === EventEmitter.ALL_EVENT) { this.removeSubscription(eventName, this._allEventSubscriptions, callback); } else { const subscriptions = this._subscriptionsMap.get(eventName); if (subscriptions === undefined) { throw new Error('No subscribtions to unsubscribe for event ' + eventName); } else { this.removeSubscription(eventName, subscriptions, callback); } } } /** @internal */ private removeSubscription(eventName: string, subscriptions: EventEmitter.UnknownCallback[], callback: EventEmitter.UnknownCallback) { const idx = subscriptions.indexOf(callback); if (idx < 0) { throw new Error('Nothing to unbind for ' + eventName); } else { subscriptions.splice(idx, 1); } } /** @internal */ private emitAllEvent(eventName: string, args: unknown[]) { const allEventSubscriptionsCount = this._allEventSubscriptions.length; if (allEventSubscriptionsCount > 0) { const unknownArgs = args.slice() as EventEmitter.UnknownParams; unknownArgs.unshift(eventName); const allEventSubcriptions = this._allEventSubscriptions.slice(); for (let i = 0; i < allEventSubscriptionsCount; i++) { allEventSubcriptions[i](...unknownArgs); } } } } /** @public */ export namespace EventEmitter { /** * The name of the event that's triggered for every event */ export const ALL_EVENT = '__all'; export const headerClickEventName = 'stackHeaderClick'; export const headerTouchStartEventName = 'stackHeaderTouchStart'; /** @internal */ export type UnknownCallback = (this: void, ...args: UnknownParams) => void; export type Callback<K extends keyof EventEmitter.EventParamsMap> = (this: void, ...args: EventParamsMap[K]) => void; export interface EventParamsMap { "__all": UnknownParams; "activeContentItemChanged": ComponentItemParam; "close": NoParams; "closed": NoParams; "destroy": NoParams; "drag": DragParams; "dragStart": DragStartParams; "dragStop": DragStopParams; "hide": NoParams; "initialised": NoParams; "itemDropped": ComponentItemParam; "maximised": NoParams; "minimised": NoParams; "open": NoParams; "popIn": NoParams; "resize": NoParams; "show": NoParams; /** @deprecated - use show instead */ "shown": NoParams; "stateChanged": NoParams; "tab": TabParam; "tabCreated": TabParam; "titleChanged": StringParam; "windowClosed": PopoutParam; "windowOpened": PopoutParam; "beforeComponentRelease": BeforeComponentReleaseParams; "beforeItemDestroyed": BubblingEventParam; "itemCreated": BubblingEventParam; "itemDestroyed": BubblingEventParam; "focus": BubblingEventParam; "blur": BubblingEventParam; "stackHeaderClick": ClickBubblingEventParam; "stackHeaderTouchStart": TouchStartBubblingEventParam; "userBroadcast": UnknownParams; } export type UnknownParams = unknown[]; export type NoParams = []; export type UnknownParam = [unknown]; export type PopoutParam = [BrowserPopout]; export type ComponentItemParam = [ComponentItem]; export type TabParam = [Tab]; export type BubblingEventParam = [EventEmitter.BubblingEvent] export type StringParam = [string]; export type DragStartParams = [originalX: number, originalY: number]; export type DragStopParams = [event: PointerEvent | undefined]; export type DragParams = [offsetX: number, offsetY: number, event: PointerEvent]; export type BeforeComponentReleaseParams = [component: unknown]; export type ClickBubblingEventParam = [ClickBubblingEvent]; export type TouchStartBubblingEventParam = [TouchStartBubblingEvent]; export class BubblingEvent { /** @internal */ private _isPropagationStopped = false; get name(): string { return this._name; } get target(): EventEmitter { return this._target; } /** @deprecated Use {@link (EventEmitter:namespace).(BubblingEvent:class).target} instead */ get origin(): EventEmitter { return this._target; } get isPropagationStopped(): boolean { return this._isPropagationStopped; } /** @internal */ constructor( /** @internal */ private readonly _name: string, /** @internal */ private readonly _target: EventEmitter) { } stopPropagation(): void { this._isPropagationStopped = true; } } export class ClickBubblingEvent extends BubblingEvent { get mouseEvent(): MouseEvent { return this._mouseEvent; } /** @internal */ constructor(name: string, target: EventEmitter, /** @internal */ private readonly _mouseEvent: MouseEvent ) { super(name, target); } } export class TouchStartBubblingEvent extends BubblingEvent { get touchEvent(): TouchEvent { return this._touchEvent; } /** @internal */ constructor(name: string, target: EventEmitter, /** @internal */ private readonly _touchEvent: TouchEvent ) { super(name, target); } } }