Skip to content
Snippets Groups Projects
button.js 4.91 KiB
Newer Older
import {html, LitElement, css} from 'lit';
Reiter, Christoph's avatar
Reiter, Christoph committed
import {ScopedElementsMixin} from '@open-wc/scoped-elements';
import {MiniSpinner} from './mini-spinner.js';
import * as commonStyles from '../styles.js';

/**
 * dbp-button implements a button with Bulma styles and automatic spinner and
Reiter, Christoph's avatar
Reiter, Christoph committed
 * disabling if button is clicked
 *
 * Use the attribute "no-spinner-on-click" to disable the spinner, then you can
 * start it with start() and stop it with stop()
 *
 * Type can be is-primary/is-info/is-success/is-warning/is-danger
 */
export class Button extends ScopedElementsMixin(LitElement) {
    constructor() {
        super();
Reiter, Christoph's avatar
Reiter, Christoph committed
        this.value = '';
        this.type = '';
Reiter, Christoph's avatar
Reiter, Christoph committed
        this.spinner = false;
        this.noSpinnerOnClick = false;
        this.disabled = false;

        // https://bugs.chromium.org/p/chromium/issues/detail?id=1061240#c12
        this.addEventListener('click', (e) => {
            if (this.disabled) {
                e.stopImmediatePropagation();
            }

            if (!this.noSpinnerOnClick) {
                this.start();
            }
        });
Reiter, Christoph's avatar
Reiter, Christoph committed
    }

    static get scopedElements() {
        return {
            'dbp-mini-spinner': MiniSpinner,
Reiter, Christoph's avatar
Reiter, Christoph committed
        };
    }

    connectedCallback() {
        super.connectedCallback();
    }

    static get properties() {
        return {
Reiter, Christoph's avatar
Reiter, Christoph committed
            value: {type: String},
            type: {type: String},
            spinner: {type: Boolean},
            noSpinnerOnClick: {type: Boolean, attribute: 'no-spinner-on-click'},
            disabled: {type: Boolean, reflect: true},
Reiter, Christoph's avatar
Reiter, Christoph committed
        };
    }

    start() {
        this.spinner = true;
        this.disabled = true;
    }

    stop() {
        this.spinner = false;
        this.disabled = false;
    }

    isDisabled() {
        return this.disabled;
    }

    static get styles() {
        // language=css
        return css`
            ${commonStyles.getThemeCSS()}
            ${commonStyles.getButtonCSS()}

Reiter, Christoph's avatar
Reiter, Christoph committed
            .spinner {
                margin-left: 0.5em;
            }
Reiter, Christoph's avatar
Reiter, Christoph committed
        `;
    }

    render() {
        return html`
Reiter, Christoph's avatar
Reiter, Christoph committed
            <button
                class="button ${this.type}"
                ?disabled="${this.disabled}">
                ${this.value}
                <dbp-mini-spinner
                    class="spinner"
                    style="display: ${this.spinner ? 'inline' : 'none'}"></dbp-mini-spinner>
Reiter, Christoph's avatar
Reiter, Christoph committed
            </button>
        `;
    }
}

export class LoadingButton extends ScopedElementsMixin(LitElement) {
    constructor() {
        super();
Reiter, Christoph's avatar
Reiter, Christoph committed
        this.value = '';
        this.type = '';
        this.loading = false;
        this.disabled = false;

        // https://bugs.chromium.org/p/chromium/issues/detail?id=1061240#c12
        this.addEventListener('click', (e) => {
            if (this.disabled) {
Reiter, Christoph's avatar
Reiter, Christoph committed
                e.stopImmediatePropagation();
    }

    static get scopedElements() {
        return {
            'dbp-mini-spinner': MiniSpinner,
        };
    }

    static get properties() {
        return {
            // value is deprecated, use the main slot instead
Reiter, Christoph's avatar
Reiter, Christoph committed
            value: {type: String},
            type: {type: String},
            loading: {type: Boolean},
            disabled: {type: Boolean, reflect: true},
        };
    }

    start() {
        this.loading = true;
        this.disabled = true;
    }

    stop() {
        this.loading = false;
        this.disabled = false;
    }

    static get styles() {
        // language=css
        return css`
            ${commonStyles.getThemeCSS()}
            ${commonStyles.getButtonCSS()}

            .spinner {
                padding-left: 0.5em;
                min-width: 16px;
            }

            .loading-container {
                display: flex;
                align-items: baseline;
            }

            .label {
                white-space: nowrap;
                overflow: hidden;
                text-overflow: ellipsis;
            }

            :host {
                display: inline-block;
            }

            .is-not-loading .label {
                padding-left: 12px;
                padding-right: 12px;
            }

            .button {
                width: 100%;
            }
Reiter, Christoph's avatar
Reiter, Christoph committed
            @media only screen and (orientation: portrait) and (max-width: 768px) {
                .button {
                    min-height: 36px;
Reiter, Christoph's avatar
Reiter, Christoph committed
            <button
                class="button ${this.type} loading-container ${!this.loading
                    ? 'is-not-loading'
                    : ''}"
                ?disabled="${this.disabled}">
                <div class="label"><slot>${this.value}</slot></div>
                <dbp-mini-spinner
                    class="spinner"
                    style="display: ${this.loading ? 'inline' : 'none'}"></dbp-mini-spinner>