@preshonyee/react-horizontal-scrolling-menu
Version:
Scrolling horizontal menu component for React, support mouse and touch devices.
2 lines (1 loc) • 14.1 kB
JavaScript
import e from"react";!function(e,t){void 0===t&&(t={});var n=t.insertAt;if(e&&"undefined"!=typeof document){var r=document.head||document.getElementsByTagName("head")[0],o=document.createElement("style");o.type="text/css","top"===n&&r.firstChild?r.insertBefore(o,r.firstChild):r.appendChild(o),o.styleSheet?o.styleSheet.cssText=e:o.appendChild(document.createTextNode(e))}}(".react-horizontal-scrolling-menu--wrapper {\n display: flex;\n flex-direction: column;\n}\n\n.react-horizontal-scrolling-menu--scroll-container {\n display: flex;\n height: max-content;\n overflow-y: hidden;\n position: relative;\n width: 100%;\n}\n\n:export {\n wrapper: react-horizontal-scrolling-menu--wrapper;\n container: react-horizontal-scrolling-menu--scroll-container;\n}\n");const t="react-horizontal-scrolling-menu",n=`${t}--separator`,r=`${t}--item`,o=`${t}--scroll-container`,i=`${t}--wrapper`,l="itemId";var s=Object.freeze({__proto__:null,rootClassName:t,separatorString:"-separator",separatorClassName:n,itemClassName:r,scrollContainerClassName:o,wrapperClassName:i,id:l,dataKeyAttribute:"data-key",dataIndexAttribute:"data-index"});function a({className:t="",children:n,onScroll:r=(()=>{}),scrollRef:i}){const l=e.useMemo((()=>`${o} ${t}`),[t]);return e.createElement("div",{className:l,onScroll:r,ref:i},n)}var u=e.memo((function({className:t,id:n,index:r,refs:o}){const i=e.useRef(null);return o[r]=i,e.createElement("div",Object.assign({className:t},{"data-key":n,"data-index":r},{ref:i}))}));var c,d=e.memo((function({children:t,className:n,id:r,index:o,refs:i}){const l=e.useRef(null);return i[String(o)]=l,e.createElement("div",Object.assign({className:n},{"data-key":r,"data-index":o},{ref:l}),t)}));function v({children:t,itemClassName:o="",refs:i,separatorClassName:s=""}){const a=e.Children.toArray(t).filter(Boolean),c=a.length,v=e.useMemo((()=>`${r} ${o}`),[o]),f=e.useMemo((()=>`${n} ${s}`),[s]);return e.createElement(e.Fragment,null,a.map(((t,n)=>{var r,o;const s=null===(o=null===(r=t)||void 0===r?void 0:r.props)||void 0===o?void 0:o[l],a=s+"-separator",m=n+1===c;return[e.createElement(d,{className:v,id:s,key:"menuItem__"+s,refs:i,index:n},t),!m&&e.createElement(u,{className:f,id:a,refs:i,key:a,index:n+.1})]})))}function f(e){return"object"==typeof e&&null!=e&&1===e.nodeType}function m(e,t){return(!t||"hidden"!==e)&&"visible"!==e&&"clip"!==e}function h(e,t){if(e.clientHeight<e.scrollHeight||e.clientWidth<e.scrollWidth){var n=getComputedStyle(e,null);return m(n.overflowY,t)||m(n.overflowX,t)||function(e){var t=function(e){if(!e.ownerDocument||!e.ownerDocument.defaultView)return null;try{return e.ownerDocument.defaultView.frameElement}catch(e){return null}}(e);return!!t&&(t.clientHeight<e.scrollHeight||t.clientWidth<e.scrollWidth)}(e)}return!1}function p(e,t,n,r,o,i,l,s){return i<e&&l>t||i>e&&l<t?0:i<=e&&s<=n||l>=t&&s>=n?i-e-r:l>t&&s<n||i<e&&s>n?l-t+o:0}function g(e,t){var n=window,r=t.scrollMode,o=t.block,i=t.inline,l=t.boundary,s=t.skipOverflowHiddenElements,a="function"==typeof l?l:function(e){return e!==l};if(!f(e))throw new TypeError("Invalid target");for(var u=document.scrollingElement||document.documentElement,c=[],d=e;f(d)&&a(d);){if((d=d.parentElement)===u){c.push(d);break}null!=d&&d===document.body&&h(d)&&!h(document.documentElement)||null!=d&&h(d,s)&&c.push(d)}for(var v=n.visualViewport?n.visualViewport.width:innerWidth,m=n.visualViewport?n.visualViewport.height:innerHeight,g=window.scrollX||pageXOffset,b=window.scrollY||pageYOffset,y=e.getBoundingClientRect(),x=y.height,w=y.width,I=y.top,E=y.right,C=y.bottom,M=y.left,N="start"===o||"nearest"===o?I:"end"===o?C:I+x/2,S="center"===i?M+w/2:"end"===i?E:M,k=[],O=0;O<c.length;O++){var A=c[O],T=A.getBoundingClientRect(),W=T.height,j=T.width,$=T.top,R=T.right,V=T.bottom,B=T.left;if("if-needed"===r&&I>=0&&M>=0&&C<=m&&E<=v&&I>=$&&C<=V&&M>=B&&E<=R)return k;var L=getComputedStyle(A),H=parseInt(L.borderLeftWidth,10),D=parseInt(L.borderTopWidth,10),z=parseInt(L.borderRightWidth,10),P=parseInt(L.borderBottomWidth,10),X=0,Y=0,_="offsetWidth"in A?A.offsetWidth-A.clientWidth-H-z:0,F="offsetHeight"in A?A.offsetHeight-A.clientHeight-D-P:0;if(u===A)X="start"===o?N:"end"===o?N-m:"nearest"===o?p(b,b+m,m,D,P,b+N,b+N+x,x):N-m/2,Y="start"===i?S:"center"===i?S-v/2:"end"===i?S-v:p(g,g+v,v,H,z,g+S,g+S+w,w),X=Math.max(0,X+b),Y=Math.max(0,Y+g);else{X="start"===o?N-$-D:"end"===o?N-V+P+F:"nearest"===o?p($,V,W,D,P+F,N,N+x,x):N-($+W/2)+F/2,Y="start"===i?S-B-H:"center"===i?S-(B+j/2)+_/2:"end"===i?S-R+z+_:p(B,R,j,H,z+_,S,S+w,w);var q=A.scrollLeft,J=A.scrollTop;N+=J-(X=Math.max(0,Math.min(J+X,A.scrollHeight-W+F))),S+=q-(Y=Math.max(0,Math.min(q+Y,A.scrollWidth-j+_)))}k.push({el:A,top:X,left:Y})}return k}function b(e){return e===Object(e)&&0!==Object.keys(e).length}function y(e,t){var n=e.isConnected||e.ownerDocument.documentElement.contains(e);if(b(t)&&"function"==typeof t.behavior)return t.behavior(n?g(e,t):[]);if(n){var r=function(e){return!1===e?{block:"end",inline:"nearest"}:b(e)?e:{block:"start",inline:"nearest"}}(t);return function(e,t){void 0===t&&(t="auto");var n="scrollBehavior"in document.body.style;e.forEach((function(e){var r=e.el,o=e.top,i=e.left;r.scroll&&n?r.scroll({top:o,left:i,behavior:t}):(r.scrollTop=o,r.scrollLeft=i)}))}(g(e,r),r.behavior)}}var x=function(){return c||(c="performance"in window?performance.now.bind(performance):Date.now),c()};function w(e){var t=x(),n=Math.min((t-e.startTime)/e.duration,1),r=e.ease(n),o=e.startX+(e.x-e.startX)*r,i=e.startY+(e.y-e.startY)*r;e.method(o,i),o!==e.x||i!==e.y?requestAnimationFrame((function(){return w(e)})):e.cb()}function I(e,t,n,r,o,i){var l,s,a,u;void 0===r&&(r=600),void 0===o&&(o=function(e){return 1+--e*e*e*e*e}),l=e,s=e.scrollLeft,a=e.scrollTop,u=function(t,n){e.scrollLeft=Math.ceil(t),e.scrollTop=Math.ceil(n)},w({scrollable:l,method:u,startTime:x(),startX:s,startY:a,x:t,y:n,duration:r,ease:o,cb:i})}var E=function(e,t){var n=t||{};return function(e){return e&&!e.behavior||"smooth"===e.behavior}(n)?y(e,{block:n.block,inline:n.inline,scrollMode:n.scrollMode,boundary:n.boundary,behavior:function(e){return Promise.all(e.reduce((function(e,t){var r=t.el,o=t.left,i=t.top,l=r.scrollLeft,s=r.scrollTop;return l===o&&s===i?e:[].concat(e,[new Promise((function(e){return I(r,o,i,n.duration,n.ease,(function(){return e({el:r,left:[l,o],top:[s,i]})}))}))])}),[]))}}):Promise.resolve(y(e,t))};function C(e,t,n,r,o){var i,l;const s=(null===(l=null===(i=e)||void 0===i?void 0:i.entry)||void 0===l?void 0:l.target)||e,a=t||"smooth";return s&&E(s,Object.assign({behavior:a,inline:n||"end",block:r||"nearest",duration:500},o))}const M=e=>document.querySelector(`[data-key='${e}']`),N=e=>document.querySelector(`[data-index='${e}']`);function S(t){return e.isValidElement(t)&&t||"function"==typeof t&&e.createElement(t,null)||null}const k=e=>e.filter((e=>!new RegExp(".*-separator$").test(e))),O="undefined"!=typeof window?e.useLayoutEffect:e.useEffect;function A({items:t,itemsChanged:n,refs:r,options:o}){const i=e.useRef(),[l,s]=e.useState([]),a=e.useRef(+setTimeout((()=>{}),0)),u=e.useCallback((e=>{t.set(function(e,t){return[...e].map((e=>{var n,r;const o=e.target,i=(null===(n=null==o?void 0:o.dataset)||void 0===n?void 0:n.key)||"";return[i,{index:String((null===(r=null==o?void 0:o.dataset)||void 0===r?void 0:r.index)||""),key:i,entry:e,visible:e.intersectionRatio>=t.ratio}]}))}(e,o)),global.clearTimeout(a.current),a.current=+setTimeout((()=>global.requestAnimationFrame((()=>{s((e=>{const n=t.getVisible().map((e=>e[1].key));return JSON.stringify(e)!==JSON.stringify(n)?n:e}))}))),o.throttle)}),[t,o]);return O((()=>{const e=(e=>Object.values(e).map((e=>e.current)).filter(Boolean))(r),t=i.current||new IntersectionObserver(u,o);return i.current=t,e.forEach((e=>t.observe(e))),()=>{clearTimeout(a.current),t.disconnect(),i.current=void 0}}),[u,n,o,r]),{visibleItems:l}}function T(t,n){const[r,o]=e.useState(""),i=e.useMemo((()=>{return n=t,e.Children.toArray(n).map((e=>{var t,n;return null===(n=null===(t=e)||void 0===t?void 0:t.props)||void 0===n?void 0:n[l]})).filter(Boolean);var n}),[t]);return e.useEffect((()=>{const e=i.filter(Boolean).join("");n.toItemsWithoutSeparators().filter((e=>!i.includes(e))).forEach((e=>{var t,r;const o=(null===(t=n.last())||void 0===t?void 0:t.key)===e&&(null===(r=n.prev(e))||void 0===r?void 0:r.key)||"";n.delete(o),n.delete(`${e}-separator`),n.delete(e)})),o(e)}),[i,n]),r}class W extends Map{toArr(){return this.sort([...this])}toItems(){return this.toArr().map((([e])=>e))}toItemsWithoutSeparators(){return k(this.toItems())}toItemsKeys(){return this.toItems()}sort(e){return e.sort((([,e],[,t])=>+e.index-+t.index))}set(e,t){return Array.isArray(e)?this.sort(e).forEach((([e,t])=>{super.set(e,t)})):super.set(e,t),this}first(){var e;return null===(e=this.toArr()[0])||void 0===e?void 0:e[1]}last(){var e,t;return null===(t=null===(e=this.toArr().slice(-1))||void 0===e?void 0:e[0])||void 0===t?void 0:t[1]}filter(e){return this.toArr().filter(e)}find(e){return this.toArr().find(e)}findIndex(e){return this.toArr().findIndex(e)}prev(e){var t;const n=this.toArr(),r=n.findIndex((([t,n])=>t===e||n===e));return-1!==r?null===(t=n[r-1])||void 0===t?void 0:t[1]:void 0}next(e){var t;const n=this.toArr(),r=n.findIndex((([t,n])=>t===e||n===e));return-1!==r?null===(t=n[r+1])||void 0===t?void 0:t[1]:void 0}getVisible(){return this.filter((e=>e[1].visible))}}const j={ratio:.9,rootMargin:"5px",threshold:[.05,.5,.75,.95],throttle:100};const $=e.createContext({}),R=e=>e.reduce(((e,t)=>e.concat(t).concat(`${t}-separator`)),[]).slice(0,-1);function V(e,t){const n=k(e),r=k(t);return{prev:()=>R(function(e,t){const n=e.findIndex((e=>e===(null==t?void 0:t[0]))),r=t.length,o=n-r,i=o<0,l=i?0:o,s=e.slice(l,i?r:n);return s.length===r?s:e.slice(n,r)}(n,r)),next:()=>R(function(e,t){const n=e.findIndex((e=>{var n;return e===(null===(n=t.slice(-1))||void 0===n?void 0:n[0])})),r=t.length,o=n+r+1,i=o>e.length-1,l=i?e.length-1:o,s=e.slice(i?l-r+1:n+1,l);return s.length===r?s:e.slice(e.length-r,e.length+r)}(n,r))}}const B=e=>{var t;const n=(e=>e.filter(((e,t,n)=>{const r=0===t,o=t===n.length-1,i=new RegExp("-separator").test(e);return!((r||o)&&i)})))(e),r=n[Math.floor(n.length/2)];return{first:null==n?void 0:n[0],center:r,last:null===(t=n.slice(-1))||void 0===t?void 0:t[0]}};function L({LeftArrow:t,RightArrow:n,children:r,transitionDuration:o=500,transitionEase:l,transitionBehavior:s,onInit:u=(()=>{}),onUpdate:c=(()=>{}),onMouseDown:d,onMouseUp:f,onMouseMove:m,onScroll:h=(()=>{}),onWheel:p=(()=>{}),options:g=j,scrollContainerClassName:b="",itemClassName:y="",separatorClassName:x="",wrapperClassName:w="",prefix:I,apiRef:E={current:{}}}){const O=S(t),R=S(n),V=e.useRef(null),[B]=e.useState({}),L=e.useMemo((()=>Object.assign(Object.assign(Object.assign({},j),g),{root:V.current})),[g,V.current]),H=e.useRef(new W).current,D=T(r,H),{visibleItems:z}=A({items:H,itemsChanged:D,options:L,refs:B}),P=!!z.length,X=e.useMemo((()=>function(e,t=[],n,r){var o,i;const l=k(t),s=!!(null===(o=e.first())||void 0===o?void 0:o.visible),a=!!(null===(i=e.last())||void 0===i?void 0:i.visible),u=t=>{var n;return null===(n=e.find((e=>e[1].key===String(t))))||void 0===n?void 0:n[1]},c=()=>{var t,n;return e.prev(null===(n=null===(t=e.getVisible())||void 0===t?void 0:t[0])||void 0===n?void 0:n[1])},d=()=>{var t,n,r,o;return e.next(null===(o=null===(r=null===(n=null===(t=e.getVisible())||void 0===t?void 0:t.slice)||void 0===n?void 0:n.call(t,-1))||void 0===r?void 0:r[0])||void 0===o?void 0:o[1])};return{getItemById:u,getItemElementById:M,getItemByIndex:t=>{var n;return null===(n=e.find((e=>String(e[1].index)===String(t))))||void 0===n?void 0:n[1]},getItemElementByIndex:N,getNextItem:d,getPrevItem:c,isFirstItemVisible:s,isItemVisible:e=>t.includes(e),isLastItem:t=>e.last()===u(t),isLastItemVisible:a,scrollNext:(e,t,o,{duration:i,ease:l,boundary:s=(null==n?void 0:n.current)}={})=>{const a=null!=e?e:null==r?void 0:r.behavior;return C(d(),a,t||"start",o||"nearest",{boundary:s,duration:null!=i?i:null==r?void 0:r.duration,ease:null!=l?l:null==r?void 0:r.ease})},scrollPrev:(e,t,o,{duration:i,ease:l,boundary:s=(null==n?void 0:n.current)}={})=>{const a=null!=e?e:null==r?void 0:r.behavior;return C(c(),a,t||"end",o||"nearest",{boundary:s,duration:null!=i?i:null==r?void 0:r.duration,ease:null!=l?l:null==r?void 0:r.ease})},scrollToItem:(e,t,o,i,l)=>{var s,a;return C(e,null!=t?t:null==r?void 0:r.behavior,o,i,Object.assign(Object.assign({boundary:null==n?void 0:n.current},l),{duration:null!==(s=null==l?void 0:l.duration)&&void 0!==s?s:null==r?void 0:r.duration,ease:null!==(a=null==l?void 0:l.ease)&&void 0!==a?a:null==r?void 0:r.ease}))},visibleItems:t,visibleItemsWithoutSeparators:l}}(H,z,V,{duration:o,ease:l,behavior:s})),[H,z,D]),Y=e.useCallback((()=>Object.assign(Object.assign({},X),{initComplete:P,items:H,visibleItems:z,scrollContainer:V})),[X,P,H,z,V]),[_,F]=e.useState(Y);!function({cb:t=(()=>{}),condition:n,hash:r}){e.useEffect((()=>{n&&t()}),[r,n])}({cb:()=>c(_),condition:function({cb:t,condition:n}){const[r,o]=e.useState(!1);return e.useEffect((()=>{n&&!r&&(o(!0),t())}),[n,r]),r}({cb:()=>u(_),condition:P}),hash:JSON.stringify(z.concat(String(null==_?void 0:_.isFirstItemVisible)).concat(String(null==_?void 0:_.isLastItemVisible)))}),e.useEffect((()=>F(Y())),[Y]),E.current=_;const q=e.useCallback((e=>h(_,e)),[h,_]),J=e.useCallback((e=>p(_,e)),[p,_]),U=e.useMemo((()=>`${i} ${w}`),[w]);return e.createElement("div",{className:U,onWheel:J,onMouseDown:null==d?void 0:d(_),onMouseUp:null==f?void 0:f(_),onMouseMove:null==m?void 0:m(_)},e.createElement($.Provider,{value:_},e.createElement("div",{style:{display:"flex",justifyContent:"space-between",alignItems:"center"}},I,e.createElement("div",{style:{display:"flex",alignItems:"center"}},e.createElement("div",null,O),e.createElement("div",null," ",R))),e.createElement(a,{className:b,onScroll:q,scrollRef:V},e.createElement(v,{refs:B,itemClassName:y,separatorClassName:x},r))))}export{L as ScrollMenu,$ as VisibilityContext,s as constants,B as getItemsPos,V as slidingWindow};