diff --git a/packages/common/components.js b/packages/common/components.js index ac6917b0663c2a19190e69bb5b7653a8a3f681e6..0bb8c3c805204c75739a0c51dc51cf79a5c89b09 100644 --- a/packages/common/components.js +++ b/packages/common/components.js @@ -4,6 +4,7 @@ import { Icon, InlineNotification, LoadingButton, + IconButton, MiniSpinner, Spinner, Translated, @@ -15,6 +16,7 @@ commonUtils.defineCustomElement('dbp-spinner', Spinner); commonUtils.defineCustomElement('dbp-icon', Icon); commonUtils.defineCustomElement('dbp-button', Button); commonUtils.defineCustomElement('dbp-loading-button', LoadingButton); +commonUtils.defineCustomElement('dbp-icon-button', IconButton); commonUtils.defineCustomElement('dbp-inline-notification', InlineNotification); commonUtils.defineCustomElement('dbp-translated', Translated); commonUtils.defineCustomElement('dbp-translation', Translation); diff --git a/packages/common/index.js b/packages/common/index.js index 3d7467fc55c3290890d31f4c2164949c6f726323..8353b6006ac7b7fce9f83fe0e6e3335bee42f6cc 100644 --- a/packages/common/index.js +++ b/packages/common/index.js @@ -2,7 +2,7 @@ import {EventBus} from './src/eventbus.js'; import {createLinkedAbortController, createTimeoutAbortSignal} from './src/abort.js'; import {getIconSVGURL, getIconCSS, Icon} from './src/icon.js'; import {MiniSpinner} from './src/mini-spinner.js'; -import {Button, LoadingButton} from './src/button.js'; +import {Button, LoadingButton, IconButton} from './src/button.js'; import {Spinner} from './src/spinner.js'; import {InlineNotification} from './src/inline-notification.js'; import {Translated} from './src/translated'; @@ -12,7 +12,7 @@ import {AdapterLitElement} from './src/adapter-lit-element.js'; export {EventBus, createLinkedAbortController, createTimeoutAbortSignal}; export {getIconSVGURL, getIconCSS, Icon}; export {MiniSpinner}; -export {Button, LoadingButton}; +export {Button, LoadingButton, IconButton}; export {Spinner}; export {InlineNotification}; export {Translated, Translation}; diff --git a/packages/common/src/button.js b/packages/common/src/button.js index e2f7bf31f9e42f00b44381aaa6ed52011c57eb9a..4428a23caf96ce064f45641e37f887bf655ca804 100644 --- a/packages/common/src/button.js +++ b/packages/common/src/button.js @@ -3,22 +3,12 @@ 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 - * 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) { +class DbpButton extends ScopedElementsMixin(LitElement) { constructor() { super(); this.value = ''; this.type = ''; - this.spinner = false; - this.noSpinnerOnClick = false; + this.loading = false; this.disabled = false; // https://bugs.chromium.org/p/chromium/issues/detail?id=1061240#c12 @@ -26,10 +16,6 @@ export class Button extends ScopedElementsMixin(LitElement) { if (this.disabled) { e.stopImmediatePropagation(); } - - if (!this.noSpinnerOnClick) { - this.start(); - } }); } @@ -39,33 +25,65 @@ export class Button extends ScopedElementsMixin(LitElement) { }; } - connectedCallback() { - super.connectedCallback(); - } - static get properties() { return { + // value is deprecated, use the main slot instead value: {type: String}, type: {type: String}, - spinner: {type: Boolean}, - noSpinnerOnClick: {type: Boolean, attribute: 'no-spinner-on-click'}, + loading: {type: Boolean}, disabled: {type: Boolean, reflect: true}, }; } start() { - this.spinner = true; + this.loading = true; this.disabled = true; } stop() { - this.spinner = false; + this.loading = false; this.disabled = false; } isDisabled() { return this.disabled; } +} + +/** + * dbp-button implements a button with Bulma styles and automatic spinner and + * 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/is-icon + */ +export class Button extends DbpButton { + constructor() { + super(); + this.spinner = false; + this.noSpinnerOnClick = false; + + // https://bugs.chromium.org/p/chromium/issues/detail?id=1061240#c12 + this.addEventListener('click', (e) => { + if (!this.noSpinnerOnClick) { + this.start(); + } + }); + } + + connectedCallback() { + super.connectedCallback(); + } + + static get properties() { + return { + ...super.properties, + spinner: {type: Boolean}, + noSpinnerOnClick: {type: Boolean, attribute: 'no-spinner-on-click'}, + }; + } static get styles() { // language=css @@ -84,7 +102,7 @@ export class Button extends ScopedElementsMixin(LitElement) { <button class="button ${this.type}" ?disabled="${this.disabled}"> - ${this.value} + <slot>${this.value}</slot> <dbp-mini-spinner class="spinner" style="display: ${this.spinner ? 'inline' : 'none'}"></dbp-mini-spinner> @@ -93,46 +111,9 @@ export class Button extends ScopedElementsMixin(LitElement) { } } -export class LoadingButton extends ScopedElementsMixin(LitElement) { +export class LoadingButton extends DbpButton { constructor() { super(); - 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) { - e.stopImmediatePropagation(); - } - }); - } - - static get scopedElements() { - return { - 'dbp-mini-spinner': MiniSpinner, - }; - } - - static get properties() { - return { - // value is deprecated, use the main slot instead - 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() { @@ -197,3 +178,79 @@ export class LoadingButton extends ScopedElementsMixin(LitElement) { `; } } + +export class IconButton extends ScopedElementsMixin(LitElement) { + constructor() { + super(); + this.iconName = ''; + } + + static get properties() { + return { + ...super.properties, + iconName: {type: String, attribute: 'icon-name'}, + }; + } + + static get styles() { + // language=css + return css` + ${commonStyles.getThemeCSS()} + ${commonStyles.getButtonCSS()} + + :host{ + font-size: 1.2rem; + } + + .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; + } + + + .button { + width: 100%; + } + + @media only screen and (orientation: portrait) and (max-width: 768px) { + .button { + min-height: 36px; + } + + .label { + margin: auto; + } + } + `; + } + + render() { + return html` + <button + class="button ${this.type} is-icon loading-container ${!this.loading + ? 'is-not-loading' + : ''}" + ?disabled="${this.disabled}"> + <slot><dbp-icon class="dbp-button-icon" name="${this.iconName}"></dbp-icon></slot> + <dbp-mini-spinner + class="spinner" + style="display: ${this.loading ? 'inline' : 'none'}"></dbp-mini-spinner> + </button> + `; + } +} diff --git a/packages/common/styles.js b/packages/common/styles.js index 85381bd04bad55f2daa67000450ff4f5f3bb92b9..f48caa99b4abecfe3a4e4a6427888462de470e95 100644 --- a/packages/common/styles.js +++ b/packages/common/styles.js @@ -638,7 +638,6 @@ export function getButtonCSS() { button.button.is-icon, .button.is-icon { border: none; background: none; - font-size: 1.2rem; padding: 0px; width: 40px; height: 40px; @@ -647,8 +646,9 @@ export function getButtonCSS() { align-items: center; } - button.button.is-icon dbp-icon, .button.is-icon dbp-icon{ + button.button.is-icon dbp-icon, .button.is-icon dbp-icon, dbp-button-icon{ top: 0px; + font-size: 1.2em; } button.button.is-icon:hover:enabled,