UNPKG

@egjs/component

Version:

A base class utility that manages events and options in modules.

297 lines (266 loc) 10.5 kB
/* * Copyright (c) 2015 NAVER Corp. * egjs projects are licensed under the MIT license */ import { isUndefined } from "./utils"; import { EventCallback, EventHash, EventKey, EventMap, EventTriggerParams } from "./types"; import ComponentEvent from "./ComponentEvent"; import ActualComponentEvent from "./ActualComponentEvent"; /** * A class used to manage events in a component * @ko 컴포넌트의 이벤트을 관리할 수 있게 하는 클래스 */ class Component<T extends EventMap> { /** * Version info string * @ko 버전정보 문자열 * @name VERSION * @static * @example * Component.VERSION; // ex) 3.0.0 * @memberof Component */ public static VERSION: string = "#__VERSION__#"; private _eventHandler: { [keys: string]: Array<(...args: any[]) => any> }; /** * @support {"ie": "7+", "ch" : "latest", "ff" : "latest", "sf" : "latest", "edge" : "latest", "ios" : "7+", "an" : "2.1+ (except 3.x)"} */ public constructor() { this._eventHandler = {}; } public trigger<K extends EventKey<T>>(event: ComponentEvent<T[K], K, this> & T[K]): this; public trigger<K extends EventKey<T>>(event: K, ...params: EventTriggerParams<T, K>): this; /** * Trigger a custom event. * @ko 커스텀 이벤트를 발생시킨다 * @param {string | ComponentEvent} event The name of the custom event to be triggered or an instance of the ComponentEvent<ko>발생할 커스텀 이벤트의 이름 또는 ComponentEvent의 인스턴스</ko> * @param {any[]} params Event data to be sent when triggering a custom event <ko>커스텀 이벤트가 발생할 때 전달할 데이터</ko> * @return An instance of the component itself<ko>컴포넌트 자신의 인스턴스</ko> * @example * ```ts * import Component, { ComponentEvent } from "@egjs/component"; * * class Some extends Component<{ * beforeHi: ComponentEvent<{ foo: number; bar: string }>; * hi: { foo: { a: number; b: boolean } }; * someEvent: (foo: number, bar: string) => void; * someOtherEvent: void; // When there's no event argument * }> { * some(){ * if(this.trigger("beforeHi")){ // When event call to stop return false. * this.trigger("hi");// fire hi event. * } * } * } * * const some = new Some(); * some.on("beforeHi", e => { * if(condition){ * e.stop(); // When event call to stop, `hi` event not call. * } * // `currentTarget` is component instance. * console.log(some === e.currentTarget); // true * * typeof e.foo; // number * typeof e.bar; // string * }); * some.on("hi", e => { * typeof e.foo.b; // boolean * }); * // If you want to more know event design. You can see article. * // https://github.com/naver/egjs-component/wiki/How-to-make-Component-event-design%3F * ``` */ public trigger<K extends EventKey<T>>(event: K | ComponentEvent<T[K], K, this>, ...params: EventTriggerParams<T, K> | void[]): this { const eventName = (event as any) instanceof ActualComponentEvent ? (event as ActualComponentEvent<T[K]>).eventType : event as K; const handlers = [...(this._eventHandler[eventName] || [])]; if (handlers.length <= 0) { return this; } if ((event as any) instanceof ActualComponentEvent) { (event as ActualComponentEvent<T[K]>).currentTarget = this; handlers.forEach((handler: (event: ComponentEvent<T[K], K, this>) => any) => { handler(event as ComponentEvent<T[K], K, this>); }); } else { handlers.forEach(handler => { // eslint-disable-next-line @typescript-eslint/no-unsafe-call handler(...params); }); } return this; } public once<K extends EventKey<T>>(eventName: K, handlerToAttach: EventCallback<T, K, this>): this; public once(eventHash: EventHash<T, this>): this; /** * Executed event just one time. * @ko 이벤트가 한번만 실행된다. * @param {string} eventName The name of the event to be attached or an event name - event handler mapped object.<ko>등록할 이벤트의 이름 또는 이벤트 이름-핸들러 오브젝트</ko> * @param {function} handlerToAttach The handler function of the event to be attached <ko>등록할 이벤트의 핸들러 함수</ko> * @return An instance of the component itself<ko>컴포넌트 자신의 인스턴스</ko> * @example * ```ts * import Component, { ComponentEvent } from "@egjs/component"; * * class Some extends Component<{ * hi: ComponentEvent; * }> { * hi() { * alert("hi"); * } * thing() { * this.once("hi", this.hi); * } * } * * var some = new Some(); * some.thing(); * some.trigger(new ComponentEvent("hi")); * // fire alert("hi"); * some.trigger(new ComponentEvent("hi")); * // Nothing happens * ``` */ public once<K extends EventKey<T>>(eventName: K | EventHash<T, this>, handlerToAttach?: EventCallback<T, K, this>): this { if (typeof eventName === "object" && isUndefined(handlerToAttach)) { const eventHash = eventName; for (const key in eventHash) { this.once((key as K), eventHash[key] as EventCallback<T, K, this>); } return this; } else if (typeof eventName === "string" && typeof handlerToAttach === "function") { const listener: any = (...args: any[]) => { // eslint-disable-next-line @typescript-eslint/no-unsafe-call handlerToAttach(...args); this.off(eventName, listener); }; this.on(eventName, listener); } return this; } /** * Checks whether an event has been attached to a component. * @ko 컴포넌트에 이벤트가 등록됐는지 확인한다. * @param {string} eventName The name of the event to be attached <ko>등록 여부를 확인할 이벤트의 이름</ko> * @return {boolean} Indicates whether the event is attached. <ko>이벤트 등록 여부</ko> * @example * ```ts * import Component from "@egjs/component"; * * class Some extends Component<{ * hi: void; * }> { * some() { * this.hasOn("hi");// check hi event. * } * } * ``` */ public hasOn<K extends EventKey<T>>(eventName: K): boolean { return !!this._eventHandler[eventName]; } public on<K extends EventKey<T>>(eventName: K, handlerToAttach: EventCallback<T, K, this>): this; public on(eventHash: EventHash<T, this>): this; /** * Attaches an event to a component. * @ko 컴포넌트에 이벤트를 등록한다. * @param {string} eventName The name of the event to be attached or an event name - event handler mapped object.<ko>등록할 이벤트의 이름 또는 이벤트 이름-핸들러 오브젝트</ko> * @param {function} handlerToAttach The handler function of the event to be attached <ko>등록할 이벤트의 핸들러 함수</ko> * @return An instance of a component itself<ko>컴포넌트 자신의 인스턴스</ko> * @example * ```ts * import Component, { ComponentEvent } from "@egjs/component"; * * class Some extends Component<{ * hi: void; * }> { * hi() { * console.log("hi"); * } * some() { * this.on("hi",this.hi); //attach event * } * } * ``` */ public on<K extends EventKey<T>>(eventName: K | EventHash<T, this>, handlerToAttach?: EventCallback<T, K, this>): this { if (typeof eventName === "object" && isUndefined(handlerToAttach)) { const eventHash = eventName; for (const name in eventHash) { this.on(name, eventHash[name] as any); } return this; } else if (typeof eventName === "string" && typeof handlerToAttach === "function") { let handlerList = this._eventHandler[eventName]; if (isUndefined(handlerList)) { this._eventHandler[eventName] = []; handlerList = this._eventHandler[eventName]; } handlerList.push(handlerToAttach as EventCallback<T, EventKey<T>, this>); } return this; } public off(eventHash?: EventHash<T, this>): this; public off<K extends EventKey<T>>(eventName: K, handlerToDetach?: EventCallback<T, K, this>): this; /** * Detaches an event from the component.<br/>If the `eventName` is not given this will detach all event handlers attached.<br/>If the `handlerToDetach` is not given, this will detach all event handlers for `eventName`. * @ko 컴포넌트에 등록된 이벤트를 해제한다.<br/>`eventName`이 주어지지 않았을 경우 모든 이벤트 핸들러를 제거한다.<br/>`handlerToAttach`가 주어지지 않았을 경우 `eventName`에 해당하는 모든 이벤트 핸들러를 제거한다. * @param {string?} eventName The name of the event to be detached <ko>해제할 이벤트의 이름</ko> * @param {function?} handlerToDetach The handler function of the event to be detached <ko>해제할 이벤트의 핸들러 함수</ko> * @return An instance of a component itself <ko>컴포넌트 자신의 인스턴스</ko> * @example * ```ts * import Component, { ComponentEvent } from "@egjs/component"; * * class Some extends Component<{ * hi: void; * }> { * hi() { * console.log("hi"); * } * some() { * this.off("hi",this.hi); //detach event * } * } * ``` */ public off<K extends EventKey<T>>(eventName?: K | EventHash<T, this>, handlerToDetach?: EventCallback<T, K, this>): this { // Detach all event handlers. if (isUndefined(eventName)) { this._eventHandler = {}; return this; } // Detach all handlers for eventname or detach event handlers by object. if (isUndefined(handlerToDetach)) { if (typeof eventName === "string") { delete this._eventHandler[eventName]; return this; } else { const eventHash = eventName; for (const name in eventHash) { this.off(name, eventHash[name] as any); } return this; } } // Detach single event handler const handlerList = this._eventHandler[eventName as K]; if (handlerList) { const length = handlerList.length; for (let i = 0; i < length; ++i) { if (handlerList[i] === handlerToDetach) { handlerList.splice(i, 1); if (length <= 1) { delete this._eventHandler[eventName as K]; } break; } } } return this; } } export default Component;