goodchuck-utils 1.7.1 → 1.8.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/FormDevTools/FormDevTools.d.ts +50 -24
- package/dist/components/dev/FormDevTools/FormDevTools.d.ts.map +1 -1
- package/dist/components/dev/FormDevTools/FormDevTools.js +203 -50
- package/dist/components/dev/FormDevTools/styles.d.ts +2 -0
- package/dist/components/dev/FormDevTools/styles.d.ts.map +1 -1
- package/dist/components/dev/FormDevTools/styles.js +21 -0
- package/package.json +1 -1
|
@@ -13,6 +13,10 @@ export type Props = {
|
|
|
13
13
|
formState: FormState;
|
|
14
14
|
/** 현재 폼 values (watch() 결과) */
|
|
15
15
|
values?: Record<string, any>;
|
|
16
|
+
/** 초기값 (defaultValues 또는 reset()으로 설정한 원본 값, changedFields 계산에 사용) */
|
|
17
|
+
originalValues?: Record<string, any>;
|
|
18
|
+
/** Validation 스키마 정보 (zod, yup 등) - refine 조건 표시에 사용 */
|
|
19
|
+
validationSchema?: Record<string, any>;
|
|
16
20
|
/** 표시 위치 (기본값: 'bottom-left') */
|
|
17
21
|
position?: 'top-left' | 'top-right' | 'bottom-left' | 'bottom-right';
|
|
18
22
|
/** 패널 제목 (기본값: 'Form DevTools') */
|
|
@@ -29,29 +33,31 @@ export type Props = {
|
|
|
29
33
|
* import { FormDevTools } from 'goodchuck-utils/components/dev';
|
|
30
34
|
*
|
|
31
35
|
* function MyForm() {
|
|
36
|
+
* // 신규 폼: defaultValues 사용
|
|
37
|
+
* const defaultValues = {
|
|
38
|
+
* username: '',
|
|
39
|
+
* email: '',
|
|
40
|
+
* age: 0,
|
|
41
|
+
* };
|
|
42
|
+
*
|
|
32
43
|
* const { register, handleSubmit, formState, watch } = useForm({
|
|
33
|
-
* defaultValues
|
|
34
|
-
* username: '',
|
|
35
|
-
* email: '',
|
|
36
|
-
* age: 0,
|
|
37
|
-
* }
|
|
44
|
+
* defaultValues,
|
|
38
45
|
* });
|
|
39
46
|
*
|
|
40
|
-
* const values = watch();
|
|
41
|
-
*
|
|
42
|
-
* const onSubmit = (data) => {
|
|
43
|
-
* console.log(data);
|
|
44
|
-
* };
|
|
47
|
+
* const values = watch();
|
|
45
48
|
*
|
|
46
49
|
* return (
|
|
47
50
|
* <form onSubmit={handleSubmit(onSubmit)}>
|
|
48
|
-
* <input {...register('username'
|
|
49
|
-
* <input {...register('email'
|
|
50
|
-
* <input type="number" {...register('age', { min: 18 })} />
|
|
51
|
+
* <input {...register('username')} />
|
|
52
|
+
* <input {...register('email')} />
|
|
51
53
|
* <button type="submit">Submit</button>
|
|
52
54
|
*
|
|
53
55
|
* {import.meta.env.DEV && (
|
|
54
|
-
* <FormDevTools
|
|
56
|
+
* <FormDevTools
|
|
57
|
+
* formState={formState}
|
|
58
|
+
* values={values}
|
|
59
|
+
* originalValues={defaultValues}
|
|
60
|
+
* />
|
|
55
61
|
* )}
|
|
56
62
|
* </form>
|
|
57
63
|
* );
|
|
@@ -60,16 +66,36 @@ export type Props = {
|
|
|
60
66
|
*
|
|
61
67
|
* @example
|
|
62
68
|
* ```tsx
|
|
63
|
-
* //
|
|
64
|
-
* {
|
|
65
|
-
*
|
|
66
|
-
*
|
|
67
|
-
*
|
|
68
|
-
*
|
|
69
|
-
*
|
|
70
|
-
*
|
|
71
|
-
*
|
|
69
|
+
* // 수정 폼: reset()으로 초기값 설정
|
|
70
|
+
* function EditForm({ userData }) {
|
|
71
|
+
* const { register, handleSubmit, formState, watch, reset } = useForm();
|
|
72
|
+
*
|
|
73
|
+
* useEffect(() => {
|
|
74
|
+
* // 서버에서 가져온 데이터로 폼 초기화
|
|
75
|
+
* reset(userData);
|
|
76
|
+
* }, [userData, reset]);
|
|
77
|
+
*
|
|
78
|
+
* const values = watch();
|
|
79
|
+
*
|
|
80
|
+
* return (
|
|
81
|
+
* <form onSubmit={handleSubmit(onSubmit)}>
|
|
82
|
+
* <input {...register('username')} />
|
|
83
|
+
* <input {...register('email')} />
|
|
84
|
+
* <button type="submit">Update</button>
|
|
85
|
+
*
|
|
86
|
+
* {process.env.NODE_ENV === 'development' && (
|
|
87
|
+
* <FormDevTools
|
|
88
|
+
* formState={formState}
|
|
89
|
+
* values={values}
|
|
90
|
+
* originalValues={userData} // reset()으로 설정한 원본 값
|
|
91
|
+
* position="top-right"
|
|
92
|
+
* title="Edit Form Debug"
|
|
93
|
+
* />
|
|
94
|
+
* )}
|
|
95
|
+
* </form>
|
|
96
|
+
* );
|
|
97
|
+
* }
|
|
72
98
|
* ```
|
|
73
99
|
*/
|
|
74
|
-
export default function FormDevTools({ formState, values, position, title }: Props): React.JSX.Element;
|
|
100
|
+
export default function FormDevTools({ formState, values, originalValues, validationSchema, position, title }: Props): React.JSX.Element;
|
|
75
101
|
//# sourceMappingURL=FormDevTools.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"FormDevTools.d.ts","sourceRoot":"","sources":["../../../../src/components/dev/FormDevTools/FormDevTools.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAsC,MAAM,OAAO,CAAC;
|
|
1
|
+
{"version":3,"file":"FormDevTools.d.ts","sourceRoot":"","sources":["../../../../src/components/dev/FormDevTools/FormDevTools.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAsC,MAAM,OAAO,CAAC;AA0B3D,MAAM,MAAM,SAAS,GAAG;IACtB,MAAM,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,CAAC;IAC7B,MAAM,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,CAAC;IAC7B,WAAW,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,CAAC;IAClC,aAAa,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,CAAC;IACpC,OAAO,CAAC,EAAE,OAAO,CAAC;IAClB,YAAY,CAAC,EAAE,OAAO,CAAC;IACvB,WAAW,CAAC,EAAE,MAAM,CAAC;CACtB,CAAC;AAEF,MAAM,MAAM,KAAK,GAAG;IAClB,iCAAiC;IACjC,SAAS,EAAE,SAAS,CAAC;IACrB,+BAA+B;IAC/B,MAAM,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,CAAC;IAC7B,sEAAsE;IACtE,cAAc,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,CAAC;IACrC,wDAAwD;IACxD,gBAAgB,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,CAAC;IACvC,iCAAiC;IACjC,QAAQ,CAAC,EAAE,UAAU,GAAG,WAAW,GAAG,aAAa,GAAG,cAAc,CAAC;IACrE,mCAAmC;IACnC,KAAK,CAAC,EAAE,MAAM,CAAC;CAChB,CAAC;AAEF;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA0EG;AACH,MAAM,CAAC,OAAO,UAAU,YAAY,CAAC,EAAE,SAAS,EAAE,MAAM,EAAE,cAAc,EAAE,gBAAgB,EAAE,QAAwB,EAAE,KAAuB,EAAE,EAAE,KAAK,qBAyarJ"}
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import React, { useState, useRef, useEffect } from 'react';
|
|
2
2
|
import { useCopyToClipboard } from '../../../hooks/useCopyToClipboard';
|
|
3
|
-
import { getContainerStyle, getToggleButtonStyle, getPanelStyle, headerStyle, headerTitleStyle, getStatusBadgeStyle, getCopyButtonStyle, contentStyle, sectionTitleStyle, codeBlockStyle, errorItemStyle, errorLabelStyle, errorMessageStyle, statsContainerStyle, statCardStyle, statLabelStyle, statValueStyle, resizeHandleStyle, resizeHandleIndicatorStyle, } from './styles';
|
|
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의 상태를 실시간으로 시각화하는 개발용 컴포넌트
|
|
6
6
|
* form의 values, errors, dirtyFields, touchedFields 등을 한눈에 확인할 수 있습니다.
|
|
@@ -12,29 +12,31 @@ import { getContainerStyle, getToggleButtonStyle, getPanelStyle, headerStyle, he
|
|
|
12
12
|
* import { FormDevTools } from 'goodchuck-utils/components/dev';
|
|
13
13
|
*
|
|
14
14
|
* function MyForm() {
|
|
15
|
+
* // 신규 폼: defaultValues 사용
|
|
16
|
+
* const defaultValues = {
|
|
17
|
+
* username: '',
|
|
18
|
+
* email: '',
|
|
19
|
+
* age: 0,
|
|
20
|
+
* };
|
|
21
|
+
*
|
|
15
22
|
* const { register, handleSubmit, formState, watch } = useForm({
|
|
16
|
-
* defaultValues
|
|
17
|
-
* username: '',
|
|
18
|
-
* email: '',
|
|
19
|
-
* age: 0,
|
|
20
|
-
* }
|
|
23
|
+
* defaultValues,
|
|
21
24
|
* });
|
|
22
25
|
*
|
|
23
|
-
* const values = watch();
|
|
24
|
-
*
|
|
25
|
-
* const onSubmit = (data) => {
|
|
26
|
-
* console.log(data);
|
|
27
|
-
* };
|
|
26
|
+
* const values = watch();
|
|
28
27
|
*
|
|
29
28
|
* return (
|
|
30
29
|
* <form onSubmit={handleSubmit(onSubmit)}>
|
|
31
|
-
* <input {...register('username'
|
|
32
|
-
* <input {...register('email'
|
|
33
|
-
* <input type="number" {...register('age', { min: 18 })} />
|
|
30
|
+
* <input {...register('username')} />
|
|
31
|
+
* <input {...register('email')} />
|
|
34
32
|
* <button type="submit">Submit</button>
|
|
35
33
|
*
|
|
36
34
|
* {import.meta.env.DEV && (
|
|
37
|
-
* <FormDevTools
|
|
35
|
+
* <FormDevTools
|
|
36
|
+
* formState={formState}
|
|
37
|
+
* values={values}
|
|
38
|
+
* originalValues={defaultValues}
|
|
39
|
+
* />
|
|
38
40
|
* )}
|
|
39
41
|
* </form>
|
|
40
42
|
* );
|
|
@@ -43,19 +45,40 @@ import { getContainerStyle, getToggleButtonStyle, getPanelStyle, headerStyle, he
|
|
|
43
45
|
*
|
|
44
46
|
* @example
|
|
45
47
|
* ```tsx
|
|
46
|
-
* //
|
|
47
|
-
* {
|
|
48
|
-
*
|
|
49
|
-
*
|
|
50
|
-
*
|
|
51
|
-
*
|
|
52
|
-
*
|
|
53
|
-
*
|
|
54
|
-
*
|
|
48
|
+
* // 수정 폼: reset()으로 초기값 설정
|
|
49
|
+
* function EditForm({ userData }) {
|
|
50
|
+
* const { register, handleSubmit, formState, watch, reset } = useForm();
|
|
51
|
+
*
|
|
52
|
+
* useEffect(() => {
|
|
53
|
+
* // 서버에서 가져온 데이터로 폼 초기화
|
|
54
|
+
* reset(userData);
|
|
55
|
+
* }, [userData, reset]);
|
|
56
|
+
*
|
|
57
|
+
* const values = watch();
|
|
58
|
+
*
|
|
59
|
+
* return (
|
|
60
|
+
* <form onSubmit={handleSubmit(onSubmit)}>
|
|
61
|
+
* <input {...register('username')} />
|
|
62
|
+
* <input {...register('email')} />
|
|
63
|
+
* <button type="submit">Update</button>
|
|
64
|
+
*
|
|
65
|
+
* {process.env.NODE_ENV === 'development' && (
|
|
66
|
+
* <FormDevTools
|
|
67
|
+
* formState={formState}
|
|
68
|
+
* values={values}
|
|
69
|
+
* originalValues={userData} // reset()으로 설정한 원본 값
|
|
70
|
+
* position="top-right"
|
|
71
|
+
* title="Edit Form Debug"
|
|
72
|
+
* />
|
|
73
|
+
* )}
|
|
74
|
+
* </form>
|
|
75
|
+
* );
|
|
76
|
+
* }
|
|
55
77
|
* ```
|
|
56
78
|
*/
|
|
57
|
-
export default function FormDevTools({ formState, values, position = 'bottom-left', title = 'Form DevTools' }) {
|
|
79
|
+
export default function FormDevTools({ formState, values, originalValues, validationSchema, position = 'bottom-left', title = 'Form DevTools' }) {
|
|
58
80
|
const [isOpen, setIsOpen] = useState(false);
|
|
81
|
+
const [activeTab, setActiveTab] = useState('all');
|
|
59
82
|
const [panelPosition, setPanelPosition] = useState({ x: 0, y: 0 });
|
|
60
83
|
const [panelSize, setPanelSize] = useState({ width: 500, height: 400 });
|
|
61
84
|
const [isDragging, setIsDragging] = useState(false);
|
|
@@ -68,6 +91,7 @@ export default function FormDevTools({ formState, values, position = 'bottom-lef
|
|
|
68
91
|
const data = {
|
|
69
92
|
values,
|
|
70
93
|
errors: formState.errors,
|
|
94
|
+
changedFields,
|
|
71
95
|
dirtyFields: formState.dirtyFields,
|
|
72
96
|
touchedFields: formState.touchedFields,
|
|
73
97
|
isValid: formState.isValid,
|
|
@@ -80,6 +104,40 @@ export default function FormDevTools({ formState, values, position = 'bottom-lef
|
|
|
80
104
|
const errorCount = Object.keys(formState.errors || {}).length;
|
|
81
105
|
const dirtyFieldsCount = Object.keys(formState.dirtyFields || {}).length;
|
|
82
106
|
const touchedFieldsCount = Object.keys(formState.touchedFields || {}).length;
|
|
107
|
+
// Changed Fields 계산: dirtyFields를 기반으로 실제 변경된 값들을 추출
|
|
108
|
+
const getChangedFields = () => {
|
|
109
|
+
if (!formState.dirtyFields || !values)
|
|
110
|
+
return {};
|
|
111
|
+
const changed = {};
|
|
112
|
+
const getNestedValue = (obj, path) => {
|
|
113
|
+
return path.split('.').reduce((acc, key) => acc?.[key], obj);
|
|
114
|
+
};
|
|
115
|
+
const processDirtyFields = (dirty, prefix = '') => {
|
|
116
|
+
Object.keys(dirty).forEach((key) => {
|
|
117
|
+
const fullPath = prefix ? `${prefix}.${key}` : key;
|
|
118
|
+
const dirtyValue = dirty[key];
|
|
119
|
+
if (dirtyValue === true) {
|
|
120
|
+
// 최종 필드인 경우
|
|
121
|
+
const currentValue = getNestedValue(values, fullPath);
|
|
122
|
+
const originalValue = originalValues ? getNestedValue(originalValues, fullPath) : undefined;
|
|
123
|
+
if (JSON.stringify(currentValue) !== JSON.stringify(originalValue)) {
|
|
124
|
+
changed[fullPath] = {
|
|
125
|
+
from: originalValue,
|
|
126
|
+
to: currentValue,
|
|
127
|
+
};
|
|
128
|
+
}
|
|
129
|
+
}
|
|
130
|
+
else if (typeof dirtyValue === 'object' && dirtyValue !== null) {
|
|
131
|
+
// 중첩된 객체인 경우 재귀적으로 처리
|
|
132
|
+
processDirtyFields(dirtyValue, fullPath);
|
|
133
|
+
}
|
|
134
|
+
});
|
|
135
|
+
};
|
|
136
|
+
processDirtyFields(formState.dirtyFields);
|
|
137
|
+
return changed;
|
|
138
|
+
};
|
|
139
|
+
const changedFields = getChangedFields();
|
|
140
|
+
const changedFieldsCount = Object.keys(changedFields).length;
|
|
83
141
|
// 드래그 핸들러
|
|
84
142
|
useEffect(() => {
|
|
85
143
|
const handleMouseMove = (e) => {
|
|
@@ -163,34 +221,129 @@ export default function FormDevTools({ formState, values, position = 'bottom-lef
|
|
|
163
221
|
if (!isCopied)
|
|
164
222
|
e.currentTarget.style.backgroundColor = '#3b82f6';
|
|
165
223
|
} }, isCopied ? '✓ Copied' : 'Copy All')),
|
|
224
|
+
React.createElement("div", { style: tabContainerStyle },
|
|
225
|
+
React.createElement("button", { onClick: () => setActiveTab('all'), style: getTabStyle(activeTab === 'all'), onMouseEnter: (e) => {
|
|
226
|
+
if (activeTab !== 'all')
|
|
227
|
+
e.currentTarget.style.backgroundColor = '#f3f4f6';
|
|
228
|
+
}, onMouseLeave: (e) => {
|
|
229
|
+
if (activeTab !== 'all')
|
|
230
|
+
e.currentTarget.style.backgroundColor = 'transparent';
|
|
231
|
+
} }, "All"),
|
|
232
|
+
React.createElement("button", { onClick: () => setActiveTab('values'), style: getTabStyle(activeTab === 'values'), onMouseEnter: (e) => {
|
|
233
|
+
if (activeTab !== 'values')
|
|
234
|
+
e.currentTarget.style.backgroundColor = '#f3f4f6';
|
|
235
|
+
}, onMouseLeave: (e) => {
|
|
236
|
+
if (activeTab !== 'values')
|
|
237
|
+
e.currentTarget.style.backgroundColor = 'transparent';
|
|
238
|
+
} }, "Values"),
|
|
239
|
+
React.createElement("button", { onClick: () => setActiveTab('errors'), style: getTabStyle(activeTab === 'errors'), onMouseEnter: (e) => {
|
|
240
|
+
if (activeTab !== 'errors')
|
|
241
|
+
e.currentTarget.style.backgroundColor = '#f3f4f6';
|
|
242
|
+
}, onMouseLeave: (e) => {
|
|
243
|
+
if (activeTab !== 'errors')
|
|
244
|
+
e.currentTarget.style.backgroundColor = 'transparent';
|
|
245
|
+
} },
|
|
246
|
+
"Errors ",
|
|
247
|
+
errorCount > 0 && `(${errorCount})`),
|
|
248
|
+
React.createElement("button", { onClick: () => setActiveTab('changed'), style: getTabStyle(activeTab === 'changed'), onMouseEnter: (e) => {
|
|
249
|
+
if (activeTab !== 'changed')
|
|
250
|
+
e.currentTarget.style.backgroundColor = '#f3f4f6';
|
|
251
|
+
}, onMouseLeave: (e) => {
|
|
252
|
+
if (activeTab !== 'changed')
|
|
253
|
+
e.currentTarget.style.backgroundColor = 'transparent';
|
|
254
|
+
} },
|
|
255
|
+
"Changed ",
|
|
256
|
+
changedFieldsCount > 0 && `(${changedFieldsCount})`),
|
|
257
|
+
React.createElement("button", { onClick: () => setActiveTab('state'), style: getTabStyle(activeTab === 'state'), onMouseEnter: (e) => {
|
|
258
|
+
if (activeTab !== 'state')
|
|
259
|
+
e.currentTarget.style.backgroundColor = '#f3f4f6';
|
|
260
|
+
}, onMouseLeave: (e) => {
|
|
261
|
+
if (activeTab !== 'state')
|
|
262
|
+
e.currentTarget.style.backgroundColor = 'transparent';
|
|
263
|
+
} }, "State"),
|
|
264
|
+
validationSchema && (React.createElement("button", { onClick: () => setActiveTab('validation'), style: getTabStyle(activeTab === 'validation'), onMouseEnter: (e) => {
|
|
265
|
+
if (activeTab !== 'validation')
|
|
266
|
+
e.currentTarget.style.backgroundColor = '#f3f4f6';
|
|
267
|
+
}, onMouseLeave: (e) => {
|
|
268
|
+
if (activeTab !== 'validation')
|
|
269
|
+
e.currentTarget.style.backgroundColor = 'transparent';
|
|
270
|
+
} }, "Validation"))),
|
|
166
271
|
React.createElement("div", { style: contentStyle },
|
|
167
|
-
React.createElement(
|
|
168
|
-
React.createElement("div", { style:
|
|
169
|
-
React.createElement("div", { style:
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
React.createElement("div", { style:
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
React.createElement("div", { style:
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
React.createElement("div", { style:
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
272
|
+
activeTab === 'all' && (React.createElement(React.Fragment, null,
|
|
273
|
+
React.createElement("div", { style: statsContainerStyle },
|
|
274
|
+
React.createElement("div", { style: statCardStyle },
|
|
275
|
+
React.createElement("div", { style: statLabelStyle }, "Dirty Fields"),
|
|
276
|
+
React.createElement("div", { style: statValueStyle }, dirtyFieldsCount)),
|
|
277
|
+
React.createElement("div", { style: statCardStyle },
|
|
278
|
+
React.createElement("div", { style: statLabelStyle }, "Touched Fields"),
|
|
279
|
+
React.createElement("div", { style: statValueStyle }, touchedFieldsCount)),
|
|
280
|
+
React.createElement("div", { style: statCardStyle },
|
|
281
|
+
React.createElement("div", { style: statLabelStyle }, "Submit Count"),
|
|
282
|
+
React.createElement("div", { style: statValueStyle }, formState.submitCount || 0)),
|
|
283
|
+
React.createElement("div", { style: statCardStyle },
|
|
284
|
+
React.createElement("div", { style: statLabelStyle }, "Submitting"),
|
|
285
|
+
React.createElement("div", { style: statValueStyle }, formState.isSubmitting ? 'Yes' : 'No'))),
|
|
286
|
+
React.createElement("div", { style: sectionTitleStyle }, "Form Values"),
|
|
287
|
+
React.createElement("pre", { style: codeBlockStyle }, JSON.stringify(values || {}, null, 2)),
|
|
288
|
+
errorCount > 0 && (React.createElement(React.Fragment, null,
|
|
289
|
+
React.createElement("div", { style: sectionTitleStyle },
|
|
290
|
+
"Validation Errors (",
|
|
291
|
+
errorCount,
|
|
292
|
+
")"),
|
|
293
|
+
renderErrors())),
|
|
294
|
+
changedFieldsCount > 0 && (React.createElement(React.Fragment, null,
|
|
295
|
+
React.createElement("div", { style: sectionTitleStyle },
|
|
296
|
+
"Changed Fields (",
|
|
297
|
+
changedFieldsCount,
|
|
298
|
+
")"),
|
|
299
|
+
React.createElement("pre", { style: codeBlockStyle }, JSON.stringify(changedFields, null, 2)))),
|
|
300
|
+
dirtyFieldsCount > 0 && (React.createElement(React.Fragment, null,
|
|
301
|
+
React.createElement("div", { style: sectionTitleStyle }, "Dirty Fields"),
|
|
302
|
+
React.createElement("pre", { style: codeBlockStyle }, JSON.stringify(formState.dirtyFields || {}, null, 2)))),
|
|
303
|
+
touchedFieldsCount > 0 && (React.createElement(React.Fragment, null,
|
|
304
|
+
React.createElement("div", { style: sectionTitleStyle }, "Touched Fields"),
|
|
305
|
+
React.createElement("pre", { style: codeBlockStyle }, JSON.stringify(formState.touchedFields || {}, null, 2)))))),
|
|
306
|
+
activeTab === 'values' && (React.createElement(React.Fragment, null, values && Object.keys(values).length > 0 ? (React.createElement(React.Fragment, null,
|
|
307
|
+
React.createElement("div", { style: sectionTitleStyle }, "Form Values"),
|
|
308
|
+
React.createElement("pre", { style: codeBlockStyle }, JSON.stringify(values, null, 2)))) : (React.createElement("div", { style: { textAlign: 'center', color: '#9ca3af', padding: '40px 20px', fontSize: '13px' } },
|
|
309
|
+
React.createElement("div", { style: { marginBottom: '8px' } }, "No form values"),
|
|
310
|
+
React.createElement("div", { style: { fontSize: '11px', color: '#d1d5db' } }, "Form values will appear here"))))),
|
|
311
|
+
activeTab === 'errors' && (React.createElement(React.Fragment, null,
|
|
312
|
+
React.createElement("div", { style: sectionTitleStyle },
|
|
313
|
+
"Validation Errors ",
|
|
314
|
+
errorCount > 0 && `(${errorCount})`),
|
|
315
|
+
errorCount > 0 ? renderErrors() : (React.createElement("div", { style: { textAlign: 'center', color: '#9ca3af', padding: '20px', fontSize: '13px' } }, "No validation errors")))),
|
|
316
|
+
activeTab === 'changed' && (React.createElement(React.Fragment, null, changedFieldsCount > 0 ? (React.createElement(React.Fragment, null,
|
|
183
317
|
React.createElement("div", { style: sectionTitleStyle },
|
|
184
|
-
"
|
|
185
|
-
|
|
318
|
+
"Changed Fields (",
|
|
319
|
+
changedFieldsCount,
|
|
186
320
|
")"),
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
React.createElement("div", { style:
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
321
|
+
React.createElement("pre", { style: codeBlockStyle }, JSON.stringify(changedFields, null, 2)))) : (React.createElement("div", { style: { textAlign: 'center', color: '#9ca3af', padding: '40px 20px', fontSize: '13px' } },
|
|
322
|
+
React.createElement("div", { style: { marginBottom: '8px' } }, "No changed fields"),
|
|
323
|
+
React.createElement("div", { style: { fontSize: '11px', color: '#d1d5db' } }, "Fields will appear here when you modify form values"))))),
|
|
324
|
+
activeTab === 'state' && (React.createElement(React.Fragment, null,
|
|
325
|
+
React.createElement("div", { style: statsContainerStyle },
|
|
326
|
+
React.createElement("div", { style: statCardStyle },
|
|
327
|
+
React.createElement("div", { style: statLabelStyle }, "Dirty Fields"),
|
|
328
|
+
React.createElement("div", { style: statValueStyle }, dirtyFieldsCount)),
|
|
329
|
+
React.createElement("div", { style: statCardStyle },
|
|
330
|
+
React.createElement("div", { style: statLabelStyle }, "Touched Fields"),
|
|
331
|
+
React.createElement("div", { style: statValueStyle }, touchedFieldsCount)),
|
|
332
|
+
React.createElement("div", { style: statCardStyle },
|
|
333
|
+
React.createElement("div", { style: statLabelStyle }, "Submit Count"),
|
|
334
|
+
React.createElement("div", { style: statValueStyle }, formState.submitCount || 0)),
|
|
335
|
+
React.createElement("div", { style: statCardStyle },
|
|
336
|
+
React.createElement("div", { style: statLabelStyle }, "Submitting"),
|
|
337
|
+
React.createElement("div", { style: statValueStyle }, formState.isSubmitting ? 'Yes' : 'No'))),
|
|
338
|
+
dirtyFieldsCount > 0 && (React.createElement(React.Fragment, null,
|
|
339
|
+
React.createElement("div", { style: sectionTitleStyle }, "Dirty Fields"),
|
|
340
|
+
React.createElement("pre", { style: codeBlockStyle }, JSON.stringify(formState.dirtyFields || {}, null, 2)))),
|
|
341
|
+
touchedFieldsCount > 0 && (React.createElement(React.Fragment, null,
|
|
342
|
+
React.createElement("div", { style: sectionTitleStyle }, "Touched Fields"),
|
|
343
|
+
React.createElement("pre", { style: codeBlockStyle }, JSON.stringify(formState.touchedFields || {}, null, 2)))))),
|
|
344
|
+
activeTab === 'validation' && validationSchema && (React.createElement(React.Fragment, null,
|
|
345
|
+
React.createElement("div", { style: sectionTitleStyle }, "Validation Schema"),
|
|
346
|
+
React.createElement("pre", { style: codeBlockStyle }, JSON.stringify(validationSchema, null, 2))))),
|
|
194
347
|
React.createElement("div", { onMouseDown: handleResizeMouseDown, style: resizeHandleStyle }),
|
|
195
348
|
React.createElement("div", { onMouseDown: handleResizeMouseDown, style: resizeHandleIndicatorStyle })))));
|
|
196
349
|
}
|
|
@@ -28,6 +28,8 @@ export declare const headerStyle: CSSProperties;
|
|
|
28
28
|
export declare const headerTitleStyle: CSSProperties;
|
|
29
29
|
export declare const getStatusBadgeStyle: (isValid: boolean | undefined) => CSSProperties;
|
|
30
30
|
export declare const getCopyButtonStyle: (isCopied: boolean) => CSSProperties;
|
|
31
|
+
export declare const tabContainerStyle: CSSProperties;
|
|
32
|
+
export declare const getTabStyle: (isActive: boolean) => CSSProperties;
|
|
31
33
|
export declare const contentStyle: CSSProperties;
|
|
32
34
|
export declare const sectionTitleStyle: CSSProperties;
|
|
33
35
|
export declare const codeBlockStyle: CSSProperties;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"styles.d.ts","sourceRoot":"","sources":["../../../../src/components/dev/FormDevTools/styles.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,aAAa,EAAE,MAAM,OAAO,CAAC;AAEtC,MAAM,MAAM,UAAU,GAAG;IACvB,OAAO,CAAC,EAAE,OAAO,CAAC;IAClB,QAAQ,EAAE,OAAO,CAAC;IAClB,QAAQ,EAAE,OAAO,CAAC;IAClB,UAAU,EAAE,OAAO,CAAC;IACpB,QAAQ,EAAE,MAAM,CAAC;IACjB,aAAa,EAAE;QAAE,CAAC,EAAE,MAAM,CAAC;QAAC,CAAC,EAAE,MAAM,CAAA;KAAE,CAAC;IACxC,SAAS,EAAE;QAAE,KAAK,EAAE,MAAM,CAAC;QAAC,MAAM,EAAE,MAAM,CAAA;KAAE,CAAC;CAC9C,CAAC;AAEF,eAAO,MAAM,iBAAiB,QAAO,MAAM,CAAC,MAAM,EAAE,aAAa,CAK/D,CAAC;AAEH,eAAO,MAAM,iBAAiB,GAAI,UAAU,MAAM,KAAG,aAQpD,CAAC;AAEF,eAAO,MAAM,oBAAoB,GAAI,SAAS,OAAO,GAAG,SAAS,KAAG,aAclE,CAAC;AAEH,eAAO,MAAM,aAAa,GACxB,UAAU,MAAM,EAChB,eAAe;IAAE,CAAC,EAAE,MAAM,CAAC;IAAC,CAAC,EAAE,MAAM,CAAA;CAAE,EACvC,WAAW;IAAE,KAAK,EAAE,MAAM,CAAC;IAAC,MAAM,EAAE,MAAM,CAAA;CAAE,EAC5C,YAAY,OAAO,KAClB,aAoBF,CAAC;AAEF,eAAO,MAAM,WAAW,EAAE,aASzB,CAAC;AAEF,eAAO,MAAM,gBAAgB,EAAE,aAI9B,CAAC;AAEF,eAAO,MAAM,mBAAmB,GAAI,SAAS,OAAO,GAAG,SAAS,KAAG,aAUjE,CAAC;AAEH,eAAO,MAAM,kBAAkB,GAAI,UAAU,OAAO,KAAG,aAUrD,CAAC;AAEH,eAAO,MAAM,YAAY,EAAE,aAM1B,CAAC;AAEF,eAAO,MAAM,iBAAiB,EAAE,aAO/B,CAAC;AAEF,eAAO,MAAM,cAAc,EAAE,
|
|
1
|
+
{"version":3,"file":"styles.d.ts","sourceRoot":"","sources":["../../../../src/components/dev/FormDevTools/styles.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,aAAa,EAAE,MAAM,OAAO,CAAC;AAEtC,MAAM,MAAM,UAAU,GAAG;IACvB,OAAO,CAAC,EAAE,OAAO,CAAC;IAClB,QAAQ,EAAE,OAAO,CAAC;IAClB,QAAQ,EAAE,OAAO,CAAC;IAClB,UAAU,EAAE,OAAO,CAAC;IACpB,QAAQ,EAAE,MAAM,CAAC;IACjB,aAAa,EAAE;QAAE,CAAC,EAAE,MAAM,CAAC;QAAC,CAAC,EAAE,MAAM,CAAA;KAAE,CAAC;IACxC,SAAS,EAAE;QAAE,KAAK,EAAE,MAAM,CAAC;QAAC,MAAM,EAAE,MAAM,CAAA;KAAE,CAAC;CAC9C,CAAC;AAEF,eAAO,MAAM,iBAAiB,QAAO,MAAM,CAAC,MAAM,EAAE,aAAa,CAK/D,CAAC;AAEH,eAAO,MAAM,iBAAiB,GAAI,UAAU,MAAM,KAAG,aAQpD,CAAC;AAEF,eAAO,MAAM,oBAAoB,GAAI,SAAS,OAAO,GAAG,SAAS,KAAG,aAclE,CAAC;AAEH,eAAO,MAAM,aAAa,GACxB,UAAU,MAAM,EAChB,eAAe;IAAE,CAAC,EAAE,MAAM,CAAC;IAAC,CAAC,EAAE,MAAM,CAAA;CAAE,EACvC,WAAW;IAAE,KAAK,EAAE,MAAM,CAAC;IAAC,MAAM,EAAE,MAAM,CAAA;CAAE,EAC5C,YAAY,OAAO,KAClB,aAoBF,CAAC;AAEF,eAAO,MAAM,WAAW,EAAE,aASzB,CAAC;AAEF,eAAO,MAAM,gBAAgB,EAAE,aAI9B,CAAC;AAEF,eAAO,MAAM,mBAAmB,GAAI,SAAS,OAAO,GAAG,SAAS,KAAG,aAUjE,CAAC;AAEH,eAAO,MAAM,kBAAkB,GAAI,UAAU,OAAO,KAAG,aAUrD,CAAC;AAEH,eAAO,MAAM,iBAAiB,EAAE,aAK/B,CAAC;AAEF,eAAO,MAAM,WAAW,GAAI,UAAU,OAAO,KAAG,aAY9C,CAAC;AAEH,eAAO,MAAM,YAAY,EAAE,aAM1B,CAAC;AAEF,eAAO,MAAM,iBAAiB,EAAE,aAO/B,CAAC;AAEF,eAAO,MAAM,cAAc,EAAE,aAY5B,CAAC;AAEF,eAAO,MAAM,cAAc,EAAE,aAM5B,CAAC;AAEF,eAAO,MAAM,eAAe,EAAE,aAK7B,CAAC;AAEF,eAAO,MAAM,iBAAiB,EAAE,aAG/B,CAAC;AAEF,eAAO,MAAM,mBAAmB,EAAE,aAKjC,CAAC;AAEF,eAAO,MAAM,aAAa,EAAE,aAK3B,CAAC;AAEF,eAAO,MAAM,cAAc,EAAE,aAM5B,CAAC;AAEF,eAAO,MAAM,cAAc,EAAE,aAI5B,CAAC;AAEF,eAAO,MAAM,iBAAiB,EAAE,aAS/B,CAAC;AAEF,eAAO,MAAM,0BAA0B,EAAE,aAUxC,CAAC"}
|
|
@@ -86,6 +86,25 @@ export const getCopyButtonStyle = (isCopied) => ({
|
|
|
86
86
|
fontWeight: 600,
|
|
87
87
|
transition: 'background-color 0.2s',
|
|
88
88
|
});
|
|
89
|
+
export const tabContainerStyle = {
|
|
90
|
+
display: 'flex',
|
|
91
|
+
borderBottom: '1px solid #e5e7eb',
|
|
92
|
+
backgroundColor: '#f9fafb',
|
|
93
|
+
overflowX: 'auto',
|
|
94
|
+
};
|
|
95
|
+
export const getTabStyle = (isActive) => ({
|
|
96
|
+
flex: '0 0 auto',
|
|
97
|
+
padding: '10px 16px',
|
|
98
|
+
border: 'none',
|
|
99
|
+
backgroundColor: isActive ? 'white' : 'transparent',
|
|
100
|
+
color: isActive ? '#8b5cf6' : '#6b7280',
|
|
101
|
+
cursor: 'pointer',
|
|
102
|
+
fontSize: '13px',
|
|
103
|
+
fontWeight: isActive ? 600 : 400,
|
|
104
|
+
borderBottom: isActive ? '2px solid #8b5cf6' : '2px solid transparent',
|
|
105
|
+
transition: 'all 0.15s',
|
|
106
|
+
whiteSpace: 'nowrap',
|
|
107
|
+
});
|
|
89
108
|
export const contentStyle = {
|
|
90
109
|
flex: 1,
|
|
91
110
|
minHeight: 0, // flexbox에서 스크롤이 제대로 작동하도록 필수
|
|
@@ -111,6 +130,8 @@ export const codeBlockStyle = {
|
|
|
111
130
|
maxHeight: '300px',
|
|
112
131
|
marginBottom: '16px',
|
|
113
132
|
color: '#111827', // 검은색 텍스트
|
|
133
|
+
textAlign: 'left', // 왼쪽 정렬
|
|
134
|
+
whiteSpace: 'pre', // 공백과 줄바꿈 유지
|
|
114
135
|
};
|
|
115
136
|
export const errorItemStyle = {
|
|
116
137
|
padding: '8px 12px',
|