@jbpark/use-hooks 2.0.2 → 2.2.0

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.
Files changed (50) hide show
  1. package/README.ko.md +18 -10
  2. package/README.md +16 -10
  3. package/dist/hooks/index.d.mts +13 -0
  4. package/dist/hooks/index.mjs +15 -0
  5. package/dist/hooks/useBodyScrollLock/index.d.mts +5 -0
  6. package/dist/hooks/useBodyScrollLock/index.mjs +115 -0
  7. package/dist/hooks/useBodyScrollLock/index.mjs.map +1 -0
  8. package/dist/hooks/useDebounce/index.d.mts +12 -0
  9. package/dist/hooks/useDebounce/index.mjs +41 -0
  10. package/dist/hooks/useDebounce/index.mjs.map +1 -0
  11. package/dist/hooks/useElementPosition/index.d.mts +8 -0
  12. package/dist/hooks/useElementPosition/index.mjs +32 -0
  13. package/dist/hooks/useElementPosition/index.mjs.map +1 -0
  14. package/dist/hooks/useElementScroll/index.d.mts +15 -0
  15. package/dist/hooks/useElementScroll/index.mjs +68 -0
  16. package/dist/hooks/useElementScroll/index.mjs.map +1 -0
  17. package/dist/hooks/useImage/index.d.mts +14 -0
  18. package/dist/hooks/useImage/index.mjs +56 -0
  19. package/dist/hooks/useImage/index.mjs.map +1 -0
  20. package/dist/hooks/useLocalStorage/index.d.mts +5 -0
  21. package/dist/hooks/useLocalStorage/index.mjs +40 -0
  22. package/dist/hooks/useLocalStorage/index.mjs.map +1 -0
  23. package/dist/hooks/useRecursiveTimeout/index.d.mts +5 -0
  24. package/dist/hooks/useRecursiveTimeout/index.mjs +27 -0
  25. package/dist/hooks/useRecursiveTimeout/index.mjs.map +1 -0
  26. package/dist/hooks/useResponsiveSize/index.d.mts +26 -0
  27. package/dist/hooks/useResponsiveSize/index.mjs +108 -0
  28. package/dist/hooks/useResponsiveSize/index.mjs.map +1 -0
  29. package/dist/hooks/useScrollToElements/index.d.mts +14 -0
  30. package/dist/hooks/useScrollToElements/index.mjs +34 -0
  31. package/dist/hooks/useScrollToElements/index.mjs.map +1 -0
  32. package/dist/hooks/useThrottle/index.d.mts +5 -0
  33. package/dist/hooks/useThrottle/index.mjs +42 -0
  34. package/dist/hooks/useThrottle/index.mjs.map +1 -0
  35. package/dist/hooks/useTimeline/index.d.mts +50 -0
  36. package/dist/hooks/useTimeline/index.mjs +175 -0
  37. package/dist/hooks/useTimeline/index.mjs.map +1 -0
  38. package/dist/hooks/useViewport/index.d.mts +18 -0
  39. package/dist/hooks/useViewport/index.mjs +87 -0
  40. package/dist/hooks/useViewport/index.mjs.map +1 -0
  41. package/dist/hooks/useWindowScroll/index.d.mts +12 -0
  42. package/dist/hooks/useWindowScroll/index.mjs +60 -0
  43. package/dist/hooks/useWindowScroll/index.mjs.map +1 -0
  44. package/dist/index.d.mts +15 -0
  45. package/dist/index.mjs +16 -0
  46. package/package.json +7 -8
  47. package/dist/index.cjs +0 -1
  48. package/dist/index.d.ts +0 -106
  49. package/dist/index.js +0 -424
  50. package/dist/vite.svg +0 -1
