UNPKG

gramli-angular-notifier

Version:

A well designed, fully animated, highly customizable, and easy-to-use notification library for your Angular application.

816 lines (802 loc) 27.3 kB
import * as i0 from '@angular/core'; import { AfterViewInit, EventEmitter, ElementRef, Renderer2, TemplateRef, OnDestroy, ChangeDetectorRef, Provider, ModuleWithProviders, InjectionToken } from '@angular/core'; import { Observable, Subject } from 'rxjs'; import * as i3 from '@angular/common'; /** * Notifier options */ interface NotifierOptions { animations?: { enabled?: boolean; hide?: { easing?: string; offset?: number | false; preset?: string; speed?: number; }; overlap?: number | false; shift?: { easing?: string; speed?: number; }; show?: { easing?: string; preset?: string; speed?: number; }; }; behaviour?: { autoHide?: number | false; onClick?: 'hide' | false; onMouseover?: 'pauseAutoHide' | 'resetAutoHide' | false; showDismissButton?: boolean; stacking?: number | false; }; position?: { horizontal?: { distance?: number; position?: 'left' | 'middle' | 'right'; }; vertical?: { distance?: number; gap?: number; position?: 'top' | 'bottom'; }; }; theme?: string; } /** * Notifier configuration * * The notifier configuration defines what notifications look like, how they behave, and how they get animated. It is a global * configuration, which means that it only can be set once (at the beginning), and cannot be changed afterwards. Aligning to the world of * Angular, this configuration can be provided in the root app module - alternatively, a meaningful default configuration will be used. */ declare class NotifierConfig implements NotifierOptions { /** * Customize animations */ animations: { enabled: boolean; hide: { easing: string; offset: number | false; preset: string; speed: number; }; overlap: number | false; shift: { easing: string; speed: number; }; show: { easing: string; preset: string; speed: number; }; }; /** * Customize behaviour */ behaviour: { autoHide: number | false; onClick: 'hide' | false; onMouseover: 'pauseAutoHide' | 'resetAutoHide' | false; showDismissButton: boolean; stacking: number | false; }; /** * Customize positioning */ position: { horizontal: { distance: number; position: 'left' | 'middle' | 'right'; }; vertical: { distance: number; gap: number; position: 'top' | 'bottom'; }; }; /** * Customize theming */ theme: string; /** * Constructor * * @param [customOptions={}] Custom notifier options, optional */ constructor(customOptions?: NotifierOptions); } /** * Notifier action * * In general, API calls don't get processed right away. Instead, we have to queue them up in order to prevent simultanious API calls * interfering with each other. This, at least in theory, is possible at any time, primarily due to overlapping animations. * * Technical sidenote: * An action looks pretty similar to the ones within the Flux / Redux pattern. */ interface NotifierAction { /** * Action payload containing all information necessary to process the action (optional) */ payload?: any; /** * Action type */ type: 'SHOW' | 'HIDE' | 'HIDE_ALL' | 'HIDE_NEWEST' | 'HIDE_OLDEST'; } /** * Notifier service * * This service provides access to the public notifier API. Once injected into a component, directive, pipe, service, or any other building * block of an applications, it can be used to show new notifications, and hide existing ones. Internally, it transforms API calls into * actions, which then get thrown into the action queue - eventually being processed at the right moment. */ declare class NotifierService { /** * Notifier queue service */ private readonly queueService; /** * Notifier configuration */ private readonly config; /** * Get the notifier configuration * * @returns Notifier configuration */ getConfig(): NotifierConfig; /** * Get the observable for handling actions * * @returns Observable of NotifierAction */ get actionStream(): Observable<NotifierAction>; /** * API: Show a new notification * * @param notificationOptions Notification options */ show(notificationOptions: NotifierNotificationOptions): void; /** * API: Hide a specific notification, given its ID * * @param notificationId ID of the notification to hide */ hide(notificationId: string): void; /** * API: Hide the newest notification */ hideNewest(): void; /** * API: Hide the oldest notification */ hideOldest(): void; /** * API: Hide all notifications at once */ hideAll(): void; /** * API: Shortcut for showing a new notification * * @param type Type of the notification * @param message Message of the notification * @param [notificationId] Unique ID for the notification (optional) */ notify(type: string, message: string, notificationId?: string): void; static ɵfac: i0.ɵɵFactoryDeclaration<NotifierService, never>; static ɵprov: i0.ɵɵInjectableDeclaration<NotifierService>; } /** * Notifier animation data * * This interface describes an object containing all information necessary to run an animation, in particular to run an animation with the * all new (shiny) Web Animations API. When other components or services request data for an animation they have to run, this is the object * they get back from the animation service. * * Technical sidenote: * Nope, it's not a coincidence - the structure looks similar to the Web Animation API syntax. */ interface NotifierAnimationData { /** * Animation keyframes; the first index ctonaining changes for animate-in, the second index those for animate-out */ keyframes: Array<{ [animatablePropertyName: string]: string; }>; /** * Futher animation options */ options: { /** * Animation duration, in ms */ duration: number; /** * Animation easing function (comp. CSS easing functions) */ easing?: 'linear' | 'ease' | 'ease-in' | 'ease-out' | 'ease-in-out' | string; /** * Animation fill mode */ fill: 'none' | 'forwards' | 'backwards'; }; } /** * Notifier animation service */ declare class NotifierAnimationService { /** * List of animation presets (currently static) */ private readonly animationPresets; /** * Constructor */ constructor(); /** * Get animation data * * This method generates all data the Web Animations API needs to animate our notification. The result depends on both the animation * direction (either in or out) as well as the notifications (and its attributes) itself. * * @param direction Animation direction, either in or out * @param notification Notification the animation data should be generated for * @returns Animation information */ getAnimationData(direction: 'show' | 'hide', notification: NotifierNotification): NotifierAnimationData; static ɵfac: i0.ɵɵFactoryDeclaration<NotifierAnimationService, never>; static ɵprov: i0.ɵɵInjectableDeclaration<NotifierAnimationService>; } /** * Notifier timer service * * This service acts as a timer, needed due to the still rather limited setTimeout JavaScript API. The timer service can start and stop a * timer. Furthermore, it can also pause the timer at any time, and resume later on. The timer API workd promise-based. */ declare class NotifierTimerService { /** * Timestamp (in ms), created in the moment the timer starts */ private now; /** * Remaining time (in ms) */ private remaining; /** * Timeout ID, used for clearing the timeout later on */ private timerId; /** * Promise resolve function, eventually getting called once the timer finishes */ private finishPromiseResolver; /** * Constructor */ constructor(); /** * Start (or resume) the timer * * @param duration Timer duration, in ms * @returns Promise, resolved once the timer finishes */ start(duration: number): Promise<void>; /** * Pause the timer */ pause(): void; /** * Continue the timer */ continue(): void; /** * Stop the timer */ stop(): void; /** * Finish up the timeout by resolving the timer promise */ private finish; static ɵfac: i0.ɵɵFactoryDeclaration<NotifierTimerService, never>; static ɵprov: i0.ɵɵInjectableDeclaration<NotifierTimerService>; } /** * Notifier notification component * ------------------------------- * This component is responsible for actually displaying the notification on screen. In addition, it's able to show and hide this * notification, in particular to animate this notification in and out, as well as shift (move) this notification vertically around. * Furthermore, the notification component handles all interactions the user has with this notification / component, such as clicks and * mouse movements. */ declare class NotifierNotificationComponent implements AfterViewInit { /** * Input: Notification object, contains all details necessary to construct the notification */ notification: NotifierNotification; /** * Output: Ready event, handles the initialization success by emitting a reference to this notification component */ ready: EventEmitter<NotifierNotificationComponent>; /** * Output: Dismiss event, handles the click on the dismiss button by emitting the notification ID of this notification component */ dismiss: EventEmitter<string>; /** * Notifier configuration */ readonly config: NotifierConfig; /** * Notifier timer service */ private readonly timerService; /** * Notifier animation service */ private readonly animationService; /** * Angular renderer, used to preserve the overall DOM abstraction & independence */ private readonly renderer; /** * Native element reference, used for manipulating DOM properties */ private readonly element; /** * Current notification height, calculated and cached here (#perfmatters) */ private elementHeight; /** * Current notification width, calculated and cached here (#perfmatters) */ private elementWidth; /** * Current notification shift, calculated and cached here (#perfmatters) */ private elementShift; /** * Constructor * * @param elementRef Reference to the component's element * @param renderer Angular renderer * @param notifierService Notifier service * @param notifierTimerService Notifier timer service * @param notifierAnimationService Notifier animation service */ constructor(elementRef: ElementRef, renderer: Renderer2, notifierService: NotifierService, notifierTimerService: NotifierTimerService, notifierAnimationService: NotifierAnimationService); /** * Component after view init lifecycle hook, setts up the component and then emits the ready event */ ngAfterViewInit(): void; /** * Get the notifier config * * @returns Notifier configuration */ getConfig(): NotifierConfig; /** * Get notification element height (in px) * * @returns Notification element height (in px) */ getHeight(): number; /** * Get notification element width (in px) * * @returns Notification element height (in px) */ getWidth(): number; /** * Get notification shift offset (in px) * * @returns Notification element shift offset (in px) */ getShift(): number; /** * Show (animate in) this notification * * @returns Promise, resolved when done */ show(): Promise<void>; /** * Hide (animate out) this notification * * @returns Promise, resolved when done */ hide(): Promise<void>; /** * Shift (move) this notification * * @param distance Distance to shift (in px) * @param shiftToMakePlace Flag, defining in which direction to shift * @returns Promise, resolved when done */ shift(distance: number, shiftToMakePlace: boolean): Promise<void>; /** * Handle click on dismiss button */ onClickDismiss(): void; /** * Handle mouseover over notification area */ onNotificationMouseover(): void; /** * Handle mouseout from notification area */ onNotificationMouseout(): void; /** * Handle click on notification area */ onNotificationClick(): void; /** * Start the auto hide timer (if enabled) */ private startAutoHideTimer; /** * Pause the auto hide timer (if enabled) */ private pauseAutoHideTimer; /** * Continue the auto hide timer (if enabled) */ private continueAutoHideTimer; /** * Stop the auto hide timer (if enabled) */ private stopAutoHideTimer; /** * Initial notification setup */ private setup; static ɵfac: i0.ɵɵFactoryDeclaration<NotifierNotificationComponent, never>; static ɵcmp: i0.ɵɵComponentDeclaration<NotifierNotificationComponent, "notifier-notification", never, { "notification": { "alias": "notification"; "required": false; }; }, { "ready": "ready"; "dismiss": "dismiss"; }, never, never, false, never>; } /** * Notification * * This class describes the structure of a notifiction, including all information it needs to live, and everyone else needs to work with it. */ declare class NotifierNotification { /** * Unique notification ID, can be set manually to control the notification from outside later on */ id: string; /** * Notification type, will be used for constructing an appropriate class name */ type: string; /** * Notification message */ message: string; /** * The template to customize * the appearance of the notification */ template?: TemplateRef<any>; /** * Component reference of this notification, created and set during creation time */ component: NotifierNotificationComponent; /** * Constructor * * @param options Notifier options */ constructor(options: NotifierNotificationOptions); } /** * Notifiction options * * This interface describes which information are needed to create a new notification, or in other words, which information the external API * call must provide. */ interface NotifierNotificationOptions { /** * Notification ID, optional */ id?: string; /** * Notification type */ type: string; /** * Notificatin message */ message: string; /** * The template to customize * the appearance of the notification */ template?: TemplateRef<any>; } /** * Notifier queue service * * In general, API calls don't get processed right away. Instead, we have to queue them up in order to prevent simultanious API calls * interfering with each other. This, at least in theory, is possible at any time. In particular, animations - which potentially overlap - * can cause changes in JS classes as well as affect the DOM. Therefore, the queue service takes all actions, puts them in a queue, and * processes them at the right time (which is when the previous action has been processed successfully). * * Technical sidenote: * An action looks pretty similar to the ones within the Flux / Redux pattern. */ declare class NotifierQueueService { /** * Stream of actions, subscribable from outside */ readonly actionStream: Subject<NotifierAction>; /** * Queue of actions */ private actionQueue; /** * Flag, true if some action is currently in progress */ private isActionInProgress; /** * Constructor */ constructor(); /** * Push a new action to the queue, and try to run it * * @param action Action object */ push(action: NotifierAction): void; /** * Continue with the next action (called when the current action is finished) */ continue(): void; /** * Try to run the next action in the queue; we skip if there already is some action in progress, or if there is no action left */ private tryToRunNextAction; static ɵfac: i0.ɵɵFactoryDeclaration<NotifierQueueService, never>; static ɵprov: i0.ɵɵInjectableDeclaration<NotifierQueueService>; } /** * Notifier container component * ---------------------------- * This component acts as a wrapper for all notification components; consequently, it is responsible for creating a new notification * component and removing an existing notification component. Being more precicely, it also handles side effects of those actions, such as * shifting or even completely removing other notifications as well. Overall, this components handles actions coming from the queue service * by subscribing to its action stream. * * Technical sidenote: * This component has to be used somewhere in an application to work; it will not inject and create itself automatically, primarily in order * to not break the Angular AoT compilation. Moreover, this component (and also the notification components) set their change detection * strategy onPush, which means that we handle change detection manually in order to get the best performance. (#perfmatters) */ declare class NotifierContainerComponent implements OnDestroy { /** * List of currently somewhat active notifications */ notifications: Array<NotifierNotification>; /** * Change detector */ private readonly changeDetector; /** * Notifier queue service */ private readonly queueService; /** * Notifier configuration */ private readonly config; /** * Queue service observable subscription (saved for cleanup) */ private queueServiceSubscription; /** * Promise resolve function reference, temporarily used while the notification child component gets created */ private tempPromiseResolver; /** * Constructor * * @param changeDetector Change detector, used for manually triggering change detection runs * @param notifierQueueService Notifier queue service * @param notifierService Notifier service */ constructor(changeDetector: ChangeDetectorRef, notifierQueueService: NotifierQueueService, notifierService: NotifierService); /** * Component destroyment lifecycle hook, cleans up the observable subsciption */ ngOnDestroy(): void; /** * Notification identifier, used as the ngFor trackby function * * @param index Index * @param notification Notifier notification * @returns Notification ID as the unique identnfier */ identifyNotification(index: number, notification: NotifierNotification): string; /** * Event handler, handles clicks on notification dismiss buttons * * @param notificationId ID of the notification to dismiss */ onNotificationDismiss(notificationId: string): void; /** * Event handler, handles notification ready events * * @param notificationComponent Notification component reference */ onNotificationReady(notificationComponent: NotifierNotificationComponent): void; /** * Handle incoming actions by mapping action types to methods, and then running them * * @param action Action object * @returns Promise, resolved when done */ private handleAction; /** * Show a new notification * * We simply add the notification to the list, and then wait until its properly initialized / created / rendered. * * @param action Action object * @returns Promise, resolved when done */ private handleShowAction; /** * Continue to show a new notification (after the notification components is initialized / created / rendered). * * If this is the first (and thus only) notification, we can simply show it. Otherwhise, if stacking is disabled (or a low value), we * switch out notifications, in particular we hide the existing one, and then show our new one. Yet, if stacking is enabled, we first * shift all older notifications, and then show our new notification. In addition, if there are too many notification on the screen, * we hide the oldest one first. Furthermore, if configured, animation overlapping is applied. * * @param notification New notification to show */ private continueHandleShowAction; /** * Hide an existing notification * * Fist, we skip everything if there are no notifications at all, or the given notification does not exist. Then, we hide the given * notification. If there exist older notifications, we then shift them around to fill the gap. Once both hiding the given notification * and shifting the older notificaitons is done, the given notification gets finally removed (from the DOM). * * @param action Action object, payload contains the notification ID * @returns Promise, resolved when done */ private handleHideAction; /** * Hide the oldest notification (bridge to handleHideAction) * * @param action Action object * @returns Promise, resolved when done */ private handleHideOldestAction; /** * Hide the newest notification (bridge to handleHideAction) * * @param action Action object * @returns Promise, resolved when done */ private handleHideNewestAction; /** * Hide all notifications at once * * @returns Promise, resolved when done */ private handleHideAllAction; /** * Shift multiple notifications at once * * @param notifications List containing the notifications to be shifted * @param distance Distance to shift (in px) * @param toMakePlace Flag, defining in which direciton to shift * @returns Promise, resolved when done */ private shiftNotifications; /** * Add a new notification to the list of notifications (triggers change detection) * * @param notification Notification to add to the list of notifications */ private addNotificationToList; /** * Remove an existing notification from the list of notifications (triggers change detection) * * @param notification Notification to be removed from the list of notifications */ private removeNotificationFromList; /** * Remove all notifications from the list (triggers change detection) */ private removeAllNotificationsFromList; /** * Helper: Find a notification in the notification list by a given notification ID * * @param notificationId Notification ID, used for finding notification * @returns Notification, undefined if not found */ private findNotificationById; /** * Helper: Find a notification's index by a given notification ID * * @param notificationId Notification ID, used for finding a notification's index * @returns Notification index, undefined if not found */ private findNotificationIndexById; static ɵfac: i0.ɵɵFactoryDeclaration<NotifierContainerComponent, never>; static ɵcmp: i0.ɵɵComponentDeclaration<NotifierContainerComponent, "notifier-container", never, {}, {}, never, never, false, never>; } /** * Factory for a notifier configuration with custom options * * Sidenote: * Required as Angular AoT compilation cannot handle dynamic functions; see <https://github.com/angular/angular/issues/11262>. * * @param options - Custom notifier options * @returns - Notifier configuration as result */ declare function notifierCustomConfigFactory(options: NotifierOptions): NotifierConfig; /** * Factory for a notifier configuration with default options * * Sidenote: * Required as Angular AoT compilation cannot handle dynamic functions; see <https://github.com/angular/angular/issues/11262>. * * @returns - Notifier configuration as result */ declare function notifierDefaultConfigFactory(): NotifierConfig; /** * Provide notifier configuration for standalone applications * * This function should be used in the application bootstrap providers (main.ts) * to configure the notifier globally. Import NotifierModule in components that need it. * * @example * ```typescript * import { bootstrapApplication } from '@angular/platform-browser'; * import { provideNotifier } from 'angular-notifier'; * * bootstrapApplication(AppComponent, { * providers: [provideNotifier({ theme: 'material' })] * }); * * @Component({ * standalone: true, * imports: [NotifierModule], // Just import, config comes from bootstrap * }) * export class AppComponent {} * ``` * * @param [options={}] - Custom notifier options * @returns - Array of providers for the notifier configuration */ declare function provideNotifier(options?: NotifierOptions): Provider[]; /** * Notifier module */ declare class NotifierModule { /** * Setup the notifier module with custom providers, in this case with a custom configuration based on the givne options * * @param [options={}] - Custom notifier options * @returns - Notifier module with custom providers */ static withConfig(options?: NotifierOptions): ModuleWithProviders<NotifierModule>; static ɵfac: i0.ɵɵFactoryDeclaration<NotifierModule, never>; static ɵmod: i0.ɵɵNgModuleDeclaration<NotifierModule, [typeof NotifierContainerComponent, typeof NotifierNotificationComponent], [typeof i3.CommonModule], [typeof NotifierContainerComponent]>; static ɵinj: i0.ɵɵInjectorDeclaration<NotifierModule>; } /** * Injection Token for notifier options */ declare const NotifierOptionsToken: InjectionToken<NotifierOptions>; /** * Injection Token for notifier configuration */ declare const NotifierConfigToken: InjectionToken<NotifierConfig>; export { NotifierConfig, NotifierConfigToken, NotifierContainerComponent, NotifierModule, NotifierNotificationComponent, NotifierOptionsToken, NotifierService, notifierCustomConfigFactory, notifierDefaultConfigFactory, provideNotifier }; export type { NotifierOptions };