diff --git a/dist/vue-observe-visibility.esm.js b/dist/vue-observe-visibility.esm.js index 13d2494..6cd535b 100644 --- a/dist/vue-observe-visibility.esm.js +++ b/dist/vue-observe-visibility.esm.js @@ -1,42 +1,318 @@ -function throwValueError(value) { - if (value !== null && typeof value !== 'function') { - throw new Error('observe-visibility directive expects a function as the value'); +var asyncGenerator = function () { + function AwaitValue(value) { + this.value = value; + } + + function AsyncGenerator(gen) { + var front, back; + + function send(key, arg) { + return new Promise(function (resolve, reject) { + var request = { + key: key, + arg: arg, + resolve: resolve, + reject: reject, + next: null + }; + + if (back) { + back = back.next = request; + } else { + front = back = request; + resume(key, arg); + } + }); + } + + function resume(key, arg) { + try { + var result = gen[key](arg); + var value = result.value; + + if (value instanceof AwaitValue) { + Promise.resolve(value.value).then(function (arg) { + resume("next", arg); + }, function (arg) { + resume("throw", arg); + }); + } else { + settle(result.done ? "return" : "normal", result.value); + } + } catch (err) { + settle("throw", err); + } + } + + function settle(type, value) { + switch (type) { + case "return": + front.resolve({ + value: value, + done: true + }); + break; + + case "throw": + front.reject(value); + break; + + default: + front.resolve({ + value: value, + done: false + }); + break; + } + + front = front.next; + + if (front) { + resume(front.key, front.arg); + } else { + back = null; + } + } + + this._invoke = send; + + if (typeof gen.return !== "function") { + this.return = undefined; + } + } + + if (typeof Symbol === "function" && Symbol.asyncIterator) { + AsyncGenerator.prototype[Symbol.asyncIterator] = function () { + return this; + }; + } + + AsyncGenerator.prototype.next = function (arg) { + return this._invoke("next", arg); + }; + + AsyncGenerator.prototype.throw = function (arg) { + return this._invoke("throw", arg); + }; + + AsyncGenerator.prototype.return = function (arg) { + return this._invoke("return", arg); + }; + + return { + wrap: function (fn) { + return function () { + return new AsyncGenerator(fn.apply(this, arguments)); + }; + }, + await: function (value) { + return new AwaitValue(value); + } + }; +}(); + + + + + +var classCallCheck = function (instance, Constructor) { + if (!(instance instanceof Constructor)) { + throw new TypeError("Cannot call a class as a function"); + } +}; + +var createClass = function () { + function defineProperties(target, props) { + for (var i = 0; i < props.length; i++) { + var descriptor = props[i]; + descriptor.enumerable = descriptor.enumerable || false; + descriptor.configurable = true; + if ("value" in descriptor) descriptor.writable = true; + Object.defineProperty(target, descriptor.key, descriptor); + } + } + + return function (Constructor, protoProps, staticProps) { + if (protoProps) defineProperties(Constructor.prototype, protoProps); + if (staticProps) defineProperties(Constructor, staticProps); + return Constructor; + }; +}(); + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +var toConsumableArray = function (arr) { + if (Array.isArray(arr)) { + for (var i = 0, arr2 = Array(arr.length); i < arr.length; i++) arr2[i] = arr[i]; + + return arr2; + } else { + return Array.from(arr); + } +}; + +function processOptions(value) { + var options = void 0; + if (typeof value === 'function') { + // Simple options (callback-only) + options = { + callback: value + }; + } else { + // Options object + options = value; } + return options; } -var ObserveVisibility = { - bind: function bind(el, _ref, vnode) { - var value = _ref.value; +function throttle(callback, delay) { + var timeout = void 0; + var lastState = void 0; + var currentArgs = void 0; + var throttled = function throttled(state) { + for (var _len = arguments.length, args = Array(_len > 1 ? _len - 1 : 0), _key = 1; _key < _len; _key++) { + args[_key - 1] = arguments[_key]; + } - if (typeof IntersectionObserver === 'undefined') { - console.warn('[vue-observe-visibility] IntersectionObserver API is not available in your browser. Please install this polyfill: https://github.com/WICG/IntersectionObserver/tree/gh-pages/polyfill'); - } else { - throwValueError(value); - el._vue_visibilityCallback = value; - var observer = el._vue_intersectionObserver = new IntersectionObserver(function (entries) { + currentArgs = args; + if (timeout && state === lastState) return; + lastState = state; + clearTimeout(timeout); + timeout = setTimeout(function () { + callback.apply(undefined, [state].concat(toConsumableArray(currentArgs))); + timeout = 0; + }, delay); + }; + throttled._clear = function () { + clearTimeout(timeout); + }; + return throttled; +} + +var VisibilityState = function () { + function VisibilityState(el, options, vnode) { + classCallCheck(this, VisibilityState); + + this.el = el; + this.observer = null; + this.createObserver(options, vnode); + } + + createClass(VisibilityState, [{ + key: 'createObserver', + value: function createObserver(options, vnode) { + var _this = this; + + if (this.observer) { + this.destroyObserver(); + } + + this.options = processOptions(options); + + this.callback = this.options.callback; + // Throttle + if (this.callback && this.options.throttle) { + this.callback = throttle(this.callback, this.options.throttle); + } + + this.observer = new IntersectionObserver(function (entries) { var entry = entries[0]; - if (el._vue_visibilityCallback) { + if (_this.callback) { // Use isIntersecting if possible because browsers can report isIntersecting as true, but intersectionRatio as 0, when something very slowly enters the viewport. - el._vue_visibilityCallback.call(null, entry.isIntersecting || entry.intersectionRatio > 0, entry); + _this.callback(entry.isIntersecting && entry.intersectionRatio >= _this.threshold, entry); } - }); + }, this.options.intersection); + // Wait for the element to be in document vnode.context.$nextTick(function () { - observer.observe(el); + _this.observer.observe(_this.el); }); } + }, { + key: 'destroyObserver', + value: function destroyObserver() { + if (this.observer) { + this.observer.disconnect(); + } + + // Cancel throttled call + if (this.callback && this.callback._clear) { + this.callback._clear(); + } + } + }, { + key: 'threshold', + get: function get$$1() { + return this.options.intersection && this.options.intersection.threshold; + } + }]); + return VisibilityState; +}(); + +var ObserveVisibility = { + bind: function bind(el, _ref, vnode) { + var value = _ref.value; + + if (typeof IntersectionObserver === 'undefined') { + console.warn('[vue-observe-visibility] IntersectionObserver API is not available in your browser. Please install this polyfill: https://github.com/w3c/IntersectionObserver/tree/master/polyfill'); + } else { + var state = new VisibilityState(el, value, vnode); + el._vue_visibilityState = state; + } }, - update: function update(el, _ref2) { + update: function update(el, _ref2, vnode) { var value = _ref2.value; - throwValueError(value); - el._vue_visibilityCallback = value; + var state = el._vue_visibilityState; + if (state) { + state.createObserver(value, vnode); + } else { + this.bind(el, { value: value }, vnode); + } }, unbind: function unbind(el) { - if (el._vue_intersectionObserver) { - el._vue_intersectionObserver.disconnect(); - delete el._vue_intersectionObserver; - delete el._vue_visibilityCallback; + var state = el._vue_visibilityState; + if (state) { + state.destroyObserver(); + delete el._vue_visibilityState; } } }; @@ -53,7 +329,7 @@ function install(Vue) { // Plugin var plugin = { // eslint-disable-next-line no-undef - version: "0.3.1", + version: "0.4.0", install: install }; diff --git a/dist/vue-observe-visibility.min.js b/dist/vue-observe-visibility.min.js index 35c6770..2ab98ab 100644 --- a/dist/vue-observe-visibility.min.js +++ b/dist/vue-observe-visibility.min.js @@ -1 +1 @@ -var VueObserveVisibility=function(e){"use strict";function i(e){if(null!==e&&"function"!=typeof e)throw new Error("observe-visibility directive expects a function as the value")}var t={bind:function(e,t,n){var l=t.value;if("undefined"==typeof IntersectionObserver)console.warn("[vue-observe-visibility] IntersectionObserver API is not available in your browser. Please install this polyfill: https://github.com/WICG/IntersectionObserver/tree/gh-pages/polyfill");else{i(l),e._vue_visibilityCallback=l;var r=e._vue_intersectionObserver=new IntersectionObserver(function(i){var t=i[0];e._vue_visibilityCallback&&e._vue_visibilityCallback.call(null,t.isIntersecting||t.intersectionRatio>0,t)});n.context.$nextTick(function(){r.observe(e)})}},update:function(e,t){var n=t.value;i(n),e._vue_visibilityCallback=n},unbind:function(e){e._vue_intersectionObserver&&(e._vue_intersectionObserver.disconnect(),delete e._vue_intersectionObserver,delete e._vue_visibilityCallback)}};function n(e){e.directive("observe-visibility",t)}var l={version:"0.3.1",install:n},r=null;return"undefined"!=typeof window?r=window.Vue:"undefined"!=typeof global&&(r=global.Vue),r&&r.use(l),e.install=n,e.ObserveVisibility=t,e.default=l,e}({}); +var VueObserveVisibility=function(e){"use strict";function t(e){return"function"==typeof e?{callback:e}:e}function n(e,t){var n=void 0,r=void 0,i=void 0,o=function(o){for(var a=arguments.length,l=Array(a>1?a-1:0),c=1;c=i.threshold,t)},this.options.intersection),r.context.$nextTick(function(){i.observer.observe(i.el)})}},{key:"destroyObserver",value:function(){this.observer&&this.observer.disconnect(),this.callback&&this.callback._clear&&this.callback._clear()}},{key:"threshold",get:function(){return this.options.intersection&&this.options.intersection.threshold}}]),e}(),l={bind:function(e,t,n){var r=t.value;if("undefined"==typeof IntersectionObserver)console.warn("[vue-observe-visibility] IntersectionObserver API is not available in your browser. Please install this polyfill: https://github.com/w3c/IntersectionObserver/tree/master/polyfill");else{var i=new a(e,r,n);e._vue_visibilityState=i}},update:function(e,t,n){var r=t.value,i=e._vue_visibilityState;i?i.createObserver(r,n):this.bind(e,{value:r},n)},unbind:function(e){var t=e._vue_visibilityState;t&&(t.destroyObserver(),delete e._vue_visibilityState)}},c={version:"0.4.0",install:r},u=null;return"undefined"!=typeof window?u=window.Vue:"undefined"!=typeof global&&(u=global.Vue),u&&u.use(c),e.install=r,e.ObserveVisibility=l,e.default=c,e}({}); diff --git a/dist/vue-observe-visibility.umd.js b/dist/vue-observe-visibility.umd.js index 95026cd..96537f4 100644 --- a/dist/vue-observe-visibility.umd.js +++ b/dist/vue-observe-visibility.umd.js @@ -4,45 +4,321 @@ (factory((global['vue-observe-visibility'] = {}))); }(this, (function (exports) { 'use strict'; -function throwValueError(value) { - if (value !== null && typeof value !== 'function') { - throw new Error('observe-visibility directive expects a function as the value'); +var asyncGenerator = function () { + function AwaitValue(value) { + this.value = value; + } + + function AsyncGenerator(gen) { + var front, back; + + function send(key, arg) { + return new Promise(function (resolve, reject) { + var request = { + key: key, + arg: arg, + resolve: resolve, + reject: reject, + next: null + }; + + if (back) { + back = back.next = request; + } else { + front = back = request; + resume(key, arg); + } + }); + } + + function resume(key, arg) { + try { + var result = gen[key](arg); + var value = result.value; + + if (value instanceof AwaitValue) { + Promise.resolve(value.value).then(function (arg) { + resume("next", arg); + }, function (arg) { + resume("throw", arg); + }); + } else { + settle(result.done ? "return" : "normal", result.value); + } + } catch (err) { + settle("throw", err); + } + } + + function settle(type, value) { + switch (type) { + case "return": + front.resolve({ + value: value, + done: true + }); + break; + + case "throw": + front.reject(value); + break; + + default: + front.resolve({ + value: value, + done: false + }); + break; + } + + front = front.next; + + if (front) { + resume(front.key, front.arg); + } else { + back = null; + } + } + + this._invoke = send; + + if (typeof gen.return !== "function") { + this.return = undefined; + } + } + + if (typeof Symbol === "function" && Symbol.asyncIterator) { + AsyncGenerator.prototype[Symbol.asyncIterator] = function () { + return this; + }; + } + + AsyncGenerator.prototype.next = function (arg) { + return this._invoke("next", arg); + }; + + AsyncGenerator.prototype.throw = function (arg) { + return this._invoke("throw", arg); + }; + + AsyncGenerator.prototype.return = function (arg) { + return this._invoke("return", arg); + }; + + return { + wrap: function (fn) { + return function () { + return new AsyncGenerator(fn.apply(this, arguments)); + }; + }, + await: function (value) { + return new AwaitValue(value); + } + }; +}(); + + + + + +var classCallCheck = function (instance, Constructor) { + if (!(instance instanceof Constructor)) { + throw new TypeError("Cannot call a class as a function"); + } +}; + +var createClass = function () { + function defineProperties(target, props) { + for (var i = 0; i < props.length; i++) { + var descriptor = props[i]; + descriptor.enumerable = descriptor.enumerable || false; + descriptor.configurable = true; + if ("value" in descriptor) descriptor.writable = true; + Object.defineProperty(target, descriptor.key, descriptor); + } + } + + return function (Constructor, protoProps, staticProps) { + if (protoProps) defineProperties(Constructor.prototype, protoProps); + if (staticProps) defineProperties(Constructor, staticProps); + return Constructor; + }; +}(); + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +var toConsumableArray = function (arr) { + if (Array.isArray(arr)) { + for (var i = 0, arr2 = Array(arr.length); i < arr.length; i++) arr2[i] = arr[i]; + + return arr2; + } else { + return Array.from(arr); + } +}; + +function processOptions(value) { + var options = void 0; + if (typeof value === 'function') { + // Simple options (callback-only) + options = { + callback: value + }; + } else { + // Options object + options = value; } + return options; } -var ObserveVisibility = { - bind: function bind(el, _ref, vnode) { - var value = _ref.value; +function throttle(callback, delay) { + var timeout = void 0; + var lastState = void 0; + var currentArgs = void 0; + var throttled = function throttled(state) { + for (var _len = arguments.length, args = Array(_len > 1 ? _len - 1 : 0), _key = 1; _key < _len; _key++) { + args[_key - 1] = arguments[_key]; + } - if (typeof IntersectionObserver === 'undefined') { - console.warn('[vue-observe-visibility] IntersectionObserver API is not available in your browser. Please install this polyfill: https://github.com/WICG/IntersectionObserver/tree/gh-pages/polyfill'); - } else { - throwValueError(value); - el._vue_visibilityCallback = value; - var observer = el._vue_intersectionObserver = new IntersectionObserver(function (entries) { + currentArgs = args; + if (timeout && state === lastState) return; + lastState = state; + clearTimeout(timeout); + timeout = setTimeout(function () { + callback.apply(undefined, [state].concat(toConsumableArray(currentArgs))); + timeout = 0; + }, delay); + }; + throttled._clear = function () { + clearTimeout(timeout); + }; + return throttled; +} + +var VisibilityState = function () { + function VisibilityState(el, options, vnode) { + classCallCheck(this, VisibilityState); + + this.el = el; + this.observer = null; + this.createObserver(options, vnode); + } + + createClass(VisibilityState, [{ + key: 'createObserver', + value: function createObserver(options, vnode) { + var _this = this; + + if (this.observer) { + this.destroyObserver(); + } + + this.options = processOptions(options); + + this.callback = this.options.callback; + // Throttle + if (this.callback && this.options.throttle) { + this.callback = throttle(this.callback, this.options.throttle); + } + + this.observer = new IntersectionObserver(function (entries) { var entry = entries[0]; - if (el._vue_visibilityCallback) { + if (_this.callback) { // Use isIntersecting if possible because browsers can report isIntersecting as true, but intersectionRatio as 0, when something very slowly enters the viewport. - el._vue_visibilityCallback.call(null, entry.isIntersecting || entry.intersectionRatio > 0, entry); + _this.callback(entry.isIntersecting && entry.intersectionRatio >= _this.threshold, entry); } - }); + }, this.options.intersection); + // Wait for the element to be in document vnode.context.$nextTick(function () { - observer.observe(el); + _this.observer.observe(_this.el); }); } + }, { + key: 'destroyObserver', + value: function destroyObserver() { + if (this.observer) { + this.observer.disconnect(); + } + + // Cancel throttled call + if (this.callback && this.callback._clear) { + this.callback._clear(); + } + } + }, { + key: 'threshold', + get: function get$$1() { + return this.options.intersection && this.options.intersection.threshold; + } + }]); + return VisibilityState; +}(); + +var ObserveVisibility = { + bind: function bind(el, _ref, vnode) { + var value = _ref.value; + + if (typeof IntersectionObserver === 'undefined') { + console.warn('[vue-observe-visibility] IntersectionObserver API is not available in your browser. Please install this polyfill: https://github.com/w3c/IntersectionObserver/tree/master/polyfill'); + } else { + var state = new VisibilityState(el, value, vnode); + el._vue_visibilityState = state; + } }, - update: function update(el, _ref2) { + update: function update(el, _ref2, vnode) { var value = _ref2.value; - throwValueError(value); - el._vue_visibilityCallback = value; + var state = el._vue_visibilityState; + if (state) { + state.createObserver(value, vnode); + } else { + this.bind(el, { value: value }, vnode); + } }, unbind: function unbind(el) { - if (el._vue_intersectionObserver) { - el._vue_intersectionObserver.disconnect(); - delete el._vue_intersectionObserver; - delete el._vue_visibilityCallback; + var state = el._vue_visibilityState; + if (state) { + state.destroyObserver(); + delete el._vue_visibilityState; } } }; @@ -59,7 +335,7 @@ function install(Vue) { // Plugin var plugin = { // eslint-disable-next-line no-undef - version: "0.3.1", + version: "0.4.0", install: install }; diff --git a/package.json b/package.json index 323ad6a..5c26dcd 100644 --- a/package.json +++ b/package.json @@ -1,7 +1,7 @@ { "name": "vue-observe-visibility", "description": "Detect when an element is becoming visible or hidden on the page. ", - "version": "0.3.1", + "version": "0.4.0", "author": { "name": "Guillaume Chau", "email": "guillaume.b.chau@gmail.com"