@blastlabs/utils 1.11.1 → 1.12.1

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 (151) hide show
  1. package/README.md +20 -13
  2. package/dist/components/auth/AuthGuard.d.ts +95 -0
  3. package/dist/components/auth/AuthGuard.d.ts.map +1 -0
  4. package/dist/components/auth/AuthGuard.js +103 -0
  5. package/dist/components/auth/index.d.ts +5 -0
  6. package/dist/components/auth/index.d.ts.map +1 -0
  7. package/dist/components/auth/index.js +4 -0
  8. package/dist/components/dev/ApiLogger.d.ts +2 -2
  9. package/dist/components/dev/ApiLogger.js +3 -3
  10. package/dist/components/dev/DevPanel.d.ts +1 -1
  11. package/dist/components/dev/DevPanel.js +2 -2
  12. package/dist/components/dev/FormDevTools/FormDevTools.d.ts +1 -1
  13. package/dist/components/dev/FormDevTools/FormDevTools.js +2 -2
  14. package/dist/components/dev/FormDevTools/index.d.ts +1 -1
  15. package/dist/components/dev/FormDevTools/index.d.ts.map +1 -1
  16. package/dist/components/dev/{IdSelector.d.ts → IdSelector/IdSelector.d.ts} +3 -4
  17. package/dist/components/dev/IdSelector/IdSelector.d.ts.map +1 -0
  18. package/dist/components/dev/IdSelector/IdSelector.js +60 -0
  19. package/dist/components/dev/IdSelector/IdSelector.test.d.ts +2 -0
  20. package/dist/components/dev/IdSelector/IdSelector.test.d.ts.map +1 -0
  21. package/dist/components/dev/IdSelector/IdSelector.test.js +203 -0
  22. package/dist/components/dev/IdSelector/LoginCard.d.ts +17 -0
  23. package/dist/components/dev/IdSelector/LoginCard.d.ts.map +1 -0
  24. package/dist/components/dev/IdSelector/LoginCard.js +16 -0
  25. package/dist/components/dev/IdSelector/index.d.ts +3 -0
  26. package/dist/components/dev/IdSelector/index.d.ts.map +1 -0
  27. package/dist/components/dev/IdSelector/index.js +1 -0
  28. package/dist/components/dev/IdSelector/styles.d.ts +16 -0
  29. package/dist/components/dev/IdSelector/styles.d.ts.map +1 -0
  30. package/dist/components/dev/IdSelector/styles.js +66 -0
  31. package/dist/components/dev/WindowSizeDisplay.d.ts +1 -1
  32. package/dist/components/dev/WindowSizeDisplay.js +2 -2
  33. package/dist/components/dev/ZIndexDebugger.d.ts +1 -1
  34. package/dist/components/dev/ZIndexDebugger.js +1 -1
  35. package/dist/components/dev/index.d.ts +2 -1
  36. package/dist/components/dev/index.d.ts.map +1 -1
  37. package/dist/components/index.d.ts +1 -0
  38. package/dist/components/index.d.ts.map +1 -1
  39. package/dist/components/index.js +1 -0
  40. package/dist/hooks/auth/__tests__/useAuth.test.d.ts +2 -0
  41. package/dist/hooks/auth/__tests__/useAuth.test.d.ts.map +1 -0
  42. package/dist/hooks/auth/__tests__/useAuth.test.js +139 -0
  43. package/dist/hooks/auth/index.d.ts +5 -0
  44. package/dist/hooks/auth/index.d.ts.map +1 -0
  45. package/dist/hooks/auth/index.js +4 -0
  46. package/dist/hooks/auth/useAuth.d.ts +275 -0
  47. package/dist/hooks/auth/useAuth.d.ts.map +1 -0
  48. package/dist/hooks/auth/useAuth.js +384 -0
  49. package/dist/hooks/event/index.d.ts +6 -0
  50. package/dist/hooks/event/index.d.ts.map +1 -0
  51. package/dist/hooks/event/index.js +5 -0
  52. package/dist/hooks/event/useClickOutside.d.ts.map +1 -0
  53. package/dist/hooks/event/useEventListener.d.ts.map +1 -0
  54. package/dist/hooks/form/__tests__/useCRUDForm.test.d.ts +2 -0
  55. package/dist/hooks/form/__tests__/useCRUDForm.test.d.ts.map +1 -0
  56. package/dist/hooks/form/__tests__/useCRUDForm.test.js +487 -0
  57. package/dist/hooks/form/index.d.ts +5 -0
  58. package/dist/hooks/form/index.d.ts.map +1 -0
  59. package/dist/hooks/form/index.js +4 -0
  60. package/dist/hooks/form/useCRUDForm.d.ts +211 -0
  61. package/dist/hooks/form/useCRUDForm.d.ts.map +1 -0
  62. package/dist/hooks/form/useCRUDForm.js +287 -0
  63. package/dist/hooks/index.d.ts +10 -14
  64. package/dist/hooks/index.d.ts.map +1 -1
  65. package/dist/hooks/index.js +18 -19
  66. package/dist/hooks/performance/index.d.ts +7 -0
  67. package/dist/hooks/performance/index.d.ts.map +1 -0
  68. package/dist/hooks/performance/index.js +6 -0
  69. package/dist/hooks/performance/useDebounce.d.ts.map +1 -0
  70. package/dist/hooks/performance/useIntersectionObserver.d.ts.map +1 -0
  71. package/dist/hooks/performance/useThrottle.d.ts.map +1 -0
  72. package/dist/hooks/state/index.d.ts +6 -0
  73. package/dist/hooks/state/index.d.ts.map +1 -0
  74. package/dist/hooks/state/index.js +5 -0
  75. package/dist/hooks/state/usePrevious.d.ts.map +1 -0
  76. package/dist/hooks/state/useToggle.d.ts.map +1 -0
  77. package/dist/hooks/storage/index.d.ts +7 -0
  78. package/dist/hooks/storage/index.d.ts.map +1 -0
  79. package/dist/hooks/storage/index.js +6 -0
  80. package/dist/hooks/storage/useCopyToClipboard.d.ts.map +1 -0
  81. package/dist/hooks/storage/useLocalStorage.d.ts.map +1 -0
  82. package/dist/hooks/storage/useSessionStorage.d.ts.map +1 -0
  83. package/dist/hooks/time/__tests__/useCountdown.test.d.ts +2 -0
  84. package/dist/hooks/time/__tests__/useCountdown.test.d.ts.map +1 -0
  85. package/dist/hooks/time/__tests__/useCountdown.test.js +150 -0
  86. package/dist/hooks/time/__tests__/useInterval.test.d.ts +2 -0
  87. package/dist/hooks/time/__tests__/useInterval.test.d.ts.map +1 -0
  88. package/dist/hooks/time/__tests__/useInterval.test.js +39 -0
  89. package/dist/hooks/time/__tests__/useStopwatch.test.d.ts +2 -0
  90. package/dist/hooks/time/__tests__/useStopwatch.test.d.ts.map +1 -0
  91. package/dist/hooks/time/__tests__/useStopwatch.test.js +149 -0
  92. package/dist/hooks/time/index.d.ts +7 -0
  93. package/dist/hooks/time/index.d.ts.map +1 -0
  94. package/dist/hooks/time/index.js +6 -0
  95. package/dist/hooks/time/useCountdown.d.ts +116 -0
  96. package/dist/hooks/time/useCountdown.d.ts.map +1 -0
  97. package/dist/hooks/time/useCountdown.js +152 -0
  98. package/dist/hooks/time/useInterval.d.ts +40 -0
  99. package/dist/hooks/time/useInterval.d.ts.map +1 -0
  100. package/dist/hooks/time/useInterval.js +61 -0
  101. package/dist/hooks/time/useStopwatch.d.ts +142 -0
  102. package/dist/hooks/time/useStopwatch.d.ts.map +1 -0
  103. package/dist/hooks/time/useStopwatch.js +179 -0
  104. package/dist/hooks/ui/index.d.ts +7 -0
  105. package/dist/hooks/ui/index.d.ts.map +1 -0
  106. package/dist/hooks/ui/index.js +6 -0
  107. package/dist/hooks/ui/useMediaQuery.d.ts.map +1 -0
  108. package/dist/hooks/ui/useTabs.d.ts +33 -0
  109. package/dist/hooks/ui/useTabs.d.ts.map +1 -0
  110. package/dist/hooks/ui/useTabs.js +117 -0
  111. package/dist/hooks/ui/useWindowSize.d.ts.map +1 -0
  112. package/dist/index.js +1 -1
  113. package/package.json +14 -4
  114. package/dist/components/dev/IdSelector.d.ts.map +0 -1
  115. package/dist/components/dev/IdSelector.js +0 -129
  116. package/dist/hooks/useClickOutside.d.ts.map +0 -1
  117. package/dist/hooks/useCopyToClipboard.d.ts.map +0 -1
  118. package/dist/hooks/useDebounce.d.ts.map +0 -1
  119. package/dist/hooks/useEventListener.d.ts.map +0 -1
  120. package/dist/hooks/useIntersectionObserver.d.ts.map +0 -1
  121. package/dist/hooks/useLocalStorage.d.ts.map +0 -1
  122. package/dist/hooks/useMediaQuery.d.ts.map +0 -1
  123. package/dist/hooks/usePrevious.d.ts.map +0 -1
  124. package/dist/hooks/useSessionStorage.d.ts.map +0 -1
  125. package/dist/hooks/useThrottle.d.ts.map +0 -1
  126. package/dist/hooks/useToggle.d.ts.map +0 -1
  127. package/dist/hooks/useWindowSize.d.ts.map +0 -1
  128. /package/dist/hooks/{useClickOutside.d.ts → event/useClickOutside.d.ts} +0 -0
  129. /package/dist/hooks/{useClickOutside.js → event/useClickOutside.js} +0 -0
  130. /package/dist/hooks/{useEventListener.d.ts → event/useEventListener.d.ts} +0 -0
  131. /package/dist/hooks/{useEventListener.js → event/useEventListener.js} +0 -0
  132. /package/dist/hooks/{useDebounce.d.ts → performance/useDebounce.d.ts} +0 -0
  133. /package/dist/hooks/{useDebounce.js → performance/useDebounce.js} +0 -0
  134. /package/dist/hooks/{useIntersectionObserver.d.ts → performance/useIntersectionObserver.d.ts} +0 -0
  135. /package/dist/hooks/{useIntersectionObserver.js → performance/useIntersectionObserver.js} +0 -0
  136. /package/dist/hooks/{useThrottle.d.ts → performance/useThrottle.d.ts} +0 -0
  137. /package/dist/hooks/{useThrottle.js → performance/useThrottle.js} +0 -0
  138. /package/dist/hooks/{usePrevious.d.ts → state/usePrevious.d.ts} +0 -0
  139. /package/dist/hooks/{usePrevious.js → state/usePrevious.js} +0 -0
  140. /package/dist/hooks/{useToggle.d.ts → state/useToggle.d.ts} +0 -0
  141. /package/dist/hooks/{useToggle.js → state/useToggle.js} +0 -0
  142. /package/dist/hooks/{useCopyToClipboard.d.ts → storage/useCopyToClipboard.d.ts} +0 -0
  143. /package/dist/hooks/{useCopyToClipboard.js → storage/useCopyToClipboard.js} +0 -0
  144. /package/dist/hooks/{useLocalStorage.d.ts → storage/useLocalStorage.d.ts} +0 -0
  145. /package/dist/hooks/{useLocalStorage.js → storage/useLocalStorage.js} +0 -0
  146. /package/dist/hooks/{useSessionStorage.d.ts → storage/useSessionStorage.d.ts} +0 -0
  147. /package/dist/hooks/{useSessionStorage.js → storage/useSessionStorage.js} +0 -0
  148. /package/dist/hooks/{useMediaQuery.d.ts → ui/useMediaQuery.d.ts} +0 -0
  149. /package/dist/hooks/{useMediaQuery.js → ui/useMediaQuery.js} +0 -0
  150. /package/dist/hooks/{useWindowSize.d.ts → ui/useWindowSize.d.ts} +0 -0
  151. /package/dist/hooks/{useWindowSize.js → ui/useWindowSize.js} +0 -0
