From ccace4e47b6ea3c732123409302e1db6aa53f251 Mon Sep 17 00:00:00 2001 From: Antony Konstantinidis Date: Fri, 16 Jul 2021 12:01:40 +0200 Subject: [PATCH] vue 3 compatibility --- package.json | 2 +- src/index.js | 14 +-- src/sticky.js | 276 +++++++++++++++++++++++++------------------------- 3 files changed, 144 insertions(+), 148 deletions(-) diff --git a/package.json b/package.json index 06178c1..17c123c 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "vue-sticky-directive", - "version": "0.0.10", + "version": "0.0.11", "description": "A powerful vue directive make element sticky.", "main": "vue-sticky-directive.js", "module": "./src/index.js", diff --git a/src/index.js b/src/index.js index 7aeca4c..d2169e2 100644 --- a/src/index.js +++ b/src/index.js @@ -1,13 +1,9 @@ -import Sticky from './sticky'; +import Sticky from './sticky' -const install = function(Vue) { - Vue.directive('Sticky', Sticky); -}; - -if (window.Vue) { - Vue.use(install); +const install = function (app) { + app.directive('Sticky', Sticky) } -Sticky.install = install; +Sticky.install = install -export default Sticky; +export default Sticky diff --git a/src/sticky.js b/src/sticky.js index 1e50d22..1f85297 100644 --- a/src/sticky.js +++ b/src/sticky.js @@ -1,4 +1,4 @@ -const namespace = '@@vue-sticky-directive'; +const namespace = '@@vue-sticky-directive' const events = [ 'resize', 'scroll', @@ -6,46 +6,46 @@ const events = [ 'touchmove', 'touchend', 'pageshow', - 'load', -]; + 'load' +] const batchStyle = (el, style = {}, className = {}) => { for (let k in style) { - el.style[k] = style[k]; + el.style[k] = style[k] } for (let k in className) { if (className[k] && !el.classList.contains(k)) { - el.classList.add(k); + el.classList.add(k) } else if (!className[k] && el.classList.contains(k)) { - el.classList.remove(k); + el.classList.remove(k) } } -}; +} class Sticky { - constructor(el, vm) { - this.el = el; - this.vm = vm; - this.unsubscribers = []; - this.isPending = false; + constructor (el, vm) { + this.el = el + this.vm = vm + this.unsubscribers = [] + this.isPending = false this.state = { isTopSticky: null, isBottomSticky: null, height: null, width: null, - xOffset: null, - }; + xOffset: null + } this.lastState = { top: null, bottom: null, - sticked: false, - }; + sticked: false + } - const offset = this.getAttribute('sticky-offset') || {}; - const side = this.getAttribute('sticky-side') || 'top'; - const zIndex = this.getAttribute('sticky-z-index') || '10'; - const onStick = this.getAttribute('on-stick') || null; + const offset = this.getAttribute('sticky-offset') || {} + const side = this.getAttribute('sticky-side') || 'top' + const zIndex = this.getAttribute('sticky-z-index') || '10' + const onStick = this.getAttribute('on-stick') || null this.options = { topOffset: Number(offset.top) || 0, @@ -53,84 +53,84 @@ class Sticky { shouldTopSticky: side === 'top' || side === 'both', shouldBottomSticky: side === 'bottom' || side === 'both', zIndex: zIndex, - onStick: onStick, - }; + onStick: onStick + } } - doBind() { + doBind () { if (this.unsubscribers.length > 0) { - return; + return } - const { el, vm } = this; + const { el, vm } = this vm.$nextTick(() => { - this.placeholderEl = document.createElement('div'); - this.containerEl = this.getContainerEl(); - el.parentNode.insertBefore(this.placeholderEl, el); + this.placeholderEl = document.createElement('div') + this.containerEl = this.getContainerEl() + el.parentNode.insertBefore(this.placeholderEl, el) events.forEach(event => { - const fn = this.update.bind(this); - this.unsubscribers.push(() => window.removeEventListener(event, fn)); + const fn = this.update.bind(this) + this.unsubscribers.push(() => window.removeEventListener(event, fn)) this.unsubscribers.push(() => - this.containerEl.removeEventListener(event, fn), - ); - window.addEventListener(event, fn, { passive: true }); - this.containerEl.addEventListener(event, fn, { passive: true }); - }); - }); + this.containerEl.removeEventListener(event, fn) + ) + window.addEventListener(event, fn, { passive: true }) + this.containerEl.addEventListener(event, fn, { passive: true }) + }) + }) } - doUnbind() { - this.unsubscribers.forEach(fn => fn()); - this.unsubscribers = []; - this.resetElement(); + doUnbind () { + this.unsubscribers.forEach(fn => fn()) + this.unsubscribers = [] + this.resetElement() } - update() { + update () { if (!this.isPending) { requestAnimationFrame(() => { - this.isPending = false; - this.recomputeState(); - this.updateElements(); - }); - this.isPending = true; + this.isPending = false + this.recomputeState() + this.updateElements() + }) + this.isPending = true } } - isTopSticky() { - if (!this.options.shouldTopSticky) return false; - const fromTop = this.state.placeholderElRect.top; - const fromBottom = this.state.containerElRect.bottom; + isTopSticky () { + if (!this.options.shouldTopSticky) return false + const fromTop = this.state.placeholderElRect.top + const fromBottom = this.state.containerElRect.bottom - const topBreakpoint = this.options.topOffset; - const bottomBreakpoint = this.options.bottomOffset; + const topBreakpoint = this.options.topOffset + const bottomBreakpoint = this.options.bottomOffset - return fromTop <= topBreakpoint && fromBottom >= bottomBreakpoint; + return fromTop <= topBreakpoint && fromBottom >= bottomBreakpoint } - isBottomSticky() { - if (!this.options.shouldBottomSticky) return false; + isBottomSticky () { + if (!this.options.shouldBottomSticky) return false const fromBottom = - window.innerHeight - this.state.placeholderElRect.top - this.state.height; - const fromTop = window.innerHeight - this.state.containerElRect.top; + window.innerHeight - this.state.placeholderElRect.top - this.state.height + const fromTop = window.innerHeight - this.state.containerElRect.top - const topBreakpoint = this.options.topOffset; - const bottomBreakpoint = this.options.bottomOffset; + const topBreakpoint = this.options.topOffset + const bottomBreakpoint = this.options.bottomOffset - return fromBottom <= bottomBreakpoint && fromTop >= topBreakpoint; + return fromBottom <= bottomBreakpoint && fromTop >= topBreakpoint } - recomputeState() { + recomputeState () { this.state = Object.assign({}, this.state, { height: this.getHeight(), width: this.getWidth(), xOffset: this.getXOffset(), placeholderElRect: this.getPlaceholderElRect(), - containerElRect: this.getContainerElRect(), - }); - this.state.isTopSticky = this.isTopSticky(); - this.state.isBottomSticky = this.isBottomSticky(); + containerElRect: this.getContainerElRect() + }) + this.state.isTopSticky = this.isTopSticky() + this.state.isBottomSticky = this.isBottomSticky() } - fireEvents() { + fireEvents () { if ( typeof this.options.onStick === 'function' && (this.lastState.top !== this.state.isTopSticky || @@ -141,83 +141,83 @@ class Sticky { this.lastState = { top: this.state.isTopSticky, bottom: this.state.isBottomSticky, - sticked: this.state.isBottomSticky || this.state.isTopSticky, - }; - this.options.onStick(this.lastState); + sticked: this.state.isBottomSticky || this.state.isTopSticky + } + this.options.onStick(this.lastState) } } - updateElements() { - const placeholderStyle = { paddingTop: 0 }; + updateElements () { + const placeholderStyle = { paddingTop: 0 } const elStyle = { position: 'static', top: 'auto', bottom: 'auto', left: 'auto', width: 'auto', - zIndex: this.options.zIndex, - }; - const placeholderClassName = { 'vue-sticky-placeholder': true }; + zIndex: this.options.zIndex + } + const placeholderClassName = { 'vue-sticky-placeholder': true } const elClassName = { 'vue-sticky-el': true, 'top-sticky': false, - 'bottom-sticky': false, - }; + 'bottom-sticky': false + } if (this.state.isTopSticky) { - elStyle.position = 'fixed'; - elStyle.top = this.options.topOffset + 'px'; - elStyle.left = this.state.xOffset + 'px'; - elStyle.width = this.state.width + 'px'; + elStyle.position = 'fixed' + elStyle.top = this.options.topOffset + 'px' + elStyle.left = this.state.xOffset + 'px' + elStyle.width = this.state.width + 'px' const bottomLimit = this.state.containerElRect.bottom - this.state.height - this.options.bottomOffset - - this.options.topOffset; + this.options.topOffset if (bottomLimit < 0) { - elStyle.top = bottomLimit + this.options.topOffset + 'px'; + elStyle.top = bottomLimit + this.options.topOffset + 'px' } - placeholderStyle.paddingTop = this.state.height + 'px'; - elClassName['top-sticky'] = true; + placeholderStyle.paddingTop = this.state.height + 'px' + elClassName['top-sticky'] = true } else if (this.state.isBottomSticky) { - elStyle.position = 'fixed'; - elStyle.bottom = this.options.bottomOffset + 'px'; - elStyle.left = this.state.xOffset + 'px'; - elStyle.width = this.state.width + 'px'; + elStyle.position = 'fixed' + elStyle.bottom = this.options.bottomOffset + 'px' + elStyle.left = this.state.xOffset + 'px' + elStyle.width = this.state.width + 'px' const topLimit = window.innerHeight - this.state.containerElRect.top - this.state.height - this.options.bottomOffset - - this.options.topOffset; + this.options.topOffset if (topLimit < 0) { - elStyle.bottom = topLimit + this.options.bottomOffset + 'px'; + elStyle.bottom = topLimit + this.options.bottomOffset + 'px' } - placeholderStyle.paddingTop = this.state.height + 'px'; - elClassName['bottom-sticky'] = true; + placeholderStyle.paddingTop = this.state.height + 'px' + elClassName['bottom-sticky'] = true } else { - placeholderStyle.paddingTop = 0; + placeholderStyle.paddingTop = 0 } - batchStyle(this.el, elStyle, elClassName); - batchStyle(this.placeholderEl, placeholderStyle, placeholderClassName); + batchStyle(this.el, elStyle, elClassName) + batchStyle(this.placeholderEl, placeholderStyle, placeholderClassName) - this.fireEvents(); + this.fireEvents() } - resetElement() { + resetElement () { ['position', 'top', 'bottom', 'left', 'width', 'zIndex'].forEach(attr => { - this.el.style.removeProperty(attr); - }); - this.el.classList.remove('bottom-sticky', 'top-sticky'); - const { parentNode } = this.placeholderEl; + this.el.style.removeProperty(attr) + }) + this.el.classList.remove('bottom-sticky', 'top-sticky') + const { parentNode } = this.placeholderEl if (parentNode) { - parentNode.removeChild(this.placeholderEl); + parentNode.removeChild(this.placeholderEl) } } - getContainerEl() { - let node = this.el.parentNode; + getContainerEl () { + let node = this.el.parentNode while ( node && node.tagName !== 'HTML' && @@ -225,74 +225,74 @@ class Sticky { node.nodeType === 1 ) { if (node.hasAttribute('sticky-container')) { - return node; + return node } - node = node.parentNode; + node = node.parentNode } - return this.el.parentNode; + return this.el.parentNode } - getXOffset() { - return this.placeholderEl.getBoundingClientRect().left; + getXOffset () { + return this.placeholderEl.getBoundingClientRect().left } - getWidth() { - return this.placeholderEl.getBoundingClientRect().width; + getWidth () { + return this.placeholderEl.getBoundingClientRect().width } - getHeight() { - return this.el.getBoundingClientRect().height; + getHeight () { + return this.el.getBoundingClientRect().height } - getPlaceholderElRect() { - return this.placeholderEl.getBoundingClientRect(); + getPlaceholderElRect () { + return this.placeholderEl.getBoundingClientRect() } - getContainerElRect() { - return this.containerEl.getBoundingClientRect(); + getContainerElRect () { + return this.containerEl.getBoundingClientRect() } - getAttribute(name) { - const expr = this.el.getAttribute(name); - let result = undefined; + getAttribute (name) { + const expr = this.el.getAttribute(name) + let result if (expr) { if (this.vm[expr]) { - result = this.vm[expr]; + result = this.vm[expr] } else { try { - result = eval(`(${expr})`); + result = eval(`(${expr})`) } catch (error) { - result = expr; + result = expr } } } - return result; + return result } } export default { - inserted(el, bind, vnode) { - if (typeof bind.value === 'undefined' || bind.value) { - el[namespace] = new Sticky(el, vnode.context); - el[namespace].doBind(); + mounted (el, binding) { + if (typeof binding.value === 'undefined' || binding.value) { + el[namespace] = new Sticky(el, binding.instance) + el[namespace].doBind() } }, - unbind(el, bind, vnode) { + unmounted (el) { if (el[namespace]) { - el[namespace].doUnbind(); - el[namespace] = undefined; + el[namespace].doUnbind() + el[namespace] = undefined } }, - componentUpdated(el, bind, vnode) { - if (typeof bind.value === 'undefined' || bind.value) { + updated (el, binding) { + if (typeof binding.value === 'undefined' || binding.value) { if (!el[namespace]) { - el[namespace] = new Sticky(el, vnode.context); + el[namespace] = new Sticky(el, binding.instance) } - el[namespace].doBind(); + el[namespace].doBind() } else { if (el[namespace]) { - el[namespace].doUnbind(); + el[namespace].doUnbind() } } - }, -}; + } +}