From 1cffabe1231b2cef2791c542b6c59c7c51e3dd08 Mon Sep 17 00:00:00 2001 From: OperKH Date: Wed, 3 Jun 2020 20:59:41 +0300 Subject: [PATCH] Add component event handling --- CHANGELOG.md | 3 + dist/vue-promise-btn.common.js | 2 +- dist/vue-promise-btn.js | 2 +- dist/vue-promise-btn.umd.js | 2 +- example/example.js | 9 +++ index.html | 41 ++++++++++++++ package.json | 2 +- src/directives/vue-promise-btn.directive.js | 61 ++++++++++++++------- 8 files changed, 98 insertions(+), 24 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 5168b86..95fe40e 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,8 @@ # Changelog +## 2.1.0 +- Add component event handling + ## 2.0.4 - Fixed button enabling when `showSpinner` is set to `false` diff --git a/dist/vue-promise-btn.common.js b/dist/vue-promise-btn.common.js index da2c438..4a7a295 100644 --- a/dist/vue-promise-btn.common.js +++ b/dist/vue-promise-btn.common.js @@ -1 +1 @@ -"use strict";function _interopDefault(e){return e&&"object"==typeof e&&"default"in e?e.default:e}var Vue=_interopDefault(require("vue")),script={name:"Spinner",props:{color:{type:String,default:"#3498db"},size:{type:Number,default:18},width:{type:Number,default:4},duration:{type:String,default:"1s"}},computed:{style:function(){return{borderTopColor:this.color,width:this.size+"px",height:this.size+"px",borderWidth:this.width+"px",animationDuration:this.duration}}}};function normalizeComponent(e,n,t,i,r,o,s,a,u,d){"boolean"!=typeof s&&(u=a,a=s,s=!1);var p,l="function"==typeof t?t.options:t;if(e&&e.render&&(l.render=e.render,l.staticRenderFns=e.staticRenderFns,l._compiled=!0,r&&(l.functional=!0)),i&&(l._scopeId=i),o?(p=function(e){(e=e||this.$vnode&&this.$vnode.ssrContext||this.parent&&this.parent.$vnode&&this.parent.$vnode.ssrContext)||"undefined"==typeof __VUE_SSR_CONTEXT__||(e=__VUE_SSR_CONTEXT__),n&&n.call(this,u(e)),e&&e._registeredComponents&&e._registeredComponents.add(o)},l._ssrRegister=p):n&&(p=s?function(){n.call(this,d(this.$root.$options.shadowRoot))}:function(e){n.call(this,a(e))}),p)if(l.functional){var c=l.render;l.render=function(e,n){return p.call(n),c(e,n)}}else{var _=l.beforeCreate;l.beforeCreate=_?[].concat(_,p):[p]}return t}var normalizeComponent_1=normalizeComponent,__vue_script__=script,__vue_render__=function(){var e=this.$createElement;return(this._self._c||e)("span",{staticClass:"spinner",style:this.style})},__vue_staticRenderFns__=[],__vue_inject_styles__=void 0,__vue_scope_id__="data-v-cc3e8e04",__vue_module_identifier__=void 0,__vue_is_functional_template__=!1,Spinner=normalizeComponent_1({render:__vue_render__,staticRenderFns:__vue_staticRenderFns__},__vue_inject_styles__,__vue_script__,__vue_scope_id__,__vue_is_functional_template__,__vue_module_identifier__,void 0,void 0),isPromise=function(e){return!!e&&("object"==typeof e||"function"==typeof e)&&"function"==typeof e.then},isString=function(e){return"string"==typeof e},isObject=function(e){return e===Object(e)},pluginElPropName="$promiseBtnId",elementId=0,stringHTMLRenderer=function(e){return'\n \n '+e.loader+"\n "},componentRenderer=function(e){return function(n){var t;return n("span",{class:(t={"promise-btn__spinner-wrapper":!0},t[e.spinnerHiddenClass]=!!e.spinnerHiddenClass&&!this.show,t),directives:[{name:"show",value:!e.autoHideSpinnerWrapper||this.show}]},[n(e.loader)])}},initSpinner=function(e,n,t,i){var r=document.createElement("SPAN");e.appendChild(r);var o={el:r,data:{show:!1},props:{parent:{type:Object,default:function(){return t.context}}},render:n.componentRenderer(n)};if(i){var s=n.stringHTMLRenderer(n),a=Vue.compile(s);o=Object.assign({},o,{data:Object.assign({},o.data,{options:n}),render:a.render,staticRenderFns:a.staticRenderFns})}return o},enableBtn=function(e,n){e.getAttribute("disabled")&&n.disableBtn&&e.removeAttribute("disabled"),e.classList.remove(n.btnLoadingClass)},disableBtn=function(e,n){n.disableBtn&&e.setAttribute("disabled","disabled"),e.classList.add(n.btnLoadingClass)},setupVuePromiseBtn=function(e){var n={listeners:{},init:function(t,i,r){var o=isObject(i.value),s=t[pluginElPropName]=elementId++,a=o?i.value:{},u=Object.assign({},e,a),d=isString(u.loader),p=t,l=null,c=!1,_=!1,f=!1,m=function(e){c&&_&&(u.showSpinner&&(l.show=!1),c=!1,_=!1,enableBtn(e,u))},v=function(){f=!1,_=!0,m(p)},h=function(e,n){return function(e){f=!0,disableBtn(e,u),u.showSpinner&&(l.show=!0),setTimeout(function(){c=!0,m(e)},u.minTimeout)}(e),n.then(function(){return v()}).catch(function(e){throw v(),e})};if(n.listeners[s]={el:t,eventType:u.action},"submit"===u.action&&!(p=t.querySelector('[type="submit"]')))throw new Error("No submit button found");if(u.showSpinner){var b=initSpinner(p,u,r,d);l=new Vue(b)}if(u.hasOwnProperty("promise"))n.listeners[s].promiseHanlder=h;else{var g=r.data.on&&r.data.on[u.action]&&(r.data.on[u.action]._withTask||r.data.on[u.action]._wrapper);if(n.listeners[s].nativeEventHandler=g,t.removeEventListener(u.action,g),!g)throw new Error("Please, provide proper handler/action for promise-btn");n.listener=function(e){if(!u.disableBtn||!f){var n=g(e),t="function"==typeof n?function(e){for(var n=e;"function"==typeof n;)n=n();return n}(n):n;return isPromise(t)?h(p,t):void 0}}}},bind:function(e,t,i){var r;(r=t.def).init.apply(r,arguments);var o=e[pluginElPropName],s=n.listeners[o],a=s.eventType;s.promiseHanlder||e.addEventListener(a,n.listener)},unbind:function(e,t){var i=e[pluginElPropName],r=n.listeners[i].eventType;e.removeEventListener(r,n.listener),delete n.listeners[i],delete e[pluginElPropName]},componentUpdated:function(e,t,i,r){if(t&&t.value&&t.value.promise&&t.value.promise!==t.oldValue.promise){var o=e[pluginElPropName];(0,n.listeners[o].promiseHanlder)(e,t.value.promise)}}};return n},defaultOptions={btnLoadingClass:"loading",showSpinner:!0,action:"click",disableBtn:!0,stringHTMLRenderer:stringHTMLRenderer,componentRenderer:componentRenderer,minTimeout:400,spinnerHiddenClass:"hidden",autoHideSpinnerWrapper:!1,loader:Spinner};function install(e,n){var t=Object.assign({},defaultOptions,n);e.directive("promise-btn",setupVuePromiseBtn(t))}var main={install:install,Spinner:Spinner,setupVuePromiseBtn:setupVuePromiseBtn};module.exports=main; +"use strict";function _interopDefault(e){return e&&"object"==typeof e&&"default"in e?e.default:e}var Vue=_interopDefault(require("vue")),script={name:"Spinner",props:{color:{type:String,default:"#3498db"},size:{type:Number,default:18},width:{type:Number,default:4},duration:{type:String,default:"1s"}},computed:{style:function(){return{borderTopColor:this.color,width:this.size+"px",height:this.size+"px",borderWidth:this.width+"px",animationDuration:this.duration}}}};function normalizeComponent(e,n,t,i,r,o,s,a,p,l){"boolean"!=typeof s&&(p=a,a=s,s=!1);var d,u="function"==typeof t?t.options:t;if(e&&e.render&&(u.render=e.render,u.staticRenderFns=e.staticRenderFns,u._compiled=!0,r&&(u.functional=!0)),i&&(u._scopeId=i),o?(d=function(e){(e=e||this.$vnode&&this.$vnode.ssrContext||this.parent&&this.parent.$vnode&&this.parent.$vnode.ssrContext)||"undefined"==typeof __VUE_SSR_CONTEXT__||(e=__VUE_SSR_CONTEXT__),n&&n.call(this,p(e)),e&&e._registeredComponents&&e._registeredComponents.add(o)},u._ssrRegister=d):n&&(d=s?function(){n.call(this,l(this.$root.$options.shadowRoot))}:function(e){n.call(this,a(e))}),d)if(u.functional){var c=u.render;u.render=function(e,n){return d.call(n),c(e,n)}}else{var _=u.beforeCreate;u.beforeCreate=_?[].concat(_,d):[d]}return t}var normalizeComponent_1=normalizeComponent,__vue_script__=script,__vue_render__=function(){var e=this.$createElement;return(this._self._c||e)("span",{staticClass:"spinner",style:this.style})},__vue_staticRenderFns__=[],__vue_inject_styles__=void 0,__vue_scope_id__="data-v-cc3e8e04",__vue_module_identifier__=void 0,__vue_is_functional_template__=!1,Spinner=normalizeComponent_1({render:__vue_render__,staticRenderFns:__vue_staticRenderFns__},__vue_inject_styles__,__vue_script__,__vue_scope_id__,__vue_is_functional_template__,__vue_module_identifier__,void 0,void 0),isPromise=function(e){return!!e&&("object"==typeof e||"function"==typeof e)&&"function"==typeof e.then},isString=function(e){return"string"==typeof e},isObject=function(e){return e===Object(e)},pluginElPropName="$promiseBtnId",elementId=0,stringHTMLRenderer=function(e){return'\n \n '+e.loader+"\n "},componentRenderer=function(e){return function(n){var t;return n("span",{class:(t={"promise-btn__spinner-wrapper":!0},t[e.spinnerHiddenClass]=!!e.spinnerHiddenClass&&!this.show,t),directives:[{name:"show",value:!e.autoHideSpinnerWrapper||this.show}]},[n(e.loader)])}},initSpinner=function(e,n,t,i){var r=document.createElement("SPAN");e.appendChild(r);var o={el:r,data:{show:!1},props:{parent:{type:Object,default:function(){return t.context}}},render:n.componentRenderer(n)};if(i){var s=n.stringHTMLRenderer(n),a=Vue.compile(s);o=Object.assign({},o,{data:Object.assign({},o.data,{options:n}),render:a.render,staticRenderFns:a.staticRenderFns})}return o},enableBtn=function(e,n){e.getAttribute("disabled")&&n.disableBtn&&e.removeAttribute("disabled"),e.classList.remove(n.btnLoadingClass)},disableBtn=function(e,n){n.disableBtn&&e.setAttribute("disabled","disabled"),e.classList.add(n.btnLoadingClass)},setupVuePromiseBtn=function(e){var n={listeners:{},init:function(t,i,r){var o=isObject(i.value),s=t[pluginElPropName]=elementId++,a=o?i.value:{},p=Object.assign({},e,a),l=isString(p.loader),d=t,u=null,c=!1,_=!1,f=!1,m=function(e){c&&_&&(p.showSpinner&&(u.show=!1),c=!1,_=!1,enableBtn(e,p))},v=function(){f=!1,_=!0,m(d)},h=function(e,n){return function(e){f=!0,disableBtn(e,p),p.showSpinner&&(u.show=!0),setTimeout(function(){c=!0,m(e)},p.minTimeout)}(e),n.then(function(){return v()}).catch(function(e){throw v(),e})};if(n.listeners[s]={el:t,eventType:p.action},"submit"===p.action&&!(d=t.querySelector('[type="submit"]')))throw new Error("No submit button found");if(p.showSpinner){var b=initSpinner(d,p,r,l);u=new Vue(b)}if(p.hasOwnProperty("promise"))n.listeners[s].promiseHanlder=h;else{if(!!r.componentInstance){var w=r.componentInstance.$listeners&&r.componentInstance.$listeners[p.action];if(!w)throw new Error("Please, provide proper handler/action for promise-btn");n.listeners[s].eventHandler=w,r.componentInstance.$off(p.action)}else{var g=r.data.on&&r.data.on[p.action]&&(r.data.on[p.action]._withTask||r.data.on[p.action]._wrapper);if(!g)throw new Error("Please, provide proper handler/action for promise-btn");n.listeners[s].eventHandler=g,t.removeEventListener(p.action,g)}n.listener=function(e){var t=n.listeners[s].eventHandler;if(!p.disableBtn||!f){var i=t(e),r="function"==typeof i?function(e){for(var n=e;"function"==typeof n;)n=n();return n}(i):i;return isPromise(r)?h(d,r):void 0}}}},bind:function(e,t,i){var r;(r=t.def).init.apply(r,arguments);var o=!!i.componentInstance,s=e[pluginElPropName],a=n.listeners[s],p=a.eventType;a.promiseHanlder||(o?i.componentInstance.$on(p,n.listener):e.addEventListener(p,n.listener))},unbind:function(e,t,i){var r=!!i.componentInstance,o=e[pluginElPropName],s=n.listeners[o].eventType;r?i.componentInstance.$off(s,n.listener):e.removeEventListener(s,n.listener),delete n.listeners[o],delete e[pluginElPropName]},componentUpdated:function(e,t,i,r){if(t&&t.value&&t.value.promise&&t.value.promise!==t.oldValue.promise){var o=e[pluginElPropName];(0,n.listeners[o].promiseHanlder)(e,t.value.promise)}}};return n},defaultOptions={btnLoadingClass:"loading",showSpinner:!0,action:"click",disableBtn:!0,stringHTMLRenderer:stringHTMLRenderer,componentRenderer:componentRenderer,minTimeout:400,spinnerHiddenClass:"hidden",autoHideSpinnerWrapper:!1,loader:Spinner};function install(e,n){var t=Object.assign({},defaultOptions,n);e.directive("promise-btn",setupVuePromiseBtn(t))}var main={install:install,Spinner:Spinner,setupVuePromiseBtn:setupVuePromiseBtn};module.exports=main; diff --git a/dist/vue-promise-btn.js b/dist/vue-promise-btn.js index 04481ce..51e1219 100644 --- a/dist/vue-promise-btn.js +++ b/dist/vue-promise-btn.js @@ -1 +1 @@ -import e from"vue";var n=function(e,n,t,r,i,o,s,a,d,p){"boolean"!=typeof s&&(d=a,a=s,s=!1);var l,u="function"==typeof t?t.options:t;if(e&&e.render&&(u.render=e.render,u.staticRenderFns=e.staticRenderFns,u._compiled=!0,i&&(u.functional=!0)),r&&(u._scopeId=r),o?(l=function(e){(e=e||this.$vnode&&this.$vnode.ssrContext||this.parent&&this.parent.$vnode&&this.parent.$vnode.ssrContext)||"undefined"==typeof __VUE_SSR_CONTEXT__||(e=__VUE_SSR_CONTEXT__),n&&n.call(this,d(e)),e&&e._registeredComponents&&e._registeredComponents.add(o)},u._ssrRegister=l):n&&(l=s?function(){n.call(this,p(this.$root.$options.shadowRoot))}:function(e){n.call(this,a(e))}),l)if(u.functional){var c=u.render;u.render=function(e,n){return l.call(n),c(e,n)}}else{var f=u.beforeCreate;u.beforeCreate=f?[].concat(f,l):[l]}return t}({render:function(){var e=this.$createElement;return(this._self._c||e)("span",{staticClass:"spinner",style:this.style})},staticRenderFns:[]},void 0,{name:"Spinner",props:{color:{type:String,default:"#3498db"},size:{type:Number,default:18},width:{type:Number,default:4},duration:{type:String,default:"1s"}},computed:{style:function(){return{borderTopColor:this.color,width:this.size+"px",height:this.size+"px",borderWidth:this.width+"px",animationDuration:this.duration}}}},"data-v-cc3e8e04",!1,void 0,void 0,void 0),t=0,r=function(n){var r={listeners:{},init:function(i,o,s){var a,d=(a=o.value)===Object(a),p=i.$promiseBtnId=t++,l=d?o.value:{},u=Object.assign({},n,l),c=function(e){return"string"==typeof e}(u.loader),f=i,v=null,h=!1,m=!1,b=!1,w=function(e){h&&m&&(u.showSpinner&&(v.show=!1),h=!1,m=!1,function(e,n){e.getAttribute("disabled")&&n.disableBtn&&e.removeAttribute("disabled"),e.classList.remove(n.btnLoadingClass)}(e,u))},_=function(){b=!1,m=!0,w(f)},y=function(e,n){return function(e){b=!0,function(e,n){n.disableBtn&&e.setAttribute("disabled","disabled"),e.classList.add(n.btnLoadingClass)}(e,u),u.showSpinner&&(v.show=!0),setTimeout(function(){h=!0,w(e)},u.minTimeout)}(e),n.then(function(){return _()}).catch(function(e){throw _(),e})};if(r.listeners[p]={el:i,eventType:u.action},"submit"===u.action&&!(f=i.querySelector('[type="submit"]')))throw new Error("No submit button found");if(u.showSpinner){var g=function(n,t,r,i){var o=document.createElement("SPAN");n.appendChild(o);var s={el:o,data:{show:!1},props:{parent:{type:Object,default:function(){return r.context}}},render:t.componentRenderer(t)};if(i){var a=t.stringHTMLRenderer(t),d=e.compile(a);s=Object.assign({},s,{data:Object.assign({},s.data,{options:t}),render:d.render,staticRenderFns:d.staticRenderFns})}return s}(f,u,s,c);v=new e(g)}if(u.hasOwnProperty("promise"))r.listeners[p].promiseHanlder=y;else{var C=s.data.on&&s.data.on[u.action]&&(s.data.on[u.action]._withTask||s.data.on[u.action]._wrapper);if(r.listeners[p].nativeEventHandler=C,i.removeEventListener(u.action,C),!C)throw new Error("Please, provide proper handler/action for promise-btn");r.listener=function(e){if(!u.disableBtn||!b){var n,t=C(e),r="function"==typeof t?function(e){for(var n=e;"function"==typeof n;)n=n();return n}(t):t;return!(n=r)||"object"!=typeof n&&"function"!=typeof n||"function"!=typeof n.then?void 0:y(f,r)}}}},bind:function(e,n,t){var i;(i=n.def).init.apply(i,arguments);var o=e.$promiseBtnId,s=r.listeners[o],a=s.eventType;s.promiseHanlder||e.addEventListener(a,r.listener)},unbind:function(e,n){var t=e.$promiseBtnId,i=r.listeners[t].eventType;e.removeEventListener(i,r.listener),delete r.listeners[t],delete e.$promiseBtnId},componentUpdated:function(e,n,t,i){if(n&&n.value&&n.value.promise&&n.value.promise!==n.oldValue.promise){var o=e.$promiseBtnId;(0,r.listeners[o].promiseHanlder)(e,n.value.promise)}}};return r},i={btnLoadingClass:"loading",showSpinner:!0,action:"click",disableBtn:!0,stringHTMLRenderer:function(e){return'\n \n '+e.loader+"\n "},componentRenderer:function(e){return function(n){var t;return n("span",{class:(t={"promise-btn__spinner-wrapper":!0},t[e.spinnerHiddenClass]=!!e.spinnerHiddenClass&&!this.show,t),directives:[{name:"show",value:!e.autoHideSpinnerWrapper||this.show}]},[n(e.loader)])}},minTimeout:400,spinnerHiddenClass:"hidden",autoHideSpinnerWrapper:!1,loader:n};export default{install:function(e,n){var t=Object.assign({},i,n);e.directive("promise-btn",r(t))},Spinner:n,setupVuePromiseBtn:r}; +import e from"vue";var n=function(e,n,t,r,i,o,s,a,d,p){"boolean"!=typeof s&&(d=a,a=s,s=!1);var c,l="function"==typeof t?t.options:t;if(e&&e.render&&(l.render=e.render,l.staticRenderFns=e.staticRenderFns,l._compiled=!0,i&&(l.functional=!0)),r&&(l._scopeId=r),o?(c=function(e){(e=e||this.$vnode&&this.$vnode.ssrContext||this.parent&&this.parent.$vnode&&this.parent.$vnode.ssrContext)||"undefined"==typeof __VUE_SSR_CONTEXT__||(e=__VUE_SSR_CONTEXT__),n&&n.call(this,d(e)),e&&e._registeredComponents&&e._registeredComponents.add(o)},l._ssrRegister=c):n&&(c=s?function(){n.call(this,p(this.$root.$options.shadowRoot))}:function(e){n.call(this,a(e))}),c)if(l.functional){var u=l.render;l.render=function(e,n){return c.call(n),u(e,n)}}else{var f=l.beforeCreate;l.beforeCreate=f?[].concat(f,c):[c]}return t}({render:function(){var e=this.$createElement;return(this._self._c||e)("span",{staticClass:"spinner",style:this.style})},staticRenderFns:[]},void 0,{name:"Spinner",props:{color:{type:String,default:"#3498db"},size:{type:Number,default:18},width:{type:Number,default:4},duration:{type:String,default:"1s"}},computed:{style:function(){return{borderTopColor:this.color,width:this.size+"px",height:this.size+"px",borderWidth:this.width+"px",animationDuration:this.duration}}}},"data-v-cc3e8e04",!1,void 0,void 0,void 0),t=0,r=function(n){var r={listeners:{},init:function(i,o,s){var a,d=(a=o.value)===Object(a),p=i.$promiseBtnId=t++,c=d?o.value:{},l=Object.assign({},n,c),u=function(e){return"string"==typeof e}(l.loader),f=i,v=null,m=!1,h=!1,b=!1,w=function(e){m&&h&&(l.showSpinner&&(v.show=!1),m=!1,h=!1,function(e,n){e.getAttribute("disabled")&&n.disableBtn&&e.removeAttribute("disabled"),e.classList.remove(n.btnLoadingClass)}(e,l))},_=function(){b=!1,h=!0,w(f)},y=function(e,n){return function(e){b=!0,function(e,n){n.disableBtn&&e.setAttribute("disabled","disabled"),e.classList.add(n.btnLoadingClass)}(e,l),l.showSpinner&&(v.show=!0),setTimeout(function(){m=!0,w(e)},l.minTimeout)}(e),n.then(function(){return _()}).catch(function(e){throw _(),e})};if(r.listeners[p]={el:i,eventType:l.action},"submit"===l.action&&!(f=i.querySelector('[type="submit"]')))throw new Error("No submit button found");if(l.showSpinner){var g=function(n,t,r,i){var o=document.createElement("SPAN");n.appendChild(o);var s={el:o,data:{show:!1},props:{parent:{type:Object,default:function(){return r.context}}},render:t.componentRenderer(t)};if(i){var a=t.stringHTMLRenderer(t),d=e.compile(a);s=Object.assign({},s,{data:Object.assign({},s.data,{options:t}),render:d.render,staticRenderFns:d.staticRenderFns})}return s}(f,l,s,u);v=new e(g)}if(l.hasOwnProperty("promise"))r.listeners[p].promiseHanlder=y;else{if(!!s.componentInstance){var C=s.componentInstance.$listeners&&s.componentInstance.$listeners[l.action];if(!C)throw new Error("Please, provide proper handler/action for promise-btn");r.listeners[p].eventHandler=C,s.componentInstance.$off(l.action)}else{var S=s.data.on&&s.data.on[l.action]&&(s.data.on[l.action]._withTask||s.data.on[l.action]._wrapper);if(!S)throw new Error("Please, provide proper handler/action for promise-btn");r.listeners[p].eventHandler=S,i.removeEventListener(l.action,S)}r.listener=function(e){var n=r.listeners[p].eventHandler;if(!l.disableBtn||!b){var t,i=n(e),o="function"==typeof i?function(e){for(var n=e;"function"==typeof n;)n=n();return n}(i):i;return!(t=o)||"object"!=typeof t&&"function"!=typeof t||"function"!=typeof t.then?void 0:y(f,o)}}}},bind:function(e,n,t){var i;(i=n.def).init.apply(i,arguments);var o=!!t.componentInstance,s=e.$promiseBtnId,a=r.listeners[s],d=a.eventType;a.promiseHanlder||(o?t.componentInstance.$on(d,r.listener):e.addEventListener(d,r.listener))},unbind:function(e,n,t){var i=!!t.componentInstance,o=e.$promiseBtnId,s=r.listeners[o].eventType;i?t.componentInstance.$off(s,r.listener):e.removeEventListener(s,r.listener),delete r.listeners[o],delete e.$promiseBtnId},componentUpdated:function(e,n,t,i){if(n&&n.value&&n.value.promise&&n.value.promise!==n.oldValue.promise){var o=e.$promiseBtnId;(0,r.listeners[o].promiseHanlder)(e,n.value.promise)}}};return r},i={btnLoadingClass:"loading",showSpinner:!0,action:"click",disableBtn:!0,stringHTMLRenderer:function(e){return'\n \n '+e.loader+"\n "},componentRenderer:function(e){return function(n){var t;return n("span",{class:(t={"promise-btn__spinner-wrapper":!0},t[e.spinnerHiddenClass]=!!e.spinnerHiddenClass&&!this.show,t),directives:[{name:"show",value:!e.autoHideSpinnerWrapper||this.show}]},[n(e.loader)])}},minTimeout:400,spinnerHiddenClass:"hidden",autoHideSpinnerWrapper:!1,loader:n};export default{install:function(e,n){var t=Object.assign({},i,n);e.directive("promise-btn",r(t))},Spinner:n,setupVuePromiseBtn:r}; diff --git a/dist/vue-promise-btn.umd.js b/dist/vue-promise-btn.umd.js index 6dda1c2..dc4b36b 100644 --- a/dist/vue-promise-btn.umd.js +++ b/dist/vue-promise-btn.umd.js @@ -1 +1 @@ -!function(e,n){"object"==typeof exports&&"undefined"!=typeof module?module.exports=n(require("vue")):"function"==typeof define&&define.amd?define(["vue"],n):(e=e||self).VuePromiseBtn=n(e.Vue)}(this,function(e){"use strict";e=e&&e.hasOwnProperty("default")?e.default:e;var n=function(e,n,t,r,i,o,s,a,d,p){"boolean"!=typeof s&&(d=a,a=s,s=!1);var u,l="function"==typeof t?t.options:t;if(e&&e.render&&(l.render=e.render,l.staticRenderFns=e.staticRenderFns,l._compiled=!0,i&&(l.functional=!0)),r&&(l._scopeId=r),o?(u=function(e){(e=e||this.$vnode&&this.$vnode.ssrContext||this.parent&&this.parent.$vnode&&this.parent.$vnode.ssrContext)||"undefined"==typeof __VUE_SSR_CONTEXT__||(e=__VUE_SSR_CONTEXT__),n&&n.call(this,d(e)),e&&e._registeredComponents&&e._registeredComponents.add(o)},l._ssrRegister=u):n&&(u=s?function(){n.call(this,p(this.$root.$options.shadowRoot))}:function(e){n.call(this,a(e))}),u)if(l.functional){var c=l.render;l.render=function(e,n){return u.call(n),c(e,n)}}else{var f=l.beforeCreate;l.beforeCreate=f?[].concat(f,u):[u]}return t}({render:function(){var e=this.$createElement;return(this._self._c||e)("span",{staticClass:"spinner",style:this.style})},staticRenderFns:[]},void 0,{name:"Spinner",props:{color:{type:String,default:"#3498db"},size:{type:Number,default:18},width:{type:Number,default:4},duration:{type:String,default:"1s"}},computed:{style:function(){return{borderTopColor:this.color,width:this.size+"px",height:this.size+"px",borderWidth:this.width+"px",animationDuration:this.duration}}}},"data-v-cc3e8e04",!1,void 0,void 0,void 0),t=0,r=function(n){var r={listeners:{},init:function(i,o,s){var a,d=(a=o.value)===Object(a),p=i.$promiseBtnId=t++,u=d?o.value:{},l=Object.assign({},n,u),c=function(e){return"string"==typeof e}(l.loader),f=i,v=null,h=!1,m=!1,b=!1,w=function(e){h&&m&&(l.showSpinner&&(v.show=!1),h=!1,m=!1,function(e,n){e.getAttribute("disabled")&&n.disableBtn&&e.removeAttribute("disabled"),e.classList.remove(n.btnLoadingClass)}(e,l))},y=function(){b=!1,m=!0,w(f)},_=function(e,n){return function(e){b=!0,function(e,n){n.disableBtn&&e.setAttribute("disabled","disabled"),e.classList.add(n.btnLoadingClass)}(e,l),l.showSpinner&&(v.show=!0),setTimeout(function(){h=!0,w(e)},l.minTimeout)}(e),n.then(function(){return y()}).catch(function(e){throw y(),e})};if(r.listeners[p]={el:i,eventType:l.action},"submit"===l.action&&!(f=i.querySelector('[type="submit"]')))throw new Error("No submit button found");if(l.showSpinner){var g=function(n,t,r,i){var o=document.createElement("SPAN");n.appendChild(o);var s={el:o,data:{show:!1},props:{parent:{type:Object,default:function(){return r.context}}},render:t.componentRenderer(t)};if(i){var a=t.stringHTMLRenderer(t),d=e.compile(a);s=Object.assign({},s,{data:Object.assign({},s.data,{options:t}),render:d.render,staticRenderFns:d.staticRenderFns})}return s}(f,l,s,c);v=new e(g)}if(l.hasOwnProperty("promise"))r.listeners[p].promiseHanlder=_;else{var C=s.data.on&&s.data.on[l.action]&&(s.data.on[l.action]._withTask||s.data.on[l.action]._wrapper);if(r.listeners[p].nativeEventHandler=C,i.removeEventListener(l.action,C),!C)throw new Error("Please, provide proper handler/action for promise-btn");r.listener=function(e){if(!l.disableBtn||!b){var n,t=C(e),r="function"==typeof t?function(e){for(var n=e;"function"==typeof n;)n=n();return n}(t):t;return!(n=r)||"object"!=typeof n&&"function"!=typeof n||"function"!=typeof n.then?void 0:_(f,r)}}}},bind:function(e,n,t){var i;(i=n.def).init.apply(i,arguments);var o=e.$promiseBtnId,s=r.listeners[o],a=s.eventType;s.promiseHanlder||e.addEventListener(a,r.listener)},unbind:function(e,n){var t=e.$promiseBtnId,i=r.listeners[t].eventType;e.removeEventListener(i,r.listener),delete r.listeners[t],delete e.$promiseBtnId},componentUpdated:function(e,n,t,i){if(n&&n.value&&n.value.promise&&n.value.promise!==n.oldValue.promise){var o=e.$promiseBtnId;(0,r.listeners[o].promiseHanlder)(e,n.value.promise)}}};return r},i={btnLoadingClass:"loading",showSpinner:!0,action:"click",disableBtn:!0,stringHTMLRenderer:function(e){return'\n \n '+e.loader+"\n "},componentRenderer:function(e){return function(n){var t;return n("span",{class:(t={"promise-btn__spinner-wrapper":!0},t[e.spinnerHiddenClass]=!!e.spinnerHiddenClass&&!this.show,t),directives:[{name:"show",value:!e.autoHideSpinnerWrapper||this.show}]},[n(e.loader)])}},minTimeout:400,spinnerHiddenClass:"hidden",autoHideSpinnerWrapper:!1,loader:n};return{install:function(e,n){var t=Object.assign({},i,n);e.directive("promise-btn",r(t))},Spinner:n,setupVuePromiseBtn:r}}); +!function(e,n){"object"==typeof exports&&"undefined"!=typeof module?module.exports=n(require("vue")):"function"==typeof define&&define.amd?define(["vue"],n):(e=e||self).VuePromiseBtn=n(e.Vue)}(this,function(e){"use strict";e=e&&e.hasOwnProperty("default")?e.default:e;var n=function(e,n,t,r,i,o,s,a,d,p){"boolean"!=typeof s&&(d=a,a=s,s=!1);var c,l="function"==typeof t?t.options:t;if(e&&e.render&&(l.render=e.render,l.staticRenderFns=e.staticRenderFns,l._compiled=!0,i&&(l.functional=!0)),r&&(l._scopeId=r),o?(c=function(e){(e=e||this.$vnode&&this.$vnode.ssrContext||this.parent&&this.parent.$vnode&&this.parent.$vnode.ssrContext)||"undefined"==typeof __VUE_SSR_CONTEXT__||(e=__VUE_SSR_CONTEXT__),n&&n.call(this,d(e)),e&&e._registeredComponents&&e._registeredComponents.add(o)},l._ssrRegister=c):n&&(c=s?function(){n.call(this,p(this.$root.$options.shadowRoot))}:function(e){n.call(this,a(e))}),c)if(l.functional){var u=l.render;l.render=function(e,n){return c.call(n),u(e,n)}}else{var f=l.beforeCreate;l.beforeCreate=f?[].concat(f,c):[c]}return t}({render:function(){var e=this.$createElement;return(this._self._c||e)("span",{staticClass:"spinner",style:this.style})},staticRenderFns:[]},void 0,{name:"Spinner",props:{color:{type:String,default:"#3498db"},size:{type:Number,default:18},width:{type:Number,default:4},duration:{type:String,default:"1s"}},computed:{style:function(){return{borderTopColor:this.color,width:this.size+"px",height:this.size+"px",borderWidth:this.width+"px",animationDuration:this.duration}}}},"data-v-cc3e8e04",!1,void 0,void 0,void 0),t=0,r=function(n){var r={listeners:{},init:function(i,o,s){var a,d=(a=o.value)===Object(a),p=i.$promiseBtnId=t++,c=d?o.value:{},l=Object.assign({},n,c),u=function(e){return"string"==typeof e}(l.loader),f=i,m=null,v=!1,h=!1,b=!1,w=function(e){v&&h&&(l.showSpinner&&(m.show=!1),v=!1,h=!1,function(e,n){e.getAttribute("disabled")&&n.disableBtn&&e.removeAttribute("disabled"),e.classList.remove(n.btnLoadingClass)}(e,l))},y=function(){b=!1,h=!0,w(f)},_=function(e,n){return function(e){b=!0,function(e,n){n.disableBtn&&e.setAttribute("disabled","disabled"),e.classList.add(n.btnLoadingClass)}(e,l),l.showSpinner&&(m.show=!0),setTimeout(function(){v=!0,w(e)},l.minTimeout)}(e),n.then(function(){return y()}).catch(function(e){throw y(),e})};if(r.listeners[p]={el:i,eventType:l.action},"submit"===l.action&&!(f=i.querySelector('[type="submit"]')))throw new Error("No submit button found");if(l.showSpinner){var g=function(n,t,r,i){var o=document.createElement("SPAN");n.appendChild(o);var s={el:o,data:{show:!1},props:{parent:{type:Object,default:function(){return r.context}}},render:t.componentRenderer(t)};if(i){var a=t.stringHTMLRenderer(t),d=e.compile(a);s=Object.assign({},s,{data:Object.assign({},s.data,{options:t}),render:d.render,staticRenderFns:d.staticRenderFns})}return s}(f,l,s,u);m=new e(g)}if(l.hasOwnProperty("promise"))r.listeners[p].promiseHanlder=_;else{if(!!s.componentInstance){var C=s.componentInstance.$listeners&&s.componentInstance.$listeners[l.action];if(!C)throw new Error("Please, provide proper handler/action for promise-btn");r.listeners[p].eventHandler=C,s.componentInstance.$off(l.action)}else{var S=s.data.on&&s.data.on[l.action]&&(s.data.on[l.action]._withTask||s.data.on[l.action]._wrapper);if(!S)throw new Error("Please, provide proper handler/action for promise-btn");r.listeners[p].eventHandler=S,i.removeEventListener(l.action,S)}r.listener=function(e){var n=r.listeners[p].eventHandler;if(!l.disableBtn||!b){var t,i=n(e),o="function"==typeof i?function(e){for(var n=e;"function"==typeof n;)n=n();return n}(i):i;return!(t=o)||"object"!=typeof t&&"function"!=typeof t||"function"!=typeof t.then?void 0:_(f,o)}}}},bind:function(e,n,t){var i;(i=n.def).init.apply(i,arguments);var o=!!t.componentInstance,s=e.$promiseBtnId,a=r.listeners[s],d=a.eventType;a.promiseHanlder||(o?t.componentInstance.$on(d,r.listener):e.addEventListener(d,r.listener))},unbind:function(e,n,t){var i=!!t.componentInstance,o=e.$promiseBtnId,s=r.listeners[o].eventType;i?t.componentInstance.$off(s,r.listener):e.removeEventListener(s,r.listener),delete r.listeners[o],delete e.$promiseBtnId},componentUpdated:function(e,n,t,i){if(n&&n.value&&n.value.promise&&n.value.promise!==n.oldValue.promise){var o=e.$promiseBtnId;(0,r.listeners[o].promiseHanlder)(e,n.value.promise)}}};return r},i={btnLoadingClass:"loading",showSpinner:!0,action:"click",disableBtn:!0,stringHTMLRenderer:function(e){return'\n \n '+e.loader+"\n "},componentRenderer:function(e){return function(n){var t;return n("span",{class:(t={"promise-btn__spinner-wrapper":!0},t[e.spinnerHiddenClass]=!!e.spinnerHiddenClass&&!this.show,t),directives:[{name:"show",value:!e.autoHideSpinnerWrapper||this.show}]},[n(e.loader)])}},minTimeout:400,spinnerHiddenClass:"hidden",autoHideSpinnerWrapper:!1,loader:n};return{install:function(e,n){var t=Object.assign({},i,n);e.directive("promise-btn",r(t))},Spinner:n,setupVuePromiseBtn:r}}); diff --git a/example/example.js b/example/example.js index 4fd91e9..a5ca690 100644 --- a/example/example.js +++ b/example/example.js @@ -1,3 +1,4 @@ +/* eslint-disable */ document.addEventListener('DOMContentLoaded', function(){ Vue.use(VuePromiseBtn) Vue.use(bootstrapVue) @@ -6,6 +7,14 @@ document.addEventListener('DOMContentLoaded', function(){ template: '...' }) + Vue.component('custom-form', { + template: `
+