package/README.md CHANGED
@@ -1,15 +1,17 @@
1
- # goodchuck-utils
1
+ # @blastlabs/utils
2
+
3
+ > **⚠️ 주의: 이 라이브러리는 사내 개발용이며, 현재 테스트 단계입니다.**
2
4
 
3
5
  React 프로젝트에서 자주 사용하는 유틸리티 훅(Hooks)과 개발용 컴포넌트 모음입니다.
4
6
 
5
7
  ## 설치
6
8
 
7
9
  ```bash
8
- npm install goodchuck-utils
10
+ npm install @blastlabs/utils
9
11
  # or
10
- yarn add goodchuck-utils
12
+ yarn add @blastlabs/utils
11
13
  # or
12
- pnpm add goodchuck-utils
14
+ pnpm add @blastlabs/utils
13
15
  ```
14
16
 
15
17
  ## 기능
@@ -19,7 +21,7 @@ pnpm add goodchuck-utils
19
21
  프로젝트에서 자주 사용하는 커스텀 훅들을 제공합니다.
20
22
 
21
23
  ```typescript
22
- import { useDebounce, useToggle, useCopyToClipboard } from 'goodchuck-utils/hooks';
24
+ import { useDebounce, useToggle, useCopyToClipboard } from '@blastlabs/utils/hooks';
23
25
  ```
