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.
Files changed (61) hide show
  1. package/dist/components/dev/ApiLogger.d.ts +136 -0
  2. package/dist/components/dev/ApiLogger.d.ts.map +1 -0
  3. package/dist/components/dev/ApiLogger.js +408 -0
  4. package/dist/components/dev/DevPanel.d.ts +32 -0
  5. package/dist/components/dev/DevPanel.d.ts.map +1 -0
  6. package/dist/components/dev/DevPanel.js +196 -0
  7. package/dist/components/dev/FormDevTools/FormDevTools.d.ts +75 -0
  8. package/dist/components/dev/FormDevTools/FormDevTools.d.ts.map +1 -0
  9. package/dist/components/dev/FormDevTools/FormDevTools.js +218 -0
  10. package/dist/components/dev/FormDevTools/index.d.ts +3 -0
  11. package/dist/components/dev/FormDevTools/index.d.ts.map +1 -0
  12. package/dist/components/dev/FormDevTools/index.js +1 -0
  13. package/dist/components/dev/FormDevTools/styles.d.ts +45 -0
  14. package/dist/components/dev/FormDevTools/styles.d.ts.map +1 -0
  15. package/dist/components/dev/FormDevTools/styles.js +187 -0
  16. package/dist/components/dev/FormDevTools.d.ts +76 -0
  17. package/dist/components/dev/FormDevTools.d.ts.map +1 -0
  18. package/dist/components/dev/FormDevTools.js +399 -0
  19. package/dist/components/dev/IdSelector.d.ts +50 -0
  20. package/dist/components/dev/IdSelector.d.ts.map +1 -0
  21. package/dist/components/dev/IdSelector.js +129 -0
  22. package/dist/components/dev/WindowSizeDisplay.d.ts +44 -0
  23. package/dist/components/dev/WindowSizeDisplay.d.ts.map +1 -0
  24. package/dist/components/dev/WindowSizeDisplay.js +74 -0
  25. package/dist/components/dev/ZIndexDebugger.d.ts +32 -0
  26. package/dist/components/dev/ZIndexDebugger.d.ts.map +1 -0
  27. package/dist/components/dev/ZIndexDebugger.js +184 -0
  28. package/dist/components/dev/index.d.ts +15 -0
  29. package/dist/components/dev/index.d.ts.map +1 -0
  30. package/dist/components/dev/index.js +12 -0
  31. package/dist/components/index.d.ts +8 -0
  32. package/dist/components/index.d.ts.map +1 -0
  33. package/dist/components/index.js +7 -0
  34. package/dist/hooks/index.d.ts +8 -0
  35. package/dist/hooks/index.d.ts.map +1 -1
  36. package/dist/hooks/index.js +11 -0
  37. package/dist/hooks/useCopyToClipboard.d.ts +67 -0
  38. package/dist/hooks/useCopyToClipboard.d.ts.map +1 -0
  39. package/dist/hooks/useCopyToClipboard.js +79 -0
  40. package/dist/hooks/useDebounce.d.ts +47 -0
  41. package/dist/hooks/useDebounce.d.ts.map +1 -0
  42. package/dist/hooks/useDebounce.js +60 -0
  43. package/dist/hooks/useEventListener.d.ts +79 -0
  44. package/dist/hooks/useEventListener.d.ts.map +1 -0
  45. package/dist/hooks/useEventListener.js +33 -0
  46. package/dist/hooks/useIntersectionObserver.d.ts +109 -0
  47. package/dist/hooks/useIntersectionObserver.d.ts.map +1 -0
  48. package/dist/hooks/useIntersectionObserver.js +128 -0
  49. package/dist/hooks/usePrevious.d.ts +58 -0
  50. package/dist/hooks/usePrevious.d.ts.map +1 -0
  51. package/dist/hooks/usePrevious.js +67 -0
  52. package/dist/hooks/useThrottle.d.ts +57 -0
  53. package/dist/hooks/useThrottle.d.ts.map +1 -0
  54. package/dist/hooks/useThrottle.js +80 -0
  55. package/dist/hooks/useToggle.d.ts +49 -0
  56. package/dist/hooks/useToggle.d.ts.map +1 -0
  57. package/dist/hooks/useToggle.js +56 -0
  58. package/dist/hooks/useWindowSize.d.ts +58 -0
  59. package/dist/hooks/useWindowSize.d.ts.map +1 -0
  60. package/dist/hooks/useWindowSize.js +79 -0
  61. 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.4.2",
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
  }