├── .gitignore ├── README.md ├── dist └── vue-button-spinner.js ├── package-lock.json ├── package.json ├── src └── VueButtonSpinner.vue └── webpack.config.js /.gitignore: -------------------------------------------------------------------------------- 1 | npm-debug.log 2 | /.idea/ 3 | node_modules/ -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # VueButtonSpinner 2 | [![npm version](https://badge.fury.io/js/vue-button-spinner.svg)](https://badge.fury.io/js/vue-button-spinner) 3 | 4 | > Simple VUE 2 component to create a button spinner. The perfect solution for your submit buttons! 5 | 6 | ![alt tag](https://ibin.co/31v55OGl4kwc.gif) 7 | 8 | # Features 9 | 10 | - Show an css loader into the button to indicate the request status. 11 | - Add a custom html inside the component. 12 | - Pure CSS loaders, no fonts or images have been used. 13 | - Different styles for each state: loading, success, error. 14 | - Available props: 15 | * 'isLoading' (boolean) to show the spinner 16 | * 'status' (String | Boolean) allow 'success' or true and 'error' or false. 17 | - **Remember use the .native modifier for the events ([docs](https://vuejs.org/v2/guide/migration.html#Listening-for-Native-Events-on-Components-with-v-on-changed))** 18 | 19 | # Install 20 | 21 | ```npm install vue-button-spinner``` 22 | 23 | # Usage 24 | 25 | ### Example: 26 | 27 | [Your .vue component (vue-loader with webpack or use vue-cli projects)] 28 | ```js 29 | 30 | import VueButtonSpinner from 'vue-button-spinner'; 31 | 32 | export default { 33 | name: 'events-form', 34 | data() { 35 | return { 36 | isLoading: false, 37 | status: '', 38 | } 39 | }, 40 | components: { 41 | VueButtonSpinner 42 | }, 43 | methods: { 44 | onSubmit() { 45 | this.isLoading = true 46 | $someRequest('/url', 'GET') 47 | .then(response => { 48 | this.isLoading = false 49 | this.status = true // or success 50 | setTimeout(() => { this.status = '' }, 2000) // to clear the status :) 51 | }) 52 | .catch(error => { 53 | console.error(error) 54 | this.isLoading = false 55 | this.status = false //or error 56 | }) 57 | } 58 | } 59 | } 60 | 61 | ``` 62 | 63 | [Your HTML code] 64 | ```html 65 | 66 | 70 | Submit 71 | 72 | 73 | ``` 74 | -------------------------------------------------------------------------------- /dist/vue-button-spinner.js: -------------------------------------------------------------------------------- 1 | !function(e,t){"object"==typeof exports&&"object"==typeof module?module.exports=t():"function"==typeof define&&define.amd?define("vue-button-spinner",[],t):"object"==typeof exports?exports["vue-button-spinner"]=t():e["vue-button-spinner"]=t()}(this,function(){return function(e){function t(r){if(n[r])return n[r].exports;var o=n[r]={i:r,l:!1,exports:{}};return e[r].call(o.exports,o,o.exports,t),o.l=!0,o.exports}var n={};return t.m=e,t.c=n,t.d=function(e,n,r){t.o(e,n)||Object.defineProperty(e,n,{configurable:!1,enumerable:!0,get:r})},t.n=function(e){var n=e&&e.__esModule?function(){return e.default}:function(){return e};return t.d(n,"a",n),n},t.o=function(e,t){return Object.prototype.hasOwnProperty.call(e,t)},t.p="",t(t.s=0)}([function(e,t,n){"use strict";function r(e){n(1)}Object.defineProperty(t,"__esModule",{value:!0});var o=n(7),a=n.n(o),i=n(8),s=n(6),c=r,d=s(a.a,i.a,!1,c,"data-v-67fd4b98",null);t.default=d.exports},function(e,t,n){var r=n(2);"string"==typeof r&&(r=[[e.i,r,""]]),r.locals&&(e.exports=r.locals);n(4)("c43219ee",r,!0)},function(e,t,n){t=e.exports=n(3)(void 0),t.push([e.i,'.fade-enter-active[data-v-67fd4b98],.fade-leave-active[data-v-67fd4b98]{transition:opacity 1s}.fade-enter[data-v-67fd4b98],.fade-leave-active[data-v-67fd4b98]{opacity:0;will-change:opacity}@keyframes rotation-data-v-67fd4b98{0%{transform:rotate(0deg)}to{transform:rotate(359deg)}}.vue-btn[data-v-67fd4b98]{-moz-appearance:none;-webkit-appearance:none;-webkit-box-align:center;-ms-flex-align:center;align-items:center;border-radius:3px;-webkit-box-shadow:none;box-shadow:none;display:-webkit-inline-box;display:-ms-inline-flexbox;display:inline-flex;font-size:1rem;height:2.25em;-webkit-box-pack:start;-ms-flex-pack:start;justify-content:flex-start;line-height:1.5;padding-left:calc(.625em - 1px);padding-right:calc(.625em - 1px);position:relative;vertical-align:top;-webkit-touch-callout:none;-webkit-user-select:none;-moz-user-select:none;-ms-user-select:none;user-select:none;background-color:#fff;border:1px solid #dbdbdb;color:#363636;cursor:pointer;-webkit-box-pack:center;-ms-flex-pack:center;justify-content:center;padding:calc(.375em - 1px) .75em;text-align:center;white-space:nowrap;transition:all .3s ease}button.vue-btn-loader-error[data-v-67fd4b98]:not(.is-loading){width:48px;background-color:#f44336;color:#fff}button.vue-btn-loader-success[data-v-67fd4b98]:not(.is-loading){width:48px;background-color:#8bc34a;color:#fff}button.vue-btn[data-v-67fd4b98]:disabled{cursor:not-allowed}.spinner[data-v-67fd4b98]{height:10px;width:10px;margin-right:8px;opacity:1;filter:alpha(opacity=100);animation:rotation-data-v-67fd4b98 .7s infinite linear;border:4px solid rgba(0,0,0,.2);border-top-color:#9e9e9e;border-radius:100%;transition:all .3s ease}.check[data-v-67fd4b98]{display:inline-block;width:23px;height:24px;border-radius:50%;transform:rotate(45deg);color:#fff;will-change:transform}.check[data-v-67fd4b98]:before{content:"";position:absolute;width:3px;height:9px;background-color:#fff;left:11px;top:6px}.check[data-v-67fd4b98]:after{content:"";position:absolute;width:3px;height:3px;background-color:#fff;left:8px;top:12px}.cross[data-v-67fd4b98]{display:inline-block;width:17px;height:16px;position:relative}.cross[data-v-67fd4b98]:after,.cross[data-v-67fd4b98]:before{position:absolute;left:8px;content:" ";height:16px;width:2px;background-color:#fff}.cross[data-v-67fd4b98]:before{transform:rotate(45deg);will-change:transform}.cross[data-v-67fd4b98]:after{transform:rotate(-45deg);will-change:transform}',""])},function(e,t){function n(e,t){var n=e[1]||"",o=e[3];if(!o)return n;if(t&&"function"==typeof btoa){var a=r(o);return[n].concat(o.sources.map(function(e){return"/*# sourceURL="+o.sourceRoot+e+" */"})).concat([a]).join("\n")}return[n].join("\n")}function r(e){return"/*# sourceMappingURL=data:application/json;charset=utf-8;base64,"+btoa(unescape(encodeURIComponent(JSON.stringify(e))))+" */"}e.exports=function(e){var t=[];return t.toString=function(){return this.map(function(t){var r=n(t,e);return t[2]?"@media "+t[2]+"{"+r+"}":r}).join("")},t.i=function(e,n){"string"==typeof e&&(e=[[null,e,""]]);for(var r={},o=0;on.parts.length&&(r.parts.length=n.parts.length)}else{for(var i=[],o=0;o 2 | 10 | 11 | 12 | 56 | 57 | 221 | -------------------------------------------------------------------------------- /webpack.config.js: -------------------------------------------------------------------------------- 1 | const webpack = require("webpack"); 2 | const path = require("path"); 3 | const PROD = process.env.NODE_ENV === "production"; 4 | 5 | module.exports = { 6 | entry: path.resolve("./src/VueButtonSpinner.vue"), 7 | 8 | output: { 9 | path: path.resolve(__dirname, "dist"), 10 | filename: "vue-button-spinner.js", 11 | 12 | libraryTarget: "umd", 13 | library: "vue-button-spinner", 14 | umdNamedDefine: true 15 | }, 16 | 17 | module: { 18 | loaders: [ 19 | { 20 | test: /\.vue$/, 21 | loader: "vue-loader", 22 | options: { 23 | loaders: { 24 | js: { 25 | loader: "babel-loader", 26 | options: { 27 | presets: ["env"] 28 | } 29 | } 30 | } 31 | } 32 | } 33 | ] 34 | }, 35 | 36 | plugins: [ 37 | new webpack.optimize.UglifyJsPlugin({ 38 | minimize: PROD ? true : false, 39 | sourceMap: PROD ? false : true, 40 | mangle: PROD ? true : false, 41 | compress: { 42 | warnings: PROD ? false : true 43 | } 44 | }) 45 | ] 46 | }; 47 | --------------------------------------------------------------------------------