24
26
 
25
27
  #### 사용 가능한 Hooks
@@ -38,7 +40,7 @@ import { useDebounce, useToggle, useCopyToClipboard } from 'goodchuck-utils/hook
38
40
  개발 환경에서만 사용하는 유틸리티 컴포넌트들입니다.
39
41
 
40
42
  ```typescript
41
- import { DevPanel, FormDevTools, ApiLogger } from 'goodchuck-utils/components/dev';
43
+ import { DevPanel, FormDevTools, ApiLogger } from '@blastlabs/utils/components/dev';
42
44
  ```
43
45
 
44
46
  #### IdSelector
@@ -46,7 +48,7 @@ import { DevPanel, FormDevTools, ApiLogger } from 'goodchuck-utils/components/de
46
48
  개발 환경에서 여러 계정으로 빠르게 로그인할 수 있는 컴포넌트입니다.
47
49
 
48
50
  ```tsx
49
- import { IdSelector } from 'goodchuck-utils/components/dev';
51
+ import { IdSelector } from '@blastlabs/utils/components/dev';
50
52
 
51
53
  const devAccounts = [
52
54
  { id: 'admin', pw: 'admin123', memo: '관리자' },
@@ -75,7 +77,7 @@ react-hook-form의 상태를 실시간으로 시각화하는 개발용 컴포넌
75
77
 
76
78
  ```tsx
