@jbpark/use-hooks 2.0.2 → 2.1.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 (47) hide show
  1. package/README.ko.md +18 -10
  2. package/README.md +16 -10
  3. package/dist/hooks/index.d.mts +12 -0
  4. package/dist/hooks/index.mjs +14 -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/useViewport/index.d.mts +18 -0
  36. package/dist/hooks/useViewport/index.mjs +87 -0
  37. package/dist/hooks/useViewport/index.mjs.map +1 -0
  38. package/dist/hooks/useWindowScroll/index.d.mts +12 -0
  39. package/dist/hooks/useWindowScroll/index.mjs +60 -0
  40. package/dist/hooks/useWindowScroll/index.mjs.map +1 -0
  41. package/dist/index.d.mts +14 -0
  42. package/dist/index.mjs +15 -0
  43. package/package.json +7 -8
  44. package/dist/index.cjs +0 -1
  45. package/dist/index.d.ts +0 -106
  46. package/dist/index.js +0 -424
  47. package/dist/vite.svg +0 -1
@@ -0,0 +1,5 @@
1
+ //#region src/hooks/useLocalStorage/index.d.ts
2
+ declare const useLocalStorage: <T>(key: string, initialValue: T) => readonly [T, (value: T | ((val: T) => T)) => void];
3
+ //#endregion
4
+ export { useLocalStorage };
5
+ //# sourceMappingURL=index.d.mts.map
@@ -0,0 +1,40 @@
1
+ import { useCallback, useEffect, useRef, useState } from "react";
2
+
3
+ //#region src/hooks/useLocalStorage/index.ts
4
+ const useLocalStorage = (key, initialValue) => {
5
+ const initialRef = useRef(initialValue);
6
+ const [storedValue, setStoredValue] = useState(() => {
7
+ if (typeof window === "undefined") return initialValue;
8
+ try {
9
+ const item = window.localStorage.getItem(key);
10
+ return item ? JSON.parse(item) : initialValue;
11
+ } catch {
12
+ return initialValue;
13
+ }
14
+ });
15
+ useEffect(() => {
16
+ if (typeof window === "undefined") return;
17
+ try {
18
+ const item = window.localStorage.getItem(key);
19
+ if (item) setStoredValue(JSON.parse(item));
20
+ else window.localStorage.setItem(key, JSON.stringify(initialRef.current));
21
+ } catch (e) {
22
+ console.error(`Error reading localStorage key "${key}":`, e);
23
+ }
24
+ }, [key]);
25
+ return [storedValue, useCallback((value) => {
26
+ try {
27
+ setStoredValue((prev) => {
28
+ const valueToStore = value instanceof Function ? value(prev) : value;
29
+ localStorage.setItem(key, JSON.stringify(valueToStore));
30
+ return valueToStore;
31
+ });
32
+ } catch (e) {
33
+ console.error(`Error setting localStorage key "${key}":`, e);
34
+ }
35
+ }, [key])];
36
+ };
37
+
38
+ //#endregion
39
+ export { useLocalStorage as default };
40
+ //# sourceMappingURL=index.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.mjs","names":[],"sources":["../../../src/hooks/useLocalStorage/index.ts"],"sourcesContent":["import { useCallback, useEffect, useRef, useState } from 'react';\n\nconst useLocalStorage = <T>(key: string, initialValue: T) => {\n const initialRef = useRef(initialValue);\n const [storedValue, setStoredValue] = useState<T>(() => {\n if (typeof window === 'undefined') {\n return initialValue;\n }\n try {\n const item = window.localStorage.getItem(key);\n return item ? (JSON.parse(item) as T) : initialValue;\n } catch {\n return initialValue;\n }\n });\n\n useEffect(() => {\n if (typeof window === 'undefined') {\n return;\n }\n\n try {\n const item = window.localStorage.getItem(key);\n if (item) {\n setStoredValue(JSON.parse(item) as T);\n } else {\n window.localStorage.setItem(key, JSON.stringify(initialRef.current));\n }\n } catch (e) {\n console.error(`Error reading localStorage key \"${key}\":`, e);\n }\n }, [key]);\n\n const setValue = useCallback(\n (value: T | ((val: T) => T)) => {\n try {\n setStoredValue(prev => {\n const valueToStore = value instanceof Function ? value(prev) : value;\n localStorage.setItem(key, JSON.stringify(valueToStore));\n return valueToStore;\n });\n } catch (e) {\n console.error(`Error setting localStorage key \"${key}\":`, e);\n }\n },\n [key],\n );\n\n return [storedValue, setValue] as const;\n};\n\nexport default useLocalStorage;\n"],"mappings":";;;AAEA,MAAM,mBAAsB,KAAa,iBAAoB;CAC3D,MAAM,aAAa,OAAO,aAAa;CACvC,MAAM,CAAC,aAAa,kBAAkB,eAAkB;AACtD,MAAI,OAAO,WAAW,YACpB,QAAO;AAET,MAAI;GACF,MAAM,OAAO,OAAO,aAAa,QAAQ,IAAI;AAC7C,UAAO,OAAQ,KAAK,MAAM,KAAK,GAAS;UAClC;AACN,UAAO;;GAET;AAEF,iBAAgB;AACd,MAAI,OAAO,WAAW,YACpB;AAGF,MAAI;GACF,MAAM,OAAO,OAAO,aAAa,QAAQ,IAAI;AAC7C,OAAI,KACF,gBAAe,KAAK,MAAM,KAAK,CAAM;OAErC,QAAO,aAAa,QAAQ,KAAK,KAAK,UAAU,WAAW,QAAQ,CAAC;WAE/D,GAAG;AACV,WAAQ,MAAM,mCAAmC,IAAI,KAAK,EAAE;;IAE7D,CAAC,IAAI,CAAC;AAiBT,QAAO,CAAC,aAfS,aACd,UAA+B;AAC9B,MAAI;AACF,mBAAe,SAAQ;IACrB,MAAM,eAAe,iBAAiB,WAAW,MAAM,KAAK,GAAG;AAC/D,iBAAa,QAAQ,KAAK,KAAK,UAAU,aAAa,CAAC;AACvD,WAAO;KACP;WACK,GAAG;AACV,WAAQ,MAAM,mCAAmC,IAAI,KAAK,EAAE;;IAGhE,CAAC,IAAI,CACN,CAE6B"}
@@ -0,0 +1,5 @@
1
+ //#region src/hooks/useRecursiveTimeout/index.d.ts
2
+ declare const useRecursiveTimeout: <T>(callback: () => Promise<T> | (() => void), delay: number | null) => void;
3
+ //#endregion
4
+ export { useRecursiveTimeout };
5
+ //# sourceMappingURL=index.d.mts.map
@@ -0,0 +1,27 @@
1
+ import { useEffect, useRef } from "react";
2
+
3
+ //#region src/hooks/useRecursiveTimeout/index.ts
4
+ const useRecursiveTimeout = (callback, delay) => {
5
+ const savedCallback = useRef(callback);
6
+ useEffect(() => {
7
+ savedCallback.current = callback;
8
+ }, [callback]);
9
+ useEffect(() => {
10
+ let id;
11
+ function tick() {
12
+ const ret = savedCallback.current();
13
+ if (ret instanceof Promise) ret.then(() => {
14
+ if (delay) id = setTimeout(tick, delay);
15
+ });
16
+ else if (delay) id = setTimeout(tick, delay);
17
+ }
18
+ if (delay) {
19
+ id = setTimeout(tick, delay);
20
+ return () => id && clearTimeout(id);
21
+ }
22
+ }, [delay]);
23
+ };
24
+
25
+ //#endregion
26
+ export { useRecursiveTimeout as default };
27
+ //# sourceMappingURL=index.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.mjs","names":[],"sources":["../../../src/hooks/useRecursiveTimeout/index.ts"],"sourcesContent":["import { useEffect, useRef } from 'react';\n\nconst useRecursiveTimeout = <T>(\n callback: () => Promise<T> | (() => void),\n delay: number | null,\n) => {\n const savedCallback = useRef(callback);\n\n useEffect(() => {\n savedCallback.current = callback;\n }, [callback]);\n\n useEffect(() => {\n let id: NodeJS.Timeout;\n\n function tick() {\n const ret = savedCallback.current();\n\n if (ret instanceof Promise) {\n ret.then(() => {\n if (delay) {\n id = setTimeout(tick, delay);\n }\n });\n } else {\n if (delay) {\n id = setTimeout(tick, delay);\n }\n }\n }\n\n if (delay) {\n id = setTimeout(tick, delay);\n return () => id && clearTimeout(id);\n }\n }, [delay]);\n};\n\nexport default useRecursiveTimeout;\n"],"mappings":";;;AAEA,MAAM,uBACJ,UACA,UACG;CACH,MAAM,gBAAgB,OAAO,SAAS;AAEtC,iBAAgB;AACd,gBAAc,UAAU;IACvB,CAAC,SAAS,CAAC;AAEd,iBAAgB;EACd,IAAI;EAEJ,SAAS,OAAO;GACd,MAAM,MAAM,cAAc,SAAS;AAEnC,OAAI,eAAe,QACjB,KAAI,WAAW;AACb,QAAI,MACF,MAAK,WAAW,MAAM,MAAM;KAE9B;YAEE,MACF,MAAK,WAAW,MAAM,MAAM;;AAKlC,MAAI,OAAO;AACT,QAAK,WAAW,MAAM,MAAM;AAC5B,gBAAa,MAAM,aAAa,GAAG;;IAEpC,CAAC,MAAM,CAAC"}
@@ -0,0 +1,26 @@
1
+ //#region src/hooks/useResponsiveSize/index.d.ts
2
+ type Breakpoint = 'xs' | 'sm' | 'md' | 'lg' | 'xl' | '2xl';
3
+ interface BreakpointInfo {
4
+ current: Breakpoint;
5
+ xs: boolean;
6
+ sm: boolean;
7
+ md: boolean;
8
+ lg: boolean;
9
+ xl: boolean;
10
+ '2xl': boolean;
11
+ }
12
+ interface Options {
13
+ delay?: number;
14
+ container?: HTMLElement | null;
15
+ }
16
+ declare const useResponsiveSize: <T extends HTMLElement>(options?: Options) => {
17
+ size: {
18
+ width: number;
19
+ height: number;
20
+ };
21
+ breakpoint: BreakpointInfo;
22
+ ref: (node: T | null) => void;
23
+ };
24
+ //#endregion
25
+ export { useResponsiveSize };
26
+ //# sourceMappingURL=index.d.mts.map
@@ -0,0 +1,108 @@
1
+ import useDebounce from "../useDebounce/index.mjs";
2
+ import { useCallback, useEffect, useRef, useState } from "react";
3
+
4
+ //#region src/hooks/useResponsiveSize/index.ts
5
+ const BREAKPOINTS = {
6
+ xs: 0,
7
+ sm: 640,
8
+ md: 768,
9
+ lg: 1024,
10
+ xl: 1280,
11
+ "2xl": 1536
12
+ };
13
+ const getBreakpointInfo = (width) => {
14
+ let current = "xs";
15
+ if (width >= BREAKPOINTS["2xl"]) current = "2xl";
16
+ else if (width >= BREAKPOINTS.xl) current = "xl";
17
+ else if (width >= BREAKPOINTS.lg) current = "lg";
18
+ else if (width >= BREAKPOINTS.md) current = "md";
19
+ else if (width >= BREAKPOINTS.sm) current = "sm";
20
+ else current = "xs";
21
+ return {
22
+ current,
23
+ xs: width < BREAKPOINTS.sm,
24
+ sm: width >= BREAKPOINTS.sm && width < BREAKPOINTS.md,
25
+ md: width >= BREAKPOINTS.md && width < BREAKPOINTS.lg,
26
+ lg: width >= BREAKPOINTS.lg && width < BREAKPOINTS.xl,
27
+ xl: width >= BREAKPOINTS.xl && width < BREAKPOINTS["2xl"],
28
+ "2xl": width >= BREAKPOINTS["2xl"]
29
+ };
30
+ };
31
+ const useResponsiveSize = (options) => {
32
+ const { delay = 100, container } = options || {};
33
+ const [size, setSize] = useState({
34
+ width: 0,
35
+ height: 0
36
+ });
37
+ const [breakpoint, setBreakpoint] = useState({
38
+ current: "xs",
39
+ xs: true,
40
+ sm: false,
41
+ md: false,
42
+ lg: false,
43
+ xl: false,
44
+ "2xl": false
45
+ });
46
+ const [debouncedSize, setDebouncedSize] = useState({
47
+ width: 0,
48
+ height: 0
49
+ });
50
+ const [element, setElement] = useState(null);
51
+ const observerRef = useRef(null);
52
+ const ref = useCallback((node) => {
53
+ setElement(node);
54
+ }, []);
55
+ useDebounce(() => {
56
+ setDebouncedSize(size);
57
+ }, { delay }, [size]);
58
+ useEffect(() => {
59
+ const updateSize = () => {
60
+ const target = container ?? element ?? document.body;
61
+ if (!target) return;
62
+ const { offsetWidth, offsetHeight } = target;
63
+ setSize((prev) => {
64
+ if (prev.width !== offsetWidth || prev.height !== offsetHeight) return {
65
+ width: offsetWidth,
66
+ height: offsetHeight
67
+ };
68
+ return prev;
69
+ });
70
+ setBreakpoint((prev) => {
71
+ const next = getBreakpointInfo(offsetWidth);
72
+ if (prev.current !== next.current) return next;
73
+ return prev;
74
+ });
75
+ };
76
+ const connect = () => {
77
+ const target = container ?? element ?? document.body;
78
+ if (!target) return;
79
+ updateSize();
80
+ if (observerRef.current) observerRef.current.disconnect();
81
+ observerRef.current = new ResizeObserver(() => {
82
+ requestAnimationFrame(() => {
83
+ updateSize();
84
+ });
85
+ });
86
+ observerRef.current.observe(target);
87
+ };
88
+ const disconnect = () => {
89
+ if (observerRef.current) {
90
+ observerRef.current.disconnect();
91
+ observerRef.current = null;
92
+ }
93
+ };
94
+ connect();
95
+ return () => {
96
+ disconnect();
97
+ };
98
+ }, [container, element]);
99
+ return {
100
+ size: debouncedSize,
101
+ breakpoint,
102
+ ref
103
+ };
104
+ };
105
+
106
+ //#endregion
107
+ export { useResponsiveSize as default };
108
+ //# sourceMappingURL=index.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.mjs","names":[],"sources":["../../../src/hooks/useResponsiveSize/index.ts"],"sourcesContent":["import { useCallback, useEffect, useRef, useState } from 'react';\n\nimport useDebounce from '../useDebounce';\n\ntype Breakpoint = 'xs' | 'sm' | 'md' | 'lg' | 'xl' | '2xl';\n\ninterface BreakpointInfo {\n current: Breakpoint;\n xs: boolean;\n sm: boolean;\n md: boolean;\n lg: boolean;\n xl: boolean;\n '2xl': boolean;\n}\n\ninterface Options {\n delay?: number;\n container?: HTMLElement | null;\n}\n\nconst BREAKPOINTS = {\n xs: 0, // < 640px\n sm: 640, // >= 640px\n md: 768, // >= 768px\n lg: 1024, // >= 1024px\n xl: 1280, // >= 1280px\n '2xl': 1536, // >= 1536px\n} as const;\n\nconst getBreakpointInfo = (width: number): BreakpointInfo => {\n let current: Breakpoint = 'xs';\n\n if (width >= BREAKPOINTS['2xl']) {\n current = '2xl';\n } else if (width >= BREAKPOINTS.xl) {\n current = 'xl';\n } else if (width >= BREAKPOINTS.lg) {\n current = 'lg';\n } else if (width >= BREAKPOINTS.md) {\n current = 'md';\n } else if (width >= BREAKPOINTS.sm) {\n current = 'sm';\n } else {\n current = 'xs';\n }\n\n return {\n current,\n xs: width < BREAKPOINTS.sm,\n sm: width >= BREAKPOINTS.sm && width < BREAKPOINTS.md,\n md: width >= BREAKPOINTS.md && width < BREAKPOINTS.lg,\n lg: width >= BREAKPOINTS.lg && width < BREAKPOINTS.xl,\n xl: width >= BREAKPOINTS.xl && width < BREAKPOINTS['2xl'],\n '2xl': width >= BREAKPOINTS['2xl'],\n };\n};\n\nconst useResponsiveSize = <T extends HTMLElement>(options?: Options) => {\n const { delay = 100, container } = options || {};\n const [size, setSize] = useState({ width: 0, height: 0 });\n const [breakpoint, setBreakpoint] = useState<BreakpointInfo>({\n current: 'xs',\n xs: true,\n sm: false,\n md: false,\n lg: false,\n xl: false,\n '2xl': false,\n });\n\n const [debouncedSize, setDebouncedSize] = useState({ width: 0, height: 0 });\n\n const [element, setElement] = useState<T | null>(null);\n const observerRef = useRef<ResizeObserver | null>(null);\n\n const ref = useCallback((node: T | null) => {\n setElement(node);\n }, []);\n\n useDebounce(\n () => {\n setDebouncedSize(size);\n },\n { delay },\n [size],\n );\n\n useEffect(() => {\n const updateSize = () => {\n const target = container ?? element ?? document.body;\n\n if (!target) {\n return;\n }\n\n const { offsetWidth, offsetHeight } = target;\n\n setSize(prev => {\n if (prev.width !== offsetWidth || prev.height !== offsetHeight) {\n return { width: offsetWidth, height: offsetHeight };\n }\n return prev;\n });\n\n setBreakpoint(prev => {\n const next = getBreakpointInfo(offsetWidth);\n if (prev.current !== next.current) {\n return next;\n }\n return prev;\n });\n };\n\n const connect = () => {\n const target = container ?? element ?? document.body;\n\n if (!target) {\n return;\n }\n\n updateSize();\n\n if (observerRef.current) {\n observerRef.current.disconnect();\n }\n\n observerRef.current = new ResizeObserver(() => {\n requestAnimationFrame(() => {\n updateSize();\n });\n });\n\n observerRef.current.observe(target);\n };\n\n const disconnect = () => {\n if (observerRef.current) {\n observerRef.current.disconnect();\n observerRef.current = null;\n }\n };\n\n connect();\n\n return () => {\n disconnect();\n };\n }, [container, element]);\n\n return {\n size: debouncedSize,\n breakpoint,\n ref,\n };\n};\n\nexport default useResponsiveSize;\n"],"mappings":";;;;AAqBA,MAAM,cAAc;CAClB,IAAI;CACJ,IAAI;CACJ,IAAI;CACJ,IAAI;CACJ,IAAI;CACJ,OAAO;CACR;AAED,MAAM,qBAAqB,UAAkC;CAC3D,IAAI,UAAsB;AAE1B,KAAI,SAAS,YAAY,OACvB,WAAU;UACD,SAAS,YAAY,GAC9B,WAAU;UACD,SAAS,YAAY,GAC9B,WAAU;UACD,SAAS,YAAY,GAC9B,WAAU;UACD,SAAS,YAAY,GAC9B,WAAU;KAEV,WAAU;AAGZ,QAAO;EACL;EACA,IAAI,QAAQ,YAAY;EACxB,IAAI,SAAS,YAAY,MAAM,QAAQ,YAAY;EACnD,IAAI,SAAS,YAAY,MAAM,QAAQ,YAAY;EACnD,IAAI,SAAS,YAAY,MAAM,QAAQ,YAAY;EACnD,IAAI,SAAS,YAAY,MAAM,QAAQ,YAAY;EACnD,OAAO,SAAS,YAAY;EAC7B;;AAGH,MAAM,qBAA4C,YAAsB;CACtE,MAAM,EAAE,QAAQ,KAAK,cAAc,WAAW,EAAE;CAChD,MAAM,CAAC,MAAM,WAAW,SAAS;EAAE,OAAO;EAAG,QAAQ;EAAG,CAAC;CACzD,MAAM,CAAC,YAAY,iBAAiB,SAAyB;EAC3D,SAAS;EACT,IAAI;EACJ,IAAI;EACJ,IAAI;EACJ,IAAI;EACJ,IAAI;EACJ,OAAO;EACR,CAAC;CAEF,MAAM,CAAC,eAAe,oBAAoB,SAAS;EAAE,OAAO;EAAG,QAAQ;EAAG,CAAC;CAE3E,MAAM,CAAC,SAAS,cAAc,SAAmB,KAAK;CACtD,MAAM,cAAc,OAA8B,KAAK;CAEvD,MAAM,MAAM,aAAa,SAAmB;AAC1C,aAAW,KAAK;IACf,EAAE,CAAC;AAEN,mBACQ;AACJ,mBAAiB,KAAK;IAExB,EAAE,OAAO,EACT,CAAC,KAAK,CACP;AAED,iBAAgB;EACd,MAAM,mBAAmB;GACvB,MAAM,SAAS,aAAa,WAAW,SAAS;AAEhD,OAAI,CAAC,OACH;GAGF,MAAM,EAAE,aAAa,iBAAiB;AAEtC,YAAQ,SAAQ;AACd,QAAI,KAAK,UAAU,eAAe,KAAK,WAAW,aAChD,QAAO;KAAE,OAAO;KAAa,QAAQ;KAAc;AAErD,WAAO;KACP;AAEF,kBAAc,SAAQ;IACpB,MAAM,OAAO,kBAAkB,YAAY;AAC3C,QAAI,KAAK,YAAY,KAAK,QACxB,QAAO;AAET,WAAO;KACP;;EAGJ,MAAM,gBAAgB;GACpB,MAAM,SAAS,aAAa,WAAW,SAAS;AAEhD,OAAI,CAAC,OACH;AAGF,eAAY;AAEZ,OAAI,YAAY,QACd,aAAY,QAAQ,YAAY;AAGlC,eAAY,UAAU,IAAI,qBAAqB;AAC7C,gCAA4B;AAC1B,iBAAY;MACZ;KACF;AAEF,eAAY,QAAQ,QAAQ,OAAO;;EAGrC,MAAM,mBAAmB;AACvB,OAAI,YAAY,SAAS;AACvB,gBAAY,QAAQ,YAAY;AAChC,gBAAY,UAAU;;;AAI1B,WAAS;AAET,eAAa;AACX,eAAY;;IAEb,CAAC,WAAW,QAAQ,CAAC;AAExB,QAAO;EACL,MAAM;EACN;EACA;EACD"}
@@ -0,0 +1,14 @@
1
+ import * as react from "react";
2
+
3
+ //#region src/hooks/useScrollToElements/index.d.ts
4
+ interface Options extends ScrollIntoViewOptions {
5
+ offset?: number;
6
+ }
7
+ declare const useScrollToElements: (options?: Options) => {
8
+ elementRefs: react.RefObject<HTMLElement[]>;
9
+ setElementRef: (element: HTMLElement, index: number) => void;
10
+ scrollToElement: (index: number) => void;
11
+ };
12
+ //#endregion
13
+ export { useScrollToElements };
14
+ //# sourceMappingURL=index.d.mts.map
@@ -0,0 +1,34 @@
1
+ import { useCallback, useRef } from "react";
2
+
3
+ //#region src/hooks/useScrollToElements/index.ts
4
+ const useScrollToElements = (options) => {
5
+ const elementRefs = useRef([]);
6
+ const scrollToElement = useCallback((index) => {
7
+ if (elementRefs.current[index]) {
8
+ elementRefs.current[index].scrollIntoView({
9
+ behavior: "smooth",
10
+ block: "start",
11
+ inline: "start",
12
+ ...options
13
+ });
14
+ if (options?.offset) {
15
+ const top = elementRefs.current[index].getBoundingClientRect().top + window.scrollY - options.offset;
16
+ window.scrollTo({
17
+ top,
18
+ behavior: options.behavior || "smooth"
19
+ });
20
+ }
21
+ }
22
+ }, [options]);
23
+ return {
24
+ elementRefs,
25
+ setElementRef: useCallback((element, index) => {
26
+ elementRefs.current[index] = element;
27
+ }, []),
28
+ scrollToElement
29
+ };
30
+ };
31
+
32
+ //#endregion
33
+ export { useScrollToElements as default };
34
+ //# sourceMappingURL=index.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.mjs","names":[],"sources":["../../../src/hooks/useScrollToElements/index.ts"],"sourcesContent":["import { useCallback, useRef } from 'react';\n\ninterface Options extends ScrollIntoViewOptions {\n offset?: number;\n}\nconst useScrollToElements = (options?: Options) => {\n const elementRefs = useRef<HTMLElement[]>([]);\n\n const scrollToElement = useCallback(\n (index: number) => {\n if (elementRefs.current[index]) {\n elementRefs.current[index].scrollIntoView({\n behavior: 'smooth',\n block: 'start',\n inline: 'start',\n ...options,\n });\n\n if (options?.offset) {\n const top =\n elementRefs.current[index].getBoundingClientRect().top +\n window.scrollY -\n options.offset;\n\n window.scrollTo({\n top,\n behavior: options.behavior || 'smooth',\n });\n }\n }\n },\n [options],\n );\n\n const setElementRef = useCallback((element: HTMLElement, index: number) => {\n elementRefs.current[index] = element;\n }, []);\n\n return { elementRefs, setElementRef, scrollToElement };\n};\n\nexport default useScrollToElements;\n"],"mappings":";;;AAKA,MAAM,uBAAuB,YAAsB;CACjD,MAAM,cAAc,OAAsB,EAAE,CAAC;CAE7C,MAAM,kBAAkB,aACrB,UAAkB;AACjB,MAAI,YAAY,QAAQ,QAAQ;AAC9B,eAAY,QAAQ,OAAO,eAAe;IACxC,UAAU;IACV,OAAO;IACP,QAAQ;IACR,GAAG;IACJ,CAAC;AAEF,OAAI,SAAS,QAAQ;IACnB,MAAM,MACJ,YAAY,QAAQ,OAAO,uBAAuB,CAAC,MACnD,OAAO,UACP,QAAQ;AAEV,WAAO,SAAS;KACd;KACA,UAAU,QAAQ,YAAY;KAC/B,CAAC;;;IAIR,CAAC,QAAQ,CACV;AAMD,QAAO;EAAE;EAAa,eAJA,aAAa,SAAsB,UAAkB;AACzE,eAAY,QAAQ,SAAS;KAC5B,EAAE,CAAC;EAE+B;EAAiB"}
@@ -0,0 +1,5 @@
1
+ //#region src/hooks/useThrottle/index.d.ts
2
+ declare const useThrottle: <T>(value: T, delay?: number) => T;
3
+ //#endregion
4
+ export { useThrottle };
5
+ //# sourceMappingURL=index.d.mts.map
@@ -0,0 +1,42 @@
1
+ import { useEffect, useRef, useState } from "react";
2
+
3
+ //#region src/hooks/useThrottle/index.ts
4
+ const useThrottle = (value, delay = 100) => {
5
+ const [throttledValue, setThrottledValue] = useState(value);
6
+ const lastExecutedRef = useRef(0);
7
+ const timeoutRef = useRef(null);
8
+ const latestValueRef = useRef(value);
9
+ useEffect(() => {
10
+ latestValueRef.current = value;
11
+ }, [value]);
12
+ useEffect(() => {
13
+ const now = Date.now();
14
+ const elapsed = now - lastExecutedRef.current;
15
+ if (elapsed >= delay) {
16
+ if (timeoutRef.current) {
17
+ clearTimeout(timeoutRef.current);
18
+ timeoutRef.current = null;
19
+ }
20
+ lastExecutedRef.current = now;
21
+ setThrottledValue(latestValueRef.current);
22
+ return;
23
+ }
24
+ const remaining = delay - elapsed;
25
+ if (timeoutRef.current) clearTimeout(timeoutRef.current);
26
+ timeoutRef.current = setTimeout(() => {
27
+ lastExecutedRef.current = Date.now();
28
+ setThrottledValue(latestValueRef.current);
29
+ }, remaining);
30
+ return () => {
31
+ if (timeoutRef.current) {
32
+ clearTimeout(timeoutRef.current);
33
+ timeoutRef.current = null;
34
+ }
35
+ };
36
+ }, [value, delay]);
37
+ return throttledValue;
38
+ };
39
+
40
+ //#endregion
41
+ export { useThrottle as default };
42
+ //# sourceMappingURL=index.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.mjs","names":[],"sources":["../../../src/hooks/useThrottle/index.ts"],"sourcesContent":["import { useEffect, useRef, useState } from 'react';\n\nconst useThrottle = <T>(value: T, delay = 100): T => {\n const [throttledValue, setThrottledValue] = useState(value);\n const lastExecutedRef = useRef(0);\n const timeoutRef = useRef<ReturnType<typeof setTimeout> | null>(null);\n const latestValueRef = useRef(value);\n\n useEffect(() => {\n latestValueRef.current = value;\n }, [value]);\n\n useEffect(() => {\n const now = Date.now();\n const elapsed = now - lastExecutedRef.current;\n\n if (elapsed >= delay) {\n if (timeoutRef.current) {\n clearTimeout(timeoutRef.current);\n timeoutRef.current = null;\n }\n\n lastExecutedRef.current = now;\n setThrottledValue(latestValueRef.current);\n return;\n }\n\n const remaining = delay - elapsed;\n\n if (timeoutRef.current) {\n clearTimeout(timeoutRef.current);\n }\n\n timeoutRef.current = setTimeout(() => {\n lastExecutedRef.current = Date.now();\n setThrottledValue(latestValueRef.current);\n }, remaining);\n\n return () => {\n if (timeoutRef.current) {\n clearTimeout(timeoutRef.current);\n timeoutRef.current = null;\n }\n };\n }, [value, delay]);\n\n return throttledValue;\n};\n\nexport default useThrottle;\n"],"mappings":";;;AAEA,MAAM,eAAkB,OAAU,QAAQ,QAAW;CACnD,MAAM,CAAC,gBAAgB,qBAAqB,SAAS,MAAM;CAC3D,MAAM,kBAAkB,OAAO,EAAE;CACjC,MAAM,aAAa,OAA6C,KAAK;CACrE,MAAM,iBAAiB,OAAO,MAAM;AAEpC,iBAAgB;AACd,iBAAe,UAAU;IACxB,CAAC,MAAM,CAAC;AAEX,iBAAgB;EACd,MAAM,MAAM,KAAK,KAAK;EACtB,MAAM,UAAU,MAAM,gBAAgB;AAEtC,MAAI,WAAW,OAAO;AACpB,OAAI,WAAW,SAAS;AACtB,iBAAa,WAAW,QAAQ;AAChC,eAAW,UAAU;;AAGvB,mBAAgB,UAAU;AAC1B,qBAAkB,eAAe,QAAQ;AACzC;;EAGF,MAAM,YAAY,QAAQ;AAE1B,MAAI,WAAW,QACb,cAAa,WAAW,QAAQ;AAGlC,aAAW,UAAU,iBAAiB;AACpC,mBAAgB,UAAU,KAAK,KAAK;AACpC,qBAAkB,eAAe,QAAQ;KACxC,UAAU;AAEb,eAAa;AACX,OAAI,WAAW,SAAS;AACtB,iBAAa,WAAW,QAAQ;AAChC,eAAW,UAAU;;;IAGxB,CAAC,OAAO,MAAM,CAAC;AAElB,QAAO"}
@@ -0,0 +1,18 @@
1
+ //#region src/hooks/useViewport/index.d.ts
2
+ type ViewportInfo = VisualViewport | {
3
+ width: number;
4
+ height: number;
5
+ offsetLeft: number;
6
+ offsetTop: number;
7
+ pageLeft: number;
8
+ pageTop: number;
9
+ scale: number;
10
+ };
11
+ interface Options {
12
+ isInApp?: boolean;
13
+ debounce?: number;
14
+ }
15
+ declare const useViewport: (options?: Options) => ViewportInfo;
16
+ //#endregion
17
+ export { useViewport };
18
+ //# sourceMappingURL=index.d.mts.map
@@ -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,14 @@
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 { useWindowScroll } from "./hooks/useWindowScroll/index.mjs";
12
+ import { useViewport } from "./hooks/useViewport/index.mjs";
13
+ import "./hooks/index.mjs";
14
+ export { useBodyScrollLock, useDebounce, useElementPosition, useElementScroll, useImage, useLocalStorage, useRecursiveTimeout, useResponsiveSize, useScrollToElements, useThrottle, useViewport, useWindowScroll };
package/dist/index.mjs ADDED
@@ -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 useWindowScroll from "./hooks/useWindowScroll/index.mjs";
12
+ import useViewport from "./hooks/useViewport/index.mjs";
13
+ import "./hooks/index.mjs";
14
+
15
+ export { useBodyScrollLock, useDebounce, useElementPosition, useElementScroll, useImage, useLocalStorage, useRecursiveTimeout, useResponsiveSize, useScrollToElements, useThrottle, 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.1.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"