+ +

+
` + }) + new Vue({ el: '#app', data: { diff --git a/index.html b/index.html index b9f6bd4..596ae94 100644 --- a/index.html +++ b/index.html @@ -77,30 +77,51 @@

Examples

<button v-promise-btn @click="asyncAction">Click handler</button>
+ +
+

<button v-promise-btn @click.prevent="asyncAction">With Prevent modifier</button>
+ +
+

<button v-promise-btn="{action: 'contextmenu'}" @click.right="asyncAction">Right mouse</button>
+ +
+

<button v-promise-btn="{action: 'mouseup'}" @click.middle="asyncAction">Middle mouse</button>
+ +
+

<button v-promise-btn="{loader: CustomSpinnerComponent}" @click="asyncAction">Loader as custom component</button>
+ +
+

<button v-promise-btn="{loader: '<b>(HTML loader...)</b>' }" @click="asyncAction">Loader as custom component</button>
+ +
+

<button v-promise-btn v-on="{ click: asyncActionWithArgs($event, 'abc') }">Btn with expression on click handle</button>
+ +
+
!!! Important: if you want to use closure - you have to define v-on="{ click: test('abc') }" instead of @click="test('abc')"
@@ -109,6 +130,9 @@

Examples

<button v-promise-btn="{ promise: dataPromise }" @click="asyncWithPromiseInData('Hello from async action!')">Extended syntax with external promise</button>
+ +
+
This param allow to use directive with external components. Use this option if you faced issued with simplified syntax

Extended with Vue Bootstrap

@@ -126,6 +150,23 @@

Examples

<button type="submit">Button Form Submit</button> </form> + +
+ + +
+
Vue.component('custom-form', {
+  template: `<form @submit.prevent="$emit('submit', 'data')">
+    <p>
+      <button type="submit">Button Form Submit - Handle Component Event</button>
+    </p>
+  </form>`
+})
+
+<custom-form @submit="dummyAsyncAction" v-promise-btn="{action: 'submit'}"></custom-form>
+
+ +

diff --git a/package.json b/package.json index 33718c9..e41c45a 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "vue-promise-btn", - "version": "2.0.4", + "version": "2.1.0", "description": "Vue.js plugin that handles buttons asynchronous lock and show loading state indicator", "scripts": { "start": "npm run dev", diff --git a/src/directives/vue-promise-btn.directive.js b/src/directives/vue-promise-btn.directive.js index ba3a863..e5727b3 100644 --- a/src/directives/vue-promise-btn.directive.js +++ b/src/directives/vue-promise-btn.directive.js @@ -6,7 +6,7 @@ let elementId = 0 export const stringHTMLRenderer = function (options) { return ` - @@ -165,25 +165,35 @@ export const setupVuePromiseBtn = function (globalOptions) { return } - // Continue initialization for simplified usage mode - // Extract default event listener internally generated by Vue - // _withTask - legacy method for Vue < 2.6, _wrapper - for Vue >= 2.6 - const nativeEventHandler = vnode.data.on && - vnode.data.on[options.action] && - (vnode.data.on[options.action]._withTask || vnode.data.on[options.action]._wrapper) - - // Save nativeEventHandler in directive instance - directive.listeners[id].nativeEventHandler = nativeEventHandler - - // Remove native event listener - el.removeEventListener(options.action, nativeEventHandler) - - if (!nativeEventHandler) throw new Error('Please, provide proper handler/action for promise-btn') + const isComponent = !!vnode.componentInstance + if (isComponent) { + // Continue initialization for component usage mode + // Extract vue component event + const componentEventHandler = vnode.componentInstance.$listeners && vnode.componentInstance.$listeners[options.action] + if (!componentEventHandler) throw new Error('Please, provide proper handler/action for promise-btn') + // Save nativeEventHandler in directive instance + directive.listeners[id].eventHandler = componentEventHandler + // Remove vue component event listener + vnode.componentInstance.$off(options.action) + } else { + // Continue initialization for simplified usage mode + // Extract default event listener internally generated by Vue + // _withTask - legacy method for Vue < 2.6, _wrapper - for Vue >= 2.6 + const nativeEventHandler = vnode.data.on && + vnode.data.on[options.action] && + (vnode.data.on[options.action]._withTask || vnode.data.on[options.action]._wrapper) + if (!nativeEventHandler) throw new Error('Please, provide proper handler/action for promise-btn') + // Save nativeEventHandler in directive instance + directive.listeners[id].eventHandler = nativeEventHandler + // Remove native event listener + el.removeEventListener(options.action, nativeEventHandler) + } // Set custom event that will replace original listener directive.listener = function (e) { + const { eventHandler } = directive.listeners[id] if (options.disableBtn && scheduled) return - const expression = nativeEventHandler(e) + const expression = eventHandler(e) const handlerPromise = typeof expression === 'function' ? getFiniteHandler(expression) : expression if (isPromise(handlerPromise)) { return promiseHanlder(btnEl, handlerPromise) @@ -195,18 +205,29 @@ export const setupVuePromiseBtn = function (globalOptions) { binding.def.init(...arguments) // Replace native event listener + const isComponent = !!vnode.componentInstance const id = el[pluginElPropName] const { eventType, promiseHanlder } = directive.listeners[id] if (!promiseHanlder) { - // Add event handler for simplified mode - el.addEventListener(eventType, directive.listener) + if (isComponent) { + // Add event handler for component mode + vnode.componentInstance.$on(eventType, directive.listener) + } else { + // Add event handler for simplified mode + el.addEventListener(eventType, directive.listener) + } } }, - unbind (el, binding) { + unbind (el, binding, vnode) { + const isComponent = !!vnode.componentInstance // Cleanups if element removed to prevent memory leaks const id = el[pluginElPropName] const { eventType } = directive.listeners[id] - el.removeEventListener(eventType, directive.listener) + if (isComponent) { + vnode.componentInstance.$off(eventType, directive.listener) + } else { + el.removeEventListener(eventType, directive.listener) + } delete directive.listeners[id] delete el[pluginElPropName] },