@3mo/loading-button
Version:
A button web-component with a loading state and capable of inferring the loading state from click event handler promises.
93 lines (92 loc) • 3.55 kB
JavaScript
import { __decorate } from "tslib";
import { EventListenerController, component, css, extractEventHandler, html, property, queryAsync, style } from '@a11d/lit';
import { Button } from '@3mo/button';
import '@3mo/circular-progress';
/**
* @attr loading
* @attr preventClickEventInference
*/
let LoadingButton = class LoadingButton extends Button {
constructor() {
super(...arguments);
this.loading = false;
this.preventClickEventInference = false;
this.clickEventListeners = new Set();
this.clickEventListenerController = new EventListenerController(this, {
type: 'click',
target: () => this.button,
listener: async (e) => {
if (this.preventClickEventInference === false) {
const results = [...this.clickEventListeners]
.map(listener => extractEventHandler(listener)(e))
.filter(Boolean);
if (results.length > 0 && this.loading === false) {
e.stopImmediatePropagation();
this.loading = true;
await Promise.allSettled(results);
this.loading = false;
}
}
},
});
}
addEventListener(type, listener, options) {
if (type === 'click') {
this.clickEventListeners.add(listener);
}
super.addEventListener(type, listener, options);
}
removeEventListener(type, listener, options) {
if (type === 'click') {
this.clickEventListeners.delete(listener);
}
super.removeEventListener(type, listener, options);
}
static get styles() {
return css `
${super.styles}
:host([loading]) { pointer-events: none; }
[md-button] { position: relative; }
`;
}
get contentTemplate() {
return html `
${super.contentTemplate}
${!this.circularProgressReplacesStartIcon && this.loading ? this.circularProgressTemplate : html.nothing}
`;
}
get isDisabled() {
return super.isDisabled || this.loading;
}
get startIconTemplate() {
return this.circularProgressReplacesStartIcon && this.loading ? this.circularProgressTemplate : super.startIconTemplate;
}
get circularProgressReplacesStartIcon() {
return !!this.startIcon;
}
get circularProgressTemplate() {
return html `
<mo-circular-progress ${style({
position: this.circularProgressReplacesStartIcon ? undefined : 'absolute',
top: this.circularProgressReplacesStartIcon ? undefined : '50%',
insetInlineStart: this.circularProgressReplacesStartIcon ? undefined : '50%',
transform: this.circularProgressReplacesStartIcon ? undefined : getComputedStyle(this).direction === 'rtl' ? 'translate(+50%, -50%)' : 'translate(-50%, -50%)',
width: this.circularProgressReplacesStartIcon ? '24px' : 'auto',
height: this.circularProgressReplacesStartIcon ? '24px' : '75%',
})}></mo-circular-progress>
`;
}
};
__decorate([
property({ type: Boolean, reflect: true })
], LoadingButton.prototype, "loading", void 0);
__decorate([
property({ type: Boolean })
], LoadingButton.prototype, "preventClickEventInference", void 0);
__decorate([
queryAsync('[md-button]')
], LoadingButton.prototype, "button", void 0);
LoadingButton = __decorate([
component('mo-loading-button')
], LoadingButton);
export { LoadingButton };