@jbpark/use-hooks 1.1.3 → 2.0.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -11,7 +11,7 @@ A collection of reusable React 19 hooks for common UI and interaction patterns.
11
11
 
12
12
  ## Features
13
13
 
14
- - 📦 **10 Production-Ready Hooks** - Utilities for scrolling, viewport, storage, and more
14
+ - 📦 **11 Production-Ready Hooks** - Utilities for scrolling, viewport, storage, and more
15
15
  - 🎯 **Full TypeScript Support** - Complete type definitions for better development experience
16
16
  - ⚡ **Tree-Shakeable** - Import only what you need
17
17
  - 🔒 **SSR-Safe** - Built-in protection for window/document globals
@@ -74,6 +74,7 @@ function MyComponent() {
74
74
  | `useImage` | Preload images and expose loading/error states |
75
75
  | `useRecursiveTimeout` | Recursively schedule async/sync callbacks |
76
76
  | `useViewport` | visualViewport support with in-app mode option and debounce |
77
+ | `useDebounce` | Delay function execution to prevent excessive updates (autoInvoke support) |
77
78
 
78
79
  ## Development
79
80
 
@@ -100,6 +101,7 @@ pnpm exec prettier --write .
100
101
  src/
101
102
  ├── hooks/ # Individual hook implementations
102
103
  │ ├── useBodyScrollLock/
104
+ │ ├── useDebounce/
103
105
  │ ├── useElementPosition/
104
106
  │ ├── useElementScroll/
105
107
  │ ├── useImage/
package/dist/index.cjs CHANGED
@@ -1 +1 @@
1
- "use strict";Object.defineProperty(exports,Symbol.toStringTag,{value:"Module"});const n=require("react"),E=(e,{delay:o,autoInvoke:d=!0},t=[])=>{const s=n.useRef(null),c=n.useRef(e),i=n.useRef(t);n.useEffect(()=>{c.current=e}),n.useEffect(()=>{i.current=t});const l=n.useRef(null);l.current||(l.current=((...u)=>{s.current&&clearTimeout(s.current),s.current=setTimeout(()=>{c.current(...u)},o)}));const r=n.useRef(t);return n.useEffect(()=>{const u=!r.current||r.current.length!==t.length||r.current.some((a,w)=>a!==t[w]);u&&s.current&&clearTimeout(s.current),d&&u&&l.current&&l.current(),r.current=t}),n.useEffect(()=>()=>{s.current&&clearTimeout(s.current)},[]),l.current},S=(e=!0)=>{n.useEffect(()=>{if(!e)return;const o=window.scrollY,d=/iPad|iPhone|iPod/.test(navigator.userAgent),t={documentElement:{overflow:document.documentElement.style.overflow,height:document.documentElement.style.height,position:document.documentElement.style.position,width:document.documentElement.style.width},body:{overflow:document.body.style.overflow,height:document.body.style.height,position:document.body.style.position,top:document.body.style.top,left:document.body.style.left,right:document.body.style.right,width:document.body.style.width,webkitOverflowScrolling:document.body.style.getPropertyValue("-webkit-overflow-scrolling")}};if(document.documentElement.style.overflow="hidden",document.documentElement.style.height="100%",document.documentElement.style.position="fixed",document.documentElement.style.width="100%",document.body.style.overflow="hidden",document.body.style.height="100%",document.body.style.position="fixed",document.body.style.top=`-${o}px`,document.body.style.left="0",document.body.style.right="0",document.body.style.width="100%",d){document.body.style.setProperty("-webkit-overflow-scrolling","touch");const s=r=>{(r.target===document.body||r.target===document.documentElement||r.target===window)&&(r.preventDefault(),r.stopPropagation(),r.stopImmediatePropagation())},c=()=>{window.scrollTo(0,0),document.body.scrollTop=0,document.documentElement.scrollTop=0},i=["scroll","touchmove","touchstart","touchend"];i.forEach(r=>{window.addEventListener(r,s,{passive:!1,capture:!0}),document.addEventListener(r,s,{passive:!1,capture:!0}),document.body.addEventListener(r,s,{passive:!1,capture:!0})});const l=setInterval(c,16);return()=>{clearInterval(l),i.forEach(r=>{window.removeEventListener(r,s,{capture:!0}),document.removeEventListener(r,s,{capture:!0}),document.body.removeEventListener(r,s,{capture:!0})}),document.documentElement.style.overflow=t.documentElement.overflow,document.documentElement.style.height=t.documentElement.height,document.documentElement.style.position=t.documentElement.position,document.documentElement.style.width=t.documentElement.width,document.body.style.overflow=t.body.overflow,document.body.style.height=t.body.height,document.body.style.position=t.body.position,document.body.style.top=t.body.top,document.body.style.left=t.body.left,document.body.style.right=t.body.right,document.body.style.width=t.body.width,t.body.webkitOverflowScrolling?document.body.style.setProperty("-webkit-overflow-scrolling",t.body.webkitOverflowScrolling):document.body.style.removeProperty("-webkit-overflow-scrolling"),window.scrollTo(0,o)}}return()=>{document.documentElement.style.overflow=t.documentElement.overflow,document.documentElement.style.height=t.documentElement.height,document.documentElement.style.position=t.documentElement.position,document.documentElement.style.width=t.documentElement.width,document.body.style.overflow=t.body.overflow,document.body.style.height=t.body.height,document.body.style.position=t.body.position,document.body.style.top=t.body.top,document.body.style.left=t.body.left,document.body.style.right=t.body.right,document.body.style.width=t.body.width,window.scrollTo(0,o)}},[e])},L=e=>{const[o,d]=n.useState(null);return n.useEffect(()=>{const t=i=>typeof i=="string"?document.querySelector(i):i.current,s=()=>{const i=t(e);d(i?i.getBoundingClientRect():null)},c=()=>{requestAnimationFrame(s)};return s(),window.addEventListener("scroll",c,{passive:!0}),window.addEventListener("resize",c,{passive:!0}),()=>{window.removeEventListener("scroll",c),window.removeEventListener("resize",c)}},[e]),o},T=()=>{const[e,o]=n.useState(null),[d,t]=n.useState({scrollY:0,scrollPercentage:0,isAtTop:!0,isAtBottom:!1,scrollableHeight:0,clientHeight:0,scrollHeight:0}),s=n.useCallback(c=>{o(c)},[]);return n.useEffect(()=>{if(!e)return;const c=()=>{const{scrollTop:r,scrollHeight:u,clientHeight:a}=e,w=u-a;if(w<=0){t({scrollY:0,scrollPercentage:0,isAtTop:!0,isAtBottom:!0,scrollableHeight:0,clientHeight:a,scrollHeight:u});return}const m=Math.min(100,Math.max(0,r/w*100));t({scrollY:r,scrollPercentage:m,isAtTop:r<=0,isAtBottom:r>=w-1,scrollableHeight:w,clientHeight:a,scrollHeight:u})};c();const i=()=>{c()};e.addEventListener("scroll",i,{passive:!0});const l=new ResizeObserver(()=>{c()});return l.observe(e),()=>{e.removeEventListener("scroll",i),l.unobserve(e)}},[e]),{...d,element:e,setRef:s}},f={sm:640,md:768,lg:1024,xl:1280,"2xl":1536},x=e=>{let o="xs";return e>=f["2xl"]?o="2xl":e>=f.xl?o="xl":e>=f.lg?o="lg":e>=f.md?o="md":e>=f.sm?o="sm":o="xs",{current:o,xs:e<f.sm,sm:e>=f.sm&&e<f.md,md:e>=f.md&&e<f.lg,lg:e>=f.lg&&e<f.xl,xl:e>=f.xl&&e<f["2xl"],"2xl":e>=f["2xl"]}},R=e=>{const{delay:o=100,container:d}=e||{},[t,s]=n.useState({width:0,height:0}),[c,i]=n.useState({current:"xs",xs:!0,sm:!1,md:!1,lg:!1,xl:!1,"2xl":!1}),[l,r]=n.useState({width:0,height:0}),u=n.useRef(null),a=n.useRef(null);return E(()=>{r(t)},{delay:o},[t]),n.useEffect(()=>{const w=()=>{const h=d??u.current??document.body;if(!h)return;const{offsetWidth:y,offsetHeight:v}=h;s(p=>p.width!==y||p.height!==v?{width:y,height:v}:p),i(p=>{const b=x(y);return p.current!==b.current?b:p})},m=()=>{const h=d??u.current??document.body;h&&(w(),a.current&&a.current.disconnect(),a.current=new ResizeObserver(()=>{requestAnimationFrame(()=>{w()})}),a.current.observe(h))},g=()=>{a.current&&(a.current.disconnect(),a.current=null)};return m(),()=>{g()}},[d]),{size:l,breakpoint:c,ref:u}},V=(e,o={})=>{const{retryCount:d=0,retryDelay:t=1e3}=o,[s,c]=n.useState(!0),[i,l]=n.useState(null),[r,u]=n.useState(!1),[a,w]=n.useState(0),m=n.useCallback(()=>{if(!e){c(!1),u(!1);return}c(!0),l(null);const h=new Image;h.src=e,h.onload=()=>{c(!1),u(!0),l(null),w(0)},h.onerror=y=>{c(!1),u(!1),l(y),a<d&&setTimeout(()=>{w(v=>v+1)},t)}},[e,a,d,t]);n.useEffect(()=>{m()},[m]);const g=n.useCallback(()=>{w(0),m()},[m]);return{loading:s,error:i,loaded:r,retry:g}},z=(e,o)=>{const d=n.useRef(o),[t,s]=n.useState(()=>{if(typeof window>"u")return o;try{const i=window.localStorage.getItem(e);return i?JSON.parse(i):o}catch{return o}});n.useEffect(()=>{if(!(typeof window>"u"))try{const i=window.localStorage.getItem(e);i?s(JSON.parse(i)):window.localStorage.setItem(e,JSON.stringify(d.current))}catch(i){console.error(`Error reading localStorage key "${e}":`,i)}},[e]);const c=n.useCallback(i=>{try{s(l=>{const r=i instanceof Function?i(l):i;return localStorage.setItem(e,JSON.stringify(r)),r})}catch(l){console.error(`Error setting localStorage key "${e}":`,l)}},[e]);return[t,c]},P=(e,o)=>{const d=n.useRef(e);n.useEffect(()=>{d.current=e},[e]),n.useEffect(()=>{let t;function s(){const c=d.current();c instanceof Promise?c.then(()=>{o&&(t=setTimeout(s,o))}):o&&(t=setTimeout(s,o))}if(o)return t=setTimeout(s,o),()=>t&&clearTimeout(t)},[o])},H=e=>{const o=n.useRef([]),d=n.useCallback(s=>{if(o.current[s]&&(o.current[s].scrollIntoView({behavior:"smooth",block:"start",inline:"start",...e}),e?.offset)){const c=o.current[s].getBoundingClientRect().top+window.scrollY-e.offset;window.scrollTo({top:c,behavior:e.behavior||"smooth"})}},[e]),t=n.useCallback((s,c)=>{o.current[c]=s},[]);return{elementRefs:o,setElementRef:t,scrollToElement:d}},I=()=>{const[e,o]=n.useState({x:0,y:0,percent:{x:0,y:0}});return n.useEffect(()=>{const d=()=>{const i=window.scrollX||0,l=window.scrollY||0,r=/iPad|iPhone|iPod/.test(navigator.userAgent),u=window.visualViewport,a=r&&u?u.width:window.innerWidth,w=r&&u?u.height:window.innerHeight,m=Math.max(0,document.documentElement.scrollWidth-a),g=Math.max(0,document.documentElement.scrollHeight-w),h=m===0?0:Math.min(100,i/m*100),y=g===0?0:Math.min(100,l/g*100);o({x:i,y:l,percent:{x:Math.floor(Math.max(0,h)),y:Math.floor(Math.max(0,y))}})};d();const t=()=>{d()},s=()=>{setTimeout(d,100)},c=()=>{setTimeout(d,50)};return window.addEventListener("scroll",t,{passive:!0}),window.addEventListener("resize",s),window.addEventListener("orientationchange",s),window.visualViewport&&window.visualViewport.addEventListener("resize",c),()=>{window.removeEventListener("scroll",t),window.removeEventListener("resize",s),window.removeEventListener("orientationchange",s),window.visualViewport&&window.visualViewport.removeEventListener("resize",c)}},[]),e},k=(e={})=>{const{isInApp:o=!1,debounce:d=100}=e,[t,s]=n.useState({width:0,height:0,offsetLeft:0,offsetTop:0,pageLeft:0,pageTop:0,scale:1}),c=n.useCallback(()=>{const l=window.innerHeight,r=window.visualViewport?.height||l,u=document.documentElement.clientHeight,a=document.body.clientHeight;return window.visualViewport&&Math.abs(r-l)>100?r:Math.max(l,u,a)},[]),i=n.useCallback(()=>{if(window.visualViewport&&!o)return window.visualViewport;const l=window.visualViewport?.width||window.innerWidth,r=o?c():window.visualViewport?.height||window.innerHeight;return{width:l,height:r,offsetLeft:window.visualViewport?.offsetLeft||0,offsetTop:window.visualViewport?.offsetTop||0,pageLeft:window.scrollX??window.pageXOffset??0,pageTop:window.scrollY??window.pageYOffset??0,scale:window.visualViewport?.scale||1}},[o,c]);return n.useEffect(()=>{let l;const r=()=>{clearTimeout(l),l=setTimeout(()=>{s(i())},d)},u=()=>s(i());u();const a=["resize","orientationchange"];o&&a.push("focus","blur","touchstart","touchend"),a.forEach(m=>{m==="resize"||m==="orientationchange"?window.addEventListener(m,r):window.addEventListener(m,u,{passive:!0})}),window.visualViewport&&(window.visualViewport.addEventListener("resize",u),window.visualViewport.addEventListener("scroll",u));let w;if(o){let m=i().height;w=setInterval(()=>{const g=i().height;Math.abs(g-m)>50&&(m=g,u())},500)}return()=>{clearTimeout(l),w&&clearInterval(w),a.forEach(m=>{window.removeEventListener(m,m==="resize"||m==="orientationchange"?r:u)}),window.visualViewport&&(window.visualViewport.removeEventListener("resize",u),window.visualViewport.removeEventListener("scroll",u))}},[i,o,d]),t};exports.useBodyScrollLock=S;exports.useDebounce=E;exports.useElementPosition=L;exports.useElementScroll=T;exports.useImage=V;exports.useLocalStorage=z;exports.useRecursiveTimeout=P;exports.useResponsiveSize=R;exports.useScrollToElements=H;exports.useViewport=k;exports.useWindowScroll=I;
1
+ "use strict";Object.defineProperty(exports,Symbol.toStringTag,{value:"Module"});const n=require("react"),E=(e,{delay:o=100,autoInvoke:d=!0},t=[])=>{const s=n.useRef(null),c=n.useRef(e),i=n.useRef(t),l=n.useRef(void 0);n.useEffect(()=>{c.current=e}),n.useEffect(()=>{i.current=t});const r=n.useRef(null);return r.current||(r.current=((...u)=>{s.current&&clearTimeout(s.current),s.current=setTimeout(()=>{c.current(...u)},o)})),n.useEffect(()=>{const u=l.current===void 0||l.current.length!==t.length||l.current.some((a,w)=>a!==t[w]);u&&s.current&&clearTimeout(s.current),d&&u&&(l.current===void 0?c.current():r.current&&r.current()),l.current=t}),n.useEffect(()=>()=>{s.current&&clearTimeout(s.current)},[]),r.current},S=(e=!0)=>{n.useEffect(()=>{if(!e)return;const o=window.scrollY,d=/iPad|iPhone|iPod/.test(navigator.userAgent),t={documentElement:{overflow:document.documentElement.style.overflow,height:document.documentElement.style.height,position:document.documentElement.style.position,width:document.documentElement.style.width},body:{overflow:document.body.style.overflow,height:document.body.style.height,position:document.body.style.position,top:document.body.style.top,left:document.body.style.left,right:document.body.style.right,width:document.body.style.width,webkitOverflowScrolling:document.body.style.getPropertyValue("-webkit-overflow-scrolling")}};if(document.documentElement.style.overflow="hidden",document.documentElement.style.height="100%",document.documentElement.style.position="fixed",document.documentElement.style.width="100%",document.body.style.overflow="hidden",document.body.style.height="100%",document.body.style.position="fixed",document.body.style.top=`-${o}px`,document.body.style.left="0",document.body.style.right="0",document.body.style.width="100%",d){document.body.style.setProperty("-webkit-overflow-scrolling","touch");const s=r=>{(r.target===document.body||r.target===document.documentElement||r.target===window)&&(r.preventDefault(),r.stopPropagation(),r.stopImmediatePropagation())},c=()=>{window.scrollTo(0,0),document.body.scrollTop=0,document.documentElement.scrollTop=0},i=["scroll","touchmove","touchstart","touchend"];i.forEach(r=>{window.addEventListener(r,s,{passive:!1,capture:!0}),document.addEventListener(r,s,{passive:!1,capture:!0}),document.body.addEventListener(r,s,{passive:!1,capture:!0})});const l=setInterval(c,16);return()=>{clearInterval(l),i.forEach(r=>{window.removeEventListener(r,s,{capture:!0}),document.removeEventListener(r,s,{capture:!0}),document.body.removeEventListener(r,s,{capture:!0})}),document.documentElement.style.overflow=t.documentElement.overflow,document.documentElement.style.height=t.documentElement.height,document.documentElement.style.position=t.documentElement.position,document.documentElement.style.width=t.documentElement.width,document.body.style.overflow=t.body.overflow,document.body.style.height=t.body.height,document.body.style.position=t.body.position,document.body.style.top=t.body.top,document.body.style.left=t.body.left,document.body.style.right=t.body.right,document.body.style.width=t.body.width,t.body.webkitOverflowScrolling?document.body.style.setProperty("-webkit-overflow-scrolling",t.body.webkitOverflowScrolling):document.body.style.removeProperty("-webkit-overflow-scrolling"),window.scrollTo(0,o)}}return()=>{document.documentElement.style.overflow=t.documentElement.overflow,document.documentElement.style.height=t.documentElement.height,document.documentElement.style.position=t.documentElement.position,document.documentElement.style.width=t.documentElement.width,document.body.style.overflow=t.body.overflow,document.body.style.height=t.body.height,document.body.style.position=t.body.position,document.body.style.top=t.body.top,document.body.style.left=t.body.left,document.body.style.right=t.body.right,document.body.style.width=t.body.width,window.scrollTo(0,o)}},[e])},L=e=>{const[o,d]=n.useState(null);return n.useEffect(()=>{const t=i=>typeof i=="string"?document.querySelector(i):i.current,s=()=>{const i=t(e);d(i?i.getBoundingClientRect():null)},c=()=>{requestAnimationFrame(s)};return s(),window.addEventListener("scroll",c,{passive:!0}),window.addEventListener("resize",c,{passive:!0}),()=>{window.removeEventListener("scroll",c),window.removeEventListener("resize",c)}},[e]),o},T=()=>{const[e,o]=n.useState(null),[d,t]=n.useState({scrollY:0,scrollPercentage:0,isAtTop:!0,isAtBottom:!1,scrollableHeight:0,clientHeight:0,scrollHeight:0}),s=n.useCallback(c=>{o(c)},[]);return n.useEffect(()=>{if(!e)return;const c=()=>{const{scrollTop:r,scrollHeight:u,clientHeight:a}=e,w=u-a;if(w<=0){t({scrollY:0,scrollPercentage:0,isAtTop:!0,isAtBottom:!0,scrollableHeight:0,clientHeight:a,scrollHeight:u});return}const m=Math.min(100,Math.max(0,r/w*100));t({scrollY:r,scrollPercentage:m,isAtTop:r<=0,isAtBottom:r>=w-1,scrollableHeight:w,clientHeight:a,scrollHeight:u})};c();const i=()=>{c()};e.addEventListener("scroll",i,{passive:!0});const l=new ResizeObserver(()=>{c()});return l.observe(e),()=>{e.removeEventListener("scroll",i),l.unobserve(e)}},[e]),{...d,element:e,setRef:s}},f={sm:640,md:768,lg:1024,xl:1280,"2xl":1536},x=e=>{let o="xs";return e>=f["2xl"]?o="2xl":e>=f.xl?o="xl":e>=f.lg?o="lg":e>=f.md?o="md":e>=f.sm?o="sm":o="xs",{current:o,xs:e<f.sm,sm:e>=f.sm&&e<f.md,md:e>=f.md&&e<f.lg,lg:e>=f.lg&&e<f.xl,xl:e>=f.xl&&e<f["2xl"],"2xl":e>=f["2xl"]}},R=e=>{const{delay:o=100,container:d}=e||{},[t,s]=n.useState({width:0,height:0}),[c,i]=n.useState({current:"xs",xs:!0,sm:!1,md:!1,lg:!1,xl:!1,"2xl":!1}),[l,r]=n.useState({width:0,height:0}),u=n.useRef(null),a=n.useRef(null);return E(()=>{r(t)},{delay:o},[t]),n.useEffect(()=>{const w=()=>{const h=d??u.current??document.body;if(!h)return;const{offsetWidth:y,offsetHeight:v}=h;s(p=>p.width!==y||p.height!==v?{width:y,height:v}:p),i(p=>{const b=x(y);return p.current!==b.current?b:p})},m=()=>{const h=d??u.current??document.body;h&&(w(),a.current&&a.current.disconnect(),a.current=new ResizeObserver(()=>{requestAnimationFrame(()=>{w()})}),a.current.observe(h))},g=()=>{a.current&&(a.current.disconnect(),a.current=null)};return m(),()=>{g()}},[d]),{size:l,breakpoint:c,ref:u}},V=(e,o={})=>{const{retryCount:d=0,retryDelay:t=1e3}=o,[s,c]=n.useState(!0),[i,l]=n.useState(null),[r,u]=n.useState(!1),[a,w]=n.useState(0),m=n.useCallback(()=>{if(!e){c(!1),u(!1);return}c(!0),l(null);const h=new Image;h.src=e,h.onload=()=>{c(!1),u(!0),l(null),w(0)},h.onerror=y=>{c(!1),u(!1),l(y),a<d&&setTimeout(()=>{w(v=>v+1)},t)}},[e,a,d,t]);n.useEffect(()=>{m()},[m]);const g=n.useCallback(()=>{w(0),m()},[m]);return{loading:s,error:i,loaded:r,retry:g}},z=(e,o)=>{const d=n.useRef(o),[t,s]=n.useState(()=>{if(typeof window>"u")return o;try{const i=window.localStorage.getItem(e);return i?JSON.parse(i):o}catch{return o}});n.useEffect(()=>{if(!(typeof window>"u"))try{const i=window.localStorage.getItem(e);i?s(JSON.parse(i)):window.localStorage.setItem(e,JSON.stringify(d.current))}catch(i){console.error(`Error reading localStorage key "${e}":`,i)}},[e]);const c=n.useCallback(i=>{try{s(l=>{const r=i instanceof Function?i(l):i;return localStorage.setItem(e,JSON.stringify(r)),r})}catch(l){console.error(`Error setting localStorage key "${e}":`,l)}},[e]);return[t,c]},P=(e,o)=>{const d=n.useRef(e);n.useEffect(()=>{d.current=e},[e]),n.useEffect(()=>{let t;function s(){const c=d.current();c instanceof Promise?c.then(()=>{o&&(t=setTimeout(s,o))}):o&&(t=setTimeout(s,o))}if(o)return t=setTimeout(s,o),()=>t&&clearTimeout(t)},[o])},H=e=>{const o=n.useRef([]),d=n.useCallback(s=>{if(o.current[s]&&(o.current[s].scrollIntoView({behavior:"smooth",block:"start",inline:"start",...e}),e?.offset)){const c=o.current[s].getBoundingClientRect().top+window.scrollY-e.offset;window.scrollTo({top:c,behavior:e.behavior||"smooth"})}},[e]),t=n.useCallback((s,c)=>{o.current[c]=s},[]);return{elementRefs:o,setElementRef:t,scrollToElement:d}},I=()=>{const[e,o]=n.useState({x:0,y:0,percent:{x:0,y:0}});return n.useEffect(()=>{const d=()=>{const i=window.scrollX||0,l=window.scrollY||0,r=/iPad|iPhone|iPod/.test(navigator.userAgent),u=window.visualViewport,a=r&&u?u.width:window.innerWidth,w=r&&u?u.height:window.innerHeight,m=Math.max(0,document.documentElement.scrollWidth-a),g=Math.max(0,document.documentElement.scrollHeight-w),h=m===0?0:Math.min(100,i/m*100),y=g===0?0:Math.min(100,l/g*100);o({x:i,y:l,percent:{x:Math.floor(Math.max(0,h)),y:Math.floor(Math.max(0,y))}})};d();const t=()=>{d()},s=()=>{setTimeout(d,100)},c=()=>{setTimeout(d,50)};return window.addEventListener("scroll",t,{passive:!0}),window.addEventListener("resize",s),window.addEventListener("orientationchange",s),window.visualViewport&&window.visualViewport.addEventListener("resize",c),()=>{window.removeEventListener("scroll",t),window.removeEventListener("resize",s),window.removeEventListener("orientationchange",s),window.visualViewport&&window.visualViewport.removeEventListener("resize",c)}},[]),e},k=(e={})=>{const{isInApp:o=!1,debounce:d=100}=e,[t,s]=n.useState({width:0,height:0,offsetLeft:0,offsetTop:0,pageLeft:0,pageTop:0,scale:1}),c=n.useCallback(()=>{const l=window.innerHeight,r=window.visualViewport?.height||l,u=document.documentElement.clientHeight,a=document.body.clientHeight;return window.visualViewport&&Math.abs(r-l)>100?r:Math.max(l,u,a)},[]),i=n.useCallback(()=>{if(window.visualViewport&&!o)return window.visualViewport;const l=window.visualViewport?.width||window.innerWidth,r=o?c():window.visualViewport?.height||window.innerHeight;return{width:l,height:r,offsetLeft:window.visualViewport?.offsetLeft||0,offsetTop:window.visualViewport?.offsetTop||0,pageLeft:window.scrollX??window.pageXOffset??0,pageTop:window.scrollY??window.pageYOffset??0,scale:window.visualViewport?.scale||1}},[o,c]);return n.useEffect(()=>{let l;const r=()=>{clearTimeout(l),l=setTimeout(()=>{s(i())},d)},u=()=>s(i());u();const a=["resize","orientationchange"];o&&a.push("focus","blur","touchstart","touchend"),a.forEach(m=>{m==="resize"||m==="orientationchange"?window.addEventListener(m,r):window.addEventListener(m,u,{passive:!0})}),window.visualViewport&&(window.visualViewport.addEventListener("resize",u),window.visualViewport.addEventListener("scroll",u));let w;if(o){let m=i().height;w=setInterval(()=>{const g=i().height;Math.abs(g-m)>50&&(m=g,u())},500)}return()=>{clearTimeout(l),w&&clearInterval(w),a.forEach(m=>{window.removeEventListener(m,m==="resize"||m==="orientationchange"?r:u)}),window.visualViewport&&(window.visualViewport.removeEventListener("resize",u),window.visualViewport.removeEventListener("scroll",u))}},[i,o,d]),t};exports.useBodyScrollLock=S;exports.useDebounce=E;exports.useElementPosition=L;exports.useElementScroll=T;exports.useImage=V;exports.useLocalStorage=z;exports.useRecursiveTimeout=P;exports.useResponsiveSize=R;exports.useScrollToElements=H;exports.useViewport=k;exports.useWindowScroll=I;
package/dist/index.d.ts CHANGED
@@ -15,7 +15,7 @@ declare interface BreakpointInfo {
15
15
  declare type ElementReference<T> = string | RefObject<T>;
16
16
 
17
17
  declare interface Options {
18
- delay: number;
18
+ delay?: number;
19
19
  autoInvoke?: boolean;
20
20
  }
21
21
 
package/dist/index.js CHANGED
@@ -1,29 +1,27 @@
1
1
  import { useRef as y, useEffect as f, useState as h, useCallback as v } from "react";
2
- const x = (e, { delay: o, autoInvoke: u = !0 }, t = []) => {
3
- const n = y(null), i = y(e), s = y(t);
2
+ const x = (e, { delay: o = 100, autoInvoke: d = !0 }, t = []) => {
3
+ const n = y(null), i = y(e), s = y(t), c = y(void 0);
4
4
  f(() => {
5
5
  i.current = e;
6
6
  }), f(() => {
7
7
  s.current = t;
8
8
  });
9
- const l = y(null);
10
- l.current || (l.current = ((...c) => {
9
+ const r = y(null);
10
+ return r.current || (r.current = ((...l) => {
11
11
  n.current && clearTimeout(n.current), n.current = setTimeout(() => {
12
- i.current(...c);
12
+ i.current(...l);
13
13
  }, o);
14
- }));
15
- const r = y(t);
16
- return f(() => {
17
- const c = !r.current || r.current.length !== t.length || r.current.some((d, a) => d !== t[a]);
18
- c && n.current && clearTimeout(n.current), u && c && l.current && l.current(), r.current = t;
14
+ })), f(() => {
15
+ const l = c.current === void 0 || c.current.length !== t.length || c.current.some((u, a) => u !== t[a]);
16
+ l && n.current && clearTimeout(n.current), d && l && (c.current === void 0 ? i.current() : r.current && r.current()), c.current = t;
19
17
  }), f(() => () => {
20
18
  n.current && clearTimeout(n.current);
21
- }, []), l.current;
19
+ }, []), r.current;
22
20
  }, z = (e = !0) => {
23
21
  f(() => {
24
22
  if (!e)
25
23
  return;
26
- const o = window.scrollY, u = /iPad|iPhone|iPod/.test(navigator.userAgent), t = {
24
+ const o = window.scrollY, d = /iPad|iPhone|iPod/.test(navigator.userAgent), t = {
27
25
  documentElement: {
28
26
  overflow: document.documentElement.style.overflow,
29
27
  height: document.documentElement.style.height,
@@ -43,7 +41,7 @@ const x = (e, { delay: o, autoInvoke: u = !0 }, t = []) => {
43
41
  )
44
42
  }
45
43
  };
46
- if (document.documentElement.style.overflow = "hidden", document.documentElement.style.height = "100%", document.documentElement.style.position = "fixed", document.documentElement.style.width = "100%", document.body.style.overflow = "hidden", document.body.style.height = "100%", document.body.style.position = "fixed", document.body.style.top = `-${o}px`, document.body.style.left = "0", document.body.style.right = "0", document.body.style.width = "100%", u) {
44
+ if (document.documentElement.style.overflow = "hidden", document.documentElement.style.height = "100%", document.documentElement.style.position = "fixed", document.documentElement.style.width = "100%", document.body.style.overflow = "hidden", document.body.style.height = "100%", document.body.style.position = "fixed", document.body.style.top = `-${o}px`, document.body.style.left = "0", document.body.style.right = "0", document.body.style.width = "100%", d) {
47
45
  document.body.style.setProperty("-webkit-overflow-scrolling", "touch");
48
46
  const n = (r) => {
49
47
  (r.target === document.body || r.target === document.documentElement || r.target === window) && (r.preventDefault(), r.stopPropagation(), r.stopImmediatePropagation());
@@ -62,9 +60,9 @@ const x = (e, { delay: o, autoInvoke: u = !0 }, t = []) => {
62
60
  capture: !0
63
61
  });
64
62
  });
65
- const l = setInterval(i, 16);
63
+ const c = setInterval(i, 16);
66
64
  return () => {
67
- clearInterval(l), s.forEach((r) => {
65
+ clearInterval(c), s.forEach((r) => {
68
66
  window.removeEventListener(r, n, { capture: !0 }), document.removeEventListener(r, n, { capture: !0 }), document.body.removeEventListener(r, n, {
69
67
  capture: !0
70
68
  });
@@ -79,11 +77,11 @@ const x = (e, { delay: o, autoInvoke: u = !0 }, t = []) => {
79
77
  };
80
78
  }, [e]);
81
79
  }, H = (e) => {
82
- const [o, u] = h(null);
80
+ const [o, d] = h(null);
83
81
  return f(() => {
84
82
  const t = (s) => typeof s == "string" ? document.querySelector(s) : s.current, n = () => {
85
83
  const s = t(e);
86
- u(s ? s.getBoundingClientRect() : null);
84
+ d(s ? s.getBoundingClientRect() : null);
87
85
  }, i = () => {
88
86
  requestAnimationFrame(n);
89
87
  };
@@ -92,7 +90,7 @@ const x = (e, { delay: o, autoInvoke: u = !0 }, t = []) => {
92
90
  };
93
91
  }, [e]), o;
94
92
  }, I = () => {
95
- const [e, o] = h(null), [u, t] = h({
93
+ const [e, o] = h(null), [d, t] = h({
96
94
  scrollY: 0,
97
95
  scrollPercentage: 0,
98
96
  isAtTop: !0,
@@ -107,7 +105,7 @@ const x = (e, { delay: o, autoInvoke: u = !0 }, t = []) => {
107
105
  if (!e)
108
106
  return;
109
107
  const i = () => {
110
- const { scrollTop: r, scrollHeight: c, clientHeight: d } = e, a = c - d;
108
+ const { scrollTop: r, scrollHeight: l, clientHeight: u } = e, a = l - u;
111
109
  if (a <= 0) {
112
110
  t({
113
111
  scrollY: 0,
@@ -115,8 +113,8 @@ const x = (e, { delay: o, autoInvoke: u = !0 }, t = []) => {
115
113
  isAtTop: !0,
116
114
  isAtBottom: !0,
117
115
  scrollableHeight: 0,
118
- clientHeight: d,
119
- scrollHeight: c
116
+ clientHeight: u,
117
+ scrollHeight: l
120
118
  });
121
119
  return;
122
120
  }
@@ -130,8 +128,8 @@ const x = (e, { delay: o, autoInvoke: u = !0 }, t = []) => {
130
128
  isAtTop: r <= 0,
131
129
  isAtBottom: r >= a - 1,
132
130
  scrollableHeight: a,
133
- clientHeight: d,
134
- scrollHeight: c
131
+ clientHeight: u,
132
+ scrollHeight: l
135
133
  });
136
134
  };
137
135
  i();
@@ -139,13 +137,13 @@ const x = (e, { delay: o, autoInvoke: u = !0 }, t = []) => {
139
137
  i();
140
138
  };
141
139
  e.addEventListener("scroll", s, { passive: !0 });
142
- const l = new ResizeObserver(() => {
140
+ const c = new ResizeObserver(() => {
143
141
  i();
144
142
  });
145
- return l.observe(e), () => {
146
- e.removeEventListener("scroll", s), l.unobserve(e);
143
+ return c.observe(e), () => {
144
+ e.removeEventListener("scroll", s), c.unobserve(e);
147
145
  };
148
- }, [e]), { ...u, element: e, setRef: n };
146
+ }, [e]), { ...d, element: e, setRef: n };
149
147
  }, w = {
150
148
  // < 640px
151
149
  sm: 640,
@@ -170,7 +168,7 @@ const x = (e, { delay: o, autoInvoke: u = !0 }, t = []) => {
170
168
  "2xl": e >= w["2xl"]
171
169
  };
172
170
  }, P = (e) => {
173
- const { delay: o = 100, container: u } = e || {}, [t, n] = h({ width: 0, height: 0 }), [i, s] = h({
171
+ const { delay: o = 100, container: d } = e || {}, [t, n] = h({ width: 0, height: 0 }), [i, s] = h({
174
172
  current: "xs",
175
173
  xs: !0,
176
174
  sm: !1,
@@ -178,7 +176,7 @@ const x = (e, { delay: o, autoInvoke: u = !0 }, t = []) => {
178
176
  lg: !1,
179
177
  xl: !1,
180
178
  "2xl": !1
181
- }), [l, r] = h({ width: 0, height: 0 }), c = y(null), d = y(null);
179
+ }), [c, r] = h({ width: 0, height: 0 }), l = y(null), u = y(null);
182
180
  return x(
183
181
  () => {
184
182
  r(t);
@@ -187,7 +185,7 @@ const x = (e, { delay: o, autoInvoke: u = !0 }, t = []) => {
187
185
  [t]
188
186
  ), f(() => {
189
187
  const a = () => {
190
- const g = u ?? c.current ?? document.body;
188
+ const g = d ?? l.current ?? document.body;
191
189
  if (!g)
192
190
  return;
193
191
  const { offsetWidth: b, offsetHeight: S } = g;
@@ -196,39 +194,39 @@ const x = (e, { delay: o, autoInvoke: u = !0 }, t = []) => {
196
194
  return E.current !== L.current ? L : E;
197
195
  });
198
196
  }, m = () => {
199
- const g = u ?? c.current ?? document.body;
200
- g && (a(), d.current && d.current.disconnect(), d.current = new ResizeObserver(() => {
197
+ const g = d ?? l.current ?? document.body;
198
+ g && (a(), u.current && u.current.disconnect(), u.current = new ResizeObserver(() => {
201
199
  requestAnimationFrame(() => {
202
200
  a();
203
201
  });
204
- }), d.current.observe(g));
202
+ }), u.current.observe(g));
205
203
  }, p = () => {
206
- d.current && (d.current.disconnect(), d.current = null);
204
+ u.current && (u.current.disconnect(), u.current = null);
207
205
  };
208
206
  return m(), () => {
209
207
  p();
210
208
  };
211
- }, [u]), {
212
- size: l,
209
+ }, [d]), {
210
+ size: c,
213
211
  breakpoint: i,
214
- ref: c
212
+ ref: l
215
213
  };
216
214
  }, R = (e, o = {}) => {
217
- const { retryCount: u = 0, retryDelay: t = 1e3 } = o, [n, i] = h(!0), [s, l] = h(null), [r, c] = h(!1), [d, a] = h(0), m = v(() => {
215
+ const { retryCount: d = 0, retryDelay: t = 1e3 } = o, [n, i] = h(!0), [s, c] = h(null), [r, l] = h(!1), [u, a] = h(0), m = v(() => {
218
216
  if (!e) {
219
- i(!1), c(!1);
217
+ i(!1), l(!1);
220
218
  return;
221
219
  }
222
- i(!0), l(null);
220
+ i(!0), c(null);
223
221
  const g = new Image();
224
222
  g.src = e, g.onload = () => {
225
- i(!1), c(!0), l(null), a(0);
223
+ i(!1), l(!0), c(null), a(0);
226
224
  }, g.onerror = (b) => {
227
- i(!1), c(!1), l(b), d < u && setTimeout(() => {
225
+ i(!1), l(!1), c(b), u < d && setTimeout(() => {
228
226
  a((S) => S + 1);
229
227
  }, t);
230
228
  };
231
- }, [e, d, u, t]);
229
+ }, [e, u, d, t]);
232
230
  f(() => {
233
231
  m();
234
232
  }, [m]);
@@ -242,7 +240,7 @@ const x = (e, { delay: o, autoInvoke: u = !0 }, t = []) => {
242
240
  retry: p
243
241
  };
244
242
  }, A = (e, o) => {
245
- const u = y(o), [t, n] = h(() => {
243
+ const d = y(o), [t, n] = h(() => {
246
244
  if (typeof window > "u")
247
245
  return o;
248
246
  try {
@@ -256,7 +254,7 @@ const x = (e, { delay: o, autoInvoke: u = !0 }, t = []) => {
256
254
  if (!(typeof window > "u"))
257
255
  try {
258
256
  const s = window.localStorage.getItem(e);
259
- s ? n(JSON.parse(s)) : window.localStorage.setItem(e, JSON.stringify(u.current));
257
+ s ? n(JSON.parse(s)) : window.localStorage.setItem(e, JSON.stringify(d.current));
260
258
  } catch (s) {
261
259
  console.error(`Error reading localStorage key "${e}":`, s);
262
260
  }
@@ -264,25 +262,25 @@ const x = (e, { delay: o, autoInvoke: u = !0 }, t = []) => {
264
262
  const i = v(
265
263
  (s) => {
266
264
  try {
267
- n((l) => {
268
- const r = s instanceof Function ? s(l) : s;
265
+ n((c) => {
266
+ const r = s instanceof Function ? s(c) : s;
269
267
  return localStorage.setItem(e, JSON.stringify(r)), r;
270
268
  });
271
- } catch (l) {
272
- console.error(`Error setting localStorage key "${e}":`, l);
269
+ } catch (c) {
270
+ console.error(`Error setting localStorage key "${e}":`, c);
273
271
  }
274
272
  },
275
273
  [e]
276
274
  );
277
275
  return [t, i];
278
276
  }, O = (e, o) => {
279
- const u = y(e);
277
+ const d = y(e);
280
278
  f(() => {
281
- u.current = e;
279
+ d.current = e;
282
280
  }, [e]), f(() => {
283
281
  let t;
284
282
  function n() {
285
- const i = u.current();
283
+ const i = d.current();
286
284
  i instanceof Promise ? i.then(() => {
287
285
  o && (t = setTimeout(n, o));
288
286
  }) : o && (t = setTimeout(n, o));
@@ -291,7 +289,7 @@ const x = (e, { delay: o, autoInvoke: u = !0 }, t = []) => {
291
289
  return t = setTimeout(n, o), () => t && clearTimeout(t);
292
290
  }, [o]);
293
291
  }, M = (e) => {
294
- const o = y([]), u = v(
292
+ const o = y([]), d = v(
295
293
  (n) => {
296
294
  if (o.current[n] && (o.current[n].scrollIntoView({
297
295
  behavior: "smooth",
@@ -310,7 +308,7 @@ const x = (e, { delay: o, autoInvoke: u = !0 }, t = []) => {
310
308
  ), t = v((n, i) => {
311
309
  o.current[i] = n;
312
310
  }, []);
313
- return { elementRefs: o, setElementRef: t, scrollToElement: u };
311
+ return { elementRefs: o, setElementRef: t, scrollToElement: d };
314
312
  }, Y = () => {
315
313
  const [e, o] = h({
316
314
  x: 0,
@@ -321,30 +319,30 @@ const x = (e, { delay: o, autoInvoke: u = !0 }, t = []) => {
321
319
  }
322
320
  });
323
321
  return f(() => {
324
- const u = () => {
325
- const s = window.scrollX || 0, l = window.scrollY || 0, r = /iPad|iPhone|iPod/.test(navigator.userAgent), c = window.visualViewport, d = r && c ? c.width : window.innerWidth, a = r && c ? c.height : window.innerHeight, m = Math.max(
322
+ const d = () => {
323
+ const s = window.scrollX || 0, c = window.scrollY || 0, r = /iPad|iPhone|iPod/.test(navigator.userAgent), l = window.visualViewport, u = r && l ? l.width : window.innerWidth, a = r && l ? l.height : window.innerHeight, m = Math.max(
326
324
  0,
327
- document.documentElement.scrollWidth - d
325
+ document.documentElement.scrollWidth - u
328
326
  ), p = Math.max(
329
327
  0,
330
328
  document.documentElement.scrollHeight - a
331
- ), g = m === 0 ? 0 : Math.min(100, s / m * 100), b = p === 0 ? 0 : Math.min(100, l / p * 100);
329
+ ), g = m === 0 ? 0 : Math.min(100, s / m * 100), b = p === 0 ? 0 : Math.min(100, c / p * 100);
332
330
  o({
333
331
  x: s,
334
- y: l,
332
+ y: c,
335
333
  percent: {
336
334
  x: Math.floor(Math.max(0, g)),
337
335
  y: Math.floor(Math.max(0, b))
338
336
  }
339
337
  });
340
338
  };
341
- u();
339
+ d();
342
340
  const t = () => {
343
- u();
341
+ d();
344
342
  }, n = () => {
345
- setTimeout(u, 100);
343
+ setTimeout(d, 100);
346
344
  }, i = () => {
347
- setTimeout(u, 50);
345
+ setTimeout(d, 50);
348
346
  };
349
347
  return window.addEventListener("scroll", t, { passive: !0 }), window.addEventListener("resize", n), window.addEventListener("orientationchange", n), window.visualViewport && window.visualViewport.addEventListener("resize", i), () => {
350
348
  window.removeEventListener("scroll", t), window.removeEventListener("resize", n), window.removeEventListener("orientationchange", n), window.visualViewport && window.visualViewport.removeEventListener(
@@ -354,7 +352,7 @@ const x = (e, { delay: o, autoInvoke: u = !0 }, t = []) => {
354
352
  };
355
353
  }, []), e;
356
354
  }, C = (e = {}) => {
357
- const { isInApp: o = !1, debounce: u = 100 } = e, [t, n] = h({
355
+ const { isInApp: o = !1, debounce: d = 100 } = e, [t, n] = h({
358
356
  width: 0,
359
357
  height: 0,
360
358
  offsetLeft: 0,
@@ -363,14 +361,14 @@ const x = (e, { delay: o, autoInvoke: u = !0 }, t = []) => {
363
361
  pageTop: 0,
364
362
  scale: 1
365
363
  }), i = v(() => {
366
- const l = window.innerHeight, r = window.visualViewport?.height || l, c = document.documentElement.clientHeight, d = document.body.clientHeight;
367
- return window.visualViewport && Math.abs(r - l) > 100 ? r : Math.max(l, c, d);
364
+ const c = window.innerHeight, r = window.visualViewport?.height || c, l = document.documentElement.clientHeight, u = document.body.clientHeight;
365
+ return window.visualViewport && Math.abs(r - c) > 100 ? r : Math.max(c, l, u);
368
366
  }, []), s = v(() => {
369
367
  if (window.visualViewport && !o)
370
368
  return window.visualViewport;
371
- const l = window.visualViewport?.width || window.innerWidth, r = o ? i() : window.visualViewport?.height || window.innerHeight;
369
+ const c = window.visualViewport?.width || window.innerWidth, r = o ? i() : window.visualViewport?.height || window.innerHeight;
372
370
  return {
373
- width: l,
371
+ width: c,
374
372
  height: r,
375
373
  offsetLeft: window.visualViewport?.offsetLeft || 0,
376
374
  offsetTop: window.visualViewport?.offsetTop || 0,
@@ -380,34 +378,34 @@ const x = (e, { delay: o, autoInvoke: u = !0 }, t = []) => {
380
378
  };
381
379
  }, [o, i]);
382
380
  return f(() => {
383
- let l;
381
+ let c;
384
382
  const r = () => {
385
- clearTimeout(l), l = setTimeout(() => {
383
+ clearTimeout(c), c = setTimeout(() => {
386
384
  n(s());
387
- }, u);
388
- }, c = () => n(s());
389
- c();
390
- const d = ["resize", "orientationchange"];
391
- o && d.push("focus", "blur", "touchstart", "touchend"), d.forEach((m) => {
392
- m === "resize" || m === "orientationchange" ? window.addEventListener(m, r) : window.addEventListener(m, c, { passive: !0 });
393
- }), window.visualViewport && (window.visualViewport.addEventListener("resize", c), window.visualViewport.addEventListener("scroll", c));
385
+ }, d);
386
+ }, l = () => n(s());
387
+ l();
388
+ const u = ["resize", "orientationchange"];
389
+ o && u.push("focus", "blur", "touchstart", "touchend"), u.forEach((m) => {
390
+ m === "resize" || m === "orientationchange" ? window.addEventListener(m, r) : window.addEventListener(m, l, { passive: !0 });
391
+ }), window.visualViewport && (window.visualViewport.addEventListener("resize", l), window.visualViewport.addEventListener("scroll", l));
394
392
  let a;
395
393
  if (o) {
396
394
  let m = s().height;
397
395
  a = setInterval(() => {
398
396
  const p = s().height;
399
- Math.abs(p - m) > 50 && (m = p, c());
397
+ Math.abs(p - m) > 50 && (m = p, l());
400
398
  }, 500);
401
399
  }
402
400
  return () => {
403
- clearTimeout(l), a && clearInterval(a), d.forEach((m) => {
401
+ clearTimeout(c), a && clearInterval(a), u.forEach((m) => {
404
402
  window.removeEventListener(
405
403
  m,
406
- m === "resize" || m === "orientationchange" ? r : c
404
+ m === "resize" || m === "orientationchange" ? r : l
407
405
  );
408
- }), window.visualViewport && (window.visualViewport.removeEventListener("resize", c), window.visualViewport.removeEventListener("scroll", c));
406
+ }), window.visualViewport && (window.visualViewport.removeEventListener("resize", l), window.visualViewport.removeEventListener("scroll", l));
409
407
  };
410
- }, [s, o, u]), t;
408
+ }, [s, o, d]), t;
411
409
  };
412
410
  export {
413
411
  z as useBodyScrollLock,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@jbpark/use-hooks",
3
- "version": "1.1.3",
3
+ "version": "2.0.1",
4
4
  "description": "A collection of reusable React 19 hooks for common UI and interaction patterns",
5
5
  "keywords": [
6
6
  "react",