goodchuck-utils 1.4.2 → 1.6.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/dist/components/dev/ApiLogger.d.ts +136 -0
- package/dist/components/dev/ApiLogger.d.ts.map +1 -0
- package/dist/components/dev/ApiLogger.js +408 -0
- package/dist/components/dev/DevPanel.d.ts +32 -0
- package/dist/components/dev/DevPanel.d.ts.map +1 -0
- package/dist/components/dev/DevPanel.js +196 -0
- package/dist/components/dev/FormDevTools/FormDevTools.d.ts +75 -0
- package/dist/components/dev/FormDevTools/FormDevTools.d.ts.map +1 -0
- package/dist/components/dev/FormDevTools/FormDevTools.js +218 -0
- package/dist/components/dev/FormDevTools/index.d.ts +3 -0
- package/dist/components/dev/FormDevTools/index.d.ts.map +1 -0
- package/dist/components/dev/FormDevTools/index.js +1 -0
- package/dist/components/dev/FormDevTools/styles.d.ts +45 -0
- package/dist/components/dev/FormDevTools/styles.d.ts.map +1 -0
- package/dist/components/dev/FormDevTools/styles.js +187 -0
- package/dist/components/dev/FormDevTools.d.ts +76 -0
- package/dist/components/dev/FormDevTools.d.ts.map +1 -0
- package/dist/components/dev/FormDevTools.js +399 -0
- package/dist/components/dev/IdSelector.d.ts +50 -0
- package/dist/components/dev/IdSelector.d.ts.map +1 -0
- package/dist/components/dev/IdSelector.js +129 -0
- package/dist/components/dev/WindowSizeDisplay.d.ts +44 -0
- package/dist/components/dev/WindowSizeDisplay.d.ts.map +1 -0
- package/dist/components/dev/WindowSizeDisplay.js +74 -0
- package/dist/components/dev/ZIndexDebugger.d.ts +32 -0
- package/dist/components/dev/ZIndexDebugger.d.ts.map +1 -0
- package/dist/components/dev/ZIndexDebugger.js +184 -0
- package/dist/components/dev/index.d.ts +15 -0
- package/dist/components/dev/index.d.ts.map +1 -0
- package/dist/components/dev/index.js +12 -0
- package/dist/components/index.d.ts +8 -0
- package/dist/components/index.d.ts.map +1 -0
- package/dist/components/index.js +7 -0
- package/dist/hooks/index.d.ts +8 -0
- package/dist/hooks/index.d.ts.map +1 -1
- package/dist/hooks/index.js +11 -0
- package/dist/hooks/useCopyToClipboard.d.ts +67 -0
- package/dist/hooks/useCopyToClipboard.d.ts.map +1 -0
- package/dist/hooks/useCopyToClipboard.js +79 -0
- package/dist/hooks/useDebounce.d.ts +47 -0
- package/dist/hooks/useDebounce.d.ts.map +1 -0
- package/dist/hooks/useDebounce.js +60 -0
- package/dist/hooks/useEventListener.d.ts +79 -0
- package/dist/hooks/useEventListener.d.ts.map +1 -0
- package/dist/hooks/useEventListener.js +33 -0
- package/dist/hooks/useIntersectionObserver.d.ts +109 -0
- package/dist/hooks/useIntersectionObserver.d.ts.map +1 -0
- package/dist/hooks/useIntersectionObserver.js +128 -0
- package/dist/hooks/usePrevious.d.ts +58 -0
- package/dist/hooks/usePrevious.d.ts.map +1 -0
- package/dist/hooks/usePrevious.js +67 -0
- package/dist/hooks/useThrottle.d.ts +57 -0
- package/dist/hooks/useThrottle.d.ts.map +1 -0
- package/dist/hooks/useThrottle.js +80 -0
- package/dist/hooks/useToggle.d.ts +49 -0
- package/dist/hooks/useToggle.d.ts.map +1 -0
- package/dist/hooks/useToggle.js +56 -0
- package/dist/hooks/useWindowSize.d.ts +58 -0
- package/dist/hooks/useWindowSize.d.ts.map +1 -0
- package/dist/hooks/useWindowSize.js +79 -0
- package/package.json +23 -2
|
@@ -0,0 +1,67 @@
|
|
|
1
|
+
import { useRef, useEffect } from 'react';
|
|
2
|
+
/**
|
|
3
|
+
* 이전 렌더링의 값을 저장하는 hook
|
|
4
|
+
*
|
|
5
|
+
* @param value - 추적할 값
|
|
6
|
+
* @returns 이전 렌더링의 값 (첫 렌더링에서는 undefined)
|
|
7
|
+
*
|
|
8
|
+
* @example
|
|
9
|
+
* // 값 변경 감지
|
|
10
|
+
* function Counter() {
|
|
11
|
+
* const [count, setCount] = useState(0);
|
|
12
|
+
* const prevCount = usePrevious(count);
|
|
13
|
+
*
|
|
14
|
+
* return (
|
|
15
|
+
* <div>
|
|
16
|
+
* <p>Current: {count}</p>
|
|
17
|
+
* <p>Previous: {prevCount}</p>
|
|
18
|
+
* <p>Changed: {count !== prevCount ? 'Yes' : 'No'}</p>
|
|
19
|
+
* <button onClick={() => setCount(count + 1)}>Increment</button>
|
|
20
|
+
* </div>
|
|
21
|
+
* );
|
|
22
|
+
* }
|
|
23
|
+
*
|
|
24
|
+
* @example
|
|
25
|
+
* // 증가/감소 방향 표시
|
|
26
|
+
* function PriceDisplay({ price }: { price: number }) {
|
|
27
|
+
* const prevPrice = usePrevious(price);
|
|
28
|
+
*
|
|
29
|
+
* const trend = prevPrice === undefined
|
|
30
|
+
* ? null
|
|
31
|
+
* : price > prevPrice
|
|
32
|
+
* ? '📈 Up'
|
|
33
|
+
* : price < prevPrice
|
|
34
|
+
* ? '📉 Down'
|
|
35
|
+
* : '➡️ Same';
|
|
36
|
+
*
|
|
37
|
+
* return (
|
|
38
|
+
* <div>
|
|
39
|
+
* <span>${price}</span>
|
|
40
|
+
* {trend && <span>{trend}</span>}
|
|
41
|
+
* </div>
|
|
42
|
+
* );
|
|
43
|
+
* }
|
|
44
|
+
*
|
|
45
|
+
* @example
|
|
46
|
+
* // 애니메이션 방향 결정
|
|
47
|
+
* function AnimatedList({ items }: { items: string[] }) {
|
|
48
|
+
* const prevItems = usePrevious(items);
|
|
49
|
+
* const isAdding = prevItems && items.length > prevItems.length;
|
|
50
|
+
*
|
|
51
|
+
* return (
|
|
52
|
+
* <ul className={isAdding ? 'slide-in' : 'slide-out'}>
|
|
53
|
+
* {items.map(item => <li key={item}>{item}</li>)}
|
|
54
|
+
* </ul>
|
|
55
|
+
* );
|
|
56
|
+
* }
|
|
57
|
+
*/
|
|
58
|
+
export function usePrevious(value) {
|
|
59
|
+
// ref는 리렌더링 간에 값을 유지
|
|
60
|
+
const ref = useRef(undefined);
|
|
61
|
+
// 렌더링 후에 현재 값을 ref에 저장
|
|
62
|
+
useEffect(() => {
|
|
63
|
+
ref.current = value;
|
|
64
|
+
}, [value]);
|
|
65
|
+
// 현재 렌더링에서는 이전 값을 반환
|
|
66
|
+
return ref.current;
|
|
67
|
+
}
|
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* 값의 업데이트를 일정 시간 간격으로 제한하는 hook
|
|
3
|
+
* debounce와 달리 일정 간격마다 최신 값을 업데이트합니다.
|
|
4
|
+
*
|
|
5
|
+
* @param value - throttle할 값
|
|
6
|
+
* @param interval - 업데이트 간격 (밀리초, 기본값: 500ms)
|
|
7
|
+
* @returns throttle된 값
|
|
8
|
+
*
|
|
9
|
+
* @example
|
|
10
|
+
* // 스크롤 위치 추적 최적화
|
|
11
|
+
* function ScrollTracker() {
|
|
12
|
+
* const [scrollY, setScrollY] = useState(0);
|
|
13
|
+
* const throttledScrollY = useThrottle(scrollY, 200);
|
|
14
|
+
*
|
|
15
|
+
* useEffect(() => {
|
|
16
|
+
* const handleScroll = () => setScrollY(window.scrollY);
|
|
17
|
+
* window.addEventListener('scroll', handleScroll);
|
|
18
|
+
* return () => window.removeEventListener('scroll', handleScroll);
|
|
19
|
+
* }, []);
|
|
20
|
+
*
|
|
21
|
+
* return <div>Scroll position: {throttledScrollY}px</div>;
|
|
22
|
+
* }
|
|
23
|
+
*
|
|
24
|
+
* @example
|
|
25
|
+
* // 무한 스크롤
|
|
26
|
+
* function InfiniteScroll() {
|
|
27
|
+
* const [scrollY, setScrollY] = useState(0);
|
|
28
|
+
* const throttledScrollY = useThrottle(scrollY, 300);
|
|
29
|
+
*
|
|
30
|
+
* useEffect(() => {
|
|
31
|
+
* const bottom = document.documentElement.scrollHeight - window.innerHeight;
|
|
32
|
+
* if (throttledScrollY >= bottom - 100) {
|
|
33
|
+
* loadMoreData();
|
|
34
|
+
* }
|
|
35
|
+
* }, [throttledScrollY]);
|
|
36
|
+
*
|
|
37
|
+
* return <div>...</div>;
|
|
38
|
+
* }
|
|
39
|
+
*
|
|
40
|
+
* @example
|
|
41
|
+
* // 검색 입력 (실시간 검색에 적합)
|
|
42
|
+
* function LiveSearch() {
|
|
43
|
+
* const [query, setQuery] = useState('');
|
|
44
|
+
* const throttledQuery = useThrottle(query, 500);
|
|
45
|
+
*
|
|
46
|
+
* useEffect(() => {
|
|
47
|
+
* // 500ms마다 최대 한 번씩만 API 호출
|
|
48
|
+
* if (throttledQuery) {
|
|
49
|
+
* searchAPI(throttledQuery);
|
|
50
|
+
* }
|
|
51
|
+
* }, [throttledQuery]);
|
|
52
|
+
*
|
|
53
|
+
* return <input value={query} onChange={(e) => setQuery(e.target.value)} />;
|
|
54
|
+
* }
|
|
55
|
+
*/
|
|
56
|
+
export declare function useThrottle<T>(value: T, interval?: number): T;
|
|
57
|
+
//# sourceMappingURL=useThrottle.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"useThrottle.d.ts","sourceRoot":"","sources":["../../src/hooks/useThrottle.ts"],"names":[],"mappings":"AAEA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAsDG;AACH,wBAAgB,WAAW,CAAC,CAAC,EAAE,KAAK,EAAE,CAAC,EAAE,QAAQ,GAAE,MAAY,GAAG,CAAC,CA0BlE"}
|
|
@@ -0,0 +1,80 @@
|
|
|
1
|
+
import { useState, useEffect, useRef } from 'react';
|
|
2
|
+
/**
|
|
3
|
+
* 값의 업데이트를 일정 시간 간격으로 제한하는 hook
|
|
4
|
+
* debounce와 달리 일정 간격마다 최신 값을 업데이트합니다.
|
|
5
|
+
*
|
|
6
|
+
* @param value - throttle할 값
|
|
7
|
+
* @param interval - 업데이트 간격 (밀리초, 기본값: 500ms)
|
|
8
|
+
* @returns throttle된 값
|
|
9
|
+
*
|
|
10
|
+
* @example
|
|
11
|
+
* // 스크롤 위치 추적 최적화
|
|
12
|
+
* function ScrollTracker() {
|
|
13
|
+
* const [scrollY, setScrollY] = useState(0);
|
|
14
|
+
* const throttledScrollY = useThrottle(scrollY, 200);
|
|
15
|
+
*
|
|
16
|
+
* useEffect(() => {
|
|
17
|
+
* const handleScroll = () => setScrollY(window.scrollY);
|
|
18
|
+
* window.addEventListener('scroll', handleScroll);
|
|
19
|
+
* return () => window.removeEventListener('scroll', handleScroll);
|
|
20
|
+
* }, []);
|
|
21
|
+
*
|
|
22
|
+
* return <div>Scroll position: {throttledScrollY}px</div>;
|
|
23
|
+
* }
|
|
24
|
+
*
|
|
25
|
+
* @example
|
|
26
|
+
* // 무한 스크롤
|
|
27
|
+
* function InfiniteScroll() {
|
|
28
|
+
* const [scrollY, setScrollY] = useState(0);
|
|
29
|
+
* const throttledScrollY = useThrottle(scrollY, 300);
|
|
30
|
+
*
|
|
31
|
+
* useEffect(() => {
|
|
32
|
+
* const bottom = document.documentElement.scrollHeight - window.innerHeight;
|
|
33
|
+
* if (throttledScrollY >= bottom - 100) {
|
|
34
|
+
* loadMoreData();
|
|
35
|
+
* }
|
|
36
|
+
* }, [throttledScrollY]);
|
|
37
|
+
*
|
|
38
|
+
* return <div>...</div>;
|
|
39
|
+
* }
|
|
40
|
+
*
|
|
41
|
+
* @example
|
|
42
|
+
* // 검색 입력 (실시간 검색에 적합)
|
|
43
|
+
* function LiveSearch() {
|
|
44
|
+
* const [query, setQuery] = useState('');
|
|
45
|
+
* const throttledQuery = useThrottle(query, 500);
|
|
46
|
+
*
|
|
47
|
+
* useEffect(() => {
|
|
48
|
+
* // 500ms마다 최대 한 번씩만 API 호출
|
|
49
|
+
* if (throttledQuery) {
|
|
50
|
+
* searchAPI(throttledQuery);
|
|
51
|
+
* }
|
|
52
|
+
* }, [throttledQuery]);
|
|
53
|
+
*
|
|
54
|
+
* return <input value={query} onChange={(e) => setQuery(e.target.value)} />;
|
|
55
|
+
* }
|
|
56
|
+
*/
|
|
57
|
+
export function useThrottle(value, interval = 500) {
|
|
58
|
+
const [throttledValue, setThrottledValue] = useState(value);
|
|
59
|
+
const lastExecuted = useRef(Date.now());
|
|
60
|
+
useEffect(() => {
|
|
61
|
+
// 마지막 실행으로부터 경과된 시간
|
|
62
|
+
const timeSinceLastExecution = Date.now() - lastExecuted.current;
|
|
63
|
+
if (timeSinceLastExecution >= interval) {
|
|
64
|
+
// 간격이 지났으면 즉시 업데이트
|
|
65
|
+
setThrottledValue(value);
|
|
66
|
+
lastExecuted.current = Date.now();
|
|
67
|
+
}
|
|
68
|
+
else {
|
|
69
|
+
// 아직 간격이 안 지났으면 남은 시간 후에 업데이트
|
|
70
|
+
const timer = setTimeout(() => {
|
|
71
|
+
setThrottledValue(value);
|
|
72
|
+
lastExecuted.current = Date.now();
|
|
73
|
+
}, interval - timeSinceLastExecution);
|
|
74
|
+
return () => {
|
|
75
|
+
clearTimeout(timer);
|
|
76
|
+
};
|
|
77
|
+
}
|
|
78
|
+
}, [value, interval]);
|
|
79
|
+
return throttledValue;
|
|
80
|
+
}
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* boolean 상태를 쉽게 토글할 수 있는 hook
|
|
3
|
+
*
|
|
4
|
+
* @param initialValue - 초기값 (기본값: false)
|
|
5
|
+
* @returns [현재 값, 토글 함수, 값 설정 함수]
|
|
6
|
+
*
|
|
7
|
+
* @example
|
|
8
|
+
* // 기본 사용
|
|
9
|
+
* function Modal() {
|
|
10
|
+
* const [isOpen, toggleOpen, setIsOpen] = useToggle(false);
|
|
11
|
+
*
|
|
12
|
+
* return (
|
|
13
|
+
* <>
|
|
14
|
+
* <button onClick={toggleOpen}>Toggle Modal</button>
|
|
15
|
+
* <button onClick={() => setIsOpen(true)}>Open Modal</button>
|
|
16
|
+
* <button onClick={() => setIsOpen(false)}>Close Modal</button>
|
|
17
|
+
* {isOpen && <div>Modal Content</div>}
|
|
18
|
+
* </>
|
|
19
|
+
* );
|
|
20
|
+
* }
|
|
21
|
+
*
|
|
22
|
+
* @example
|
|
23
|
+
* // 다크모드 토글
|
|
24
|
+
* function ThemeToggle() {
|
|
25
|
+
* const [isDark, toggleTheme] = useToggle(false);
|
|
26
|
+
*
|
|
27
|
+
* return (
|
|
28
|
+
* <button onClick={toggleTheme}>
|
|
29
|
+
* {isDark ? '🌙 Dark' : '☀️ Light'}
|
|
30
|
+
* </button>
|
|
31
|
+
* );
|
|
32
|
+
* }
|
|
33
|
+
*
|
|
34
|
+
* @example
|
|
35
|
+
* // 메뉴 열기/닫기
|
|
36
|
+
* function Sidebar() {
|
|
37
|
+
* const [isExpanded, toggleExpanded] = useToggle(true);
|
|
38
|
+
*
|
|
39
|
+
* return (
|
|
40
|
+
* <aside className={isExpanded ? 'expanded' : 'collapsed'}>
|
|
41
|
+
* <button onClick={toggleExpanded}>
|
|
42
|
+
* {isExpanded ? '◀' : '▶'}
|
|
43
|
+
* </button>
|
|
44
|
+
* </aside>
|
|
45
|
+
* );
|
|
46
|
+
* }
|
|
47
|
+
*/
|
|
48
|
+
export declare function useToggle(initialValue?: boolean): [boolean, () => void, (value: boolean) => void];
|
|
49
|
+
//# sourceMappingURL=useToggle.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"useToggle.d.ts","sourceRoot":"","sources":["../../src/hooks/useToggle.ts"],"names":[],"mappings":"AAEA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA8CG;AACH,wBAAgB,SAAS,CACvB,YAAY,GAAE,OAAe,GAC5B,CAAC,OAAO,EAAE,MAAM,IAAI,EAAE,CAAC,KAAK,EAAE,OAAO,KAAK,IAAI,CAAC,CASjD"}
|
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
import { useState, useCallback } from 'react';
|
|
2
|
+
/**
|
|
3
|
+
* boolean 상태를 쉽게 토글할 수 있는 hook
|
|
4
|
+
*
|
|
5
|
+
* @param initialValue - 초기값 (기본값: false)
|
|
6
|
+
* @returns [현재 값, 토글 함수, 값 설정 함수]
|
|
7
|
+
*
|
|
8
|
+
* @example
|
|
9
|
+
* // 기본 사용
|
|
10
|
+
* function Modal() {
|
|
11
|
+
* const [isOpen, toggleOpen, setIsOpen] = useToggle(false);
|
|
12
|
+
*
|
|
13
|
+
* return (
|
|
14
|
+
* <>
|
|
15
|
+
* <button onClick={toggleOpen}>Toggle Modal</button>
|
|
16
|
+
* <button onClick={() => setIsOpen(true)}>Open Modal</button>
|
|
17
|
+
* <button onClick={() => setIsOpen(false)}>Close Modal</button>
|
|
18
|
+
* {isOpen && <div>Modal Content</div>}
|
|
19
|
+
* </>
|
|
20
|
+
* );
|
|
21
|
+
* }
|
|
22
|
+
*
|
|
23
|
+
* @example
|
|
24
|
+
* // 다크모드 토글
|
|
25
|
+
* function ThemeToggle() {
|
|
26
|
+
* const [isDark, toggleTheme] = useToggle(false);
|
|
27
|
+
*
|
|
28
|
+
* return (
|
|
29
|
+
* <button onClick={toggleTheme}>
|
|
30
|
+
* {isDark ? '🌙 Dark' : '☀️ Light'}
|
|
31
|
+
* </button>
|
|
32
|
+
* );
|
|
33
|
+
* }
|
|
34
|
+
*
|
|
35
|
+
* @example
|
|
36
|
+
* // 메뉴 열기/닫기
|
|
37
|
+
* function Sidebar() {
|
|
38
|
+
* const [isExpanded, toggleExpanded] = useToggle(true);
|
|
39
|
+
*
|
|
40
|
+
* return (
|
|
41
|
+
* <aside className={isExpanded ? 'expanded' : 'collapsed'}>
|
|
42
|
+
* <button onClick={toggleExpanded}>
|
|
43
|
+
* {isExpanded ? '◀' : '▶'}
|
|
44
|
+
* </button>
|
|
45
|
+
* </aside>
|
|
46
|
+
* );
|
|
47
|
+
* }
|
|
48
|
+
*/
|
|
49
|
+
export function useToggle(initialValue = false) {
|
|
50
|
+
const [value, setValue] = useState(initialValue);
|
|
51
|
+
// 토글 함수는 리렌더링 시에도 동일한 참조를 유지
|
|
52
|
+
const toggle = useCallback(() => {
|
|
53
|
+
setValue((prev) => !prev);
|
|
54
|
+
}, []);
|
|
55
|
+
return [value, toggle, setValue];
|
|
56
|
+
}
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* 윈도우 크기 정보
|
|
3
|
+
*/
|
|
4
|
+
export interface WindowSize {
|
|
5
|
+
/** 윈도우 너비 (픽셀) */
|
|
6
|
+
width: number;
|
|
7
|
+
/** 윈도우 높이 (픽셀) */
|
|
8
|
+
height: number;
|
|
9
|
+
}
|
|
10
|
+
/**
|
|
11
|
+
* 윈도우 크기를 추적하는 hook
|
|
12
|
+
*
|
|
13
|
+
* @returns 윈도우의 현재 너비와 높이
|
|
14
|
+
*
|
|
15
|
+
* @example
|
|
16
|
+
* // 기본 사용
|
|
17
|
+
* function ResponsiveComponent() {
|
|
18
|
+
* const { width, height } = useWindowSize();
|
|
19
|
+
*
|
|
20
|
+
* return (
|
|
21
|
+
* <div>
|
|
22
|
+
* Window size: {width} x {height}
|
|
23
|
+
* </div>
|
|
24
|
+
* );
|
|
25
|
+
* }
|
|
26
|
+
*
|
|
27
|
+
* @example
|
|
28
|
+
* // 반응형 레이아웃
|
|
29
|
+
* function AdaptiveLayout() {
|
|
30
|
+
* const { width } = useWindowSize();
|
|
31
|
+
*
|
|
32
|
+
* if (width < 768) {
|
|
33
|
+
* return <MobileLayout />;
|
|
34
|
+
* } else if (width < 1024) {
|
|
35
|
+
* return <TabletLayout />;
|
|
36
|
+
* } else {
|
|
37
|
+
* return <DesktopLayout />;
|
|
38
|
+
* }
|
|
39
|
+
* }
|
|
40
|
+
*
|
|
41
|
+
* @example
|
|
42
|
+
* // 캔버스 크기 조정
|
|
43
|
+
* function Canvas() {
|
|
44
|
+
* const { width, height } = useWindowSize();
|
|
45
|
+
* const canvasRef = useRef<HTMLCanvasElement>(null);
|
|
46
|
+
*
|
|
47
|
+
* useEffect(() => {
|
|
48
|
+
* if (canvasRef.current) {
|
|
49
|
+
* canvasRef.current.width = width;
|
|
50
|
+
* canvasRef.current.height = height;
|
|
51
|
+
* }
|
|
52
|
+
* }, [width, height]);
|
|
53
|
+
*
|
|
54
|
+
* return <canvas ref={canvasRef} />;
|
|
55
|
+
* }
|
|
56
|
+
*/
|
|
57
|
+
export declare function useWindowSize(): WindowSize;
|
|
58
|
+
//# sourceMappingURL=useWindowSize.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"useWindowSize.d.ts","sourceRoot":"","sources":["../../src/hooks/useWindowSize.ts"],"names":[],"mappings":"AAEA;;GAEG;AACH,MAAM,WAAW,UAAU;IACzB,kBAAkB;IAClB,KAAK,EAAE,MAAM,CAAC;IACd,kBAAkB;IAClB,MAAM,EAAE,MAAM,CAAC;CAChB;AAED;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA8CG;AACH,wBAAgB,aAAa,IAAI,UAAU,CAoC1C"}
|
|
@@ -0,0 +1,79 @@
|
|
|
1
|
+
import { useState, useEffect } from 'react';
|
|
2
|
+
/**
|
|
3
|
+
* 윈도우 크기를 추적하는 hook
|
|
4
|
+
*
|
|
5
|
+
* @returns 윈도우의 현재 너비와 높이
|
|
6
|
+
*
|
|
7
|
+
* @example
|
|
8
|
+
* // 기본 사용
|
|
9
|
+
* function ResponsiveComponent() {
|
|
10
|
+
* const { width, height } = useWindowSize();
|
|
11
|
+
*
|
|
12
|
+
* return (
|
|
13
|
+
* <div>
|
|
14
|
+
* Window size: {width} x {height}
|
|
15
|
+
* </div>
|
|
16
|
+
* );
|
|
17
|
+
* }
|
|
18
|
+
*
|
|
19
|
+
* @example
|
|
20
|
+
* // 반응형 레이아웃
|
|
21
|
+
* function AdaptiveLayout() {
|
|
22
|
+
* const { width } = useWindowSize();
|
|
23
|
+
*
|
|
24
|
+
* if (width < 768) {
|
|
25
|
+
* return <MobileLayout />;
|
|
26
|
+
* } else if (width < 1024) {
|
|
27
|
+
* return <TabletLayout />;
|
|
28
|
+
* } else {
|
|
29
|
+
* return <DesktopLayout />;
|
|
30
|
+
* }
|
|
31
|
+
* }
|
|
32
|
+
*
|
|
33
|
+
* @example
|
|
34
|
+
* // 캔버스 크기 조정
|
|
35
|
+
* function Canvas() {
|
|
36
|
+
* const { width, height } = useWindowSize();
|
|
37
|
+
* const canvasRef = useRef<HTMLCanvasElement>(null);
|
|
38
|
+
*
|
|
39
|
+
* useEffect(() => {
|
|
40
|
+
* if (canvasRef.current) {
|
|
41
|
+
* canvasRef.current.width = width;
|
|
42
|
+
* canvasRef.current.height = height;
|
|
43
|
+
* }
|
|
44
|
+
* }, [width, height]);
|
|
45
|
+
*
|
|
46
|
+
* return <canvas ref={canvasRef} />;
|
|
47
|
+
* }
|
|
48
|
+
*/
|
|
49
|
+
export function useWindowSize() {
|
|
50
|
+
// SSR 환경에서는 기본값 사용
|
|
51
|
+
const [windowSize, setWindowSize] = useState(() => {
|
|
52
|
+
if (typeof window === 'undefined') {
|
|
53
|
+
return { width: 0, height: 0 };
|
|
54
|
+
}
|
|
55
|
+
return {
|
|
56
|
+
width: window.innerWidth,
|
|
57
|
+
height: window.innerHeight,
|
|
58
|
+
};
|
|
59
|
+
});
|
|
60
|
+
useEffect(() => {
|
|
61
|
+
// SSR 환경에서는 이벤트 리스너 추가하지 않음
|
|
62
|
+
if (typeof window === 'undefined') {
|
|
63
|
+
return;
|
|
64
|
+
}
|
|
65
|
+
const handleResize = () => {
|
|
66
|
+
setWindowSize({
|
|
67
|
+
width: window.innerWidth,
|
|
68
|
+
height: window.innerHeight,
|
|
69
|
+
});
|
|
70
|
+
};
|
|
71
|
+
// 초기 크기 설정
|
|
72
|
+
handleResize();
|
|
73
|
+
window.addEventListener('resize', handleResize);
|
|
74
|
+
return () => {
|
|
75
|
+
window.removeEventListener('resize', handleResize);
|
|
76
|
+
};
|
|
77
|
+
}, []);
|
|
78
|
+
return windowSize;
|
|
79
|
+
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "goodchuck-utils",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.6.0",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"main": "dist/index.js",
|
|
6
6
|
"types": "dist/index.d.ts",
|
|
@@ -12,6 +12,7 @@
|
|
|
12
12
|
"prepublishOnly": "npm run test:run && npm run build:tsc",
|
|
13
13
|
"build": "npm run build",
|
|
14
14
|
"build:tsc": "tsc",
|
|
15
|
+
"dev": "tsc --watch",
|
|
15
16
|
"test": "vitest",
|
|
16
17
|
"test:ui": "vitest --ui",
|
|
17
18
|
"test:run": "vitest run",
|
|
@@ -27,12 +28,28 @@
|
|
|
27
28
|
"types": "./dist/hooks/index.d.ts",
|
|
28
29
|
"import": "./dist/hooks/index.js",
|
|
29
30
|
"default": "./dist/hooks/index.js"
|
|
31
|
+
},
|
|
32
|
+
"./components": {
|
|
33
|
+
"types": "./dist/components/index.d.ts",
|
|
34
|
+
"import": "./dist/components/index.js",
|
|
35
|
+
"default": "./dist/components/index.js"
|
|
36
|
+
},
|
|
37
|
+
"./components/dev": {
|
|
38
|
+
"types": "./dist/components/dev/index.d.ts",
|
|
39
|
+
"import": "./dist/components/dev/index.js",
|
|
40
|
+
"default": "./dist/components/dev/index.js"
|
|
30
41
|
}
|
|
31
42
|
},
|
|
32
43
|
"typesVersions": {
|
|
33
44
|
"*": {
|
|
34
45
|
"hooks": [
|
|
35
46
|
"./dist/hooks/index.d.ts"
|
|
47
|
+
],
|
|
48
|
+
"components": [
|
|
49
|
+
"./dist/components/index.d.ts"
|
|
50
|
+
],
|
|
51
|
+
"components/dev": [
|
|
52
|
+
"./dist/components/dev/index.d.ts"
|
|
36
53
|
]
|
|
37
54
|
}
|
|
38
55
|
},
|
|
@@ -51,11 +68,15 @@
|
|
|
51
68
|
"vitest": "^4.0.16"
|
|
52
69
|
},
|
|
53
70
|
"peerDependencies": {
|
|
54
|
-
"react": ">=16.8.0"
|
|
71
|
+
"react": ">=16.8.0",
|
|
72
|
+
"react-dom": ">=16.8.0"
|
|
55
73
|
},
|
|
56
74
|
"peerDependenciesMeta": {
|
|
57
75
|
"react": {
|
|
58
76
|
"optional": true
|
|
77
|
+
},
|
|
78
|
+
"react-dom": {
|
|
79
|
+
"optional": true
|
|
59
80
|
}
|
|
60
81
|
}
|
|
61
82
|
}
|