vue-sticky-element
Version:
A simple vue sticky component wrapper that will stick to screen when scrolled past it
3 lines (2 loc) • 5.46 kB
JavaScript
(function(l,i){typeof exports=="object"&&typeof module<"u"?i(exports,require("vue"),require("v-scroll-threshold")):typeof define=="function"&&define.amd?define(["exports","vue","v-scroll-threshold"],i):(l=typeof globalThis<"u"?globalThis:l||self,i(l.VueStickyElement={},l.Vue,l.VScrollThreshold))})(this,function(l,i,d){"use strict";var f=document.createElement("style");f.textContent=`.vue-sticky-element{will-change:transform}.vue-sticky-element--transition{transition:transform .1s ease}.vue-sticky-element--stuck{width:100%;position:fixed!important;top:0;transform:translateY(-100%);z-index:10}.vue-sticky-element--show{transform:translateY(0)}.vue-sticky-element--hide{transform:translateY(-100%)!important}
`,document.head.appendChild(f);function p(e,s){if(i.cloneVNode)return i.cloneVNode(e,{...e.props});const n=e.children&&e.children.map(u=>p(u,s)),t=s(e.tag,e.data,n);return t.text=e.text,t.isComment=e.isComment,t.componentOptions=e.componentOptions,t.elm=e.elm,t.context=e.context,t.ns=e.ns,t.isStatic=e.isStatic,t.key=e.key,t}function b(e,s){return typeof i.withDirectives=="function"?i.withDirectives(e,s):e}function w(e){const s={threshold:e.directiveThreshold,callback:e.toggleStickiness,scrollBackThreshold:e.scrollBackThreshold,scrollElement:e.scrollElement},n={[e.visibleOnDirection]:!0};return typeof i.withDirectives=="function"?[d,s,"",n]:{name:"scroll-threshold",value:s,modifiers:n}}function S(e){if(Object.is(e,-0))return!0}function g(e,s=500){let n;return(...t)=>{clearTimeout(n),n=setTimeout(()=>{e.apply(null,t)},s)}}const m={passive:!0},h={directives:{"scroll-threshold":d},props:{visibleOnDirection:{type:String,default:"up",validator:e=>["up","down","disabled"].includes(e)},stickMode:{type:String,default:"element-end",validator:e=>["element-end","element-start"].includes(e)},stuckClass:{type:String,default:"vue-sticky-element--stuck"},showClass:{type:String,default:"vue-sticky-element--show"},hideClass:{type:String,default:"vue-sticky-element--hide"},transitionClass:{type:String,default:"vue-sticky-element--transition"},transitionDuration:{type:Number,default:50},scrollBackThreshold:{type:Number,default:65},skipChecks:{type:Boolean,default:!1},forceShow:{type:Boolean,default:!1},scrollElement:{type:Object,default:void 0}},emits:["stuck","show"],data(){return{navbarStuck:!1,navbarShow:!1,applyTransition:!1,height:void 0,forceHide:!1,observer:void 0,lastScrollPos:void 0,scrollBackValue:void 0}},computed:{alwaysStick(){return this.visibleOnDirection==="disabled"},shouldApplyTransition(){return!this.alwaysStick},stickWithElementStart(){return this.stickMode==="element-start"},directiveThreshold(){return this.stickWithElementStart?0:this.height||0}},mounted(){const e=()=>{var s;this.height=(s=this.$el.firstElementChild)==null?void 0:s.clientHeight};typeof window<"u"&&("ResizeObserver"in window?(this.observer=new ResizeObserver(e),this.observer.observe(this.$el)):(this.observer=g(e),window.addEventListener("resize",this.observer,m))),e()},beforeUnmount(){this.crossBeforeUnmount()},beforeDestroy(){this.crossBeforeUnmount()},methods:{addHide(){this.forceHide=!0},removeHide(){this.forceHide=!1},toggleStickiness(e,s){this.skipChecks||(e<0||S(e)?(this.navbarStuck=!1,this.$emit("stuck",!1),this.shouldApplyTransition&&this.$nextTick().then(()=>{this.applyTransition=!1})):e>0&&(this.height=this.$el?this.$el.clientHeight:this.height,this.navbarStuck=!0,this.$emit("stuck",!0),this.shouldApplyTransition&&this.$nextTick().then(()=>{setTimeout(()=>{this.applyTransition=!0},this.transitionDuration)})),this.navbarStuck&&(s||this.alwaysStick)?(this.navbarShow=!0,this.$emit("show",!0)):(this.navbarShow=!1,this.$emit("show",!1)))},crossBeforeUnmount(){this.observer&&("disconnect"in this.observer?(this.observer.disconnect(),this.observer=void 0):window.removeEventListener("resize",this.observer,m))}},render(e){const s=i.h?i.h:e;let n;if("$scopedSlots"in this?n=this.$scopedSlots.default():"$slots"in this&&(n=this.$slots.default()),!(n&&n[0]))return i.h?null:e();const t=p(n[0],s),u={"vue-sticky-element":!0,[this.stuckClass]:this.navbarStuck,[this.showClass]:this.navbarShow||this.forceShow,[this.hideClass]:this.forceHide,[this.transitionClass]:this.applyTransition};t.props?(t.props.class?typeof t.props.class=="string"&&(t.props.class=t.props.class.split(" ")):t.props.class=[],Array.isArray(t.props.class)&&(t.props.class=t.props.class.reduce((r,o)=>(r[o]=!0,r),{})),t.props.class={...t.props.class,...u},t.props.class=Object.entries(t.props.class).map(([r,o])=>o?r:null).filter(r=>r).join(" ")):t.data&&(t.data.class?typeof t.data.class=="string"&&(t.data.class=t.data.class.split(" ")):t.data.class=[],Array.isArray(t.data.class)&&(t.data.class=t.data.class.reduce((r,o)=>(r[o]=!0,r),{})),t.data.class={...t.data.class,...u},t.data.class=Object.entries(t.data.class).map(([r,o])=>o?r:null).filter(r=>r).join(" "));const y={},k={"pointer-events":"none"},v=[w(this)];return this.height&&(y.height=`${this.height}px`,k.height=`${this.height}px`),b(s("div",{style:y,...typeof i.withDirectives!="function"?{directives:v}:{}},[t,s("div",{style:k})]),v)}},T="",a=function(s){a.installed||(a.installed=!0,s.component("VueStickyElement",h),s.use(d))},C={install:a};let c=null;typeof window<"u"?c=window.Vue:typeof global<"u"&&(c=global.Vue),c&&c.use&&c.use(C),h.install=a,l.VueStickyElement=h,l.default=h,l.install=a,Object.defineProperties(l,{__esModule:{value:!0},[Symbol.toStringTag]:{value:"Module"}})});