aero-slider
Version:
A lightweight, CSS-first slider library for the modern web
3 lines (2 loc) • 12 kB
JavaScript
/*! Aero Slider v1.0.0-beta.11 | https://github.com/Negative-Space-Dev/aero-slider */
function Ie(n){let{container:s,signal:t}=n,o=s.querySelector(".aero-slider__nav--prev"),b=s.querySelector(".aero-slider__nav--next");return o?.addEventListener("click",n.prev,{signal:t}),b?.addEventListener("click",n.next,{signal:t}),{refresh(){let d=n.maxIndex()===0,c=!n.loop();if(o)o.disabled=d||c&&n.current()===0;if(b)b.disabled=d||c&&n.current()>=n.maxIndex()}}}function He(n){let s=n.container.querySelector(".aero-slider__pagination"),t=s?.querySelector(".aero-slider__dot");if(!s||!t)return{build(){},refresh(){}};return Ye(n,s,t)}function Ye(n,s,t){let{config:o,signal:b}=n,d=[],c=!1,a=null,i=0;function u(){for(let m of d)if(m!==t)m.remove();a?.remove(),a=null,d=[],t.style.display="",s.classList.remove("aero-slider__pagination--windowed"),s.style.removeProperty("--pagination-width")}function E(){u();let m=n.pageCount(),{maxDots:f}=o;if(c=f>0&&m>f,c)a=document.createElement("div"),a.className="aero-slider__pagination-track";let h=a??s;for(let v=0;v<m;v++){let S=!c&&v===0?t:t.cloneNode(!0);if(S.setAttribute("role","tab"),S.setAttribute("aria-label",`Go to slide ${v+1}`),S.dataset.slideIndex=String(v),S!==t)h.append(S);d.push(S)}if(a){t.style.display="none",s.classList.add("aero-slider__pagination--windowed"),s.append(a),i=d[1].offsetLeft-d[0].offsetLeft;let v=(f-1)*Math.abs(i)+d[0].offsetWidth;s.style.setProperty("--pagination-width",`${v}px`)}g()}function g(){let m=n.current(),f=d.length,{maxDots:h}=o,v=c?Math.max(0,Math.min(m-Math.floor(h/2),f-h)):0,S=v===0,y=v>=f-h;a?.style.setProperty("--track-offset",`${-v*i}px`),d.forEach((k,_)=>{let D=_===m;if(k.classList.toggle("aero-slider__dot--active",D),k.setAttribute("aria-selected",String(D)),!c)return;let T=_-v,B=T<0||T>=h?"hidden":T===0&&!S||T===h-1&&!y?"edge":T===1&&!S||T===h-2&&!y?"near-edge":"";if(k.classList.remove("aero-slider__dot--hidden","aero-slider__dot--edge","aero-slider__dot--near-edge"),B)k.classList.add(`aero-slider__dot--${B}`)})}return s.setAttribute("role","tablist"),s.addEventListener("click",(m)=>{let f=m.target.closest(".aero-slider__dot");if(f?.dataset.slideIndex)n.goTo(Number(f.dataset.slideIndex))},{signal:b}),b.addEventListener("abort",u),{build:E,refresh:g}}function ke(n){let{container:s,signal:t}=n;if(!s.hasAttribute("tabindex"))s.tabIndex=0,t.addEventListener("abort",()=>s.removeAttribute("tabindex"));s.addEventListener("keydown",(o)=>{if(o.target.closest("input, textarea, select, [contenteditable]"))return;let{direction:b}=n.config,[d,c]=b==="ttb"?["ArrowUp","ArrowDown"]:b==="rtl"?["ArrowRight","ArrowLeft"]:["ArrowLeft","ArrowRight"];if(o.key===d)n.prev();else if(o.key===c)n.next();else return;o.preventDefault()},{signal:t})}function De(n){let{container:s,config:t,signal:o}=n,b=matchMedia("(prefers-reduced-motion: reduce)"),d=new Set,c=0;function a(){if(c||!t.autoplay||d.size||document.hidden||b.matches)return;c=window.setInterval(u,t.autoplayInterval),n.emit("autoplayStart",{})}function i(){if(!c)return;clearInterval(c),c=0,n.emit("autoplayStop",{})}function u(){if(!n.loop()&&n.current()>=n.maxIndex())n.goTo(0);else n.next()}function E(f){d.add(f),i()}function g(f){d.delete(f),a()}let m=(f,h,v)=>f.addEventListener(h,v,{passive:!0,signal:o});return m(s,"pointerenter",()=>E("pointer")),m(s,"pointerleave",()=>g("pointer")),m(s,"focusin",(f)=>{if(f.target.matches(":focus-visible"))E("focus")}),m(s,"focusout",(f)=>{if(!s.contains(f.relatedTarget))g("focus")}),m(document,"visibilitychange",()=>document.hidden?i():a()),o.addEventListener("abort",i),{start:a,stop:i,hold:E,release:g}}var Q="data-aero-slider-index",ve="data-aero-slider-clone",je={loop:!1,autoplay:!1,autoplayInterval:5000,draggable:!0,alignment:"center",maxDots:0,noDrag:"",perMove:1,direction:"ltr"},Je=5,Qe=0.3,Ze=250,et=150;function tt(n){let s=n.querySelector(".aero-slider__track");if(!s)throw Error("aero-slider: missing .aero-slider__track");if(!s.children.length)throw Error("aero-slider: no slides found");return s}function Re(n,s={}){let t=tt(n),o={...je,...s},b=new AbortController,{signal:d}=b,c=[],a=0,i=0,u=!1,E=1,g=0,m=0,f=0,h=0,v=0,S=0,y=!1,k=0,_=!1,D=!1,T=0,B=0,w=()=>o.direction==="ttb",R=()=>o.direction==="rtl",G=(e,r,l)=>Math.max(r,Math.min(e,l)),O=(e)=>(e%a+a)%a;function C(){let e=o.alignment==="left"&&Number.isInteger(E);return Math.max(0,e?a-E:a-1)}let L=()=>o.loop&&C()>0,le=()=>L()?a:C()+1,be=()=>L()?Math.max(a,Math.ceil(E)+2):0;function M(e,r){n.dispatchEvent(new CustomEvent(`aero:${e}`,{detail:r,bubbles:!0}))}function x(){if(w())return t.scrollTop;return R()?-t.scrollLeft:t.scrollLeft}function N(e,r=!1){let l=r?"smooth":"instant";if(w())t.scrollTo({top:e,behavior:l});else t.scrollTo({left:R()?-e:e,behavior:l})}function z(e){let r=e.getBoundingClientRect(),l=t.getBoundingClientRect(),[p,A,I,X]=w()?[l.top+t.clientTop,t.clientHeight,r.top,r.bottom]:R()?[-(l.left+t.clientLeft+t.clientWidth),t.clientWidth,-r.right,-r.left]:[l.left+t.clientLeft,t.clientWidth,r.left,r.right],H=p+v,j=p+A-S,J=o.alignment==="left"?I-H:o.alignment==="right"?X-j:(I+X-H-j)/2;return x()+J}function he(){let e=getComputedStyle(n).getPropertyValue("--slides-per-view");E=parseFloat(e)||1}function Ee(){let e=getComputedStyle(t);if(w())v=parseFloat(e.paddingTop),S=parseFloat(e.paddingBottom);else v=parseFloat(R()?e.paddingRight:e.paddingLeft),S=parseFloat(R()?e.paddingLeft:e.paddingRight);let[r,l]=t.children,p=w()?t.clientHeight:t.clientWidth;f=r?z(r):0,m=l?z(l)-f:p,h=w()?t.scrollHeight-t.clientHeight:t.scrollWidth-t.clientWidth}let W=(e)=>Math.round((e-f)/m),Se=(e)=>e>=g&&e<g+a,Z=(e)=>L()?O(e-g):G(e,0,C());function Le(e){if(!L()){if(e<=1)return 0;if(e>=h-1)return C()}return Z(W(e))}function ee(e){let r=z(t.children[g+e]);return L()?r:G(r,0,h)}function ae(e){if(e===i)return;i=e,M("slideChange",{index:e}),ge.refresh(),pe.refresh()}function ye(e){let r=t.children[e],l=t.children[g+O(e-g)];return r&&l?z(l)-z(r):0}function Te(){if(!L())return;let e=W(x());if(!Se(e))N(x()+ye(e))}function xe(e){let r=e.cloneNode(!0);r.removeAttribute(Q),r.setAttribute(ve,""),r.setAttribute("aria-hidden","true");for(let l of[r,...r.querySelectorAll("[id]")])l.removeAttribute("id");for(let l of r.querySelectorAll("a, button, input, select, textarea, [tabindex]"))l.tabIndex=-1;return r}function Y(e){if(u||!a)return;let r=L()?O(Math.trunc(e)):G(Math.trunc(e),0,C());if(ae(r),!m)return;let l;if(L()){Te();let p=W(x()),A=O(r-Z(p)),I=A>a/2?A-a:A;l=z(t.children[p+I])}else l=ee(r);if(Math.abs(l-x())<1)return de();y=!0,k=l,N(l,!0)}let Me=()=>Math.max(1,Math.trunc(o.perMove)),Ae=()=>Y(i+Me()),we=()=>Y(i-Me());function qe(){if(!y&&!T)T=requestAnimationFrame(()=>{T=0,ae(Le(x()))});if(!("onscrollend"in t))clearTimeout(B),B=window.setTimeout(de,et)}function de(){if(_)return;if(y&&Math.abs(x()-k)>1)return;y=!1,t.style.scrollSnapType="",Te();let e=x(),r=Le(e),l=L()||Math.abs(e-ee(i))>1;if(r!==i&&l)ae(r)}function Fe(e){if(D=!1,y=!1,!o.draggable||e.button!==0||e.pointerType==="touch")return;if(o.noDrag&&e.target.closest(o.noDrag))return;let r=w()?"clientY":"clientX",l=R()?-1:1,p=e[r],A=i,I=new AbortController,X=x(),H=X,j=p,J=e.timeStamp,P=0,K=0;function Ge(U){let q=U[r];if(!_){if(Math.abs(q-p)<Je)return;_=!0,D=!0,t.setPointerCapture(U.pointerId),t.style.scrollSnapType="none",n.classList.add("aero-slider--dragging"),oe.hold("drag"),M("dragStart",{index:A})}let F=U.timeStamp-J;if(F>0){let se=(j-q)*l/F;P=F<100?0.3*se+0.7*P:P/2,j=q,J=U.timeStamp}if(H=X+(p-q)*l,L()){let se=W(H);if(!Se(se)){let Ce=ye(se);H+=Ce,X+=Ce}}if(!K)K=requestAnimationFrame(()=>{K=0,N(H)})}function _e(U){if(I.abort(),!_)return;if(_=!1,n.classList.remove("aero-slider--dragging"),K)cancelAnimationFrame(K),K=0,N(H);let q=U.timeStamp-J;if(q>50)P*=Math.max(0,1-q/200);let F=W(x()+P*Ze);if(Math.abs(P)>Qe&&F===W(x()))F+=Math.sign(P);Y(Z(F)),M("dragEnd",{index:i,fromIndex:A}),oe.release("drag")}let ie={signal:I.signal};t.addEventListener("pointermove",Ge,ie),t.addEventListener("pointerup",_e,ie),t.addEventListener("pointercancel",_e,ie),d.addEventListener("abort",()=>I.abort(),ie)}function Be(e){if(!D)return;D=!1,e.preventDefault(),e.stopPropagation()}let ce=!1,te=new ResizeObserver(()=>{if(ce){ce=!1;return}if(u)return;M("resize",{}),We(),M("resized",{})}),ne,ue=new Map,fe=new Set;function Ne(e){for(let p of e)ue.set(p.target,p.isIntersecting);let r=Array.from(t.children),l=new Set;for(let[p,A]of ue)if(A)l.add(Z(r.indexOf(p)));for(let p of fe)if(!l.has(p))M("hidden",{index:p});for(let p of l)if(!fe.has(p))M("visible",{index:p});fe=l}function ze(){if(te.disconnect(),ce=!0,te.observe(t),t.firstElementChild)te.observe(t.firstElementChild);ne?.disconnect(),ue.clear(),ne=new IntersectionObserver(Ne,{root:t,rootMargin:w()?"-1px 0px":"0px -1px"});for(let e of t.children)ne.observe(e)}function We(){let e=le();if(he(),be()!==g)return V();if(Ee(),le()!==e)ge.build();i=L()?O(i):G(i,0,C()),y=!1,N(ee(i)),pe.refresh()}function V(){for(let e of t.querySelectorAll(`[${ve}]`))e.remove();if(c=Array.from(t.children),a=c.length,!a)return;if(c.forEach((e,r)=>{e.classList.add("aero-slider__slide"),e.setAttribute(Q,String(r))}),n.classList.toggle("aero-slider--vertical",w()),R())n.setAttribute("dir","rtl");else n.removeAttribute("dir");n.setAttribute("data-aero-alignment",o.alignment),he(),g=be();for(let e=1;e<=g;e++)t.prepend(xe(c[O(-e)]));for(let e=0;e<g;e++)t.append(xe(c[e%a]));if(Ee(),i=L()?O(i):G(i,0,C()),y=!1,N(ee(i)),ge.build(),pe.refresh(),ze(),oe.stop(),o.autoplay)oe.start()}function Ve(e){if(u)return;Object.assign(o,e),V()}function $e(){if(!u)V()}function Xe(e,r){if(u)return;let l=r===void 0?null:c[r]??null;for(let p of[].concat(e))t.insertBefore(p,l);V()}function Ke(e){if(u)return;for(let r of[].concat(e))c[r]?.remove();V()}function Ue(){if(u)return;M("destroy",{}),u=!0,b.abort(),te.disconnect(),ne?.disconnect(),clearTimeout(B),cancelAnimationFrame(T);for(let e of t.querySelectorAll(`[${ve}]`))e.remove();t.style.scrollSnapType="",n.classList.remove("aero-slider--dragging","aero-slider--vertical","aero-slider--ready");for(let e of["aria-roledescription","dir","data-aero-alignment"])n.removeAttribute(e);if(n.aeroSlider===me)delete n.aeroSlider}let me={get element(){return n},get currentIndex(){return i},get slideCount(){return a},next:Ae,prev:we,goTo:Y,update:Ve,refresh:$e,add:Xe,remove:Ke,destroy:Ue},re={container:n,track:t,config:o,signal:d,current:()=>i,maxIndex:C,pageCount:le,loop:L,next:Ae,prev:we,goTo:Y,emit:M},pe=Ie(re),ge=He(re),oe=De(re);return ke(re),n.removeAttribute("data-aero-defer-visibility"),n.classList.add("aero-slider"),n.setAttribute("aria-roledescription","carousel"),t.classList.add("aero-slider__track"),t.addEventListener("scroll",qe,{passive:!0,signal:d}),t.addEventListener("scrollend",de,{signal:d}),t.addEventListener("wheel",()=>y=!1,{passive:!0,signal:d}),t.addEventListener("pointerdown",Fe,{signal:d}),t.addEventListener("click",Be,{capture:!0,signal:d}),t.addEventListener("dragstart",(e)=>o.draggable&&e.preventDefault(),{signal:d}),V(),n.aeroSlider=me,n.classList.add("aero-slider--ready"),M("ready",{}),me}var Oe="aero-slider__thumb--active";function Pe(n,s,t={}){let o=s.element.querySelector(".aero-slider__track");if(!o)return()=>{};let b=new AbortController,{signal:d}=b;s.update({draggable:!1,loop:t.loop??!0}),o.style.cursor="pointer";let c=(i)=>{let u=i.getAttribute(Q);if(u!==null)return Number(u);let E=Array.from(o.children),g=E.findIndex((f)=>f.hasAttribute(Q)),m=s.slideCount;return((E.indexOf(i)-g)%m+m)%m},a=(i)=>{for(let u of o.children)u.classList.toggle(Oe,c(u)===i);s.goTo(i)};return o.addEventListener("click",(i)=>{let u=i.target;while(u&&u.parentElement!==o)u=u.parentElement;if(u)n.goTo(c(u))},{signal:d}),n.element.addEventListener("aero:slideChange",(i)=>a(i.detail.index),{signal:d}),a(n.currentIndex),()=>{b.abort(),o.style.cursor="";for(let i of o.children)i.classList.remove(Oe)}}if(typeof window<"u")window.AeroSlider={createSlider:Re,syncThumbnails:Pe};export{Re as createSlider,Pe as syncThumbnails};