diff --git a/packages/common/src/button.js b/packages/common/src/button.js new file mode 100644 index 0000000000000000000000000000000000000000..a370ddb381607b16534b9598c98ad90b0e860501 --- /dev/null +++ b/packages/common/src/button.js @@ -0,0 +1,82 @@ +import {html, LitElement, css} from 'lit-element'; +import {ScopedElementsMixin} from '@open-wc/scoped-elements'; +import {MiniSpinner} from './mini-spinner.js'; +import * as commonStyles from '../styles.js'; + +/** + * vpu-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) { + constructor() { + super(); + this.value = ""; + this.type = ""; + this.spinner = false; + this.noSpinnerOnClick = false; + this.disabled = false; + } + + static get scopedElements() { + return { + 'vpu-mini-spinner': MiniSpinner, + }; + } + + connectedCallback() { + super.connectedCallback(); + } + + static get properties() { + return { + value: { type: String }, + type: { type: String }, + spinner: { type: Boolean }, + noSpinnerOnClick: { type: Boolean, attribute: 'no-spinner-on-click' }, + disabled: { type: Boolean, reflect: true }, + }; + } + + clickHandler() { + if (!this.noSpinnerOnClick) { + this.start(); + } + } + + 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()} + + .spinner { margin-left: 0.5em; } + `; + } + + render() { + return html` + <button @click="${this.clickHandler}" class="button ${this.type}" ?disabled="${this.disabled}"> + ${this.value} <vpu-mini-spinner class="spinner" style="display: ${this.spinner ? "inline" : "none"}"></vpu-mini-spinner> + </button> + `; + } +} diff --git a/packages/common/src/mini-spinner.js b/packages/common/src/mini-spinner.js new file mode 100644 index 0000000000000000000000000000000000000000..67e769997b2984343db1c1ac06ad4efbe37f7c25 --- /dev/null +++ b/packages/common/src/mini-spinner.js @@ -0,0 +1,70 @@ +import {html, LitElement, css} from 'lit-element'; + +export class MiniSpinner extends LitElement { + constructor() { + super(); + this.text = ""; + } + + static get properties() { + return { + text: { type: String }, + }; + } + + static get styles() { + // language=css + return css` + .outer { + display: inline-block; + } + .inner { + display: flex; + } + .ring { + display: inline-block; + position: relative; + width: 1em; + height: 1em; + } + .ring div { + box-sizing: border-box; + display: block; + position: absolute; + width: 100%; + height: 100%; + border: 0.2em solid currentColor; + border-radius: 50%; + animation: ring 1.2s cubic-bezier(0.5, 0, 0.5, 1) infinite; + border-color: currentColor transparent transparent transparent; + } + .ring div:nth-child(1) { + animation-delay: -0.45s; + } + .ring div:nth-child(2) { + animation-delay: -0.3s; + } + .ring div:nth-child(3) { + animation-delay: -0.15s; + } + @keyframes ring { + 0% { + transform: rotate(0deg); + } + 100% { + transform: rotate(360deg); + } + } + .text { + display: inline-block; + margin-left: 0.5em; + font-size: 0.7em; + }`; + } + + render() { + const textHtml = this.text !== "" ? html`<div class="text">${this.text}</div>` : html``; + + return html`<div class="outer"><div class="inner"><div class="ring"><div></div><div></div><div></div><div></div></div>${textHtml}</div></div>`; + } +} diff --git a/packages/common/src/spinner.js b/packages/common/src/spinner.js new file mode 100644 index 0000000000000000000000000000000000000000..88e20f5fd23f071e48192a5faee813e2e88bb78f --- /dev/null +++ b/packages/common/src/spinner.js @@ -0,0 +1,118 @@ +// Note: This doesn't have a lit-element dependency on purpose, so it can be loaded faster before +// other web components (assuming it's not bundled) + +export class Spinner extends HTMLElement { + + constructor() { + super(); + let shadowRoot = this.attachShadow({mode: 'open'}); + shadowRoot.innerHTML = ` +<style> +:host { + display: block; +} +#all-spinner-tuglogo { + width:130px; + height:130px; + position:relative; + background-color:transparent; + margin:0 auto; +} + +.all-spinner-tuglogo-box { + width:20%; + height:20%; + background-color:#e4154b; + position:absolute; + top:50%; + left:50%; + animation-duration: 1.6s; + animation-direction:alternate; + animation-iteration-count:infinite; + animation-fill-mode:both; + animation-timing-function:ease; + transition: transform 0.5s, background-color 0.2s 0.5s; +} + +#all-spinner-tuglogo-box-1 { + animation-name: box1; + transform:translateX(-160%) translateY(-50%); +} + +#all-spinner-tuglogo-box-2 { + transform-origin:0 0; + animation-name: box2; +transform:scale(1) translateX(-50%) translateY(-50%); +} + +#all-spinner-tuglogo-box-3 { + + animation-name: box3; + animation-delay:0.3s; + transform:translateX(60%) translateY(-50%); visibility:visible; +} + +#all-spinner-tuglogo-box-4 { + + animation-name: box4; + animation-delay:0.1s; +transform:translateX(0%) translateY(-100%); visibility:visible; +} + +#all-spinner-tuglogo-box-5 { + + animation-name: box5; + animation-delay:0.2s; +transform:translateX(-100%) translateY(0%); visibility:visible; +} + +@keyframes box1 { + 0% { transform:translateX(-50%) translateY(-50%); visibility:hidden; } + 50% { transform:translateX(-50%) translateY(-50%); visibility:hidden; } + 70% { transform:translateX(-170%) translateY(-50%); visibility:visible;} + 80% { transform:translateX(-160%) translateY(-50%); visibility:visible;} + 100% { transform:translateX(-160%) translateY(-50%); visibility:visible;} +} + +@keyframes box2 { + 0% { transform:scale(0) translateX(-50%) translateY(-50%);} + 5% { transform:scale(0) translateX(-50%) translateY(-50%);} + 30% { transform:scale(1.2) translateX(-50%) translateY(-50%);} + 35% { transform:scale(1) translateX(-50%) translateY(-50%);} + 100% { transform:scale(1) translateX(-50%) translateY(-50%);} + +} + +@keyframes box3 { + 0% { transform:translateX(-50%) translateY(-50%); visibility:hidden; } + 50% { transform:translateX(-50%) translateY(-50%); visibility:hidden; } + 70% { transform:translateX(70%) translateY(-50%); visibility:visible;} + 80% { transform:translateX(60%) translateY(-50%); visibility:visible;} + 100% { transform:translateX(60%) translateY(-50%); visibility:visible;} +} + +@keyframes box4 { + 0% { transform:translateX(-50%) translateY(-50%); visibility:hidden; } + 50% { transform:translateX(-50%) translateY(-50%); visibility:hidden; } + 70% { transform:translateX(10%) translateY(-110%); visibility:visible;} + 80% { transform:translateX(0%) translateY(-100%); visibility:visible;} + 100% { transform:translateX(0%) translateY(-100%); visibility:visible;} +} +@keyframes box5 { + 0% { transform:translateX(-50%) translateY(-50%); visibility:hidden; } + 50% { transform:translateX(-50%) translateY(-50%); visibility:hidden; } + 70% { transform:translateX(-110%) translateY(10%); visibility:visible;} + 80% { transform:translateX(-100%) translateY(0%); visibility:visible;} + 100% { transform:translateX(-100%) translateY(0%); visibility:visible;} +} +</style> + +<div id="all-spinner-tuglogo"> + <div id="all-spinner-tuglogo-box-1" class="all-spinner-tuglogo-box"></div> + <div id="all-spinner-tuglogo-box-2" class="all-spinner-tuglogo-box"></div> + <div id="all-spinner-tuglogo-box-3" class="all-spinner-tuglogo-box"></div> + <div id="all-spinner-tuglogo-box-4" class="all-spinner-tuglogo-box"></div> + <div id="all-spinner-tuglogo-box-5" class="all-spinner-tuglogo-box"></div> +</div>`; + } +} \ No newline at end of file