77
79
  import { useForm } from 'react-hook-form';
78
- import { FormDevTools } from 'goodchuck-utils/components/dev';
80
+ import { FormDevTools } from '@blastlabs/utils/components/dev';
79
81
 
80
82
  function MyForm() {
81
83
  const form = useForm({
@@ -112,7 +114,7 @@ API 요청/응답을 로깅하고 모니터링하는 컴포넌트입니다.
112
114
 
113
115
  ```tsx
114
116
  import axios from 'axios';
115
- import { ApiLogger, addApiLog } from 'goodchuck-utils/components/dev';
117
+ import { ApiLogger, addApiLog } from '@blastlabs/utils/components/dev';
116
118
 
117
119
  // Axios interceptor 설정
118
120
  axios.interceptors.request.use(
@@ -178,7 +180,7 @@ function App() {
178
180
  여러 개발 도구를 하나의 패널에서 관리할 수 있는 통합 컴포넌트입니다.
179
181
 
180
182
  ```tsx
181
- import { DevPanel } from 'goodchuck-utils/components/dev';
183
+ import { DevPanel } from '@blastlabs/utils/components/dev';
182
184
 
183
185
  function App() {
184
186
  return (
@@ -200,7 +202,7 @@ function App() {
200
202
  페이지의 모든 z-index 값을 시각화하고 디버깅하는 도구입니다.
201
203
 
202
204
  ```tsx
203
- import { ZIndexDebugger } from 'goodchuck-utils/components/dev';
205
+ import { ZIndexDebugger } from '@blastlabs/utils/components/dev';
204
206
 
205
207
  function App() {
206
208
  return (
@@ -222,7 +224,7 @@ function App() {
222
224
  현재 윈도우 크기를 화면에 표시하는 간단한 컴포넌트입니다.
223
225
 
224
226
  ```tsx
225
- import { WindowSizeDisplay } from 'goodchuck-utils/components/dev';
227
+ import { WindowSizeDisplay } from '@blastlabs/utils/components/dev';
226
228
 
227
229
  function App() {
228
230
  return (
@@ -250,7 +252,7 @@ function App() {
250
252
  모든 컴포넌트와 훅은 TypeScript로 작성되었으며, 타입 정의가 포함되어 있습니다.
251
253
 
252
254
  ```typescript
253
- import type { FormDevToolsProps, ApiLogEntry } from 'goodchuck-utils/components/dev';
255
+ import type { FormDevToolsProps, ApiLogEntry } from '@blastlabs/utils/components/dev';
254
256
  ```
255
257
 
256
258
  ## 주의사항
@@ -258,6 +260,11 @@ import type { FormDevToolsProps, ApiLogEntry } from 'goodchuck-utils/components/
258
260
  - **개발용 컴포넌트(`components/dev`)는 프로덕션 환경에서 제외하는 것을 권장합니다.**
259
261
  - 개발 환경 구분을 위해 `import.meta.env.DEV` (Vite) 또는 `process.env.NODE_ENV === 'development'` (CRA)를 사용하세요.
260
262
 
263
+ ## 문의
264
+
265
+ - goodchuck852@gmail.com
266
+ - taehyunyang@blast-team.com
267
+
261
268
  ## 라이선스
262
269
 
263
270
  ISC
@@ -0,0 +1,95 @@
1
+ import React from 'react';
2
+ import { ReactNode } from 'react';
3
+ import { UseAuthOptions } from '../../hooks/auth';
4
+ type AuthGuardProps = {
5
+ /** 인증된 경우 렌더링할 컴포넌트 */
6
+ children: ReactNode;
7
+ /** 로딩 중 표시할 컴포넌트 */
8
+ fallback?: ReactNode;
9
+ /** 인증되지 않은 경우 호출되는 콜백 (리다이렉트 로직 구현) */
10
+ onUnauthenticated?: () => void;
11
+ /** 로딩 완료 후 인증되지 않은 경우 렌더링할 컴포넌트 (onUnauthenticated가 없을 때) */
12
+ unauthenticatedFallback?: ReactNode;
13
+ /** useAuth 훅 옵션 (선택사항) */
14
+ authOptions?: UseAuthOptions;
15
+ };
16
+ /**
17
+ * 인증 가드 컴포넌트
18
+ * 인증이 필요한 페이지를 보호하는 컴포넌트
19
+ *
20
+ * 내부적으로 useAuth 훅을 호출하여 localStorage/sessionStorage에서 인증 상태를 확인합니다.
21
+ * 각 AuthGuard는 독립적으로 동작하며, storage를 통해 자동으로 동기화됩니다.
22
+ *
23
+ * @example
24
+ * ```tsx
25
+ * // 기본 사용 (localStorage 자동 로드)
26
+ * import { AuthGuard } from '@blastlabs/utils';
27
+ * import { useRouter } from 'next/navigation';
28
+ *
29
+ * function ProtectedPage() {
30
+ * const router = useRouter();
31
+ *
32
+ * return (
33
+ * <AuthGuard
34
+ * authOptions={{ autoLoadUser: true }}
35
+ * onUnauthenticated={() => router.push('/login')}
36
+ * fallback={<LoadingSpinner />}
37
+ * >
38
+ * <YourProtectedContent />
39
+ * </AuthGuard>
40
+ * );
41
+ * }
42
+ * ```
43
+ *
44
+ * @example
45
+ * ```tsx
46
+ * // React Router에서 사용
47
+ * import { useNavigate } from 'react-router-dom';
48
+ *
49
+ * function ProtectedPage() {
50
+ * const navigate = useNavigate();
51
+ *
52
+ * return (
53
+ * <AuthGuard
54
+ * authOptions={{ autoLoadUser: true }}
55
+ * onUnauthenticated={() => navigate('/login')}
56
+ * >
57
+ * <YourProtectedContent />
58
+ * </AuthGuard>
59
+ * );
60
+ * }
61
+ * ```
62
+ *
63
+ * @example
64
+ * ```tsx
65
+ * // 리다이렉트 없이 대체 UI 표시
66
+ * <AuthGuard
67
+ * authOptions={{ autoLoadUser: true }}
68
+ * unauthenticatedFallback={
69
+ * <div>
70
+ * <h1>로그인이 필요합니다</h1>
71
+ * <Link to="/login">로그인 페이지로 이동</Link>
72
+ * </div>
73
+ * }
74
+ * >
75
+ * <YourProtectedContent />
76
+ * </AuthGuard>
77
+ * ```
78
+ *
79
+ * @example
80
+ * ```tsx
81
+ * // 여러 페이지에서 사용해도 storage로 자동 동기화
82
+ * // Page1.tsx
83
+ * <AuthGuard authOptions={{ autoLoadUser: true }} onUnauthenticated={...}>
84
+ * <Page1Content />
85
+ * </AuthGuard>
86
+ *
87
+ * // Page2.tsx - 동일한 인증 상태 공유 (localStorage 사용)
88
+ * <AuthGuard authOptions={{ autoLoadUser: true }} onUnauthenticated={...}>
89
+ * <Page2Content />
90
+ * </AuthGuard>
91
+ * ```
92
+ */
93
+ export declare function AuthGuard({ children, fallback, onUnauthenticated, unauthenticatedFallback, authOptions, }: AuthGuardProps): React.JSX.Element | null;
94
+ export {};
95
+ //# sourceMappingURL=AuthGuard.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"AuthGuard.d.ts","sourceRoot":"","sources":["../../../src/components/auth/AuthGuard.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAK,MAAM,OAAO,CAAC;AAC1B,OAAO,EAAE,SAAS,EAAa,MAAM,OAAO,CAAC;AAC7C,OAAO,EAA0B,cAAc,EAAE,MAAM,kBAAkB,CAAC;AAE1E,KAAK,cAAc,GAAG;IACpB,uBAAuB;IACvB,QAAQ,EAAE,SAAS,CAAC;IACpB,oBAAoB;IACpB,QAAQ,CAAC,EAAE,SAAS,CAAC;IACrB,uCAAuC;IACvC,iBAAiB,CAAC,EAAE,MAAM,IAAI,CAAC;IAC/B,6DAA6D;IAC7D,uBAAuB,CAAC,EAAE,SAAS,CAAC;IACpC,0BAA0B;IAC1B,WAAW,CAAC,EAAE,cAAc,CAAC;CAC9B,CAAC;AAEF;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA4EG;AACH,wBAAgB,SAAS,CAAC,EACxB,QAAQ,EACR,QAAQ,EACR,iBAAiB,EACjB,uBAAuB,EACvB,WAAW,GACZ,EAAE,cAAc,4BA0BhB"}
@@ -0,0 +1,103 @@
1
+ import React from 'react';
2
+ import { useEffect } from 'react';
3
+ import { useAuth as useAuthHook } from '../../hooks/auth';
4
+ /**
5
+ * 인증 가드 컴포넌트
6
+ * 인증이 필요한 페이지를 보호하는 컴포넌트
7
+ *
8
+ * 내부적으로 useAuth 훅을 호출하여 localStorage/sessionStorage에서 인증 상태를 확인합니다.
9
+ * 각 AuthGuard는 독립적으로 동작하며, storage를 통해 자동으로 동기화됩니다.
10
+ *
11
+ * @example
12
+ * ```tsx
13
+ * // 기본 사용 (localStorage 자동 로드)
14
+ * import { AuthGuard } from '@blastlabs/utils';
15
+ * import { useRouter } from 'next/navigation';
16
+ *
17
+ * function ProtectedPage() {
18
+ * const router = useRouter();
19
+ *
20
+ * return (
21
+ * <AuthGuard
22
+ * authOptions={{ autoLoadUser: true }}
23
+ * onUnauthenticated={() => router.push('/login')}
24
+ * fallback={<LoadingSpinner />}
25
+ * >
26
+ * <YourProtectedContent />
27
+ * </AuthGuard>
28
+ * );
29
+ * }
30
+ * ```
31
+ *
32
+ * @example
33
+ * ```tsx
34
+ * // React Router에서 사용
35
+ * import { useNavigate } from 'react-router-dom';
36
+ *
37
+ * function ProtectedPage() {
38
+ * const navigate = useNavigate();
39
+ *
40
+ * return (
41
+ * <AuthGuard
42
+ * authOptions={{ autoLoadUser: true }}
43
+ * onUnauthenticated={() => navigate('/login')}
44
+ * >
45
+ * <YourProtectedContent />
46
+ * </AuthGuard>
47
+ * );
48
+ * }
49
+ * ```
50
+ *
51
+ * @example
52
+ * ```tsx
53
+ * // 리다이렉트 없이 대체 UI 표시
54
+ * <AuthGuard
55
+ * authOptions={{ autoLoadUser: true }}
56
+ * unauthenticatedFallback={
57
+ * <div>
58
+ * <h1>로그인이 필요합니다</h1>
59
+ * <Link to="/login">로그인 페이지로 이동</Link>
60
+ * </div>
61
+ * }
62
+ * >
63
+ * <YourProtectedContent />
64
+ * </AuthGuard>
65
+ * ```
66
+ *
67
+ * @example
68
+ * ```tsx
69
+ * // 여러 페이지에서 사용해도 storage로 자동 동기화
70
+ * // Page1.tsx
71
+ * <AuthGuard authOptions={{ autoLoadUser: true }} onUnauthenticated={...}>
72
+ * <Page1Content />
73
+ * </AuthGuard>
74
+ *
75
+ * // Page2.tsx - 동일한 인증 상태 공유 (localStorage 사용)
76
+ * <AuthGuard authOptions={{ autoLoadUser: true }} onUnauthenticated={...}>
77
+ * <Page2Content />
78
+ * </AuthGuard>
79
+ * ```
80
+ */
81
+ export function AuthGuard({ children, fallback, onUnauthenticated, unauthenticatedFallback, authOptions, }) {
82
+ const { isAuthenticated, isLoading } = useAuthHook(authOptions);
83
+ useEffect(() => {
84
+ if (!isLoading && !isAuthenticated && onUnauthenticated) {
85
+ onUnauthenticated();
86
+ }
87
+ }, [isAuthenticated, isLoading, onUnauthenticated]);
88
+ // 로딩 중이면 fallback 표시
89
+ if (isLoading) {
90
+ return React.createElement(React.Fragment, null, fallback || null);
91
+ }
92
+ // 인증되지 않았으면
93
+ if (!isAuthenticated) {
94
+ // onUnauthenticated 콜백이 있으면 리다이렉트 중이므로 null
95
+ if (onUnauthenticated) {
96
+ return null;
97
+ }
98
+ // 콜백이 없으면 unauthenticatedFallback 표시
99
+ return React.createElement(React.Fragment, null, unauthenticatedFallback || null);
100
+ }
101
+ // 인증되었으면 자식 컴포넌트 렌더링
102
+ return React.createElement(React.Fragment, null, children);
103
+ }
@@ -0,0 +1,5 @@
1
+ /**
2
+ * Authentication Components
3
+ */
4
+ export * from './AuthGuard';
5
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/components/auth/index.ts"],"names":[],"mappings":"AAAA;;GAEG;AAEH,cAAc,aAAa,CAAC"}
@@ -0,0 +1,4 @@
1
+ /**
2
+ * Authentication Components
3
+ */
4
+ export * from './AuthGuard';
@@ -34,7 +34,7 @@ export declare function clearApiLogs(): void;
34
34
  * ```tsx
35
35
  * // Axios interceptor 사용 (가장 일반적)
36
36
  * import axios from 'axios';
37
- * import { ApiLogger, addApiLog } from 'goodchuck-utils/components/dev';
37
+ * import { ApiLogger, addApiLog } from '@blastlabs/utils/components/dev';
38
38
  *
39
39
  * // Request interceptor
40
40
  * axios.interceptors.request.use(
@@ -92,7 +92,7 @@ export declare function clearApiLogs(): void;
92
92
  * @example
93
93
  * ```tsx
94
94
  * // fetch wrapper 사용
95
- * import { addApiLog } from 'goodchuck-utils/components/dev';
95
+ * import { addApiLog } from '@blastlabs/utils/components/dev';
96
96
  *
97
97
  * const originalFetch = window.fetch;
98
98
  * window.fetch = async (...args) => {
@@ -1,5 +1,5 @@
1
1
  import React, { useState, useEffect } from 'react';
2
- import { useCopyToClipboard } from '../../hooks/useCopyToClipboard';
2
+ import { useCopyToClipboard } from '../../hooks';
3
3
  // 전역 로그 저장소
4
4
  let globalLogs = [];
5
5
  let logListeners = [];
@@ -31,7 +31,7 @@ export function clearApiLogs() {
31
31
  * ```tsx
32
32
  * // Axios interceptor 사용 (가장 일반적)
33
33
  * import axios from 'axios';
34
- * import { ApiLogger, addApiLog } from 'goodchuck-utils/components/dev';
34
+ * import { ApiLogger, addApiLog } from '@blastlabs/utils/components/dev';
35
35
  *
36
36
  * // Request interceptor
37
37
  * axios.interceptors.request.use(
@@ -89,7 +89,7 @@ export function clearApiLogs() {
89
89
  * @example
90
90
  * ```tsx
91
91
  * // fetch wrapper 사용
92
- * import { addApiLog } from 'goodchuck-utils/components/dev';
92
+ * import { addApiLog } from '@blastlabs/utils/components/dev';
93
93
  *
94
94
  * const originalFetch = window.fetch;
95
95
  * window.fetch = async (...args) => {
@@ -10,7 +10,7 @@ type Props = {
10
10
  * @example
11
11
  * ```tsx
12
12
  * // Vite 프로젝트
13
- * import { DevPanel } from 'goodchuck-utils/components/dev';
13
+ * import { DevPanel } from '@blastlabs/utils/components/dev';
14
14
  *
15
15
  * function App() {
16
16
  * return (
@@ -1,5 +1,5 @@
1
1
  import React, { useState, useRef } from 'react';
2
- import { useWindowSize } from '../../hooks/useWindowSize';
2
+ import { useWindowSize } from '../../hooks';
3
3
  /**
4
4
  * 개발자 도구 패널
5
5
  * 여러 개발용 도구를 하나의 패널에서 관리할 수 있습니다.
@@ -7,7 +7,7 @@ import { useWindowSize } from '../../hooks/useWindowSize';
7
7
  * @example
8
8
  * ```tsx
9
9
  * // Vite 프로젝트
10
- * import { DevPanel } from 'goodchuck-utils/components/dev';
10
+ * import { DevPanel } from '@blastlabs/utils/components/dev';
11
11
  *
12
12
  * function App() {
13
13
  * return (
@@ -45,7 +45,7 @@ export type Props = {
45
45
  * ```tsx
46
46
  * // Vite 프로젝트 - 간단한 사용법
47
47
  * import { useForm } from 'react-hook-form';
48
- * import { FormDevTools } from 'goodchuck-utils/components/dev';
48
+ * import { FormDevTools } from '@blastlabs/utils/components/dev';
49
49
  *
50
50
  * function MyForm() {
51
51
  * const form = useForm({
@@ -1,5 +1,5 @@
1
1
  import React, { useState, useRef, useEffect } from 'react';
2
- import { useCopyToClipboard } from '../../../hooks/useCopyToClipboard';
2
+ import { useCopyToClipboard } from '../../../hooks';
3
3
  import { getContainerStyle, getToggleButtonStyle, getPanelStyle, headerStyle, headerTitleStyle, getStatusBadgeStyle, getCopyButtonStyle, tabContainerStyle, getTabStyle, contentStyle, sectionTitleStyle, codeBlockStyle, errorItemStyle, errorLabelStyle, errorMessageStyle, statsContainerStyle, statCardStyle, statLabelStyle, statValueStyle, resizeHandleStyle, resizeHandleIndicatorStyle, } from './styles';
4
4
  /**
5
5
  * react-hook-form의 상태를 실시간으로 시각화하는 개발용 컴포넌트
@@ -9,7 +9,7 @@ import { getContainerStyle, getToggleButtonStyle, getPanelStyle, headerStyle, he
9
9
  * ```tsx
10
10
  * // Vite 프로젝트 - 간단한 사용법
11
11
  * import { useForm } from 'react-hook-form';
12
- * import { FormDevTools } from 'goodchuck-utils/components/dev';
12
+ * import { FormDevTools } from '@blastlabs/utils/components/dev';
13
13
  *
14
14
  * function MyForm() {
15
15
  * const form = useForm({
@@ -1,3 +1,3 @@
1
1
  export { default } from './FormDevTools';
2
- export type { Props as FormDevToolsProps, UseFormReturn } from './FormDevTools';
2
+ export type { Props as FormDevToolsProps } from './FormDevTools';
3
3
  //# sourceMappingURL=index.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../../src/components/dev/FormDevTools/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,OAAO,EAAE,MAAM,gBAAgB,CAAC;AACzC,YAAY,EAAE,KAAK,IAAI,iBAAiB,EAAE,aAAa,EAAE,MAAM,gBAAgB,CAAC"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../../src/components/dev/FormDevTools/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,OAAO,EAAE,MAAM,gBAAgB,CAAC;AACzC,YAAY,EAAE,KAAK,IAAI,iBAAiB,EAAE,MAAM,gBAAgB,CAAC"}
@@ -1,10 +1,10 @@
1
1
  import React from 'react';
2
- type LoginInfo = {
2
+ export type LoginInfo = {
3
3
  id: string;
4
4
  pw: string;
5
5
  memo: string;
6
6
  };
7
- type Props = {
7
+ export type Props = {
8
8
  onLogin: (email: string, password: string) => Promise<void>;
9
9
  infos: LoginInfo[];
10
10
  };
@@ -14,7 +14,7 @@ type Props = {
14
14
  * @example
15
15
  * ```tsx
16
16
  * // Vite 프로젝트
17
- * import { IdSelector } from 'goodchuck-utils/components/dev';
17
+ * import { IdSelector } from '@blastlabs/utils/components/dev';
18
18
  *
19
19
  * function LoginPage() {
20
20
  * const handleLogin = async (email: string, password: string) => {
@@ -46,5 +46,4 @@ type Props = {
46
46
  * ```
47
47
  */
48
48
  export default function IdSelector({ onLogin, infos }: Props): React.JSX.Element;
49
- export {};
50
49
  //# sourceMappingURL=IdSelector.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"IdSelector.d.ts","sourceRoot":"","sources":["../../../../src/components/dev/IdSelector/IdSelector.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAmB,MAAM,OAAO,CAAC;AAIxC,MAAM,MAAM,SAAS,GAAG;IACtB,EAAE,EAAE,MAAM,CAAC;IACX,EAAE,EAAE,MAAM,CAAC;IACX,IAAI,EAAE,MAAM,CAAC;CACd,CAAC;AAEF,MAAM,MAAM,KAAK,GAAG;IAClB,OAAO,EAAE,CAAC,KAAK,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,KAAK,OAAO,CAAC,IAAI,CAAC,CAAC;IAC5D,KAAK,EAAE,SAAS,EAAE,CAAC;CACpB,CAAC;AAEF;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAoCG;AACH,MAAM,CAAC,OAAO,UAAU,UAAU,CAAC,EAAE,OAAO,EAAE,KAAK,EAAE,EAAE,KAAK,qBAoC3D"}
@@ -0,0 +1,60 @@
1
+ import React, { useState } from 'react';
2
+ import { LoginCard } from './LoginCard';
3
+ import { getContainerStyle, getHeaderStyle } from './styles';
4
+ /**
5
+ * 개발용 로그인 shortcut 컴포넌트
6
+ *
7
+ * @example
8
+ * ```tsx
9
+ * // Vite 프로젝트
10
+ * import { IdSelector } from '@blastlabs/utils/components/dev';
11
+ *
12
+ * function LoginPage() {
13
+ * const handleLogin = async (email: string, password: string) => {
14
+ * await loginAPI(email, password);
15
+ * };
16
+ *
17
+ * const devAccounts = [
18
+ * { id: 'admin@test.com', pw: 'admin123', memo: '관리자' },
19
+ * { id: 'user@test.com', pw: 'user123', memo: '일반 사용자' },
20
+ * ];
21
+ *
22
+ * return (
23
+ * <div>
24
+ * <LoginForm />
25
+ * {import.meta.env.DEV && (
26
+ * <IdSelector onLogin={handleLogin} infos={devAccounts} />
27
+ * )}
28
+ * </div>
29
+ * );
30
+ * }
31
+ * ```
32
+ *
33
+ * @example
34
+ * ```tsx
35
+ * // Create React App 프로젝트
36
+ * {process.env.NODE_ENV === 'development' && (
37
+ * <IdSelector onLogin={handleLogin} infos={devAccounts} />
38
+ * )}
39
+ * ```
40
+ */
41
+ export default function IdSelector({ onLogin, infos }) {
42
+ const [loading, setLoading] = useState(null);
43
+ const [hoveredIndex, setHoveredIndex] = useState(null);
44
+ const [hoveredButton, setHoveredButton] = useState(null);
45
+ const handleQuickLogin = async (info, index) => {
46
+ setLoading(index);
47
+ try {
48
+ await onLogin(info.id, info.pw);
49
+ }
50
+ catch (error) {
51
+ // 에러는 무시하고 상태만 해제 (에러 처리는 onLogin 호출자가 담당)
52
+ }
53
+ finally {
54
+ setLoading(null);
55
+ }
56
+ };
57
+ return (React.createElement("div", { style: getContainerStyle() },
58
+ React.createElement("div", { style: getHeaderStyle() }, "\uD83D\uDE80 \uAC1C\uBC1C\uC6A9 \uBE60\uB978 \uB85C\uADF8\uC778"),
59
+ infos.map((info, index) => (React.createElement(LoginCard, { key: index, info: info, index: index, loading: loading, hoveredIndex: hoveredIndex, hoveredButton: hoveredButton, onMouseEnter: () => setHoveredIndex(index), onMouseLeave: () => setHoveredIndex(null), onButtonMouseEnter: () => setHoveredButton(index), onButtonMouseLeave: () => setHoveredButton(null), onLogin: handleQuickLogin })))));
60
+ }
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=IdSelector.test.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"IdSelector.test.d.ts","sourceRoot":"","sources":["../../../../src/components/dev/IdSelector/IdSelector.test.tsx"],"names":[],"mappings":""}