angular2-promise-buttons
Version:
Chilled loading buttons for angular
306 lines (295 loc) • 13.7 kB
JavaScript
(function (global, factory) {
typeof exports === 'object' && typeof module !== 'undefined' ? factory(exports, require('@angular/core'), require('rxjs')) :
typeof define === 'function' && define.amd ? define('angular2-promise-buttons', ['exports', '@angular/core', 'rxjs'], factory) :
(global = typeof globalThis !== 'undefined' ? globalThis : global || self, factory(global["angular2-promise-buttons"] = {}, global.ng.core, global.rxjs));
})(this, (function (exports, i0, rxjs) { 'use strict';
function _interopNamespace(e) {
if (e && e.__esModule) return e;
var n = Object.create(null);
if (e) {
Object.keys(e).forEach(function (k) {
if (k !== 'default') {
var d = Object.getOwnPropertyDescriptor(e, k);
Object.defineProperty(n, k, d.get ? d : {
enumerable: true,
get: function () { return e[k]; }
});
}
});
}
n["default"] = e;
return Object.freeze(n);
}
var i0__namespace = /*#__PURE__*/_interopNamespace(i0);
var DEFAULT_CFG = {
spinnerTpl: '<span class="btn-spinner"></span>',
disableBtn: true,
btnLoadingClass: 'is-loading',
handleCurrentBtnOnly: false,
minDuration: null,
};
var userCfg = new i0.InjectionToken('cfg');
var PromiseBtnDirective = /** @class */ (function () {
function PromiseBtnDirective(el, cfg) {
// provide configuration
this.cfg = Object.assign({}, DEFAULT_CFG, cfg);
// save element
this.btnEl = el.nativeElement;
}
Object.defineProperty(PromiseBtnDirective.prototype, "isDisabledFromTheOutsideSetter", {
// this is added to fix the overriding of the disabled state by the loading indicator button.
// https://github.com/johannesjo/angular2-promise-buttons/issues/34
set: function (v) {
this.isDisabledFromTheOutside = v;
if (v) {
// disabled means always disabled
this.btnEl.setAttribute('disabled', 'disabled');
}
else if (this.isPromiseDone || this.isPromiseDone === undefined) {
this.btnEl.removeAttribute('disabled');
}
// else the button is loading, so do not change the disabled loading state.
},
enumerable: false,
configurable: true
});
Object.defineProperty(PromiseBtnDirective.prototype, "promiseBtn", {
set: function (passedValue) {
var isObservable = passedValue instanceof rxjs.Observable;
var isSubscription = passedValue instanceof rxjs.Subscription;
var isBoolean = typeof passedValue === 'boolean';
var isPromise = passedValue instanceof Promise || (passedValue !== null &&
typeof passedValue === 'object' &&
typeof passedValue.then === 'function' &&
typeof passedValue.catch === 'function');
if (isObservable) {
throw new TypeError('promiseBtn must be an instance of Subscription, instance of Observable given');
}
else if (isSubscription) {
var sub_1 = passedValue;
if (!sub_1.closed) {
this.promise = new Promise(function (resolve) {
sub_1.add(resolve);
});
}
}
else if (isPromise) {
this.promise = passedValue;
}
else if (isBoolean) {
this.promise = this.createPromiseFromBoolean(passedValue);
}
this.checkAndInitPromiseHandler(this.btnEl);
},
enumerable: false,
configurable: true
});
PromiseBtnDirective.prototype.ngAfterContentInit = function () {
this.prepareBtnEl(this.btnEl);
// trigger changes once to handle initial promises
this.checkAndInitPromiseHandler(this.btnEl);
};
PromiseBtnDirective.prototype.ngOnDestroy = function () {
// cleanup
if (this.minDurationTimeout) {
clearTimeout(this.minDurationTimeout);
}
};
PromiseBtnDirective.prototype.createPromiseFromBoolean = function (val) {
var _this = this;
if (val) {
return new Promise(function (resolve) {
_this._fakePromiseResolve = resolve;
});
}
else {
if (this._fakePromiseResolve) {
this._fakePromiseResolve();
}
return this.promise;
}
};
/**
* Initializes all html and event handlers
*/
PromiseBtnDirective.prototype.prepareBtnEl = function (btnEl) {
// handle promises passed via promiseBtn attribute
this.appendSpinnerTpl(btnEl);
};
/**
* Checks if all required parameters are there and inits the promise handler
*/
PromiseBtnDirective.prototype.checkAndInitPromiseHandler = function (btnEl) {
// check if element and promise is set
if (btnEl && this.promise) {
this.initPromiseHandler(btnEl);
}
};
/**
* Helper FN to add class
*/
PromiseBtnDirective.prototype.addLoadingClass = function (el) {
if (typeof this.cfg.btnLoadingClass === 'string') {
el.classList.add(this.cfg.btnLoadingClass);
}
};
/**
* Helper FN to remove classes
*/
PromiseBtnDirective.prototype.removeLoadingClass = function (el) {
if (typeof this.cfg.btnLoadingClass === 'string') {
el.classList.remove(this.cfg.btnLoadingClass);
}
};
/**
* Handles everything to be triggered when the button is set
* to loading state.
*/
PromiseBtnDirective.prototype.initLoadingState = function (btnEl) {
this.addLoadingClass(btnEl);
this.disableBtn(btnEl);
};
/**
* Handles everything to be triggered when loading is finished
*/
PromiseBtnDirective.prototype.cancelLoadingStateIfPromiseAndMinDurationDone = function (btnEl) {
if ((!this.cfg.minDuration || this.isMinDurationTimeoutDone) && this.isPromiseDone) {
this.removeLoadingClass(btnEl);
this.enableBtn(btnEl);
}
};
PromiseBtnDirective.prototype.disableBtn = function (btnEl) {
if (this.cfg.disableBtn) {
btnEl.setAttribute('disabled', 'disabled');
}
};
PromiseBtnDirective.prototype.enableBtn = function (btnEl) {
if (this.cfg.disableBtn) {
if (this.isDisabledFromTheOutside) {
btnEl.setAttribute('disabled', 'disabled');
}
else {
btnEl.removeAttribute('disabled');
}
}
};
/**
* Initializes a watcher for the promise. Also takes
* this.cfg.minDuration into account if given.
*/
PromiseBtnDirective.prototype.initPromiseHandler = function (btnEl) {
var _this = this;
var promise = this.promise;
// watch promise to resolve or fail
this.isMinDurationTimeoutDone = false;
this.isPromiseDone = false;
// create timeout if option is set
if (this.cfg.minDuration) {
this.minDurationTimeout = window.setTimeout(function () {
_this.isMinDurationTimeoutDone = true;
_this.cancelLoadingStateIfPromiseAndMinDurationDone(btnEl);
}, this.cfg.minDuration);
}
var resolveLoadingState = function () {
_this.isPromiseDone = true;
_this.cancelLoadingStateIfPromiseAndMinDurationDone(btnEl);
};
if (!this.cfg.handleCurrentBtnOnly) {
this.initLoadingState(btnEl);
}
// native Promise doesn't have finally
if (promise.finally) {
promise.finally(resolveLoadingState);
}
else {
promise
.then(resolveLoadingState)
.catch(resolveLoadingState);
}
};
/**
* $compile and append the spinner template to the button.
*/
PromiseBtnDirective.prototype.appendSpinnerTpl = function (btnEl) {
// TODO add some kind of compilation later on
btnEl.insertAdjacentHTML('beforeend', this.cfg.spinnerTpl);
};
/**
* Limit loading state to show only for the currently clicked button.
* Executed only if this.cfg.handleCurrentBtnOnly is set
*/
PromiseBtnDirective.prototype.handleCurrentBtnOnly = function () {
var _this = this;
if (!this.cfg.handleCurrentBtnOnly) {
return true; // return true for testing
}
// Click triggers @Input update
// We need to use timeout to wait for @Input to update
window.setTimeout(function () {
// return if something else than a promise is passed
if (!_this.promise) {
return;
}
_this.initLoadingState(_this.btnEl);
}, 0);
};
return PromiseBtnDirective;
}());
PromiseBtnDirective.ɵfac = i0__namespace.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "12.2.17", ngImport: i0__namespace, type: PromiseBtnDirective, deps: [{ token: i0__namespace.ElementRef }, { token: userCfg }], target: i0__namespace.ɵɵFactoryTarget.Directive });
PromiseBtnDirective.ɵdir = i0__namespace.ɵɵngDeclareDirective({ minVersion: "12.0.0", version: "12.2.17", type: PromiseBtnDirective, selector: "[promiseBtn]", inputs: { isDisabledFromTheOutsideSetter: ["disabled", "isDisabledFromTheOutsideSetter"], promiseBtn: "promiseBtn" }, host: { listeners: { "click": "handleCurrentBtnOnly()" } }, ngImport: i0__namespace });
i0__namespace.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "12.2.17", ngImport: i0__namespace, type: PromiseBtnDirective, decorators: [{
type: i0.Directive,
args: [{
selector: '[promiseBtn]'
}]
}], ctorParameters: function () {
return [{ type: i0__namespace.ElementRef }, { type: undefined, decorators: [{
type: i0.Inject,
args: [userCfg]
}] }];
}, propDecorators: { isDisabledFromTheOutsideSetter: [{
type: i0.Input,
args: ['disabled']
}], promiseBtn: [{
type: i0.Input
}], handleCurrentBtnOnly: [{
type: i0.HostListener,
args: ['click']
}] } });
var Angular2PromiseButtonModule = /** @class */ (function () {
function Angular2PromiseButtonModule() {
}
// add forRoot to make it configurable
Angular2PromiseButtonModule.forRoot = function (config) {
// NOTE: this is never allowed to contain any conditional logic
return {
ngModule: Angular2PromiseButtonModule,
providers: [{ provide: userCfg, useValue: config }]
};
};
return Angular2PromiseButtonModule;
}());
Angular2PromiseButtonModule.ɵfac = i0__namespace.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "12.2.17", ngImport: i0__namespace, type: Angular2PromiseButtonModule, deps: [], target: i0__namespace.ɵɵFactoryTarget.NgModule });
Angular2PromiseButtonModule.ɵmod = i0__namespace.ɵɵngDeclareNgModule({ minVersion: "12.0.0", version: "12.2.17", ngImport: i0__namespace, type: Angular2PromiseButtonModule, declarations: [PromiseBtnDirective], exports: [PromiseBtnDirective] });
Angular2PromiseButtonModule.ɵinj = i0__namespace.ɵɵngDeclareInjector({ minVersion: "12.0.0", version: "12.2.17", ngImport: i0__namespace, type: Angular2PromiseButtonModule, providers: [], imports: [[]] });
i0__namespace.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "12.2.17", ngImport: i0__namespace, type: Angular2PromiseButtonModule, decorators: [{
type: i0.NgModule,
args: [{
declarations: [
PromiseBtnDirective,
],
imports: [],
exports: [
PromiseBtnDirective,
],
providers: []
}]
}] });
/**
* Generated bundle index. Do not edit.
*/
exports.Angular2PromiseButtonModule = Angular2PromiseButtonModule;
exports.PromiseBtnDirective = PromiseBtnDirective;
Object.defineProperty(exports, '__esModule', { value: true });
}));
//# sourceMappingURL=angular2-promise-buttons.umd.js.map