From 94747c6e994e195b0101ae266956ddc745ce336e Mon Sep 17 00:00:00 2001
From: Tamara Steinwender <tamara.steinwender@tugraz.at>
Date: Wed, 9 Nov 2022 13:47:24 +0100
Subject: [PATCH] Add new dbp-icon-button element, refactor button code to
 prevent duplicated code

---
 packages/common/components.js |   2 +
 packages/common/index.js      |   4 +-
 packages/common/src/button.js | 183 ++++++++++++++++++++++------------
 packages/common/styles.js     |   4 +-
 4 files changed, 126 insertions(+), 67 deletions(-)

diff --git a/packages/common/components.js b/packages/common/components.js
index ac6917b0..0bb8c3c8 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 3d7467fc..8353b600 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 e2f7bf31..4428a23c 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 85381bd0..f48caa99 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,
-- 
GitLab