ngx-smart-loader
Version:
Smart loader handler to manage loaders everywhere in Angular apps.
483 lines (473 loc) • 14.2 kB
JavaScript
/**
* @license ngx-smart-loader
* MIT license
*/
import { ChangeDetectorRef, Component, EventEmitter, Injectable, Input, NgModule, Output } from '@angular/core';
import { CommonModule } from '@angular/common';
/**
* @fileoverview added by tsickle
* @suppress {checkTypes} checked by tsc
*/
class NgxSmartLoaderService {
constructor() {
this._loaderStack = [];
this._actions = [];
}
/**
* Add a new loader instance. This step is essential and allows to retrieve any loader at any time.
* It stores an object that contains the given loader identifier and the loader itself directly in the `loaderStack`.
*
* @param {?} loaderInstance The object that contains the given loader identifier and the loader itself.
* @param {?=} force Optional parameter that forces the overriding of loader instance if it already exists.
* @return {?} Returns nothing special.
*/
addLoader(loaderInstance, force) {
if (force) {
const /** @type {?} */ i = this._loaderStack.findIndex((o) => {
return o.id === loaderInstance.id;
});
if (i > -1) {
this._loaderStack[i].component = loaderInstance.component;
}
else {
this._loaderStack.push(loaderInstance);
}
return;
}
let /** @type {?} */ loader;
if (loader = this._getLoader(loaderInstance.id)) {
throw (new Error('Loader with ' + loaderInstance.id + ' identifier already exist'));
}
else {
this._loaderStack.push(loaderInstance);
}
}
/**
* Remove a loader instance from the loader stack.
*
* @param {?} id The loader identifier.
* @return {?}
*/
removeLoader(id) {
this._loaderStack = this._loaderStack.filter((loader) => loader.id !== id);
this._removeAction(id, '*');
}
/**
* Retrieve all the created loaders.
*
* @return {?} Returns an array that contains all loader instances.
*/
getLoaderStack() {
return this._loaderStack;
}
/**
* It gives the number of loader instances. It's helpful to know if the loader stack is empty or not.
*
* @return {?} Returns the number of loader instances.
*/
getLoaderStackCount() {
return this._loaderStack.length;
}
/**
* Retrieve all the opened loaders. It looks for all loader instances with their `visible` property set to `true`.
*
* @return {?} Returns an array that contains all the opened loaders.
*/
getOpenedLoaders() {
return this._loaderStack.filter((loader) => loader.component.visible);
}
/**
* Retrieve all the active loaders. It looks for all loader instances with their `loading` property set to `true`.
*
* @return {?} Returns an array that contains all the active loaders.
*/
getActiveLoaders() {
return this._loaderStack.filter((loader) => loader.component.loading);
}
/**
* Get the higher `z-index` value between all the loader instances. It iterates over the `LoaderStack` array and
* calculates a higher value (it takes the highest index value between all the loader instances and adds 1).
* Use it to make a loader appear foreground.
*
* @return {?} Returns a higher index from all the existing loader instances.
*/
getHigherIndex() {
const /** @type {?} */ index = this.getOpenedLoaders().map((loader) => loader.component.layerPosition);
return Math.max(...index) + 1;
}
/**
* Enable loading state to one or several loaders.
*
* @param {?} id The loader identifier.
* @return {?}
*/
start(id) {
let /** @type {?} */ loader;
if (Array.isArray(id)) {
id.forEach((i) => {
this.start(i);
});
}
else if (loader = this._getLoader(id)) {
loader.component.start();
this._removeAction(id, 'start');
}
else {
this._addAction(id, 'start');
}
}
/**
* Enable loading state to all loaders.
* @return {?}
*/
startAll() {
this._loaderStack.forEach((loader) => this.start(loader.id));
}
/**
* Disable loading state to one or several loaders.
*
* @param {?} id The loader identifier.
* @return {?}
*/
stop(id) {
let /** @type {?} */ loader;
if (Array.isArray(id)) {
id.forEach((i) => {
this.stop(i);
});
}
else if (loader = this._getLoader(id)) {
loader.component.stop();
this._removeAction(id, 'stop');
}
else {
this._addAction(id, 'stop');
}
}
/**
* Disable loading state to all loaders.
* @return {?}
*/
stopAll() {
this._loaderStack.forEach((loader) => this.stop(loader.id));
}
/**
* @param {?} id
* @return {?}
*/
isLoading(id) {
let /** @type {?} */ loader;
if (Array.isArray(id)) {
const /** @type {?} */ tmp = [];
id.forEach((i) => {
this._loaderStack.forEach((load) => {
if (load.id === i) {
tmp.push(load.component.loading);
}
});
});
return tmp.indexOf(false) === -1;
}
else if (loader = this._getLoader(id)) {
return loader.component.loading;
}
else {
return false;
}
}
/**
* Execute an action on loaders
*
* @param {?} id The loader identifier.
* @param {?} action Name of the action.
* @return {?}
*/
executeAction(id, action) {
if (this._actions.find((act) => act.identifier === id && act.action === action)) {
switch (action) {
case 'start':
this.start(id);
break;
case 'stop':
this.stop(id);
break;
}
}
}
/**
* Retrieve a loader instance by its identifier.
* If there's several loaders with same identifier, the first is returned.
*
* @param {?} id The loader identifier used at creation time.
* @return {?}
*/
_getLoader(id) {
return this._loaderStack.find((load) => load.id === id) || null;
}
/**
* Adds an action on one or more loaders
*
* @param {?} id The loader identifier.
* @param {?} action Name of the action.
* @return {?}
*/
_addAction(id, action) {
if (Array.isArray(id)) {
id.forEach((i) => {
this._addAction(i, action);
});
}
else {
this._actions.push({ identifier: id, action: action });
}
}
/**
* Remove an action on one or more loaders
*
* @param {?} id The loader identifier.
* @param {?} action Name of the action.
* @return {?}
*/
_removeAction(id, action) {
if (Array.isArray(id)) {
id.forEach((i) => {
this._removeAction(i, action);
});
}
else {
this._actions = this._actions.filter((act) => act.identifier !== id || (act.action !== action && action !== '*'));
}
}
}
NgxSmartLoaderService.decorators = [
{ type: Injectable },
];
/** @nocollapse */
NgxSmartLoaderService.ctorParameters = () => [];
/**
* @fileoverview added by tsickle
* @suppress {checkTypes} checked by tsc
*/
class LoaderInstance {
/**
* @param {?} component
*/
constructor(component) {
this.id = component.identifier;
this.component = component;
}
}
/**
* @fileoverview added by tsickle
* @suppress {checkTypes} checked by tsc
*/
class NgxSmartLoaderComponent {
/**
* @param {?} ngxSmartLoaderService
* @param {?} changeDetectorRef
*/
constructor(ngxSmartLoaderService, changeDetectorRef) {
this.ngxSmartLoaderService = ngxSmartLoaderService;
this.changeDetectorRef = changeDetectorRef;
this.identifier = '';
this.customClass = '';
this.force = false;
this.delayIn = 0;
this.delayOut = 0;
this.autostart = false;
this.onStart = new EventEmitter();
this.onStop = new EventEmitter();
this.onVisibleChange = new EventEmitter();
this.loading = false;
this.visible = false;
this.layerPosition = 999;
this._isProcessing = false;
this._loaderBodyClass = 'loader-open';
this._enterClass = 'enter';
this._leaveClass = 'leave';
}
/**
* @return {?}
*/
ngOnInit() {
try {
const /** @type {?} */ loader = new LoaderInstance(this);
this.ngxSmartLoaderService.addLoader(loader, this.force);
this.layerPosition += this.ngxSmartLoaderService.getLoaderStackCount();
this.addCustomClass(this.identifier.replace(/([a-z])([A-Z])/g, '$1-$2').toLowerCase());
if (this.autostart) {
this.ngxSmartLoaderService.start(this.identifier);
}
else {
this.ngxSmartLoaderService.executeAction(this.identifier, 'start');
}
}
catch (/** @type {?} */ error) {
throw (error);
}
}
/**
* @return {?}
*/
ngOnDestroy() {
this.ngxSmartLoaderService.removeLoader(this.identifier);
}
/**
* @param {?=} top
* @return {?}
*/
start(top) {
this._isProcessing = true;
clearInterval(this._debouncer);
this.visible = true;
setTimeout(() => {
this.addCustomClass(this._enterClass);
});
this._debouncer = setTimeout(() => {
if (top) {
this.layerPosition = this.ngxSmartLoaderService.getHigherIndex();
}
if (!document.body.classList.contains(this._loaderBodyClass)) {
document.body.classList.add(this._loaderBodyClass);
}
this.loading = true;
this.onStart.emit(this);
this.onVisibleChange.emit(this);
this.removeCustomClass(this._enterClass);
this._isProcessing = false;
}, this.delayIn);
}
/**
* @return {?}
*/
stop() {
if (this._isProcessing) {
this.visible = false;
this.loading = false;
}
clearInterval(this._debouncer);
this.addCustomClass(this._leaveClass);
this.loading = false;
this._debouncer = setTimeout(() => {
if (document.body.classList.contains(this._loaderBodyClass)) {
document.body.classList.remove(this._loaderBodyClass);
}
this.visible = false;
this.onStop.emit(this);
this.onVisibleChange.emit(this);
this.removeCustomClass(this._leaveClass);
setTimeout(() => {
this.changeDetectorRef.markForCheck();
});
}, this.delayOut);
}
/**
* @param {?} className
* @return {?}
*/
addCustomClass(className) {
if (!this.customClass.length) {
this.customClass = className;
}
else {
if (this.customClass.indexOf(className) === -1) {
this.customClass += ' ' + className;
}
}
}
/**
* @param {?=} className
* @return {?}
*/
removeCustomClass(className) {
if (className) {
this.customClass = this.customClass.replace(className, '').trim();
}
else {
this.customClass = '';
}
}
}
NgxSmartLoaderComponent.decorators = [
{ type: Component, args: [{
selector: 'ngx-smart-loader',
template: `
<div class="loader-container {{customClass}}" [ngClass]="{'active': loading}"
[style.z-index]="layerPosition - 1" *ngIf="visible">
<ng-content></ng-content>
</div>
`
},] },
];
/** @nocollapse */
NgxSmartLoaderComponent.ctorParameters = () => [
{ type: NgxSmartLoaderService, },
{ type: ChangeDetectorRef, },
];
NgxSmartLoaderComponent.propDecorators = {
"identifier": [{ type: Input },],
"customClass": [{ type: Input },],
"force": [{ type: Input },],
"delayIn": [{ type: Input },],
"delayOut": [{ type: Input },],
"autostart": [{ type: Input },],
"onStart": [{ type: Output },],
"onStop": [{ type: Output },],
"onVisibleChange": [{ type: Output },],
};
/**
* @fileoverview added by tsickle
* @suppress {checkTypes} checked by tsc
*/
class NgxSmartLoaderModule {
/**
* Use in AppModule: new instance of NgxSmartLoader.
* @return {?}
*/
static forRoot() {
return {
ngModule: NgxSmartLoaderModule,
providers: [NgxSmartLoaderService]
};
}
/**
* Use in features modules with lazy loading: new instance of NgxSmartLoader.
* @return {?}
*/
static forChild() {
return {
ngModule: NgxSmartLoaderModule,
providers: [NgxSmartLoaderService]
};
}
}
NgxSmartLoaderModule.decorators = [
{ type: NgModule, args: [{
declarations: [NgxSmartLoaderComponent],
exports: [NgxSmartLoaderComponent],
imports: [CommonModule]
},] },
];
/** @nocollapse */
NgxSmartLoaderModule.ctorParameters = () => [];
/**
* @fileoverview added by tsickle
* @suppress {checkTypes} checked by tsc
*/
// Public classes.
/**
* @fileoverview added by tsickle
* @suppress {checkTypes} checked by tsc
*/
/**
* Entry point for all public APIs of the package.
*/
/**
* @fileoverview added by tsickle
* @suppress {checkTypes} checked by tsc
*/
/**
* Generated bundle index. Do not edit.
*/
export { NgxSmartLoaderService, NgxSmartLoaderComponent, NgxSmartLoaderModule };
//# sourceMappingURL=ngx-smart-loader.js.map