A custom Button component built using Vue and Tailwindcss for an ongoing opensource project.
Designed by @mithicher
A Pen by abhishek sarmah on CodePen.
| <div id="app"> | |
| <div class="h-2 w-100 bg-blue-500"></div> | |
| <div class="max-w-lg mx-auto my-12"> | |
| <!-- primary started --> | |
| <h2 class="flex text-xl mb-3 text-gray-700"> | |
| <span class="text-lg bg-gray-200 text-gray-800 h-6 w-6 rounded-full inline-flex justify-center items-center mt-px mr-3">1</span> | |
| Primary Loading Button | |
| </h2> | |
| <loading-button ref="primaryLoadingButton" @click="click('primaryLoadingButton')">Save Changes</loading-button> | |
| <h2 class="flex text-xl mb-3 text-gray-700 mt-6"> | |
| <span class="text-lg bg-gray-200 text-gray-800 h-6 w-6 rounded-full inline-flex justify-center items-center mt-px mr-3">2</span> | |
| Primary Disabled Button | |
| </h2> | |
| <loading-button ref="primaryDisabledButton" @click="click" :disabled="true">Save Changes</loading-button> | |
| <h2 class="flex text-xl mb-3 text-gray-700 mt-6"> | |
| <span class="text-lg bg-gray-200 text-gray-800 h-6 w-6 rounded-full inline-flex justify-center items-center mt-px mr-3">3</span> | |
| Primary Outline Button | |
| </h2> | |
| <loading-button ref="primaryOutlineButton" @click="alert('Editing work :)')" variant-type="outline">Edit</loading-button> | |
| <!-- primary ended --> | |
| <!-- danger started --> | |
| <h2 class="flex text-xl mb-3 text-gray-700 mt-6"> | |
| <span class="text-lg bg-gray-200 text-gray-800 h-6 w-6 rounded-full inline-flex justify-center items-center mt-px mr-3">4</span> | |
| Danger Loading Button | |
| </h2> | |
| <loading-button ref="dangerLoadingButton" @click="click('dangerLoadingButton')" variant="danger">Confirm Delete</loading-button> | |
| <h2 class="flex text-xl mb-3 text-gray-700 mt-6"> | |
| <span class="text-lg bg-gray-200 text-gray-800 h-6 w-6 rounded-full inline-flex justify-center items-center mt-px mr-3">5</span> | |
| Danger Disabled Button | |
| </h2> | |
| <loading-button ref="dangerDisabledButton" @click="click" :disabled="true" variant="danger">Delete Project</loading-button> | |
| <h2 class="flex text-xl mb-3 text-gray-700 mt-6"> | |
| <span class="text-lg bg-gray-200 text-gray-800 h-6 w-6 rounded-full inline-flex justify-center items-center mt-px mr-3">6</span> | |
| Danger Outline Button | |
| </h2> | |
| <loading-button ref="primaryOutlineButton" @click="alert('Delete work :)')" variant-type="outline" variant="danger">Delete</loading-button> | |
| <!-- danger ended --> | |
| <!-- success started --> | |
| <h2 class="flex text-xl mb-3 text-gray-700 mt-6"> | |
| <span class="text-lg bg-gray-200 text-gray-800 h-6 w-6 rounded-full inline-flex justify-center items-center mt-px mr-3">7</span> | |
| Success Loading Button | |
| </h2> | |
| <loading-button ref="successLoadingButton" @click="click('successLoadingButton')" variant="success">Pay Now</loading-button> | |
| <h2 class="flex text-xl mb-3 text-gray-700 mt-6"> | |
| <span class="text-lg bg-gray-200 text-gray-800 h-6 w-6 rounded-full inline-flex justify-center items-center mt-px mr-3">8</span> | |
| Success Disabled Button | |
| </h2> | |
| <loading-button ref="dangerDisabledButton" @click="click" :disabled="true" variant="success">Processing ...</loading-button> | |
| <h2 class="flex text-xl mb-3 text-gray-700 mt-6"> | |
| <span class="text-lg bg-gray-200 text-gray-800 h-6 w-6 rounded-full inline-flex justify-center items-center mt-px mr-3">9</span> | |
| Success Outline Button | |
| </h2> | |
| <loading-button ref="primaryOutlineButton" @click="alert('Signing up:)')" variant-type="outline" variant="success">Sign Up</loading-button> | |
| <!-- success ended --> | |
| <!-- warning started --> | |
| <h2 class="flex text-xl mb-3 text-gray-700 mt-6"> | |
| <span class="text-lg bg-gray-200 text-gray-800 h-6 w-6 rounded-full inline-flex justify-center items-center mt-px mr-3">10</span> | |
| Warning Button | |
| </h2> | |
| <loading-button ref="warningLoadingButton" @click="click('warningLoadingButton')" variant="warning">Warning !</loading-button> | |
| <!-- warning ended --> | |
| <!-- secondary started --> | |
| <h2 class="flex text-xl mb-3 text-gray-700 mt-6"> | |
| <span class="text-lg bg-gray-200 text-gray-800 h-6 w-6 rounded-full inline-flex justify-center items-center mt-px mr-3">11</span> | |
| Secondary Button | |
| </h2> | |
| <loading-button ref="secondaryLoadingButton" @click="click('secondaryLoadingButton')" variant="secondary" variant-type="outline">Cancel</loading-button> | |
| <!-- secondary ended --> | |
| </div> | |
| </div> |
| const LoadingButton = { | |
| name: "LoadingButton", | |
| template: ` | |
| <component | |
| :is="tag" | |
| :type="type" | |
| v-on="$listeners" | |
| :disabled="disableButton" | |
| :class="[btnClass, colorVariants]" | |
| :variant="variant" | |
| :variant-type="variantType" | |
| :size="size" | |
| :href="to" | |
| > | |
| <slot /> | |
| </component>`, | |
| props: { | |
| tag: { | |
| type: String, | |
| default: "button" | |
| }, | |
| disabled: { | |
| type: Boolean, | |
| default: false | |
| }, | |
| variant: { | |
| type: String, | |
| default: "primary" | |
| }, | |
| variantType: { | |
| type: String, | |
| default: "normal" | |
| }, | |
| size: { | |
| type: String, | |
| default: "normal" | |
| }, | |
| rounded: { | |
| type: String, | |
| default: "medium" | |
| }, | |
| type: { | |
| type: String, | |
| default: "" | |
| }, | |
| to: { | |
| type: String | |
| } | |
| }, | |
| data() { | |
| return { | |
| loading: false, | |
| disableButton: this.disabled | |
| }; | |
| }, | |
| methods: { | |
| startLoading() { | |
| this.loading = true; | |
| this.disableButton = true; | |
| }, | |
| stopLoading() { | |
| this.loading = false; | |
| this.disableButton = false; | |
| } | |
| }, | |
| computed: { | |
| btnClass() { | |
| return { | |
| "base-spinner": this.loading == true, | |
| "cursor-not-allowed": this.disableButton == true, | |
| "base-button inline-flex align-middle align-items-center justify-center font-medium focus:outline-none border-2": true, | |
| "rounded-lg": this.rounded === "medium", | |
| "rounded-full": this.rounded === "large", | |
| "px-6 py-3": this.size == "normal", | |
| "px-4 py-2": this.size == "small" | |
| }; | |
| }, | |
| colorVariants() { | |
| switch (this.variant) { | |
| case "primary": | |
| switch (this.variantType) { | |
| case "normal": | |
| switch (this.disableButton) { | |
| case true: | |
| return "border-blue-300 bg-blue-300 text-white"; | |
| break; | |
| default: | |
| break; | |
| } | |
| return "border-blue-600 bg-blue-600 hover:bg-blue-700 hover:border-blue-700 text-white"; | |
| break; | |
| case "outline": | |
| return "border-gray-200 text-blue-500 hover:text-blue-700"; | |
| break; | |
| default: | |
| break; | |
| } | |
| break; | |
| case "danger": | |
| switch (this.variantType) { | |
| case "normal": | |
| switch (this.disableButton) { | |
| case true: | |
| return "border-red-300 bg-red-300 text-white"; | |
| break; | |
| default: | |
| break; | |
| } | |
| return "border-red-600 bg-red-600 hover:bg-red-700 hover:border-red-700 text-white"; | |
| break; | |
| case "outline": | |
| return "border-gray-200 text-red-500 hover:text-red-600"; | |
| break; | |
| default: | |
| break; | |
| } | |
| break; | |
| case "warning": | |
| switch (this.variantType) { | |
| case "normal": | |
| return "border-orange-600 bg-orange-600 hover:bg-orange-700 hover:border-orange-700 text-white"; | |
| break; | |
| default: | |
| break; | |
| } | |
| break; | |
| case "success": | |
| switch (this.variantType) { | |
| case "normal": | |
| switch (this.disableButton) { | |
| case true: | |
| return "border-green-300 bg-green-300 text-white"; | |
| break; | |
| default: | |
| break; | |
| } | |
| return "border-green-600 bg-green-600 hover:bg-green-700 hover:border-green-700 text-white"; | |
| break; | |
| case "outline": | |
| return "border-2 border-gray-200 text-green-500 hover:text-green-700"; | |
| break; | |
| default: | |
| break; | |
| } | |
| break; | |
| case "white": | |
| switch (this.variantType) { | |
| case "normal": | |
| return "border-white bg-white bg-white text-blue-600 hover:text-blue-800"; | |
| break; | |
| default: | |
| break; | |
| } | |
| break; | |
| case "secondary": | |
| switch (this.variantType) { | |
| case "outline": | |
| return "border-gray-300 text-gray-500 hover:text-blue-500"; | |
| break; | |
| default: | |
| break; | |
| } | |
| break; | |
| default: | |
| break; | |
| } | |
| } | |
| } | |
| }; | |
| new Vue({ | |
| el: "#app", | |
| components: { | |
| LoadingButton | |
| }, | |
| methods: { | |
| click(ref) { | |
| this.$refs[`${ref}`].startLoading(); | |
| setTimeout(() => this.$refs[`${ref}`].stopLoading(), 3000); | |
| } | |
| } | |
| }); |
| <script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.6.11/vue.min.js"></script> |
| .base-button + .base-button { | |
| margin-left: 1em; | |
| } | |
| @keyframes spinner { | |
| to { | |
| transform: rotate(360deg); | |
| } | |
| } | |
| .base-spinner { | |
| position: relative; | |
| overflow: hidden; | |
| } | |
| .base-spinner:before { | |
| content: ""; | |
| box-sizing: border-box; | |
| position: absolute; | |
| background-color: inherit; | |
| width: 100%; | |
| height: 100%; | |
| display: block; | |
| z-index: 1; | |
| top: 0; | |
| left: 0; | |
| } | |
| .base-spinner:after { | |
| content: ""; | |
| box-sizing: border-box; | |
| position: absolute; | |
| top: 50%; | |
| left: 50%; | |
| width: 20px; | |
| height: 20px; | |
| margin-top: -10px; | |
| margin-left: -10px; | |
| border-radius: 50%; | |
| border: 2px solid rgba(255, 255, 255, 0.45); | |
| border-top-color: inherit; | |
| animation: spinner 0.6s linear infinite; | |
| z-index: 2; | |
| } |
| <link href="https://cdnjs.cloudflare.com/ajax/libs/tailwindcss/1.2.0/tailwind.min.css" rel="stylesheet" /> |