vue-promise-btn
Advanced tools
Comparing version 2.0.0 to 2.0.1
# Changelog | ||
## 2.0.1 | ||
- Updated example: added case how to use closure for inline-expression | ||
## 2.0.0 | ||
@@ -3,0 +6,0 @@ Added `Extended` mode: |
@@ -1,1 +0,423 @@ | ||
"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 <span class="promise-btn__spinner-wrapper" \n :class="{[options.spinnerHiddenClass]: !show || false}"\n v-show="options.autoHideSpinnerWrapper ? show : true"\n >\n '+e.loader+"\n </span>"},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&&_&&(l.show=!1,c=!1,_=!1,enableBtn(e,u))},v=function(){f=!1,u.showSpinner&&(_=!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; | ||
(function(l, i, v, e) { v = l.createElement(i); v.async = 1; v.src = '//' + (location.host || 'localhost').split(':')[0] + ':35729/livereload.js?snipver=1'; e = l.getElementsByTagName(i)[0]; e.parentNode.insertBefore(v, e)})(document, 'script'); | ||
'use strict'; | ||
function _interopDefault (ex) { return (ex && (typeof ex === 'object') && 'default' in ex) ? ex['default'] : ex; } | ||
var Vue = _interopDefault(require('vue')); | ||
// | ||
// | ||
// | ||
var 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 style () { | ||
return { | ||
borderTopColor: this.color, | ||
width: ((this.size) + "px"), | ||
height: ((this.size) + "px"), | ||
borderWidth: ((this.width) + "px"), | ||
animationDuration: this.duration | ||
} | ||
} | ||
} | ||
}; | ||
function normalizeComponent(template, style, script, scopeId, isFunctionalTemplate, moduleIdentifier | ||
/* server only */ | ||
, shadowMode, createInjector, createInjectorSSR, createInjectorShadow) { | ||
if (typeof shadowMode !== 'boolean') { | ||
createInjectorSSR = createInjector; | ||
createInjector = shadowMode; | ||
shadowMode = false; | ||
} // Vue.extend constructor export interop. | ||
var options = typeof script === 'function' ? script.options : script; // render functions | ||
if (template && template.render) { | ||
options.render = template.render; | ||
options.staticRenderFns = template.staticRenderFns; | ||
options._compiled = true; // functional template | ||
if (isFunctionalTemplate) { | ||
options.functional = true; | ||
} | ||
} // scopedId | ||
if (scopeId) { | ||
options._scopeId = scopeId; | ||
} | ||
var hook; | ||
if (moduleIdentifier) { | ||
// server build | ||
hook = function hook(context) { | ||
// 2.3 injection | ||
context = context || // cached call | ||
this.$vnode && this.$vnode.ssrContext || // stateful | ||
this.parent && this.parent.$vnode && this.parent.$vnode.ssrContext; // functional | ||
// 2.2 with runInNewContext: true | ||
if (!context && typeof __VUE_SSR_CONTEXT__ !== 'undefined') { | ||
context = __VUE_SSR_CONTEXT__; | ||
} // inject component styles | ||
if (style) { | ||
style.call(this, createInjectorSSR(context)); | ||
} // register component module identifier for async chunk inference | ||
if (context && context._registeredComponents) { | ||
context._registeredComponents.add(moduleIdentifier); | ||
} | ||
}; // used by ssr in case component is cached and beforeCreate | ||
// never gets called | ||
options._ssrRegister = hook; | ||
} else if (style) { | ||
hook = shadowMode ? function () { | ||
style.call(this, createInjectorShadow(this.$root.$options.shadowRoot)); | ||
} : function (context) { | ||
style.call(this, createInjector(context)); | ||
}; | ||
} | ||
if (hook) { | ||
if (options.functional) { | ||
// register for functional component in vue file | ||
var originalRender = options.render; | ||
options.render = function renderWithStyleInjection(h, context) { | ||
hook.call(context); | ||
return originalRender(h, context); | ||
}; | ||
} else { | ||
// inject component registration as beforeCreate hook | ||
var existing = options.beforeCreate; | ||
options.beforeCreate = existing ? [].concat(existing, hook) : [hook]; | ||
} | ||
} | ||
return script; | ||
} | ||
var normalizeComponent_1 = normalizeComponent; | ||
/* script */ | ||
var __vue_script__ = script; | ||
/* template */ | ||
var __vue_render__ = function() { | ||
var _vm = this; | ||
var _h = _vm.$createElement; | ||
var _c = _vm._self._c || _h; | ||
return _c("span", { staticClass: "spinner", style: _vm.style }) | ||
}; | ||
var __vue_staticRenderFns__ = []; | ||
__vue_render__._withStripped = true; | ||
/* style */ | ||
var __vue_inject_styles__ = undefined; | ||
/* scoped */ | ||
var __vue_scope_id__ = "data-v-8170247a"; | ||
/* module identifier */ | ||
var __vue_module_identifier__ = undefined; | ||
/* functional template */ | ||
var __vue_is_functional_template__ = false; | ||
/* style inject */ | ||
/* style inject SSR */ | ||
var Spinner = normalizeComponent_1( | ||
{ render: __vue_render__, staticRenderFns: __vue_staticRenderFns__ }, | ||
__vue_inject_styles__, | ||
__vue_script__, | ||
__vue_scope_id__, | ||
__vue_is_functional_template__, | ||
__vue_module_identifier__, | ||
undefined, | ||
undefined | ||
); | ||
var isPromise = function (obj) { | ||
return !!obj && (typeof obj === 'object' || typeof obj === 'function') && typeof obj.then === 'function' | ||
}; | ||
var isString = function (obj) { | ||
return typeof obj === 'string' | ||
}; | ||
var isObject = function (obj) { | ||
return obj === Object(obj) | ||
}; | ||
var pluginElPropName = '$promiseBtnId'; | ||
var elementId = 0; | ||
var stringHTMLRenderer = function (options) { | ||
return ("\n <span class=\"promise-btn__spinner-wrapper\" \n :class=\"{[options.spinnerHiddenClass]: !show || false}\"\n v-show=\"options.autoHideSpinnerWrapper ? show : true\"\n >\n " + (options.loader) + "\n </span>") | ||
}; | ||
var componentRenderer = function (options) { | ||
return function (h) { | ||
var obj; | ||
return h('span', { | ||
class: ( obj = { | ||
'promise-btn__spinner-wrapper': true | ||
}, obj[options.spinnerHiddenClass] = options.spinnerHiddenClass ? !this.show : false, obj ), | ||
directives: [{ | ||
name: 'show', | ||
value: options.autoHideSpinnerWrapper ? this.show : true | ||
}] | ||
}, [ | ||
h(options.loader) | ||
]) | ||
} | ||
}; | ||
var initSpinner = function (btnEl, options, vnode, isLoaderString) { | ||
var dummyEl = document.createElement('SPAN'); | ||
btnEl.appendChild(dummyEl); | ||
var vueSpinnerOptions = { | ||
el: dummyEl, | ||
data: { show: false }, | ||
props: { | ||
parent: { | ||
type: Object, | ||
default: function () { return vnode.context; } | ||
} | ||
}, | ||
render: options.componentRenderer(options) | ||
}; | ||
if (isLoaderString) { | ||
var tpl = options.stringHTMLRenderer(options); | ||
var res = Vue.compile(tpl); | ||
vueSpinnerOptions = Object.assign({}, vueSpinnerOptions, | ||
{data: Object.assign({}, vueSpinnerOptions.data, | ||
{options: options}), | ||
render: res.render, | ||
staticRenderFns: res.staticRenderFns}); | ||
} | ||
return vueSpinnerOptions | ||
}; | ||
var enableBtn = function (el, options) { | ||
if (el.getAttribute('disabled') && options.disableBtn) { el.removeAttribute('disabled'); } | ||
el.classList.remove(options.btnLoadingClass); | ||
}; | ||
var disableBtn = function (el, options) { | ||
if (options.disableBtn) { el.setAttribute('disabled', 'disabled'); } | ||
el.classList.add(options.btnLoadingClass); | ||
}; | ||
var setupVuePromiseBtn = function (globalOptions) { | ||
var directive = { | ||
listeners: {}, | ||
init: function init (el, binding, vnode) { | ||
var isBindingValueObj = isObject(binding.value); | ||
var id = el[pluginElPropName] = elementId++; | ||
var instanceOptions = isBindingValueObj ? binding.value : {}; | ||
var options = Object.assign({}, globalOptions, | ||
instanceOptions); | ||
var isLoaderString = isString(options.loader); | ||
var btnEl = el; | ||
var spinnerVM = null; | ||
var minTimeoutDone = false; | ||
var promiseDone = false; | ||
var scheduled = false; | ||
// START: loading state handlers | ||
var handleLoadingFinished = function (btnEl) { | ||
if (minTimeoutDone && promiseDone) { | ||
spinnerVM.show = false; | ||
minTimeoutDone = false; | ||
promiseDone = false; | ||
enableBtn(btnEl, options); | ||
} | ||
}; | ||
var initLoading = function (btnEl) { | ||
scheduled = true; | ||
disableBtn(btnEl, options); | ||
if (options.showSpinner) { | ||
spinnerVM.show = true; | ||
setTimeout(function () { | ||
minTimeoutDone = true; | ||
handleLoadingFinished(btnEl); | ||
}, options.minTimeout); | ||
} | ||
}; | ||
var finishLoading = function () { | ||
scheduled = false; | ||
if (options.showSpinner) { | ||
promiseDone = true; | ||
handleLoadingFinished(btnEl); | ||
} | ||
}; | ||
var getFiniteHandler = function (expression) { | ||
var result = expression; | ||
while (typeof result === 'function') { | ||
result = result(); | ||
} | ||
return result | ||
}; | ||
var promiseHanlder = function (btnEl, promise) { | ||
initLoading(btnEl); | ||
return promise | ||
.then(function () { return finishLoading(btnEl); }) | ||
.catch(function (e) { | ||
finishLoading(btnEl); | ||
throw e | ||
}) | ||
}; | ||
// END: loading state handlers | ||
// Bind individual params to directive | ||
directive.listeners[id] = { | ||
el: el, | ||
eventType: options.action | ||
}; | ||
// Setup button element if directive defined on form element | ||
if (options.action === 'submit') { | ||
btnEl = el.querySelector('[type="submit"]'); | ||
if (!btnEl) { throw new Error('No submit button found') } | ||
} | ||
// Setup option to show/hide spinner | ||
if (options.showSpinner) { | ||
var vueSpinnerOptions = initSpinner(btnEl, options, vnode, isLoaderString); | ||
spinnerVM = new Vue(vueSpinnerOptions); | ||
} | ||
// Finish initialization for Extended Mode (if we want use external promise from model) | ||
if (options.hasOwnProperty('promise')) { | ||
directive.listeners[id].promiseHanlder = promiseHanlder; | ||
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 | ||
var 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') } | ||
// Set custom event that will replace original listener | ||
directive.listener = function (e) { | ||
if (options.disableBtn && scheduled) { return } | ||
var expression = nativeEventHandler(e); | ||
var handlerPromise = typeof expression === 'function' ? getFiniteHandler(expression) : expression; | ||
if (isPromise(handlerPromise)) { | ||
return promiseHanlder(btnEl, handlerPromise) | ||
} | ||
}; | ||
}, | ||
bind: function bind (el, binding, vnode) { | ||
var ref; | ||
// Init | ||
(ref = binding.def).init.apply(ref, arguments); | ||
// Replace native event listener | ||
var id = el[pluginElPropName]; | ||
var ref$1 = directive.listeners[id]; | ||
var eventType = ref$1.eventType; | ||
var promiseHanlder = ref$1.promiseHanlder; | ||
if (!promiseHanlder) { | ||
// Add event handler for simplified mode | ||
el.addEventListener(eventType, directive.listener); | ||
} | ||
}, | ||
unbind: function unbind (el, binding) { | ||
// Cleanups if element removed to prevent memory leaks | ||
var id = el[pluginElPropName]; | ||
var ref = directive.listeners[id]; | ||
var eventType = ref.eventType; | ||
el.removeEventListener(eventType, directive.listener); | ||
delete directive.listeners[id]; | ||
delete el[pluginElPropName]; | ||
}, | ||
componentUpdated: function componentUpdated (el, binding, vnode, oldVnode) { | ||
// Extended Mode trigger | ||
if (binding && binding.value && | ||
binding.value.promise && | ||
binding.value.promise !== binding.oldValue.promise | ||
) { | ||
var id = el[pluginElPropName]; | ||
var ref = directive.listeners[id]; | ||
var promiseHanlder = ref.promiseHanlder; | ||
promiseHanlder(el, binding.value.promise); | ||
} | ||
} | ||
}; | ||
return directive | ||
}; | ||
var defaultOptions = { | ||
btnLoadingClass: 'loading', | ||
showSpinner: true, | ||
action: 'click', | ||
disableBtn: true, | ||
stringHTMLRenderer: stringHTMLRenderer, | ||
componentRenderer: componentRenderer, | ||
minTimeout: 400, | ||
spinnerHiddenClass: 'hidden', | ||
autoHideSpinnerWrapper: false, | ||
loader: Spinner | ||
}; | ||
function install (Vue$$1, options) { | ||
var globalOptions = Object.assign({}, defaultOptions, options); | ||
Vue$$1.directive('promise-btn', setupVuePromiseBtn(globalOptions)); | ||
} | ||
var main = { | ||
install: install, | ||
Spinner: Spinner, | ||
setupVuePromiseBtn: setupVuePromiseBtn | ||
}; | ||
module.exports = main; |
@@ -1,1 +0,419 @@ | ||
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&&(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,u.showSpinner&&(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 <span class="promise-btn__spinner-wrapper" \n :class="{[options.spinnerHiddenClass]: !show || false}"\n v-show="options.autoHideSpinnerWrapper ? show : true"\n >\n '+e.loader+"\n </span>"},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}; | ||
(function(l, i, v, e) { v = l.createElement(i); v.async = 1; v.src = '//' + (location.host || 'localhost').split(':')[0] + ':35729/livereload.js?snipver=1'; e = l.getElementsByTagName(i)[0]; e.parentNode.insertBefore(v, e)})(document, 'script'); | ||
import Vue from 'vue'; | ||
// | ||
// | ||
// | ||
var 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 style () { | ||
return { | ||
borderTopColor: this.color, | ||
width: ((this.size) + "px"), | ||
height: ((this.size) + "px"), | ||
borderWidth: ((this.width) + "px"), | ||
animationDuration: this.duration | ||
} | ||
} | ||
} | ||
}; | ||
function normalizeComponent(template, style, script, scopeId, isFunctionalTemplate, moduleIdentifier | ||
/* server only */ | ||
, shadowMode, createInjector, createInjectorSSR, createInjectorShadow) { | ||
if (typeof shadowMode !== 'boolean') { | ||
createInjectorSSR = createInjector; | ||
createInjector = shadowMode; | ||
shadowMode = false; | ||
} // Vue.extend constructor export interop. | ||
var options = typeof script === 'function' ? script.options : script; // render functions | ||
if (template && template.render) { | ||
options.render = template.render; | ||
options.staticRenderFns = template.staticRenderFns; | ||
options._compiled = true; // functional template | ||
if (isFunctionalTemplate) { | ||
options.functional = true; | ||
} | ||
} // scopedId | ||
if (scopeId) { | ||
options._scopeId = scopeId; | ||
} | ||
var hook; | ||
if (moduleIdentifier) { | ||
// server build | ||
hook = function hook(context) { | ||
// 2.3 injection | ||
context = context || // cached call | ||
this.$vnode && this.$vnode.ssrContext || // stateful | ||
this.parent && this.parent.$vnode && this.parent.$vnode.ssrContext; // functional | ||
// 2.2 with runInNewContext: true | ||
if (!context && typeof __VUE_SSR_CONTEXT__ !== 'undefined') { | ||
context = __VUE_SSR_CONTEXT__; | ||
} // inject component styles | ||
if (style) { | ||
style.call(this, createInjectorSSR(context)); | ||
} // register component module identifier for async chunk inference | ||
if (context && context._registeredComponents) { | ||
context._registeredComponents.add(moduleIdentifier); | ||
} | ||
}; // used by ssr in case component is cached and beforeCreate | ||
// never gets called | ||
options._ssrRegister = hook; | ||
} else if (style) { | ||
hook = shadowMode ? function () { | ||
style.call(this, createInjectorShadow(this.$root.$options.shadowRoot)); | ||
} : function (context) { | ||
style.call(this, createInjector(context)); | ||
}; | ||
} | ||
if (hook) { | ||
if (options.functional) { | ||
// register for functional component in vue file | ||
var originalRender = options.render; | ||
options.render = function renderWithStyleInjection(h, context) { | ||
hook.call(context); | ||
return originalRender(h, context); | ||
}; | ||
} else { | ||
// inject component registration as beforeCreate hook | ||
var existing = options.beforeCreate; | ||
options.beforeCreate = existing ? [].concat(existing, hook) : [hook]; | ||
} | ||
} | ||
return script; | ||
} | ||
var normalizeComponent_1 = normalizeComponent; | ||
/* script */ | ||
var __vue_script__ = script; | ||
/* template */ | ||
var __vue_render__ = function() { | ||
var _vm = this; | ||
var _h = _vm.$createElement; | ||
var _c = _vm._self._c || _h; | ||
return _c("span", { staticClass: "spinner", style: _vm.style }) | ||
}; | ||
var __vue_staticRenderFns__ = []; | ||
__vue_render__._withStripped = true; | ||
/* style */ | ||
var __vue_inject_styles__ = undefined; | ||
/* scoped */ | ||
var __vue_scope_id__ = "data-v-8170247a"; | ||
/* module identifier */ | ||
var __vue_module_identifier__ = undefined; | ||
/* functional template */ | ||
var __vue_is_functional_template__ = false; | ||
/* style inject */ | ||
/* style inject SSR */ | ||
var Spinner = normalizeComponent_1( | ||
{ render: __vue_render__, staticRenderFns: __vue_staticRenderFns__ }, | ||
__vue_inject_styles__, | ||
__vue_script__, | ||
__vue_scope_id__, | ||
__vue_is_functional_template__, | ||
__vue_module_identifier__, | ||
undefined, | ||
undefined | ||
); | ||
var isPromise = function (obj) { | ||
return !!obj && (typeof obj === 'object' || typeof obj === 'function') && typeof obj.then === 'function' | ||
}; | ||
var isString = function (obj) { | ||
return typeof obj === 'string' | ||
}; | ||
var isObject = function (obj) { | ||
return obj === Object(obj) | ||
}; | ||
var pluginElPropName = '$promiseBtnId'; | ||
var elementId = 0; | ||
var stringHTMLRenderer = function (options) { | ||
return ("\n <span class=\"promise-btn__spinner-wrapper\" \n :class=\"{[options.spinnerHiddenClass]: !show || false}\"\n v-show=\"options.autoHideSpinnerWrapper ? show : true\"\n >\n " + (options.loader) + "\n </span>") | ||
}; | ||
var componentRenderer = function (options) { | ||
return function (h) { | ||
var obj; | ||
return h('span', { | ||
class: ( obj = { | ||
'promise-btn__spinner-wrapper': true | ||
}, obj[options.spinnerHiddenClass] = options.spinnerHiddenClass ? !this.show : false, obj ), | ||
directives: [{ | ||
name: 'show', | ||
value: options.autoHideSpinnerWrapper ? this.show : true | ||
}] | ||
}, [ | ||
h(options.loader) | ||
]) | ||
} | ||
}; | ||
var initSpinner = function (btnEl, options, vnode, isLoaderString) { | ||
var dummyEl = document.createElement('SPAN'); | ||
btnEl.appendChild(dummyEl); | ||
var vueSpinnerOptions = { | ||
el: dummyEl, | ||
data: { show: false }, | ||
props: { | ||
parent: { | ||
type: Object, | ||
default: function () { return vnode.context; } | ||
} | ||
}, | ||
render: options.componentRenderer(options) | ||
}; | ||
if (isLoaderString) { | ||
var tpl = options.stringHTMLRenderer(options); | ||
var res = Vue.compile(tpl); | ||
vueSpinnerOptions = Object.assign({}, vueSpinnerOptions, | ||
{data: Object.assign({}, vueSpinnerOptions.data, | ||
{options: options}), | ||
render: res.render, | ||
staticRenderFns: res.staticRenderFns}); | ||
} | ||
return vueSpinnerOptions | ||
}; | ||
var enableBtn = function (el, options) { | ||
if (el.getAttribute('disabled') && options.disableBtn) { el.removeAttribute('disabled'); } | ||
el.classList.remove(options.btnLoadingClass); | ||
}; | ||
var disableBtn = function (el, options) { | ||
if (options.disableBtn) { el.setAttribute('disabled', 'disabled'); } | ||
el.classList.add(options.btnLoadingClass); | ||
}; | ||
var setupVuePromiseBtn = function (globalOptions) { | ||
var directive = { | ||
listeners: {}, | ||
init: function init (el, binding, vnode) { | ||
var isBindingValueObj = isObject(binding.value); | ||
var id = el[pluginElPropName] = elementId++; | ||
var instanceOptions = isBindingValueObj ? binding.value : {}; | ||
var options = Object.assign({}, globalOptions, | ||
instanceOptions); | ||
var isLoaderString = isString(options.loader); | ||
var btnEl = el; | ||
var spinnerVM = null; | ||
var minTimeoutDone = false; | ||
var promiseDone = false; | ||
var scheduled = false; | ||
// START: loading state handlers | ||
var handleLoadingFinished = function (btnEl) { | ||
if (minTimeoutDone && promiseDone) { | ||
spinnerVM.show = false; | ||
minTimeoutDone = false; | ||
promiseDone = false; | ||
enableBtn(btnEl, options); | ||
} | ||
}; | ||
var initLoading = function (btnEl) { | ||
scheduled = true; | ||
disableBtn(btnEl, options); | ||
if (options.showSpinner) { | ||
spinnerVM.show = true; | ||
setTimeout(function () { | ||
minTimeoutDone = true; | ||
handleLoadingFinished(btnEl); | ||
}, options.minTimeout); | ||
} | ||
}; | ||
var finishLoading = function () { | ||
scheduled = false; | ||
if (options.showSpinner) { | ||
promiseDone = true; | ||
handleLoadingFinished(btnEl); | ||
} | ||
}; | ||
var getFiniteHandler = function (expression) { | ||
var result = expression; | ||
while (typeof result === 'function') { | ||
result = result(); | ||
} | ||
return result | ||
}; | ||
var promiseHanlder = function (btnEl, promise) { | ||
initLoading(btnEl); | ||
return promise | ||
.then(function () { return finishLoading(btnEl); }) | ||
.catch(function (e) { | ||
finishLoading(btnEl); | ||
throw e | ||
}) | ||
}; | ||
// END: loading state handlers | ||
// Bind individual params to directive | ||
directive.listeners[id] = { | ||
el: el, | ||
eventType: options.action | ||
}; | ||
// Setup button element if directive defined on form element | ||
if (options.action === 'submit') { | ||
btnEl = el.querySelector('[type="submit"]'); | ||
if (!btnEl) { throw new Error('No submit button found') } | ||
} | ||
// Setup option to show/hide spinner | ||
if (options.showSpinner) { | ||
var vueSpinnerOptions = initSpinner(btnEl, options, vnode, isLoaderString); | ||
spinnerVM = new Vue(vueSpinnerOptions); | ||
} | ||
// Finish initialization for Extended Mode (if we want use external promise from model) | ||
if (options.hasOwnProperty('promise')) { | ||
directive.listeners[id].promiseHanlder = promiseHanlder; | ||
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 | ||
var 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') } | ||
// Set custom event that will replace original listener | ||
directive.listener = function (e) { | ||
if (options.disableBtn && scheduled) { return } | ||
var expression = nativeEventHandler(e); | ||
var handlerPromise = typeof expression === 'function' ? getFiniteHandler(expression) : expression; | ||
if (isPromise(handlerPromise)) { | ||
return promiseHanlder(btnEl, handlerPromise) | ||
} | ||
}; | ||
}, | ||
bind: function bind (el, binding, vnode) { | ||
var ref; | ||
// Init | ||
(ref = binding.def).init.apply(ref, arguments); | ||
// Replace native event listener | ||
var id = el[pluginElPropName]; | ||
var ref$1 = directive.listeners[id]; | ||
var eventType = ref$1.eventType; | ||
var promiseHanlder = ref$1.promiseHanlder; | ||
if (!promiseHanlder) { | ||
// Add event handler for simplified mode | ||
el.addEventListener(eventType, directive.listener); | ||
} | ||
}, | ||
unbind: function unbind (el, binding) { | ||
// Cleanups if element removed to prevent memory leaks | ||
var id = el[pluginElPropName]; | ||
var ref = directive.listeners[id]; | ||
var eventType = ref.eventType; | ||
el.removeEventListener(eventType, directive.listener); | ||
delete directive.listeners[id]; | ||
delete el[pluginElPropName]; | ||
}, | ||
componentUpdated: function componentUpdated (el, binding, vnode, oldVnode) { | ||
// Extended Mode trigger | ||
if (binding && binding.value && | ||
binding.value.promise && | ||
binding.value.promise !== binding.oldValue.promise | ||
) { | ||
var id = el[pluginElPropName]; | ||
var ref = directive.listeners[id]; | ||
var promiseHanlder = ref.promiseHanlder; | ||
promiseHanlder(el, binding.value.promise); | ||
} | ||
} | ||
}; | ||
return directive | ||
}; | ||
var defaultOptions = { | ||
btnLoadingClass: 'loading', | ||
showSpinner: true, | ||
action: 'click', | ||
disableBtn: true, | ||
stringHTMLRenderer: stringHTMLRenderer, | ||
componentRenderer: componentRenderer, | ||
minTimeout: 400, | ||
spinnerHiddenClass: 'hidden', | ||
autoHideSpinnerWrapper: false, | ||
loader: Spinner | ||
}; | ||
function install (Vue$$1, options) { | ||
var globalOptions = Object.assign({}, defaultOptions, options); | ||
Vue$$1.directive('promise-btn', setupVuePromiseBtn(globalOptions)); | ||
} | ||
var main = { | ||
install: install, | ||
Spinner: Spinner, | ||
setupVuePromiseBtn: setupVuePromiseBtn | ||
}; | ||
export default main; |
@@ -1,1 +0,427 @@ | ||
!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&&(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,l.showSpinner&&(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 <span class="promise-btn__spinner-wrapper" \n :class="{[options.spinnerHiddenClass]: !show || false}"\n v-show="options.autoHideSpinnerWrapper ? show : true"\n >\n '+e.loader+"\n </span>"},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(l, i, v, e) { v = l.createElement(i); v.async = 1; v.src = '//' + (location.host || 'localhost').split(':')[0] + ':35729/livereload.js?snipver=1'; e = l.getElementsByTagName(i)[0]; e.parentNode.insertBefore(v, e)})(document, 'script'); | ||
(function (global, factory) { | ||
typeof exports === 'object' && typeof module !== 'undefined' ? module.exports = factory(require('vue')) : | ||
typeof define === 'function' && define.amd ? define(['vue'], factory) : | ||
(global = global || self, global.VuePromiseBtn = factory(global.Vue)); | ||
}(this, function (Vue) { 'use strict'; | ||
Vue = Vue && Vue.hasOwnProperty('default') ? Vue['default'] : Vue; | ||
// | ||
// | ||
// | ||
var 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 style () { | ||
return { | ||
borderTopColor: this.color, | ||
width: ((this.size) + "px"), | ||
height: ((this.size) + "px"), | ||
borderWidth: ((this.width) + "px"), | ||
animationDuration: this.duration | ||
} | ||
} | ||
} | ||
}; | ||
function normalizeComponent(template, style, script, scopeId, isFunctionalTemplate, moduleIdentifier | ||
/* server only */ | ||
, shadowMode, createInjector, createInjectorSSR, createInjectorShadow) { | ||
if (typeof shadowMode !== 'boolean') { | ||
createInjectorSSR = createInjector; | ||
createInjector = shadowMode; | ||
shadowMode = false; | ||
} // Vue.extend constructor export interop. | ||
var options = typeof script === 'function' ? script.options : script; // render functions | ||
if (template && template.render) { | ||
options.render = template.render; | ||
options.staticRenderFns = template.staticRenderFns; | ||
options._compiled = true; // functional template | ||
if (isFunctionalTemplate) { | ||
options.functional = true; | ||
} | ||
} // scopedId | ||
if (scopeId) { | ||
options._scopeId = scopeId; | ||
} | ||
var hook; | ||
if (moduleIdentifier) { | ||
// server build | ||
hook = function hook(context) { | ||
// 2.3 injection | ||
context = context || // cached call | ||
this.$vnode && this.$vnode.ssrContext || // stateful | ||
this.parent && this.parent.$vnode && this.parent.$vnode.ssrContext; // functional | ||
// 2.2 with runInNewContext: true | ||
if (!context && typeof __VUE_SSR_CONTEXT__ !== 'undefined') { | ||
context = __VUE_SSR_CONTEXT__; | ||
} // inject component styles | ||
if (style) { | ||
style.call(this, createInjectorSSR(context)); | ||
} // register component module identifier for async chunk inference | ||
if (context && context._registeredComponents) { | ||
context._registeredComponents.add(moduleIdentifier); | ||
} | ||
}; // used by ssr in case component is cached and beforeCreate | ||
// never gets called | ||
options._ssrRegister = hook; | ||
} else if (style) { | ||
hook = shadowMode ? function () { | ||
style.call(this, createInjectorShadow(this.$root.$options.shadowRoot)); | ||
} : function (context) { | ||
style.call(this, createInjector(context)); | ||
}; | ||
} | ||
if (hook) { | ||
if (options.functional) { | ||
// register for functional component in vue file | ||
var originalRender = options.render; | ||
options.render = function renderWithStyleInjection(h, context) { | ||
hook.call(context); | ||
return originalRender(h, context); | ||
}; | ||
} else { | ||
// inject component registration as beforeCreate hook | ||
var existing = options.beforeCreate; | ||
options.beforeCreate = existing ? [].concat(existing, hook) : [hook]; | ||
} | ||
} | ||
return script; | ||
} | ||
var normalizeComponent_1 = normalizeComponent; | ||
/* script */ | ||
var __vue_script__ = script; | ||
/* template */ | ||
var __vue_render__ = function() { | ||
var _vm = this; | ||
var _h = _vm.$createElement; | ||
var _c = _vm._self._c || _h; | ||
return _c("span", { staticClass: "spinner", style: _vm.style }) | ||
}; | ||
var __vue_staticRenderFns__ = []; | ||
__vue_render__._withStripped = true; | ||
/* style */ | ||
var __vue_inject_styles__ = undefined; | ||
/* scoped */ | ||
var __vue_scope_id__ = "data-v-8170247a"; | ||
/* module identifier */ | ||
var __vue_module_identifier__ = undefined; | ||
/* functional template */ | ||
var __vue_is_functional_template__ = false; | ||
/* style inject */ | ||
/* style inject SSR */ | ||
var Spinner = normalizeComponent_1( | ||
{ render: __vue_render__, staticRenderFns: __vue_staticRenderFns__ }, | ||
__vue_inject_styles__, | ||
__vue_script__, | ||
__vue_scope_id__, | ||
__vue_is_functional_template__, | ||
__vue_module_identifier__, | ||
undefined, | ||
undefined | ||
); | ||
var isPromise = function (obj) { | ||
return !!obj && (typeof obj === 'object' || typeof obj === 'function') && typeof obj.then === 'function' | ||
}; | ||
var isString = function (obj) { | ||
return typeof obj === 'string' | ||
}; | ||
var isObject = function (obj) { | ||
return obj === Object(obj) | ||
}; | ||
var pluginElPropName = '$promiseBtnId'; | ||
var elementId = 0; | ||
var stringHTMLRenderer = function (options) { | ||
return ("\n <span class=\"promise-btn__spinner-wrapper\" \n :class=\"{[options.spinnerHiddenClass]: !show || false}\"\n v-show=\"options.autoHideSpinnerWrapper ? show : true\"\n >\n " + (options.loader) + "\n </span>") | ||
}; | ||
var componentRenderer = function (options) { | ||
return function (h) { | ||
var obj; | ||
return h('span', { | ||
class: ( obj = { | ||
'promise-btn__spinner-wrapper': true | ||
}, obj[options.spinnerHiddenClass] = options.spinnerHiddenClass ? !this.show : false, obj ), | ||
directives: [{ | ||
name: 'show', | ||
value: options.autoHideSpinnerWrapper ? this.show : true | ||
}] | ||
}, [ | ||
h(options.loader) | ||
]) | ||
} | ||
}; | ||
var initSpinner = function (btnEl, options, vnode, isLoaderString) { | ||
var dummyEl = document.createElement('SPAN'); | ||
btnEl.appendChild(dummyEl); | ||
var vueSpinnerOptions = { | ||
el: dummyEl, | ||
data: { show: false }, | ||
props: { | ||
parent: { | ||
type: Object, | ||
default: function () { return vnode.context; } | ||
} | ||
}, | ||
render: options.componentRenderer(options) | ||
}; | ||
if (isLoaderString) { | ||
var tpl = options.stringHTMLRenderer(options); | ||
var res = Vue.compile(tpl); | ||
vueSpinnerOptions = Object.assign({}, vueSpinnerOptions, | ||
{data: Object.assign({}, vueSpinnerOptions.data, | ||
{options: options}), | ||
render: res.render, | ||
staticRenderFns: res.staticRenderFns}); | ||
} | ||
return vueSpinnerOptions | ||
}; | ||
var enableBtn = function (el, options) { | ||
if (el.getAttribute('disabled') && options.disableBtn) { el.removeAttribute('disabled'); } | ||
el.classList.remove(options.btnLoadingClass); | ||
}; | ||
var disableBtn = function (el, options) { | ||
if (options.disableBtn) { el.setAttribute('disabled', 'disabled'); } | ||
el.classList.add(options.btnLoadingClass); | ||
}; | ||
var setupVuePromiseBtn = function (globalOptions) { | ||
var directive = { | ||
listeners: {}, | ||
init: function init (el, binding, vnode) { | ||
var isBindingValueObj = isObject(binding.value); | ||
var id = el[pluginElPropName] = elementId++; | ||
var instanceOptions = isBindingValueObj ? binding.value : {}; | ||
var options = Object.assign({}, globalOptions, | ||
instanceOptions); | ||
var isLoaderString = isString(options.loader); | ||
var btnEl = el; | ||
var spinnerVM = null; | ||
var minTimeoutDone = false; | ||
var promiseDone = false; | ||
var scheduled = false; | ||
// START: loading state handlers | ||
var handleLoadingFinished = function (btnEl) { | ||
if (minTimeoutDone && promiseDone) { | ||
spinnerVM.show = false; | ||
minTimeoutDone = false; | ||
promiseDone = false; | ||
enableBtn(btnEl, options); | ||
} | ||
}; | ||
var initLoading = function (btnEl) { | ||
scheduled = true; | ||
disableBtn(btnEl, options); | ||
if (options.showSpinner) { | ||
spinnerVM.show = true; | ||
setTimeout(function () { | ||
minTimeoutDone = true; | ||
handleLoadingFinished(btnEl); | ||
}, options.minTimeout); | ||
} | ||
}; | ||
var finishLoading = function () { | ||
scheduled = false; | ||
if (options.showSpinner) { | ||
promiseDone = true; | ||
handleLoadingFinished(btnEl); | ||
} | ||
}; | ||
var getFiniteHandler = function (expression) { | ||
var result = expression; | ||
while (typeof result === 'function') { | ||
result = result(); | ||
} | ||
return result | ||
}; | ||
var promiseHanlder = function (btnEl, promise) { | ||
initLoading(btnEl); | ||
return promise | ||
.then(function () { return finishLoading(btnEl); }) | ||
.catch(function (e) { | ||
finishLoading(btnEl); | ||
throw e | ||
}) | ||
}; | ||
// END: loading state handlers | ||
// Bind individual params to directive | ||
directive.listeners[id] = { | ||
el: el, | ||
eventType: options.action | ||
}; | ||
// Setup button element if directive defined on form element | ||
if (options.action === 'submit') { | ||
btnEl = el.querySelector('[type="submit"]'); | ||
if (!btnEl) { throw new Error('No submit button found') } | ||
} | ||
// Setup option to show/hide spinner | ||
if (options.showSpinner) { | ||
var vueSpinnerOptions = initSpinner(btnEl, options, vnode, isLoaderString); | ||
spinnerVM = new Vue(vueSpinnerOptions); | ||
} | ||
// Finish initialization for Extended Mode (if we want use external promise from model) | ||
if (options.hasOwnProperty('promise')) { | ||
directive.listeners[id].promiseHanlder = promiseHanlder; | ||
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 | ||
var 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') } | ||
// Set custom event that will replace original listener | ||
directive.listener = function (e) { | ||
if (options.disableBtn && scheduled) { return } | ||
var expression = nativeEventHandler(e); | ||
var handlerPromise = typeof expression === 'function' ? getFiniteHandler(expression) : expression; | ||
if (isPromise(handlerPromise)) { | ||
return promiseHanlder(btnEl, handlerPromise) | ||
} | ||
}; | ||
}, | ||
bind: function bind (el, binding, vnode) { | ||
var ref; | ||
// Init | ||
(ref = binding.def).init.apply(ref, arguments); | ||
// Replace native event listener | ||
var id = el[pluginElPropName]; | ||
var ref$1 = directive.listeners[id]; | ||
var eventType = ref$1.eventType; | ||
var promiseHanlder = ref$1.promiseHanlder; | ||
if (!promiseHanlder) { | ||
// Add event handler for simplified mode | ||
el.addEventListener(eventType, directive.listener); | ||
} | ||
}, | ||
unbind: function unbind (el, binding) { | ||
// Cleanups if element removed to prevent memory leaks | ||
var id = el[pluginElPropName]; | ||
var ref = directive.listeners[id]; | ||
var eventType = ref.eventType; | ||
el.removeEventListener(eventType, directive.listener); | ||
delete directive.listeners[id]; | ||
delete el[pluginElPropName]; | ||
}, | ||
componentUpdated: function componentUpdated (el, binding, vnode, oldVnode) { | ||
// Extended Mode trigger | ||
if (binding && binding.value && | ||
binding.value.promise && | ||
binding.value.promise !== binding.oldValue.promise | ||
) { | ||
var id = el[pluginElPropName]; | ||
var ref = directive.listeners[id]; | ||
var promiseHanlder = ref.promiseHanlder; | ||
promiseHanlder(el, binding.value.promise); | ||
} | ||
} | ||
}; | ||
return directive | ||
}; | ||
var defaultOptions = { | ||
btnLoadingClass: 'loading', | ||
showSpinner: true, | ||
action: 'click', | ||
disableBtn: true, | ||
stringHTMLRenderer: stringHTMLRenderer, | ||
componentRenderer: componentRenderer, | ||
minTimeout: 400, | ||
spinnerHiddenClass: 'hidden', | ||
autoHideSpinnerWrapper: false, | ||
loader: Spinner | ||
}; | ||
function install (Vue$$1, options) { | ||
var globalOptions = Object.assign({}, defaultOptions, options); | ||
Vue$$1.directive('promise-btn', setupVuePromiseBtn(globalOptions)); | ||
} | ||
var main = { | ||
install: install, | ||
Spinner: Spinner, | ||
setupVuePromiseBtn: setupVuePromiseBtn | ||
}; | ||
return main; | ||
})); |
{ | ||
"name": "vue-promise-btn", | ||
"version": "2.0.0", | ||
"version": "2.0.1", | ||
"description": "Vue.js plugin that handles buttons asynchronous lock and show loading state indicator", | ||
@@ -5,0 +5,0 @@ "scripts": { |
Sorry, the diff of this file is not supported yet
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
Major refactor
Supply chain riskPackage has recently undergone a major refactor. It may be unstable or indicate significant internal changes. Use caution when updating to versions that include significant changes.
Found 1 instance in 1 package
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
Minified code
QualityThis package contains minified code. This may be harmless in some cases where minified code is included in packaged libraries, however packages on npm should not minify code.
Found 1 instance in 1 package
53576
1337
0
1