@@ -0,0 +1,87 @@
1
+ import { useCallback, useEffect, useState } from "react";
2
+
3
+ //#region src/hooks/useViewport/index.ts
4
+ const useViewport = (options = {}) => {
5
+ const { isInApp = false, debounce = 100 } = options;
6
+ const [viewport, setViewport] = useState({
7
+ width: 0,
8
+ height: 0,
9
+ offsetLeft: 0,
10
+ offsetTop: 0,
11
+ pageLeft: 0,
12
+ pageTop: 0,
13
+ scale: 1
14
+ });
15
+ const getAppViewHeight = useCallback(() => {
16
+ const windowHeight = window.innerHeight;
17
+ const visualHeight = window.visualViewport?.height || windowHeight;
18
+ const documentHeight = document.documentElement.clientHeight;
19
+ const bodyHeight = document.body.clientHeight;
20
+ if (window.visualViewport && Math.abs(visualHeight - windowHeight) > 100) return visualHeight;
21
+ return Math.max(windowHeight, documentHeight, bodyHeight);
22
+ }, []);
23
+ const readViewport = useCallback(() => {
24
+ if (window.visualViewport && !isInApp) return window.visualViewport;
25
+ return {
26
+ width: window.visualViewport?.width || window.innerWidth,
27
+ height: isInApp ? getAppViewHeight() : window.visualViewport?.height || window.innerHeight,
28
+ offsetLeft: window.visualViewport?.offsetLeft || 0,
29
+ offsetTop: window.visualViewport?.offsetTop || 0,
30
+ pageLeft: window.scrollX ?? window.pageXOffset ?? 0,
31
+ pageTop: window.scrollY ?? window.pageYOffset ?? 0,
32
+ scale: window.visualViewport?.scale || 1
33
+ };
34
+ }, [isInApp, getAppViewHeight]);
35
+ useEffect(() => {
36
+ let timeoutId;
37
+ const debouncedUpdate = () => {
38
+ clearTimeout(timeoutId);
39
+ timeoutId = setTimeout(() => {
40
+ setViewport(readViewport());
41
+ }, debounce);
42
+ };
43
+ const immediateUpdate = () => setViewport(readViewport());
44
+ immediateUpdate();
45
+ const events = ["resize", "orientationchange"];
46
+ if (isInApp) events.push("focus", "blur", "touchstart", "touchend");
47
+ events.forEach((event) => {
48
+ if (event === "resize" || event === "orientationchange") window.addEventListener(event, debouncedUpdate);
49
+ else window.addEventListener(event, immediateUpdate, { passive: true });
50
+ });
51
+ if (window.visualViewport) {
52
+ window.visualViewport.addEventListener("resize", immediateUpdate);
53
+ window.visualViewport.addEventListener("scroll", immediateUpdate);
54
+ }
55
+ let intervalId;
56
+ if (isInApp) {
57
+ let lastHeight = readViewport().height;
58
+ intervalId = setInterval(() => {
59
+ const currentHeight = readViewport().height;
60
+ if (Math.abs(currentHeight - lastHeight) > 50) {
61
+ lastHeight = currentHeight;
62
+ immediateUpdate();
63
+ }
64
+ }, 500);
65
+ }
66
+ return () => {
67
+ clearTimeout(timeoutId);
68
+ if (intervalId) clearInterval(intervalId);
69
+ events.forEach((event) => {
70
+ window.removeEventListener(event, event === "resize" || event === "orientationchange" ? debouncedUpdate : immediateUpdate);
71
+ });
72
+ if (window.visualViewport) {
73
+ window.visualViewport.removeEventListener("resize", immediateUpdate);
74
+ window.visualViewport.removeEventListener("scroll", immediateUpdate);
75
+ }
76
+ };
77
+ }, [
78
+ readViewport,
79
+ isInApp,
80
+ debounce
81
+ ]);
82
+ return viewport;
83
+ };
84
+
85
+ //#endregion
86
+ export { useViewport as default };
87
+ //# sourceMappingURL=index.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.mjs","names":[],"sources":["../../../src/hooks/useViewport/index.ts"],"sourcesContent":["import { useCallback, useEffect, useState } from 'react';\n\ntype ViewportInfo =\n | VisualViewport\n | {\n width: number;\n height: number;\n offsetLeft: number;\n offsetTop: number;\n pageLeft: number;\n pageTop: number;\n scale: number;\n };\n\ninterface Options {\n isInApp?: boolean;\n debounce?: number;\n}\n\nconst useViewport = (options: Options = {}) => {\n const { isInApp = false, debounce = 100 } = options;\n\n const [viewport, setViewport] = useState<ViewportInfo>({\n width: 0,\n height: 0,\n offsetLeft: 0,\n offsetTop: 0,\n pageLeft: 0,\n pageTop: 0,\n scale: 1,\n });\n\n const getAppViewHeight = useCallback(() => {\n const windowHeight = window.innerHeight;\n const visualHeight = window.visualViewport?.height || windowHeight;\n const documentHeight = document.documentElement.clientHeight;\n const bodyHeight = document.body.clientHeight;\n\n if (window.visualViewport && Math.abs(visualHeight - windowHeight) > 100) {\n return visualHeight;\n }\n\n return Math.max(windowHeight, documentHeight, bodyHeight);\n }, []);\n\n const readViewport = useCallback((): ViewportInfo => {\n if (window.visualViewport && !isInApp) {\n return window.visualViewport;\n }\n\n const width = window.visualViewport?.width || window.innerWidth;\n const height = isInApp\n ? getAppViewHeight()\n : window.visualViewport?.height || window.innerHeight;\n\n return {\n width,\n height,\n offsetLeft: window.visualViewport?.offsetLeft || 0,\n offsetTop: window.visualViewport?.offsetTop || 0,\n pageLeft: window.scrollX ?? window.pageXOffset ?? 0,\n pageTop: window.scrollY ?? window.pageYOffset ?? 0,\n scale: window.visualViewport?.scale || 1,\n };\n }, [isInApp, getAppViewHeight]);\n\n useEffect(() => {\n let timeoutId: NodeJS.Timeout;\n\n const debouncedUpdate = () => {\n clearTimeout(timeoutId);\n timeoutId = setTimeout(() => {\n setViewport(readViewport());\n }, debounce);\n };\n\n const immediateUpdate = () => setViewport(readViewport());\n\n immediateUpdate();\n\n const events = ['resize', 'orientationchange'];\n\n if (isInApp) {\n events.push('focus', 'blur', 'touchstart', 'touchend');\n }\n\n events.forEach(event => {\n if (event === 'resize' || event === 'orientationchange') {\n window.addEventListener(event, debouncedUpdate);\n } else {\n window.addEventListener(event, immediateUpdate, { passive: true });\n }\n });\n\n if (window.visualViewport) {\n window.visualViewport.addEventListener('resize', immediateUpdate);\n window.visualViewport.addEventListener('scroll', immediateUpdate);\n }\n\n let intervalId: NodeJS.Timeout;\n if (isInApp) {\n let lastHeight = readViewport().height;\n intervalId = setInterval(() => {\n const currentHeight = readViewport().height;\n if (Math.abs(currentHeight - lastHeight) > 50) {\n lastHeight = currentHeight;\n immediateUpdate();\n }\n }, 500);\n }\n\n return () => {\n clearTimeout(timeoutId);\n if (intervalId) clearInterval(intervalId);\n\n events.forEach(event => {\n window.removeEventListener(\n event,\n event === 'resize' || event === 'orientationchange'\n ? debouncedUpdate\n : immediateUpdate,\n );\n });\n\n if (window.visualViewport) {\n window.visualViewport.removeEventListener('resize', immediateUpdate);\n window.visualViewport.removeEventListener('scroll', immediateUpdate);\n }\n };\n }, [readViewport, isInApp, debounce]);\n\n return viewport;\n};\n\nexport default useViewport;\n"],"mappings":";;;AAmBA,MAAM,eAAe,UAAmB,EAAE,KAAK;CAC7C,MAAM,EAAE,UAAU,OAAO,WAAW,QAAQ;CAE5C,MAAM,CAAC,UAAU,eAAe,SAAuB;EACrD,OAAO;EACP,QAAQ;EACR,YAAY;EACZ,WAAW;EACX,UAAU;EACV,SAAS;EACT,OAAO;EACR,CAAC;CAEF,MAAM,mBAAmB,kBAAkB;EACzC,MAAM,eAAe,OAAO;EAC5B,MAAM,eAAe,OAAO,gBAAgB,UAAU;EACtD,MAAM,iBAAiB,SAAS,gBAAgB;EAChD,MAAM,aAAa,SAAS,KAAK;AAEjC,MAAI,OAAO,kBAAkB,KAAK,IAAI,eAAe,aAAa,GAAG,IACnE,QAAO;AAGT,SAAO,KAAK,IAAI,cAAc,gBAAgB,WAAW;IACxD,EAAE,CAAC;CAEN,MAAM,eAAe,kBAAgC;AACnD,MAAI,OAAO,kBAAkB,CAAC,QAC5B,QAAO,OAAO;AAQhB,SAAO;GACL,OANY,OAAO,gBAAgB,SAAS,OAAO;GAOnD,QANa,UACX,kBAAkB,GAClB,OAAO,gBAAgB,UAAU,OAAO;GAK1C,YAAY,OAAO,gBAAgB,cAAc;GACjD,WAAW,OAAO,gBAAgB,aAAa;GAC/C,UAAU,OAAO,WAAW,OAAO,eAAe;GAClD,SAAS,OAAO,WAAW,OAAO,eAAe;GACjD,OAAO,OAAO,gBAAgB,SAAS;GACxC;IACA,CAAC,SAAS,iBAAiB,CAAC;AAE/B,iBAAgB;EACd,IAAI;EAEJ,MAAM,wBAAwB;AAC5B,gBAAa,UAAU;AACvB,eAAY,iBAAiB;AAC3B,gBAAY,cAAc,CAAC;MAC1B,SAAS;;EAGd,MAAM,wBAAwB,YAAY,cAAc,CAAC;AAEzD,mBAAiB;EAEjB,MAAM,SAAS,CAAC,UAAU,oBAAoB;AAE9C,MAAI,QACF,QAAO,KAAK,SAAS,QAAQ,cAAc,WAAW;AAGxD,SAAO,SAAQ,UAAS;AACtB,OAAI,UAAU,YAAY,UAAU,oBAClC,QAAO,iBAAiB,OAAO,gBAAgB;OAE/C,QAAO,iBAAiB,OAAO,iBAAiB,EAAE,SAAS,MAAM,CAAC;IAEpE;AAEF,MAAI,OAAO,gBAAgB;AACzB,UAAO,eAAe,iBAAiB,UAAU,gBAAgB;AACjE,UAAO,eAAe,iBAAiB,UAAU,gBAAgB;;EAGnE,IAAI;AACJ,MAAI,SAAS;GACX,IAAI,aAAa,cAAc,CAAC;AAChC,gBAAa,kBAAkB;IAC7B,MAAM,gBAAgB,cAAc,CAAC;AACrC,QAAI,KAAK,IAAI,gBAAgB,WAAW,GAAG,IAAI;AAC7C,kBAAa;AACb,sBAAiB;;MAElB,IAAI;;AAGT,eAAa;AACX,gBAAa,UAAU;AACvB,OAAI,WAAY,eAAc,WAAW;AAEzC,UAAO,SAAQ,UAAS;AACtB,WAAO,oBACL,OACA,UAAU,YAAY,UAAU,sBAC5B,kBACA,gBACL;KACD;AAEF,OAAI,OAAO,gBAAgB;AACzB,WAAO,eAAe,oBAAoB,UAAU,gBAAgB;AACpE,WAAO,eAAe,oBAAoB,UAAU,gBAAgB;;;IAGvE;EAAC;EAAc;EAAS;EAAS,CAAC;AAErC,QAAO"}
@@ -0,0 +1,12 @@
1
+ //#region src/hooks/useWindowScroll/index.d.ts
2
+ declare const useWindowScroll: () => {
3
+ x: number;
4
+ y: number;
5
+ percent: {
6
+ x: number;
7
+ y: number;
8
+ };
9
+ };
10
+ //#endregion
11
+ export { useWindowScroll };
12
+ //# sourceMappingURL=index.d.mts.map
@@ -0,0 +1,60 @@
1
+ import { useEffect, useState } from "react";
2
+
3
+ //#region src/hooks/useWindowScroll/index.ts
4
+ const useWindowScroll = () => {
5
+ const [state, setState] = useState({
6
+ x: 0,
7
+ y: 0,
8
+ percent: {
9
+ x: 0,
10
+ y: 0
11
+ }
12
+ });
13
+ useEffect(() => {
14
+ const calculate = () => {
15
+ const x = window.scrollX || 0;
16
+ const y = window.scrollY || 0;
17
+ const isIOS = /iPad|iPhone|iPod/.test(navigator.userAgent);
18
+ const visualViewport = window.visualViewport;
19
+ const viewportWidth = isIOS && visualViewport ? visualViewport.width : window.innerWidth;
20
+ const viewportHeight = isIOS && visualViewport ? visualViewport.height : window.innerHeight;
21
+ const maxScrollX = Math.max(0, document.documentElement.scrollWidth - viewportWidth);
22
+ const maxScrollY = Math.max(0, document.documentElement.scrollHeight - viewportHeight);
23
+ const percentX = maxScrollX === 0 ? 0 : Math.min(100, x / maxScrollX * 100);
24
+ const percentY = maxScrollY === 0 ? 0 : Math.min(100, y / maxScrollY * 100);
25
+ setState({
26
+ x,
27
+ y,
28
+ percent: {
29
+ x: Math.floor(Math.max(0, percentX)),
30
+ y: Math.floor(Math.max(0, percentY))
31
+ }
32
+ });
33
+ };
34
+ calculate();
35
+ const onScroll = () => {
36
+ calculate();
37
+ };
38
+ const onResize = () => {
39
+ setTimeout(calculate, 100);
40
+ };
41
+ const onVisualViewportChange = () => {
42
+ setTimeout(calculate, 50);
43
+ };
44
+ window.addEventListener("scroll", onScroll, { passive: true });
45
+ window.addEventListener("resize", onResize);
46
+ window.addEventListener("orientationchange", onResize);
47
+ if (window.visualViewport) window.visualViewport.addEventListener("resize", onVisualViewportChange);
48
+ return () => {
49
+ window.removeEventListener("scroll", onScroll);
50
+ window.removeEventListener("resize", onResize);
51
+ window.removeEventListener("orientationchange", onResize);
52
+ if (window.visualViewport) window.visualViewport.removeEventListener("resize", onVisualViewportChange);
53
+ };
54
+ }, []);
55
+ return state;
56
+ };
57
+
58
+ //#endregion
59
+ export { useWindowScroll as default };
60
+ //# sourceMappingURL=index.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.mjs","names":[],"sources":["../../../src/hooks/useWindowScroll/index.ts"],"sourcesContent":["import { useEffect, useState } from 'react';\n\nconst useWindowScroll = () => {\n const [state, setState] = useState({\n x: 0,\n y: 0,\n percent: {\n x: 0,\n y: 0,\n },\n });\n\n useEffect(() => {\n const calculate = () => {\n const x = window.scrollX || 0;\n const y = window.scrollY || 0;\n\n const isIOS = /iPad|iPhone|iPod/.test(navigator.userAgent);\n const visualViewport = window.visualViewport;\n\n const viewportWidth =\n isIOS && visualViewport ? visualViewport.width : window.innerWidth;\n\n const viewportHeight =\n isIOS && visualViewport ? visualViewport.height : window.innerHeight;\n\n const maxScrollX = Math.max(\n 0,\n document.documentElement.scrollWidth - viewportWidth,\n );\n const maxScrollY = Math.max(\n 0,\n document.documentElement.scrollHeight - viewportHeight,\n );\n\n const percentX =\n maxScrollX === 0 ? 0 : Math.min(100, (x / maxScrollX) * 100);\n const percentY =\n maxScrollY === 0 ? 0 : Math.min(100, (y / maxScrollY) * 100);\n\n setState({\n x,\n y,\n percent: {\n x: Math.floor(Math.max(0, percentX)),\n y: Math.floor(Math.max(0, percentY)),\n },\n });\n };\n\n calculate();\n\n const onScroll = () => {\n calculate();\n };\n\n const onResize = () => {\n setTimeout(calculate, 100);\n };\n\n const onVisualViewportChange = () => {\n setTimeout(calculate, 50);\n };\n\n window.addEventListener('scroll', onScroll, { passive: true });\n window.addEventListener('resize', onResize);\n window.addEventListener('orientationchange', onResize);\n\n if (window.visualViewport) {\n window.visualViewport.addEventListener('resize', onVisualViewportChange);\n }\n\n return () => {\n window.removeEventListener('scroll', onScroll);\n window.removeEventListener('resize', onResize);\n window.removeEventListener('orientationchange', onResize);\n\n if (window.visualViewport) {\n window.visualViewport.removeEventListener(\n 'resize',\n onVisualViewportChange,\n );\n }\n };\n }, []);\n\n return state;\n};\n\nexport default useWindowScroll;\n"],"mappings":";;;AAEA,MAAM,wBAAwB;CAC5B,MAAM,CAAC,OAAO,YAAY,SAAS;EACjC,GAAG;EACH,GAAG;EACH,SAAS;GACP,GAAG;GACH,GAAG;GACJ;EACF,CAAC;AAEF,iBAAgB;EACd,MAAM,kBAAkB;GACtB,MAAM,IAAI,OAAO,WAAW;GAC5B,MAAM,IAAI,OAAO,WAAW;GAE5B,MAAM,QAAQ,mBAAmB,KAAK,UAAU,UAAU;GAC1D,MAAM,iBAAiB,OAAO;GAE9B,MAAM,gBACJ,SAAS,iBAAiB,eAAe,QAAQ,OAAO;GAE1D,MAAM,iBACJ,SAAS,iBAAiB,eAAe,SAAS,OAAO;GAE3D,MAAM,aAAa,KAAK,IACtB,GACA,SAAS,gBAAgB,cAAc,cACxC;GACD,MAAM,aAAa,KAAK,IACtB,GACA,SAAS,gBAAgB,eAAe,eACzC;GAED,MAAM,WACJ,eAAe,IAAI,IAAI,KAAK,IAAI,KAAM,IAAI,aAAc,IAAI;GAC9D,MAAM,WACJ,eAAe,IAAI,IAAI,KAAK,IAAI,KAAM,IAAI,aAAc,IAAI;AAE9D,YAAS;IACP;IACA;IACA,SAAS;KACP,GAAG,KAAK,MAAM,KAAK,IAAI,GAAG,SAAS,CAAC;KACpC,GAAG,KAAK,MAAM,KAAK,IAAI,GAAG,SAAS,CAAC;KACrC;IACF,CAAC;;AAGJ,aAAW;EAEX,MAAM,iBAAiB;AACrB,cAAW;;EAGb,MAAM,iBAAiB;AACrB,cAAW,WAAW,IAAI;;EAG5B,MAAM,+BAA+B;AACnC,cAAW,WAAW,GAAG;;AAG3B,SAAO,iBAAiB,UAAU,UAAU,EAAE,SAAS,MAAM,CAAC;AAC9D,SAAO,iBAAiB,UAAU,SAAS;AAC3C,SAAO,iBAAiB,qBAAqB,SAAS;AAEtD,MAAI,OAAO,eACT,QAAO,eAAe,iBAAiB,UAAU,uBAAuB;AAG1E,eAAa;AACX,UAAO,oBAAoB,UAAU,SAAS;AAC9C,UAAO,oBAAoB,UAAU,SAAS;AAC9C,UAAO,oBAAoB,qBAAqB,SAAS;AAEzD,OAAI,OAAO,eACT,QAAO,eAAe,oBACpB,UACA,uBACD;;IAGJ,EAAE,CAAC;AAEN,QAAO"}
@@ -0,0 +1,15 @@
1
+ import { useDebounce } from "./hooks/useDebounce/index.mjs";
2
+ import { useBodyScrollLock } from "./hooks/useBodyScrollLock/index.mjs";
3
+ import { useElementPosition } from "./hooks/useElementPosition/index.mjs";
4
+ import { useElementScroll } from "./hooks/useElementScroll/index.mjs";
5
+ import { useResponsiveSize } from "./hooks/useResponsiveSize/index.mjs";
6
+ import { useImage } from "./hooks/useImage/index.mjs";
7
+ import { useLocalStorage } from "./hooks/useLocalStorage/index.mjs";
8
+ import { useRecursiveTimeout } from "./hooks/useRecursiveTimeout/index.mjs";
9
+ import { useScrollToElements } from "./hooks/useScrollToElements/index.mjs";
10
+ import { useThrottle } from "./hooks/useThrottle/index.mjs";
11
+ import { useTimeline } from "./hooks/useTimeline/index.mjs";
12
+ import { useWindowScroll } from "./hooks/useWindowScroll/index.mjs";
13
+ import { useViewport } from "./hooks/useViewport/index.mjs";
14
+ import "./hooks/index.mjs";
15
+ export { useBodyScrollLock, useDebounce, useElementPosition, useElementScroll, useImage, useLocalStorage, useRecursiveTimeout, useResponsiveSize, useScrollToElements, useThrottle, useTimeline, useViewport, useWindowScroll };
package/dist/index.mjs ADDED
@@ -0,0 +1,16 @@
1
+ import useDebounce from "./hooks/useDebounce/index.mjs";
2
+ import useBodyScrollLock from "./hooks/useBodyScrollLock/index.mjs";
3
+ import useElementPosition from "./hooks/useElementPosition/index.mjs";
4
+ import useElementScroll from "./hooks/useElementScroll/index.mjs";
5
+ import useResponsiveSize from "./hooks/useResponsiveSize/index.mjs";
6
+ import useImage from "./hooks/useImage/index.mjs";
7
+ import useLocalStorage from "./hooks/useLocalStorage/index.mjs";
8
+ import useRecursiveTimeout from "./hooks/useRecursiveTimeout/index.mjs";
9
+ import useScrollToElements from "./hooks/useScrollToElements/index.mjs";
10
+ import useThrottle from "./hooks/useThrottle/index.mjs";
11
+ import useTimeline from "./hooks/useTimeline/index.mjs";
12
+ import useWindowScroll from "./hooks/useWindowScroll/index.mjs";
13
+ import useViewport from "./hooks/useViewport/index.mjs";
14
+ import "./hooks/index.mjs";
15
+
16
+ export { useBodyScrollLock, useDebounce, useElementPosition, useElementScroll, useImage, useLocalStorage, useRecursiveTimeout, useResponsiveSize, useScrollToElements, useThrottle, useTimeline, useViewport, useWindowScroll };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@jbpark/use-hooks",
3
- "version": "2.0.2",
3
+ "version": "2.2.0",
4
4
  "description": "A collection of reusable React 19 hooks for common UI and interaction patterns",
