react-sticky-box
Version:
Boxes that behave nicely while scrolling
2 lines (1 loc) • 4.02 kB
JavaScript
import{useEffect as R,useState as N}from"react";import{jsx as A}from"react/jsx-runtime";var V=e=>{let t=e;for(;t=t.parentElement;){let s=getComputedStyle(t,null).getPropertyValue("overflow-y");if(t===document.body)return window;if(s==="auto"||s==="scroll"||s==="overlay")return t}return window},D=e=>e.firstChild?e.firstChild.offsetParent===e:!0,U=(e,t)=>{let s=e,n=0;D(t)||(n+=e.offsetTop-t.offsetTop,t=e.offsetParent,n+=-e.offsetTop);do n+=s.offsetTop,s=s.offsetParent;while(s&&s!==t);return n},j=e=>{let t=e.parentElement;for(;t&&getComputedStyle(t,null).getPropertyValue("display")==="contents";)t=t.parentElement;return t||window},S=null;typeof CSS<"u"&&CSS.supports&&(CSS.supports("position","sticky")?S="sticky":CSS.supports("position","-webkit-sticky")&&(S="-webkit-sticky"));var x=!1;try{let e=Object.defineProperty({},"passive",{get(){x={passive:!0}}}),t=()=>{};window.addEventListener("testPassive",t,e),window.removeEventListener("testPassive",t,e)}catch{}var k=e=>{let{el:t,onChange:s,unsubs:n,measure:i}=e;if(t===window){let r=()=>({top:0,left:0,height:window.innerHeight,width:window.innerWidth}),l=i(r()),p=()=>{Object.assign(l,i(r())),s()};return window.addEventListener("resize",p,x),n.push(()=>window.removeEventListener("resize",p)),l}else{let r=i(t.getBoundingClientRect()),l=()=>{Object.assign(r,i(t.getBoundingClientRect())),s()},p=new ResizeObserver(l);return p.observe(t),n.push(()=>p.disconnect()),r}},z=e=>{let t=getComputedStyle(e,null),s=parseInt(t.getPropertyValue("padding-top"),10),n=parseInt(t.getPropertyValue("padding-bottom"),10);return{top:s,bottom:n}};var F=(e,t,s)=>{let{bottom:n,offsetBottom:i,offsetTop:r}=s,l=V(e),p=!1,L=()=>{p||requestAnimationFrame(()=>{let o=C();if(o!==y)d(o);else if(o===1&&!n){let{height:c}=g,{height:a}=w;e.style.top=`${c-a-i}px`}else if(o===2){let{height:c,offsetTop:a}=g,{height:m,naturalTop:f}=b,{height:T}=w,u=Math.max(0,a+h+c-(f+T+i));if(n){let E=Math.max(0,m-T-u);e.style.bottom=`${E}px`}else e.style.top=`${u}px`}p=!1}),p=!0},h=l===window?window.scrollY:l.scrollTop,O=o=>{let{offsetTop:c,height:a}=g,{naturalTop:m}=b,{height:f}=w;return o+c+a>=m+f+M+i},C=()=>{let{height:o}=g,{height:c}=w;return c+r+i<=o?3:O(h)?1:2},$=l!==window&&D(l),g=k({el:l,onChange:L,unsubs:t,measure:({height:o,top:c})=>({height:o,offsetTop:$?c:0})}),v=j(e),P=v===window?{top:0,bottom:0}:z(v),b=k({el:v,onChange:L,unsubs:t,measure:({height:o})=>({height:o-P.top-P.bottom,naturalTop:v===window?0:U(v,l)+P.top+g.offsetTop})}),w=k({el:e,onChange:L,unsubs:t,measure:({height:o})=>({height:o})}),M=0,y=C(),d=o=>{let c=y;if(y=o,c===2&&(M=-1),o===3){e.style.position=S,n?e.style.bottom=`${i}px`:e.style.top=`${r}px`;return}let{height:a,offsetTop:m}=g,{height:f,naturalTop:T}=b,{height:u}=w;if(o===2)if(e.style.position="relative",M=c===0?Math.max(0,m+h-T+r):Math.max(0,m+h+a-(T+u+i)),n){let E=Math.max(0,f-u-M);e.style.bottom=`${E}px`}else e.style.top=`${M}px`;else e.style.position=S,o===1?n?e.style.bottom=`${i}px`:e.style.top=`${a-u-i}px`:n?e.style.bottom=`${a-u-i}px`:e.style.top=`${r}px`};d(y);let B=o=>{if(o===h)return;let c=o-h;if(h=o,y===3)return;let{offsetTop:a,height:m}=g,{naturalTop:f,height:T}=b,{height:u}=w;if(c>0)if(y===0){if(o+a+r>f){let E=Math.max(0,a+h-f+r);o+a+m<=f+u+E+i?d(2):d(1)}}else y===2&&O(o)&&d(1);else if(y===1){if(a+o+m<f+T+i){let E=Math.max(0,a+h+m-(f+u+i));a+o+r>=f+E?d(2):d(0)}}else y===2&&a+o+r<f+M&&d(0)},H=l===window?()=>B(window.scrollY):()=>B(l.scrollTop);l.addEventListener("scroll",H,x),l.addEventListener("mousewheel",H,x),t.push(()=>l.removeEventListener("scroll",H),()=>l.removeEventListener("mousewheel",H))},I=({offsetTop:e=0,offsetBottom:t=0,bottom:s=!1}={})=>{let[n,i]=N(null);return R(()=>{if(!n||!S)return;let r=[];return F(n,r,{offsetBottom:t,offsetTop:e,bottom:s}),()=>{r.forEach(l=>l())}},[n,t,e,s]),i},q=e=>{let{offsetTop:t,offsetBottom:s,bottom:n,children:i,className:r,style:l}=e,p=I({offsetTop:t,offsetBottom:s,bottom:n});return A("div",{className:r,style:l,ref:p,children:i})},J=q;export{J as default,I as useStickyBox};