@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.
- package/README.ko.md +18 -10
- package/README.md +16 -10
- package/dist/hooks/index.d.mts +12 -0
- package/dist/hooks/index.mjs +14 -0
- package/dist/hooks/useBodyScrollLock/index.d.mts +5 -0
- package/dist/hooks/useBodyScrollLock/index.mjs +115 -0
- package/dist/hooks/useBodyScrollLock/index.mjs.map +1 -0
- package/dist/hooks/useDebounce/index.d.mts +12 -0
- package/dist/hooks/useDebounce/index.mjs +41 -0
- package/dist/hooks/useDebounce/index.mjs.map +1 -0
- package/dist/hooks/useElementPosition/index.d.mts +8 -0
- package/dist/hooks/useElementPosition/index.mjs +32 -0
- package/dist/hooks/useElementPosition/index.mjs.map +1 -0
- package/dist/hooks/useElementScroll/index.d.mts +15 -0
- package/dist/hooks/useElementScroll/index.mjs +68 -0
- package/dist/hooks/useElementScroll/index.mjs.map +1 -0
- package/dist/hooks/useImage/index.d.mts +14 -0
- package/dist/hooks/useImage/index.mjs +56 -0
- package/dist/hooks/useImage/index.mjs.map +1 -0
- package/dist/hooks/useLocalStorage/index.d.mts +5 -0
- package/dist/hooks/useLocalStorage/index.mjs +40 -0
- package/dist/hooks/useLocalStorage/index.mjs.map +1 -0
- package/dist/hooks/useRecursiveTimeout/index.d.mts +5 -0
- package/dist/hooks/useRecursiveTimeout/index.mjs +27 -0
- package/dist/hooks/useRecursiveTimeout/index.mjs.map +1 -0
- package/dist/hooks/useResponsiveSize/index.d.mts +26 -0
- package/dist/hooks/useResponsiveSize/index.mjs +108 -0
- package/dist/hooks/useResponsiveSize/index.mjs.map +1 -0
- package/dist/hooks/useScrollToElements/index.d.mts +14 -0
- package/dist/hooks/useScrollToElements/index.mjs +34 -0
- package/dist/hooks/useScrollToElements/index.mjs.map +1 -0
- package/dist/hooks/useThrottle/index.d.mts +5 -0
- package/dist/hooks/useThrottle/index.mjs +42 -0
- package/dist/hooks/useThrottle/index.mjs.map +1 -0
- package/dist/hooks/useViewport/index.d.mts +18 -0
- package/dist/hooks/useViewport/index.mjs +87 -0
- package/dist/hooks/useViewport/index.mjs.map +1 -0
- package/dist/hooks/useWindowScroll/index.d.mts +12 -0
- package/dist/hooks/useWindowScroll/index.mjs +60 -0
- package/dist/hooks/useWindowScroll/index.mjs.map +1 -0
- package/dist/index.d.mts +14 -0
- package/dist/index.mjs +15 -0
- package/package.json +7 -8
- package/dist/index.cjs +0 -1
- package/dist/index.d.ts +0 -106
- package/dist/index.js +0 -424
- package/dist/vite.svg +0 -1
|
@@ -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,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,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,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"}
|
package/dist/index.d.mts
ADDED
|
@@ -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
|
|
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.
|
|
26
|
-
"
|
|
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.
|
|
32
|
-
"import": "./dist/index.
|
|
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 &&
|
|
76
|
+
"build": "tsc -b && tsdown",
|
|
78
77
|
"lint": "eslint .",
|
|
79
78
|
"preview": "vite preview",
|
|
80
79
|
"changeset": "changeset"
|