5
5
  "keywords": [
6
6
  "react",
@@ -22,15 +22,13 @@
22
22
  },
23
23
  "private": false,
24
24
  "type": "module",
25
- "main": "./dist/index.cjs",
26
- "module": "./dist/index.js",
27
- "types": "./dist/index.d.ts",
25
+ "main": "./dist/index.mjs",
26
+ "types": "./dist/index.d.mts",
28
27
  "exports": {
29
28
  ".": {
30
29
  "source": "./src/index.ts",
31
- "types": "./dist/index.d.ts",
32
- "import": "./dist/index.js",
33
- "require": "./dist/index.cjs"
30
+ "types": "./dist/index.d.mts",
31
+ "import": "./dist/index.mjs"
34
32
  }
35
33
  },
36
34
  "files": [
@@ -60,6 +58,7 @@
60
58
  "prettier-plugin-tailwindcss": "^0.6.14",
61
59
  "react": "^19.1.1",
62
60
  "react-dom": "^19.1.1",
61
+ "tsdown": "^0.20.3",
63
62
  "typescript": "~5.8.3",
64
63
  "typescript-eslint": "^8.39.1",
65
64
  "vite": "^7.1.2",
@@ -74,7 +73,7 @@
74
73
  },
75
74
  "scripts": {
76
75
  "dev": "vite",
77
- "build": "tsc -b && vite build",
76
+ "build": "tsc -b && tsdown",
78
77
  "lint": "eslint .",
79
78
  "preview": "vite preview",
80
79
  "changeset": "changeset"
package/dist/index.cjs DELETED
@@ -1 +0,0 @@
1
- "use strict";Object.defineProperty(exports,Symbol.toStringTag,{value:"Module"});const n=require("react"),L=(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((w,a)=>w!==t[a]);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},T=(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])},x=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},R=()=>{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:w}=e,a=u-w;if(a<=0){t({scrollY:0,scrollPercentage:0,isAtTop:!0,isAtBottom:!0,scrollableHeight:0,clientHeight:w,scrollHeight:u});return}const m=Math.min(100,Math.max(0,r/a*100));t({scrollY:r,scrollPercentage:m,isAtTop:r<=0,isAtBottom:r>=a-1,scrollableHeight:a,clientHeight:w,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},V=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"]}},z=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,w]=n.useState(null),a=n.useRef(null),m=n.useCallback(h=>{w(h)},[]);return L(()=>{r(t)},{delay:o},[t]),n.useEffect(()=>{const h=()=>{const y=d??u??document.body;if(!y)return;const{offsetWidth:b,offsetHeight:E}=y;s(p=>p.width!==b||p.height!==E?{width:b,height:E}:p),i(p=>{const S=V(b);return p.current!==S.current?S:p})},g=()=>{const y=d??u??document.body;y&&(h(),a.current&&a.current.disconnect(),a.current=new ResizeObserver(()=>{requestAnimationFrame(()=>{h()})}),a.current.observe(y))},v=()=>{a.current&&(a.current.disconnect(),a.current=null)};return g(),()=>{v()}},[d,u]),{size:l,breakpoint:c,ref:m}},P=(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),[w,a]=n.useState(0),m=n.useCallback(()=>{if(!e){c(!1),u(!1);return}c(!0),l(null);const g=new Image;g.src=e,g.onload=()=>{c(!1),u(!0),l(null),a(0)},g.onerror=v=>{c(!1),u(!1),l(v),w<d&&setTimeout(()=>{a(y=>y+1)},t)}},[e,w,d,t]);n.useEffect(()=>{m()},[m]);const h=n.useCallback(()=>{a(0),m()},[m]);return{loading:s,error:i,loaded:r,retry:h}},H=(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]},I=(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])},k=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}},C=()=>{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,w=r&&u?u.width:window.innerWidth,a=r&&u?u.height:window.innerHeight,m=Math.max(0,document.documentElement.scrollWidth-w),h=Math.max(0,document.documentElement.scrollHeight-a),g=m===0?0:Math.min(100,i/m*100),v=h===0?0:Math.min(100,l/h*100);o({x:i,y:l,percent:{x:Math.floor(Math.max(0,g)),y:Math.floor(Math.max(0,v))}})};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},O=(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,w=document.body.clientHeight;return window.visualViewport&&Math.abs(r-l)>100?r:Math.max(l,u,w)},[]),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 w=["resize","orientationchange"];o&&w.push("focus","blur","touchstart","touchend"),w.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 a;if(o){let m=i().height;a=setInterval(()=>{const h=i().height;Math.abs(h-m)>50&&(m=h,u())},500)}return()=>{clearTimeout(l),a&&clearInterval(a),w.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=T;exports.useDebounce=L;exports.useElementPosition=x;exports.useElementScroll=R;exports.useImage=P;exports.useLocalStorage=H;exports.useRecursiveTimeout=I;exports.useResponsiveSize=z;exports.useScrollToElements=k;exports.useViewport=O;exports.useWindowScroll=C;
package/dist/index.d.ts DELETED
@@ -1,106 +0,0 @@
1
- import { RefObject } from 'react';
2
-
3
- declare type Breakpoint = 'xs' | 'sm' | 'md' | 'lg' | 'xl' | '2xl';
4
-
5
- declare interface BreakpointInfo {
6
- current: Breakpoint;
7
- xs: boolean;
8
- sm: boolean;
9
- md: boolean;
10
- lg: boolean;
11
- xl: boolean;
12
- '2xl': boolean;
13
- }
14
-
15
- declare type ElementReference<T> = string | RefObject<T>;
16
-
17
- declare interface Options {
18
- delay?: number;
19
- autoInvoke?: boolean;
20
- }
21
-
22
- declare interface Options_2 {
23
- delay?: number;
24
- container?: HTMLElement | null;
25
- }
26
-
27
- declare interface Options_3 {
28
- retryCount?: number;
29
- retryDelay?: number;
30
- }
31
-
32
- declare interface Options_4 extends ScrollIntoViewOptions {
33
- offset?: number;
34
- }
35
-
36
- declare interface Options_5 {
37
- isInApp?: boolean;
38
- debounce?: number;
39
- }
40
-
41
- export declare const useBodyScrollLock: (enabled?: boolean) => void;
42
-
43
- export declare const useDebounce: <T extends (...args: unknown[]) => unknown>(callback: T, { delay, autoInvoke }: Options, deps?: React.DependencyList) => T;
44
-
45
- export declare const useElementPosition: <T>(elementRef: ElementReference<T>) => DOMRect | null;
46
-
47
- export declare const useElementScroll: () => {
48
- element: HTMLElement | null;
49
- setRef: (el: HTMLElement | null) => void;
50
- scrollY: number;
51
- scrollPercentage: number;
52
- isAtTop: boolean;
53
- isAtBottom: boolean;
54
- scrollableHeight: number;
55
- clientHeight: number;
56
- scrollHeight: number;
57
- };
58
-
59
- export declare const useImage: (src: string, options?: Options_3) => {
60
- loading: boolean;
61
- error: string | Event | null;
62
- loaded: boolean;
63
- retry: () => void;
64
- };
65
-
66
- export declare const useLocalStorage: <T>(key: string, initialValue: T) => readonly [T, (value: T | ((val: T) => T)) => void];
67
-
68
- export declare const useRecursiveTimeout: <T>(callback: () => Promise<T> | (() => void), delay: number | null) => void;
69
-
70
- export declare const useResponsiveSize: <T extends HTMLElement>(options?: Options_2) => {
71
- size: {
72
- width: number;
73
- height: number;
74
- };
75
- breakpoint: BreakpointInfo;
76
- ref: (node: T | null) => void;
77
- };
78
-
79
- export declare const useScrollToElements: (options?: Options_4) => {
80
- elementRefs: RefObject<HTMLElement[]>;
81
- setElementRef: (element: HTMLElement, index: number) => void;
82
- scrollToElement: (index: number) => void;
83
- };
84
-
85
- export declare const useViewport: (options?: Options_5) => ViewportInfo;
86
-
87
- export declare const useWindowScroll: () => {
88
- x: number;
89
- y: number;
90
- percent: {
91
- x: number;
92
- y: number;
93
- };
94
- };
95
-
96
- declare type ViewportInfo = VisualViewport | {
97
- width: number;
98
- height: number;
99
- offsetLeft: number;
100
- offsetTop: number;
101
- pageLeft: number;
102
- pageTop: number;
103
- scale: number;
104
- };
105
-
106
- export { }