goodchuck-utils 1.5.0 → 1.7.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 (28) 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 +196 -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 +43 -0
  14. package/dist/components/dev/FormDevTools/styles.d.ts.map +1 -0
  15. package/dist/components/dev/FormDevTools/styles.js +176 -0
  16. package/dist/components/dev/IdSelector.d.ts +10 -1
  17. package/dist/components/dev/IdSelector.d.ts.map +1 -1
  18. package/dist/components/dev/IdSelector.js +89 -12
  19. package/dist/components/dev/WindowSizeDisplay.d.ts +44 -0
  20. package/dist/components/dev/WindowSizeDisplay.d.ts.map +1 -0
  21. package/dist/components/dev/WindowSizeDisplay.js +74 -0
  22. package/dist/components/dev/ZIndexDebugger.d.ts +32 -0
  23. package/dist/components/dev/ZIndexDebugger.d.ts.map +1 -0
  24. package/dist/components/dev/ZIndexDebugger.js +184 -0
  25. package/dist/components/dev/index.d.ts +7 -0
  26. package/dist/components/dev/index.d.ts.map +1 -1
  27. package/dist/components/dev/index.js +5 -0
  28. package/package.json +4 -2
@@ -0,0 +1,196 @@
1
+ import React, { useState, useRef } from 'react';
2
+ import { useWindowSize } from '../../hooks/useWindowSize';
3
+ /**
4
+ * 개발자 도구 패널
5
+ * 여러 개발용 도구를 하나의 패널에서 관리할 수 있습니다.
6
+ *
7
+ * @example
8
+ * ```tsx
9
+ * // Vite 프로젝트
10
+ * import { DevPanel } from 'goodchuck-utils/components/dev';
11
+ *
12
+ * function App() {
13
+ * return (
14
+ * <div>
15
+ * {import.meta.env.DEV && <DevPanel />}
16
+ * </div>
17
+ * );
18
+ * }
19
+ * ```
20
+ *
21
+ * @example
22
+ * ```tsx
23
+ * // Create React App 프로젝트
24
+ * {process.env.NODE_ENV === 'development' && <DevPanel position="top-left" />}
25
+ * ```
26
+ */
27
+ export default function DevPanel({ position = 'bottom-right' }) {
28
+ const [isOpen, setIsOpen] = useState(false);
29
+ const [showWindowSize, setShowWindowSize] = useState(false);
30
+ const [showRenderCount, setShowRenderCount] = useState(false);
31
+ const renderCount = useRef(0);
32
+ const { width, height } = useWindowSize();
33
+ // 렌더 카운트 증가
34
+ renderCount.current += 1;
35
+ const positionStyles = {
36
+ 'top-left': { top: 16, left: 16 },
37
+ 'top-right': { top: 16, right: 16 },
38
+ 'bottom-left': { bottom: 16, left: 16 },
39
+ 'bottom-right': { bottom: 16, right: 16 },
40
+ };
41
+ const containerStyle = {
42
+ position: 'fixed',
43
+ ...positionStyles[position],
44
+ zIndex: 99999,
45
+ fontFamily: 'system-ui, -apple-system, sans-serif',
46
+ };
47
+ const toggleButtonStyle = {
48
+ width: '48px',
49
+ height: '48px',
50
+ borderRadius: '50%',
51
+ backgroundColor: '#3b82f6',
52
+ color: 'white',
53
+ border: 'none',
54
+ cursor: 'pointer',
55
+ fontSize: '20px',
56
+ fontWeight: 'bold',
57
+ boxShadow: '0 4px 6px rgba(0, 0, 0, 0.1)',
58
+ display: 'flex',
59
+ alignItems: 'center',
60
+ justifyContent: 'center',
61
+ transition: 'background-color 0.2s',
62
+ };
63
+ const panelStyle = {
64
+ position: 'absolute',
65
+ bottom: position.includes('bottom') ? '60px' : undefined,
66
+ top: position.includes('top') ? '60px' : undefined,
67
+ right: position.includes('right') ? '0' : undefined,
68
+ left: position.includes('left') ? '0' : undefined,
69
+ backgroundColor: 'white',
70
+ borderRadius: '12px',
71
+ boxShadow: '0 10px 25px rgba(0, 0, 0, 0.15)',
72
+ border: '1px solid #e5e7eb',
73
+ minWidth: '280px',
74
+ overflow: 'hidden',
75
+ color: '#111827',
76
+ };
77
+ const headerStyle = {
78
+ padding: '12px 16px',
79
+ borderBottom: '1px solid #e5e7eb',
80
+ fontWeight: 'bold',
81
+ fontSize: '14px',
82
+ color: '#111827',
83
+ backgroundColor: '#f9fafb',
84
+ };
85
+ const contentStyle = {
86
+ padding: '8px',
87
+ display: 'flex',
88
+ flexDirection: 'column',
89
+ gap: '4px',
90
+ };
91
+ const toggleItemStyle = {
92
+ display: 'flex',
93
+ alignItems: 'center',
94
+ justifyContent: 'space-between',
95
+ padding: '8px 12px',
96
+ borderRadius: '6px',
97
+ fontSize: '13px',
98
+ cursor: 'pointer',
99
+ transition: 'background-color 0.15s',
100
+ backgroundColor: 'transparent',
101
+ };
102
+ const getSwitchStyle = (isOn) => ({
103
+ width: '36px',
104
+ height: '20px',
105
+ backgroundColor: isOn ? '#3b82f6' : '#d1d5db',
106
+ borderRadius: '10px',
107
+ position: 'relative',
108
+ transition: 'background-color 0.2s',
109
+ cursor: 'pointer',
110
+ });
111
+ const getSwitchKnobStyle = (isOn) => ({
112
+ width: '16px',
113
+ height: '16px',
114
+ backgroundColor: 'white',
115
+ borderRadius: '50%',
116
+ position: 'absolute',
117
+ top: '2px',
118
+ left: isOn ? '18px' : '2px',
119
+ transition: 'left 0.2s',
120
+ });
121
+ const overlayStyle = {
122
+ position: 'fixed',
123
+ padding: '8px 12px',
124
+ backgroundColor: 'rgba(0, 0, 0, 0.8)',
125
+ color: 'white',
126
+ fontSize: '14px',
127
+ fontFamily: 'monospace',
128
+ borderRadius: '8px',
129
+ zIndex: 99998,
130
+ };
131
+ const handleClearLocalStorage = () => {
132
+ if (confirm('LocalStorage를 모두 삭제하시겠습니까?')) {
133
+ localStorage.clear();
134
+ alert('LocalStorage가 삭제되었습니다.');
135
+ }
136
+ };
137
+ const handleClearSessionStorage = () => {
138
+ if (confirm('SessionStorage를 모두 삭제하시겠습니까?')) {
139
+ sessionStorage.clear();
140
+ alert('SessionStorage가 삭제되었습니다.');
141
+ }
142
+ };
143
+ return (React.createElement(React.Fragment, null,
144
+ React.createElement("div", { style: containerStyle },
145
+ React.createElement("button", { onClick: () => setIsOpen(!isOpen), style: toggleButtonStyle, onMouseEnter: (e) => (e.currentTarget.style.backgroundColor = '#2563eb'), onMouseLeave: (e) => (e.currentTarget.style.backgroundColor = '#3b82f6') }, isOpen ? '✕' : '🛠'),
146
+ isOpen && (React.createElement("div", { style: panelStyle },
147
+ React.createElement("div", { style: headerStyle }, "\u2699\uFE0F \uAC1C\uBC1C\uC790 \uB3C4\uAD6C"),
148
+ React.createElement("div", { style: contentStyle },
149
+ React.createElement("div", { style: toggleItemStyle, onMouseEnter: (e) => (e.currentTarget.style.backgroundColor = '#f3f4f6'), onMouseLeave: (e) => (e.currentTarget.style.backgroundColor = 'transparent'), onClick: () => setShowWindowSize(!showWindowSize) },
150
+ React.createElement("span", null, "\uC708\uB3C4\uC6B0 \uD06C\uAE30 \uD45C\uC2DC"),
151
+ React.createElement("div", { style: getSwitchStyle(showWindowSize) },
152
+ React.createElement("div", { style: getSwitchKnobStyle(showWindowSize) }))),
153
+ React.createElement("div", { style: toggleItemStyle, onMouseEnter: (e) => (e.currentTarget.style.backgroundColor = '#f3f4f6'), onMouseLeave: (e) => (e.currentTarget.style.backgroundColor = 'transparent'), onClick: () => setShowRenderCount(!showRenderCount) },
154
+ React.createElement("span", null, "\uB80C\uB354 \uCE74\uC6B4\uD2B8 \uD45C\uC2DC"),
155
+ React.createElement("div", { style: getSwitchStyle(showRenderCount) },
156
+ React.createElement("div", { style: getSwitchKnobStyle(showRenderCount) }))),
157
+ React.createElement("div", { style: { height: '1px', backgroundColor: '#e5e7eb', margin: '8px 0' } }),
158
+ React.createElement("button", { onClick: handleClearLocalStorage, style: {
159
+ ...toggleItemStyle,
160
+ border: '1px solid #e5e7eb',
161
+ backgroundColor: 'white',
162
+ color: '#dc2626',
163
+ fontWeight: 500,
164
+ }, onMouseEnter: (e) => (e.currentTarget.style.backgroundColor = '#fef2f2'), onMouseLeave: (e) => (e.currentTarget.style.backgroundColor = 'white') }, "\uD83D\uDDD1\uFE0F LocalStorage \uC0AD\uC81C"),
165
+ React.createElement("button", { onClick: handleClearSessionStorage, style: {
166
+ ...toggleItemStyle,
167
+ border: '1px solid #e5e7eb',
168
+ backgroundColor: 'white',
169
+ color: '#dc2626',
170
+ fontWeight: 500,
171
+ }, onMouseEnter: (e) => (e.currentTarget.style.backgroundColor = '#fef2f2'), onMouseLeave: (e) => (e.currentTarget.style.backgroundColor = 'white') }, "\uD83D\uDDD1\uFE0F SessionStorage \uC0AD\uC81C"))))),
172
+ showWindowSize && (React.createElement("div", { style: {
173
+ ...overlayStyle,
174
+ top: 16,
175
+ left: 16,
176
+ } },
177
+ React.createElement("span", { style: { color: '#60a5fa' } }, "W:"),
178
+ ' ',
179
+ React.createElement("span", { style: { fontWeight: 'bold' } },
180
+ width,
181
+ "px"),
182
+ React.createElement("span", { style: { color: '#9ca3af', margin: '0 4px' } }, "\u00D7"),
183
+ React.createElement("span", { style: { color: '#4ade80' } }, "H:"),
184
+ ' ',
185
+ React.createElement("span", { style: { fontWeight: 'bold' } },
186
+ height,
187
+ "px"))),
188
+ showRenderCount && (React.createElement("div", { style: {
189
+ ...overlayStyle,
190
+ top: 16,
191
+ right: 16,
192
+ } },
193
+ React.createElement("span", { style: { color: '#fbbf24' } }, "Renders:"),
194
+ ' ',
195
+ React.createElement("span", { style: { fontWeight: 'bold' } }, renderCount.current)))));
196
+ }
@@ -0,0 +1,75 @@
1
+ import React from 'react';
2
+ export type FormState = {
3
+ values?: Record<string, any>;
4
+ errors?: Record<string, any>;
5
+ dirtyFields?: Record<string, any>;
6
+ touchedFields?: Record<string, any>;
7
+ isValid?: boolean;
8
+ isSubmitting?: boolean;
9
+ submitCount?: number;
10
+ };
11
+ export type Props = {
12
+ /** react-hook-form의 formState */
13
+ formState: FormState;
14
+ /** 현재 폼 values (watch() 결과) */
15
+ values?: Record<string, any>;
16
+ /** 표시 위치 (기본값: 'bottom-left') */
17
+ position?: 'top-left' | 'top-right' | 'bottom-left' | 'bottom-right';
18
+ /** 패널 제목 (기본값: 'Form DevTools') */
19
+ title?: string;
20
+ };
21
+ /**
22
+ * react-hook-form의 상태를 실시간으로 시각화하는 개발용 컴포넌트
23
+ * form의 values, errors, dirtyFields, touchedFields 등을 한눈에 확인할 수 있습니다.
24
+ *
25
+ * @example
26
+ * ```tsx
27
+ * // Vite 프로젝트
28
+ * import { useForm } from 'react-hook-form';
29
+ * import { FormDevTools } from 'goodchuck-utils/components/dev';
30
+ *
31
+ * function MyForm() {
32
+ * const { register, handleSubmit, formState, watch } = useForm({
33
+ * defaultValues: {
34
+ * username: '',
35
+ * email: '',
36
+ * age: 0,
37
+ * }
38
+ * });
39
+ *
40
+ * const values = watch(); // 모든 값 watch
41
+ *
42
+ * const onSubmit = (data) => {
43
+ * console.log(data);
44
+ * };
45
+ *
46
+ * return (
47
+ * <form onSubmit={handleSubmit(onSubmit)}>
48
+ * <input {...register('username', { required: 'Username is required' })} />
49
+ * <input {...register('email', { required: 'Email is required' })} />
50
+ * <input type="number" {...register('age', { min: 18 })} />
51
+ * <button type="submit">Submit</button>
52
+ *
53
+ * {import.meta.env.DEV && (
54
+ * <FormDevTools formState={formState} values={values} />
55
+ * )}
56
+ * </form>
57
+ * );
58
+ * }
59
+ * ```
60
+ *
61
+ * @example
62
+ * ```tsx
63
+ * // Create React App 프로젝트
64
+ * {process.env.NODE_ENV === 'development' && (
65
+ * <FormDevTools
66
+ * formState={formState}
67
+ * values={values}
68
+ * position="top-right"
69
+ * title="User Form Debug"
70
+ * />
71
+ * )}
72
+ * ```
73
+ */
74
+ export default function FormDevTools({ formState, values, position, title }: Props): React.JSX.Element;
75
+ //# sourceMappingURL=FormDevTools.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"FormDevTools.d.ts","sourceRoot":"","sources":["../../../../src/components/dev/FormDevTools/FormDevTools.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAsC,MAAM,OAAO,CAAC;AAwB3D,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,iCAAiC;IACjC,QAAQ,CAAC,EAAE,UAAU,GAAG,WAAW,GAAG,aAAa,GAAG,cAAc,CAAC;IACrE,mCAAmC;IACnC,KAAK,CAAC,EAAE,MAAM,CAAC;CAChB,CAAC;AAEF;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAoDG;AACH,MAAM,CAAC,OAAO,UAAU,YAAY,CAAC,EAAE,SAAS,EAAE,MAAM,EAAE,QAAwB,EAAE,KAAuB,EAAE,EAAE,KAAK,qBA2MnH"}
@@ -0,0 +1,196 @@
1
+ import React, { useState, useRef, useEffect } from 'react';
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';
4
+ /**
5
+ * react-hook-form의 상태를 실시간으로 시각화하는 개발용 컴포넌트
6
+ * form의 values, errors, dirtyFields, touchedFields 등을 한눈에 확인할 수 있습니다.
7
+ *
8
+ * @example
9
+ * ```tsx
10
+ * // Vite 프로젝트
11
+ * import { useForm } from 'react-hook-form';
12
+ * import { FormDevTools } from 'goodchuck-utils/components/dev';
13
+ *
14
+ * function MyForm() {
15
+ * const { register, handleSubmit, formState, watch } = useForm({
16
+ * defaultValues: {
17
+ * username: '',
18
+ * email: '',
19
+ * age: 0,
20
+ * }
21
+ * });
22
+ *
23
+ * const values = watch(); // 모든 값 watch
24
+ *
25
+ * const onSubmit = (data) => {
26
+ * console.log(data);
27
+ * };
28
+ *
29
+ * return (
30
+ * <form onSubmit={handleSubmit(onSubmit)}>
31
+ * <input {...register('username', { required: 'Username is required' })} />
32
+ * <input {...register('email', { required: 'Email is required' })} />
33
+ * <input type="number" {...register('age', { min: 18 })} />
34
+ * <button type="submit">Submit</button>
35
+ *
36
+ * {import.meta.env.DEV && (
37
+ * <FormDevTools formState={formState} values={values} />
38
+ * )}
39
+ * </form>
40
+ * );
41
+ * }
42
+ * ```
43
+ *
44
+ * @example
45
+ * ```tsx
46
+ * // Create React App 프로젝트
47
+ * {process.env.NODE_ENV === 'development' && (
48
+ * <FormDevTools
49
+ * formState={formState}
50
+ * values={values}
51
+ * position="top-right"
52
+ * title="User Form Debug"
53
+ * />
54
+ * )}
55
+ * ```
56
+ */
57
+ export default function FormDevTools({ formState, values, position = 'bottom-left', title = 'Form DevTools' }) {
58
+ const [isOpen, setIsOpen] = useState(false);
59
+ const [panelPosition, setPanelPosition] = useState({ x: 0, y: 0 });
60
+ const [panelSize, setPanelSize] = useState({ width: 500, height: 400 });
61
+ const [isDragging, setIsDragging] = useState(false);
62
+ const [isResizing, setIsResizing] = useState(false);
63
+ const [dragStart, setDragStart] = useState({ x: 0, y: 0 });
64
+ const [resizeStart, setResizeStart] = useState({ x: 0, y: 0, width: 0, height: 0 });
65
+ const panelRef = useRef(null);
66
+ const { copy, copiedText } = useCopyToClipboard();
67
+ const handleCopy = () => {
68
+ const data = {
69
+ values,
70
+ errors: formState.errors,
71
+ dirtyFields: formState.dirtyFields,
72
+ touchedFields: formState.touchedFields,
73
+ isValid: formState.isValid,
74
+ isSubmitting: formState.isSubmitting,
75
+ submitCount: formState.submitCount,
76
+ };
77
+ copy(JSON.stringify(data, null, 2));
78
+ };
79
+ const isCopied = copiedText !== null;
80
+ const errorCount = Object.keys(formState.errors || {}).length;
81
+ const dirtyFieldsCount = Object.keys(formState.dirtyFields || {}).length;
82
+ const touchedFieldsCount = Object.keys(formState.touchedFields || {}).length;
83
+ // 드래그 핸들러
84
+ useEffect(() => {
85
+ const handleMouseMove = (e) => {
86
+ if (isDragging) {
87
+ const deltaX = e.clientX - dragStart.x;
88
+ const deltaY = e.clientY - dragStart.y;
89
+ setPanelPosition((prev) => ({
90
+ x: prev.x + deltaX,
91
+ y: prev.y + deltaY,
92
+ }));
93
+ setDragStart({ x: e.clientX, y: e.clientY });
94
+ }
95
+ if (isResizing) {
96
+ const deltaX = e.clientX - resizeStart.x;
97
+ const deltaY = e.clientY - resizeStart.y;
98
+ const maxHeight = window.innerHeight * 0.85; // 화면 높이의 85%를 최대값으로
99
+ setPanelSize({
100
+ width: Math.max(300, resizeStart.width + deltaX),
101
+ height: Math.min(maxHeight, Math.max(200, resizeStart.height + deltaY)),
102
+ });
103
+ }
104
+ };
105
+ const handleMouseUp = () => {
106
+ setIsDragging(false);
107
+ setIsResizing(false);
108
+ };
109
+ if (isDragging || isResizing) {
110
+ document.addEventListener('mousemove', handleMouseMove);
111
+ document.addEventListener('mouseup', handleMouseUp);
112
+ return () => {
113
+ document.removeEventListener('mousemove', handleMouseMove);
114
+ document.removeEventListener('mouseup', handleMouseUp);
115
+ };
116
+ }
117
+ }, [isDragging, isResizing, dragStart, resizeStart]);
118
+ const handleHeaderMouseDown = (e) => {
119
+ if (panelRef.current) {
120
+ const rect = panelRef.current.getBoundingClientRect();
121
+ setIsDragging(true);
122
+ setDragStart({
123
+ x: e.clientX,
124
+ y: e.clientY,
125
+ });
126
+ setPanelPosition({
127
+ x: rect.left,
128
+ y: rect.top,
129
+ });
130
+ }
131
+ };
132
+ const handleResizeMouseDown = (e) => {
133
+ e.stopPropagation();
134
+ setIsResizing(true);
135
+ setResizeStart({
136
+ x: e.clientX,
137
+ y: e.clientY,
138
+ width: panelSize.width,
139
+ height: panelSize.height,
140
+ });
141
+ };
142
+ const renderErrors = () => {
143
+ if (!formState.errors || Object.keys(formState.errors).length === 0) {
144
+ return (React.createElement("div", { style: { textAlign: 'center', color: '#9ca3af', padding: '20px', fontSize: '13px' } }, "No validation errors"));
145
+ }
146
+ return Object.entries(formState.errors).map(([field, error]) => (React.createElement("div", { key: field, style: errorItemStyle },
147
+ React.createElement("div", { style: errorLabelStyle }, field),
148
+ React.createElement("div", { style: errorMessageStyle }, error?.message || 'Invalid value'))));
149
+ };
150
+ return (React.createElement("div", { style: getContainerStyle(position) },
151
+ React.createElement("button", { onClick: () => setIsOpen(!isOpen), style: getToggleButtonStyle(formState.isValid), onMouseEnter: (e) => (e.currentTarget.style.backgroundColor = formState.isValid === false ? '#dc2626' : '#7c3aed'), onMouseLeave: (e) => (e.currentTarget.style.backgroundColor = formState.isValid === false ? '#ef4444' : '#8b5cf6') }, isOpen ? '✕' : '📝'),
152
+ isOpen && (React.createElement("div", { ref: panelRef, style: getPanelStyle(position, panelPosition, panelSize, isDragging) },
153
+ React.createElement("div", { style: headerStyle, onMouseDown: handleHeaderMouseDown },
154
+ React.createElement("div", { style: { display: 'flex', alignItems: 'center', gap: '8px' } },
155
+ React.createElement("div", { style: headerTitleStyle },
156
+ "\uD83D\uDCDD ",
157
+ title),
158
+ React.createElement("div", { style: getStatusBadgeStyle(formState.isValid) }, formState.isValid ? '✓ Valid' : `✗ ${errorCount} Error${errorCount > 1 ? 's' : ''}`)),
159
+ React.createElement("button", { onClick: handleCopy, style: getCopyButtonStyle(isCopied), onMouseEnter: (e) => {
160
+ if (!isCopied)
161
+ e.currentTarget.style.backgroundColor = '#2563eb';
162
+ }, onMouseLeave: (e) => {
163
+ if (!isCopied)
164
+ e.currentTarget.style.backgroundColor = '#3b82f6';
165
+ } }, isCopied ? '✓ Copied' : 'Copy All')),
166
+ React.createElement("div", { style: contentStyle },
167
+ React.createElement("div", { style: statsContainerStyle },
168
+ React.createElement("div", { style: statCardStyle },
169
+ React.createElement("div", { style: statLabelStyle }, "Dirty Fields"),
170
+ React.createElement("div", { style: statValueStyle }, dirtyFieldsCount)),
171
+ React.createElement("div", { style: statCardStyle },
172
+ React.createElement("div", { style: statLabelStyle }, "Touched Fields"),
173
+ React.createElement("div", { style: statValueStyle }, touchedFieldsCount)),
174
+ React.createElement("div", { style: statCardStyle },
175
+ React.createElement("div", { style: statLabelStyle }, "Submit Count"),
176
+ React.createElement("div", { style: statValueStyle }, formState.submitCount || 0)),
177
+ React.createElement("div", { style: statCardStyle },
178
+ React.createElement("div", { style: statLabelStyle }, "Submitting"),
179
+ React.createElement("div", { style: statValueStyle }, formState.isSubmitting ? 'Yes' : 'No'))),
180
+ React.createElement("div", { style: sectionTitleStyle }, "Form Values"),
181
+ React.createElement("pre", { style: codeBlockStyle }, JSON.stringify(values || {}, null, 2)),
182
+ errorCount > 0 && (React.createElement(React.Fragment, null,
183
+ React.createElement("div", { style: sectionTitleStyle },
184
+ "Validation Errors (",
185
+ errorCount,
186
+ ")"),
187
+ renderErrors())),
188
+ dirtyFieldsCount > 0 && (React.createElement(React.Fragment, null,
189
+ React.createElement("div", { style: sectionTitleStyle }, "Dirty Fields"),
190
+ React.createElement("pre", { style: codeBlockStyle }, JSON.stringify(formState.dirtyFields || {}, null, 2)))),
191
+ touchedFieldsCount > 0 && (React.createElement(React.Fragment, null,
192
+ React.createElement("div", { style: sectionTitleStyle }, "Touched Fields"),
193
+ React.createElement("pre", { style: codeBlockStyle }, JSON.stringify(formState.touchedFields || {}, null, 2))))),
194
+ React.createElement("div", { onMouseDown: handleResizeMouseDown, style: resizeHandleStyle }),
195
+ React.createElement("div", { onMouseDown: handleResizeMouseDown, style: resizeHandleIndicatorStyle })))));
196
+ }
@@ -0,0 +1,3 @@
1
+ export { default } from './FormDevTools';
2
+ export type { Props as FormDevToolsProps, FormState } from './FormDevTools';
3
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +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,SAAS,EAAE,MAAM,gBAAgB,CAAC"}
@@ -0,0 +1 @@
1
+ export { default } from './FormDevTools';
@@ -0,0 +1,43 @@
1
+ import { CSSProperties } from 'react';
2
+ export type StyleProps = {
3
+ isValid?: boolean;
4
+ isCopied: boolean;
5
+ isActive: boolean;
6
+ isDragging: boolean;
7
+ position: string;
8
+ panelPosition: {
9
+ x: number;
10
+ y: number;
11
+ };
12
+ panelSize: {
13
+ width: number;
14
+ height: number;
15
+ };
16
+ };
17
+ export declare const getPositionStyles: () => Record<string, CSSProperties>;
18
+ export declare const getContainerStyle: (position: string) => CSSProperties;
19
+ export declare const getToggleButtonStyle: (isValid: boolean | undefined) => CSSProperties;
20
+ export declare const getPanelStyle: (position: string, panelPosition: {
21
+ x: number;
22
+ y: number;
23
+ }, panelSize: {
24
+ width: number;
25
+ height: number;
26
+ }, isDragging: boolean) => CSSProperties;
27
+ export declare const headerStyle: CSSProperties;
28
+ export declare const headerTitleStyle: CSSProperties;
29
+ export declare const getStatusBadgeStyle: (isValid: boolean | undefined) => CSSProperties;
30
+ export declare const getCopyButtonStyle: (isCopied: boolean) => CSSProperties;
31
+ export declare const contentStyle: CSSProperties;
32
+ export declare const sectionTitleStyle: CSSProperties;
33
+ export declare const codeBlockStyle: CSSProperties;
34
+ export declare const errorItemStyle: CSSProperties;
35
+ export declare const errorLabelStyle: CSSProperties;
36
+ export declare const errorMessageStyle: CSSProperties;
37
+ export declare const statsContainerStyle: CSSProperties;
38
+ export declare const statCardStyle: CSSProperties;
39
+ export declare const statLabelStyle: CSSProperties;
40
+ export declare const statValueStyle: CSSProperties;
41
+ export declare const resizeHandleStyle: CSSProperties;
42
+ export declare const resizeHandleIndicatorStyle: CSSProperties;
43
+ //# sourceMappingURL=styles.d.ts.map
@@ -0,0 +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,aAU5B,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"}