goodchuck-utils 1.8.0 → 1.9.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.
@@ -17,6 +17,24 @@ export type Props = {
17
17
  originalValues?: Record<string, any>;
18
18
  /** Validation 스키마 정보 (zod, yup 등) - refine 조건 표시에 사용 */
19
19
  validationSchema?: Record<string, any>;
20
+ /** react-hook-form의 reset 함수 (mock 데이터 생성 후 폼에 채우기 위해 필요) */
21
+ reset?: (values: Record<string, any>, options?: {
22
+ keepDirty?: boolean;
23
+ keepTouched?: boolean;
24
+ }) => void;
25
+ /** react-hook-form의 setValue 함수 (dirty 상태를 true로 만들기 위해 필요, 선택사항) */
26
+ setValue?: (name: string, value: any, options?: {
27
+ shouldDirty?: boolean;
28
+ shouldValidate?: boolean;
29
+ }) => void;
30
+ /** react-hook-form의 trigger 함수 (validation 실행을 위해 필요, 선택사항) */
31
+ trigger?: (name?: string | string[]) => Promise<boolean>;
32
+ /** Mock 데이터 생성 함수 (비동기 가능) */
33
+ generateMock?: (params: {
34
+ values?: Record<string, any>;
35
+ originalValues?: Record<string, any>;
36
+ validationSchema?: Record<string, any>;
37
+ }) => Promise<Record<string, any>> | Record<string, any>;
20
38
  /** 표시 위치 (기본값: 'bottom-left') */
21
39
  position?: 'top-left' | 'top-right' | 'bottom-left' | 'bottom-right';
22
40
  /** 패널 제목 (기본값: 'Form DevTools') */
@@ -57,6 +75,26 @@ export type Props = {
57
75
  * formState={formState}
58
76
  * values={values}
59
77
  * originalValues={defaultValues}
78
+ * reset={reset} // mock 데이터 생성 후 폼에 채우기 위해 필요
79
+ * generateMock={async ({ values, validationSchema }) => {
80
+ * // AI를 사용한 mock 데이터 생성 (예시)
81
+ * const response = await fetch('https://api.openai.com/v1/chat/completions', {
82
+ * method: 'POST',
83
+ * headers: {
84
+ * 'Content-Type': 'application/json',
85
+ * 'Authorization': `Bearer ${process.env.OPENAI_API_KEY}`,
86
+ * },
87
+ * body: JSON.stringify({
88
+ * model: 'gpt-3.5-turbo',
89
+ * messages: [{
90
+ * role: 'user',
91
+ * content: `다음 폼 필드에 맞는 mock 데이터를 생성해주세요: ${JSON.stringify(values)}`
92
+ * }],
93
+ * }),
94
+ * });
95
+ * const data = await response.json();
96
+ * return JSON.parse(data.choices[0].message.content);
97
+ * }}
60
98
  * />
61
99
  * )}
62
100
  * </form>
@@ -97,5 +135,5 @@ export type Props = {
97
135
  * }
98
136
  * ```
99
137
  */
100
- export default function FormDevTools({ formState, values, originalValues, validationSchema, position, title }: Props): React.JSX.Element;
138
+ export default function FormDevTools({ formState, values, originalValues, validationSchema, reset, setValue, trigger, generateMock, position, title }: Props): React.JSX.Element;
101
139
  //# 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;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
+ {"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,6DAA6D;IAC7D,KAAK,CAAC,EAAE,CAAC,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,EAAE,OAAO,CAAC,EAAE;QAAE,SAAS,CAAC,EAAE,OAAO,CAAC;QAAC,WAAW,CAAC,EAAE,OAAO,CAAA;KAAE,KAAK,IAAI,CAAC;IACxG,qEAAqE;IACrE,QAAQ,CAAC,EAAE,CAAC,IAAI,EAAE,MAAM,EAAE,KAAK,EAAE,GAAG,EAAE,OAAO,CAAC,EAAE;QAAE,WAAW,CAAC,EAAE,OAAO,CAAC;QAAC,cAAc,CAAC,EAAE,OAAO,CAAA;KAAE,KAAK,IAAI,CAAC;IAC7G,+DAA+D;IAC/D,OAAO,CAAC,EAAE,CAAC,IAAI,CAAC,EAAE,MAAM,GAAG,MAAM,EAAE,KAAK,OAAO,CAAC,OAAO,CAAC,CAAC;IACzD,8BAA8B;IAC9B,YAAY,CAAC,EAAE,CAAC,MAAM,EAAE;QACtB,MAAM,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,CAAC;QAC7B,cAAc,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,CAAC;QACrC,gBAAgB,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,CAAC;KACxC,KAAK,OAAO,CAAC,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,CAAC,GAAG,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,CAAC;IACzD,iCAAiC;IACjC,QAAQ,CAAC,EAAE,UAAU,GAAG,WAAW,GAAG,aAAa,GAAG,cAAc,CAAC;IACrE,mCAAmC;IACnC,KAAK,CAAC,EAAE,MAAM,CAAC;CAChB,CAAC;AAEF;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA8FG;AACH,MAAM,CAAC,OAAO,UAAU,YAAY,CAAC,EAAE,SAAS,EAAE,MAAM,EAAE,cAAc,EAAE,gBAAgB,EAAE,KAAK,EAAE,QAAQ,EAAE,OAAO,EAAE,YAAY,EAAE,QAAwB,EAAE,KAAuB,EAAE,EAAE,KAAK,qBAggB7L"}
@@ -36,6 +36,26 @@ import { getContainerStyle, getToggleButtonStyle, getPanelStyle, headerStyle, he
36
36
  * formState={formState}
37
37
  * values={values}
38
38
  * originalValues={defaultValues}
39
+ * reset={reset} // mock 데이터 생성 후 폼에 채우기 위해 필요
40
+ * generateMock={async ({ values, validationSchema }) => {
41
+ * // AI를 사용한 mock 데이터 생성 (예시)
42
+ * const response = await fetch('https://api.openai.com/v1/chat/completions', {
43
+ * method: 'POST',
44
+ * headers: {
45
+ * 'Content-Type': 'application/json',
46
+ * 'Authorization': `Bearer ${process.env.OPENAI_API_KEY}`,
47
+ * },
48
+ * body: JSON.stringify({
49
+ * model: 'gpt-3.5-turbo',
50
+ * messages: [{
51
+ * role: 'user',
52
+ * content: `다음 폼 필드에 맞는 mock 데이터를 생성해주세요: ${JSON.stringify(values)}`
53
+ * }],
54
+ * }),
55
+ * });
56
+ * const data = await response.json();
57
+ * return JSON.parse(data.choices[0].message.content);
58
+ * }}
39
59
  * />
40
60
  * )}
41
61
  * </form>
@@ -76,7 +96,7 @@ import { getContainerStyle, getToggleButtonStyle, getPanelStyle, headerStyle, he
76
96
  * }
77
97
  * ```
78
98
  */
79
- export default function FormDevTools({ formState, values, originalValues, validationSchema, position = 'bottom-left', title = 'Form DevTools' }) {
99
+ export default function FormDevTools({ formState, values, originalValues, validationSchema, reset, setValue, trigger, generateMock, position = 'bottom-left', title = 'Form DevTools' }) {
80
100
  const [isOpen, setIsOpen] = useState(false);
81
101
  const [activeTab, setActiveTab] = useState('all');
82
102
  const [panelPosition, setPanelPosition] = useState({ x: 0, y: 0 });
@@ -85,6 +105,8 @@ export default function FormDevTools({ formState, values, originalValues, valida
85
105
  const [isResizing, setIsResizing] = useState(false);
86
106
  const [dragStart, setDragStart] = useState({ x: 0, y: 0 });
87
107
  const [resizeStart, setResizeStart] = useState({ x: 0, y: 0, width: 0, height: 0 });
108
+ const [isGenerating, setIsGenerating] = useState(false);
109
+ const [generateError, setGenerateError] = useState(null);
88
110
  const panelRef = useRef(null);
89
111
  const { copy, copiedText } = useCopyToClipboard();
90
112
  const handleCopy = () => {
@@ -205,6 +227,52 @@ export default function FormDevTools({ formState, values, originalValues, valida
205
227
  React.createElement("div", { style: errorLabelStyle }, field),
206
228
  React.createElement("div", { style: errorMessageStyle }, error?.message || 'Invalid value'))));
207
229
  };
230
+ // Mock 데이터 생성 핸들러
231
+ const handleGenerateMock = async () => {
232
+ if (!reset) {
233
+ setGenerateError('reset 함수가 필요합니다');
234
+ return;
235
+ }
236
+ if (!generateMock) {
237
+ setGenerateError('generateMock 함수가 필요합니다');
238
+ return;
239
+ }
240
+ setIsGenerating(true);
241
+ setGenerateError(null);
242
+ try {
243
+ // 사용자가 제공한 generateMock 함수 사용
244
+ const mockData = await generateMock({
245
+ values,
246
+ originalValues,
247
+ validationSchema,
248
+ });
249
+ // reset으로 값 설정
250
+ // keepDirty: false는 reset 후 dirty를 초기화하는데,
251
+ // 이후 setValue로 shouldDirty: true를 설정하면 dirty가 true가 됨
252
+ reset(mockData, { keepDirty: false, keepTouched: false });
253
+ // dirty와 validate를 true로 만들기 위해 setValue 사용
254
+ // reset 후에 setValue를 호출하면 dirty 상태가 true가 됨
255
+ if (setValue) {
256
+ // 모든 필드를 dirty로 만들고 validation 실행
257
+ Object.keys(mockData).forEach((key) => {
258
+ setValue(key, mockData[key], {
259
+ shouldDirty: true, // dirty를 true로 설정
260
+ shouldValidate: true, // validation 실행
261
+ });
262
+ });
263
+ }
264
+ // 전체 validation 실행
265
+ if (trigger) {
266
+ await trigger();
267
+ }
268
+ }
269
+ catch (error) {
270
+ setGenerateError(error.message || 'Mock 데이터 생성 실패');
271
+ }
272
+ finally {
273
+ setIsGenerating(false);
274
+ }
275
+ };
208
276
  return (React.createElement("div", { style: getContainerStyle(position) },
209
277
  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 ? '✕' : '📝'),
210
278
  isOpen && (React.createElement("div", { ref: panelRef, style: getPanelStyle(position, panelPosition, panelSize, isDragging) },
@@ -214,13 +282,33 @@ export default function FormDevTools({ formState, values, originalValues, valida
214
282
  "\uD83D\uDCDD ",
215
283
  title),
216
284
  React.createElement("div", { style: getStatusBadgeStyle(formState.isValid) }, formState.isValid ? '✓ Valid' : `✗ ${errorCount} Error${errorCount > 1 ? 's' : ''}`)),
217
- React.createElement("button", { onClick: handleCopy, style: getCopyButtonStyle(isCopied), onMouseEnter: (e) => {
218
- if (!isCopied)
219
- e.currentTarget.style.backgroundColor = '#2563eb';
220
- }, onMouseLeave: (e) => {
221
- if (!isCopied)
222
- e.currentTarget.style.backgroundColor = '#3b82f6';
223
- } }, isCopied ? '✓ Copied' : 'Copy All')),
285
+ React.createElement("div", { style: { display: 'flex', gap: '8px', alignItems: 'center' } },
286
+ reset && generateMock && (React.createElement("button", { onClick: handleGenerateMock, disabled: isGenerating, style: {
287
+ ...getCopyButtonStyle(false),
288
+ backgroundColor: isGenerating ? '#9ca3af' : '#10b981',
289
+ opacity: isGenerating ? 0.6 : 1,
290
+ cursor: isGenerating ? 'not-allowed' : 'pointer',
291
+ }, onMouseEnter: (e) => {
292
+ if (!isGenerating)
293
+ e.currentTarget.style.backgroundColor = '#059669';
294
+ }, onMouseLeave: (e) => {
295
+ if (!isGenerating)
296
+ e.currentTarget.style.backgroundColor = '#10b981';
297
+ } }, isGenerating ? '⏳ Generating...' : '🤖 Generate Mock')),
298
+ React.createElement("button", { onClick: handleCopy, style: getCopyButtonStyle(isCopied), onMouseEnter: (e) => {
299
+ if (!isCopied)
300
+ e.currentTarget.style.backgroundColor = '#2563eb';
301
+ }, onMouseLeave: (e) => {
302
+ if (!isCopied)
303
+ e.currentTarget.style.backgroundColor = '#3b82f6';
304
+ } }, isCopied ? '✓ Copied' : 'Copy All'))),
305
+ generateError && (React.createElement("div", { style: {
306
+ padding: '8px 16px',
307
+ backgroundColor: '#fef2f2',
308
+ borderBottom: '1px solid #fecaca',
309
+ fontSize: '12px',
310
+ color: '#991b1b',
311
+ } }, generateError)),
224
312
  React.createElement("div", { style: tabContainerStyle },
225
313
  React.createElement("button", { onClick: () => setActiveTab('all'), style: getTabStyle(activeTab === 'all'), onMouseEnter: (e) => {
226
314
  if (activeTab !== 'all')
package/dist/index.d.ts CHANGED
@@ -1,4 +1,6 @@
1
1
  export * from './date';
2
2
  export * from './form';
3
3
  export * from './string';
4
+ export * from './number';
5
+ export * from './mock';
4
6
  //# sourceMappingURL=index.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AACA,cAAc,QAAQ,CAAC;AAGvB,cAAc,QAAQ,CAAC;AAGvB,cAAc,UAAU,CAAC"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AACA,cAAc,QAAQ,CAAC;AAGvB,cAAc,QAAQ,CAAC;AAGvB,cAAc,UAAU,CAAC;AAGzB,cAAc,UAAU,CAAC;AAGzB,cAAc,QAAQ,CAAC"}
package/dist/index.js CHANGED
@@ -4,6 +4,10 @@ export * from './date';
4
4
  export * from './form';
5
5
  // String utilities
6
6
  export * from './string';
7
+ // Number utilities
8
+ export * from './number';
9
+ // Mock data utilities
10
+ export * from './mock';
7
11
  // React Hooks (import separately: 'goodchuck-utils/hooks')
8
12
  // Note: Hooks are not exported from main entry to avoid React dependency for non-React users
9
13
  // export * from './hooks';
@@ -0,0 +1,41 @@
1
+ /**
2
+ * Form Mock Data Generator
3
+ *
4
+ * 폼 필드 타입에 맞는 mock 데이터 자동 생성
5
+ */
6
+ /**
7
+ * 폼 스키마로부터 mock 데이터 생성
8
+ * @param schema - 폼 스키마 (필드명과 타입 정보)
9
+ * @param count - 생성할 데이터 개수 (기본값: 1)
10
+ * @returns 생성된 mock 데이터 배열
11
+ *
12
+ * @example
13
+ * generateFormMockData({
14
+ * name: { type: 'text' },
15
+ * email: { type: 'email' },
16
+ * age: { type: 'number', min: 18, max: 80 }
17
+ * }) // [{ name: "김민수", email: "user123@example.com", age: 25 }]
18
+ *
19
+ * @example
20
+ * generateFormMockData({
21
+ * username: 'text',
22
+ * email: 'email',
23
+ * phone: 'tel'
24
+ * }, 5) // 5개의 mock 데이터 배열
25
+ */
26
+ export declare function generateFormMockData(schema: Record<string, any>, count?: number): Record<string, any>[];
27
+ /**
28
+ * react-hook-form의 defaultValues로부터 mock 데이터 생성
29
+ * @param defaultValues - react-hook-form의 defaultValues
30
+ * @param count - 생성할 데이터 개수 (기본값: 1)
31
+ * @returns 생성된 mock 데이터 배열
32
+ *
33
+ * @example
34
+ * generateFromDefaultValues({
35
+ * username: '',
36
+ * email: '',
37
+ * age: 0
38
+ * }) // [{ username: "johnsmith", email: "user123@example.com", age: 25 }]
39
+ */
40
+ export declare function generateFromDefaultValues(defaultValues: Record<string, any>, count?: number): Record<string, any>[];
41
+ //# sourceMappingURL=form.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"form.d.ts","sourceRoot":"","sources":["../../src/mock/form.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AA4GH;;;;;;;;;;;;;;;;;;;GAmBG;AACH,wBAAgB,oBAAoB,CAClC,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,EAC3B,KAAK,GAAE,MAAU,GAChB,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,EAAE,CAiCvB;AAED;;;;;;;;;;;;GAYG;AACH,wBAAgB,yBAAyB,CACvC,aAAa,EAAE,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,EAClC,KAAK,GAAE,MAAU,GAChB,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,EAAE,CAuBvB"}
@@ -0,0 +1,195 @@
1
+ /**
2
+ * Form Mock Data Generator
3
+ *
4
+ * 폼 필드 타입에 맞는 mock 데이터 자동 생성
5
+ */
6
+ import { generateKoreanName, generateEnglishName, generateEmail, generatePhoneNumber, generateAddress, generateDate, generateNumber, generateKoreanText, generateBoolean, generateFromArray, } from './generators';
7
+ /**
8
+ * 필드 타입별 mock 데이터 생성 맵핑
9
+ */
10
+ const fieldTypeMap = {
11
+ // 이름 관련
12
+ name: () => generateKoreanName(),
13
+ username: () => generateEnglishName().toLowerCase().replace(/\s/g, ''),
14
+ fullName: () => generateKoreanName(),
15
+ firstName: () => generateKoreanName().substring(1),
16
+ lastName: () => generateKoreanName().substring(0, 1),
17
+ // 연락처 관련
18
+ email: (fieldName) => generateEmail(),
19
+ phone: () => generatePhoneNumber(),
20
+ phoneNumber: () => generatePhoneNumber(),
21
+ mobile: () => generatePhoneNumber(),
22
+ tel: () => generatePhoneNumber(),
23
+ // 주소 관련
24
+ address: () => generateAddress(),
25
+ zipCode: () => String(generateNumber(10000, 99999)),
26
+ postalCode: () => String(generateNumber(10000, 99999)),
27
+ // 날짜 관련
28
+ date: () => generateDate(),
29
+ birthDate: () => generateDate('1950-01-01', '2010-12-31'),
30
+ birthday: () => generateDate('1950-01-01', '2010-12-31'),
31
+ // 숫자 관련
32
+ age: () => generateNumber(18, 80),
33
+ price: () => generateNumber(1000, 1000000),
34
+ amount: () => generateNumber(1, 1000),
35
+ quantity: () => generateNumber(1, 100),
36
+ count: () => generateNumber(0, 100),
37
+ // 텍스트 관련
38
+ title: () => generateKoreanText(10),
39
+ content: () => generateKoreanText(50),
40
+ description: () => generateKoreanText(30),
41
+ message: () => generateKoreanText(20),
42
+ comment: () => generateKoreanText(15),
43
+ note: () => generateKoreanText(20),
44
+ // 불린 관련
45
+ isActive: () => generateBoolean(),
46
+ enabled: () => generateBoolean(),
47
+ checked: () => generateBoolean(),
48
+ agree: () => generateBoolean(),
49
+ accept: () => generateBoolean(),
50
+ // 기본값
51
+ default: (fieldName) => {
52
+ // 필드명에 따라 추론
53
+ const lowerName = fieldName.toLowerCase();
54
+ if (lowerName.includes('email'))
55
+ return generateEmail();
56
+ if (lowerName.includes('phone') || lowerName.includes('tel') || lowerName.includes('mobile'))
57
+ return generatePhoneNumber();
58
+ if (lowerName.includes('name'))
59
+ return generateKoreanName();
60
+ if (lowerName.includes('address'))
61
+ return generateAddress();
62
+ if (lowerName.includes('date') || lowerName.includes('birth'))
63
+ return generateDate();
64
+ if (lowerName.includes('age') || lowerName.includes('count') || lowerName.includes('quantity'))
65
+ return generateNumber(1, 100);
66
+ if (lowerName.includes('price') || lowerName.includes('amount') || lowerName.includes('cost'))
67
+ return generateNumber(1000, 100000);
68
+ if (lowerName.includes('is') || lowerName.includes('has') || lowerName.includes('can'))
69
+ return generateBoolean();
70
+ return generateKoreanText(10);
71
+ },
72
+ };
73
+ /**
74
+ * 필드 타입을 추론하여 mock 데이터 생성
75
+ * @param fieldName - 필드 이름
76
+ * @param fieldType - 필드 타입 (선택)
77
+ * @param options - 추가 옵션
78
+ * @returns 생성된 mock 데이터
79
+ */
80
+ function inferAndGenerate(fieldName, fieldType, options) {
81
+ const lowerName = fieldName.toLowerCase();
82
+ const lowerType = fieldType?.toLowerCase() || '';
83
+ // 타입 기반 생성
84
+ if (lowerType.includes('email'))
85
+ return generateEmail();
86
+ if (lowerType.includes('tel') || lowerType.includes('phone'))
87
+ return generatePhoneNumber();
88
+ if (lowerType.includes('date'))
89
+ return generateDate();
90
+ if (lowerType.includes('number') || lowerType.includes('int'))
91
+ return generateNumber(options?.min, options?.max);
92
+ if (lowerType.includes('boolean') || lowerType.includes('bool'))
93
+ return generateBoolean();
94
+ if (lowerType.includes('text') || lowerType.includes('string')) {
95
+ return options?.maxLength ? generateKoreanText(Math.min(options.maxLength, 50)) : generateKoreanText(10);
96
+ }
97
+ // 필드명 기반 생성
98
+ const generator = fieldTypeMap[lowerName] || fieldTypeMap.default;
99
+ return generator(fieldName, options);
100
+ }
101
+ /**
102
+ * 폼 스키마로부터 mock 데이터 생성
103
+ * @param schema - 폼 스키마 (필드명과 타입 정보)
104
+ * @param count - 생성할 데이터 개수 (기본값: 1)
105
+ * @returns 생성된 mock 데이터 배열
106
+ *
107
+ * @example
108
+ * generateFormMockData({
109
+ * name: { type: 'text' },
110
+ * email: { type: 'email' },
111
+ * age: { type: 'number', min: 18, max: 80 }
112
+ * }) // [{ name: "김민수", email: "user123@example.com", age: 25 }]
113
+ *
114
+ * @example
115
+ * generateFormMockData({
116
+ * username: 'text',
117
+ * email: 'email',
118
+ * phone: 'tel'
119
+ * }, 5) // 5개의 mock 데이터 배열
120
+ */
121
+ export function generateFormMockData(schema, count = 1) {
122
+ const results = [];
123
+ for (let i = 0; i < count; i++) {
124
+ const data = {};
125
+ for (const [fieldName, fieldConfig] of Object.entries(schema)) {
126
+ if (typeof fieldConfig === 'string') {
127
+ // 간단한 타입 문자열인 경우
128
+ data[fieldName] = inferAndGenerate(fieldName, fieldConfig);
129
+ }
130
+ else if (typeof fieldConfig === 'object' && fieldConfig !== null) {
131
+ // 객체 형태인 경우
132
+ if (fieldConfig.type) {
133
+ data[fieldName] = inferAndGenerate(fieldName, fieldConfig.type, fieldConfig);
134
+ }
135
+ else if (fieldConfig.enum) {
136
+ // enum 값이 있는 경우
137
+ data[fieldName] = generateFromArray(fieldConfig.enum);
138
+ }
139
+ else if (fieldConfig.default !== undefined) {
140
+ // default 값이 있는 경우
141
+ data[fieldName] = fieldConfig.default;
142
+ }
143
+ else {
144
+ data[fieldName] = inferAndGenerate(fieldName, undefined, fieldConfig);
145
+ }
146
+ }
147
+ else {
148
+ // 그 외의 경우 필드명으로 추론
149
+ data[fieldName] = inferAndGenerate(fieldName);
150
+ }
151
+ }
152
+ results.push(data);
153
+ }
154
+ return results;
155
+ }
156
+ /**
157
+ * react-hook-form의 defaultValues로부터 mock 데이터 생성
158
+ * @param defaultValues - react-hook-form의 defaultValues
159
+ * @param count - 생성할 데이터 개수 (기본값: 1)
160
+ * @returns 생성된 mock 데이터 배열
161
+ *
162
+ * @example
163
+ * generateFromDefaultValues({
164
+ * username: '',
165
+ * email: '',
166
+ * age: 0
167
+ * }) // [{ username: "johnsmith", email: "user123@example.com", age: 25 }]
168
+ */
169
+ export function generateFromDefaultValues(defaultValues, count = 1) {
170
+ const schema = {};
171
+ // defaultValues의 타입을 추론하여 schema 생성
172
+ for (const [fieldName, defaultValue] of Object.entries(defaultValues)) {
173
+ if (typeof defaultValue === 'string') {
174
+ if (defaultValue === '') {
175
+ schema[fieldName] = { type: 'text' };
176
+ }
177
+ else if (defaultValue.includes('@')) {
178
+ schema[fieldName] = { type: 'email' };
179
+ }
180
+ else {
181
+ schema[fieldName] = { type: 'text', default: defaultValue };
182
+ }
183
+ }
184
+ else if (typeof defaultValue === 'number') {
185
+ schema[fieldName] = { type: 'number', default: defaultValue };
186
+ }
187
+ else if (typeof defaultValue === 'boolean') {
188
+ schema[fieldName] = { type: 'boolean', default: defaultValue };
189
+ }
190
+ else {
191
+ schema[fieldName] = { type: 'text' };
192
+ }
193
+ }
194
+ return generateFormMockData(schema, count);
195
+ }
@@ -0,0 +1,112 @@
1
+ /**
2
+ * Mock Data Generators
3
+ *
4
+ * 폼 필드 타입에 맞는 mock 데이터 생성 유틸리티
5
+ */
6
+ /**
7
+ * 한국어 이름 생성
8
+ * @param gender - 성별 ('male' | 'female' | 'random', 기본값: 'random')
9
+ * @returns 한국어 이름
10
+ *
11
+ * @example
12
+ * generateKoreanName() // "김민수"
13
+ * generateKoreanName('female') // "이지은"
14
+ */
15
+ export declare function generateKoreanName(gender?: 'male' | 'female' | 'random'): string;
16
+ /**
17
+ * 영어 이름 생성
18
+ * @param gender - 성별 ('male' | 'female' | 'random', 기본값: 'random')
19
+ * @returns 영어 이름
20
+ *
21
+ * @example
22
+ * generateEnglishName() // "John Smith"
23
+ * generateEnglishName('female') // "Emily Johnson"
24
+ */
25
+ export declare function generateEnglishName(gender?: 'male' | 'female' | 'random'): string;
26
+ /**
27
+ * 이메일 생성
28
+ * @param name - 이름 (선택, 없으면 랜덤 생성)
29
+ * @param domain - 도메인 (기본값: 'example.com')
30
+ * @returns 이메일 주소
31
+ *
32
+ * @example
33
+ * generateEmail() // "user123@example.com"
34
+ * generateEmail('홍길동') // "honggildong@example.com"
35
+ */
36
+ export declare function generateEmail(name?: string, domain?: string): string;
37
+ /**
38
+ * 전화번호 생성 (한국 형식)
39
+ * @returns 전화번호 (010-1234-5678 형식)
40
+ *
41
+ * @example
42
+ * generatePhoneNumber() // "010-1234-5678"
43
+ */
44
+ export declare function generatePhoneNumber(): string;
45
+ /**
46
+ * 주소 생성 (한국 형식)
47
+ * @returns 한국 주소
48
+ *
49
+ * @example
50
+ * generateAddress() // "서울특별시 강남구 테헤란로 123"
51
+ */
52
+ export declare function generateAddress(): string;
53
+ /**
54
+ * 날짜 생성 (범위 내)
55
+ * @param startDate - 시작 날짜 (기본값: 1년 전)
56
+ * @param endDate - 종료 날짜 (기본값: 오늘)
57
+ * @returns 날짜 문자열 (YYYY-MM-DD)
58
+ *
59
+ * @example
60
+ * generateDate() // "2023-06-15"
61
+ * generateDate('2024-01-01', '2024-12-31') // "2024-08-20"
62
+ */
63
+ export declare function generateDate(startDate?: string, endDate?: string): string;
64
+ /**
65
+ * 숫자 생성 (범위 내)
66
+ * @param min - 최소값 (기본값: 0)
67
+ * @param max - 최대값 (기본값: 100)
68
+ * @returns 랜덤 숫자
69
+ *
70
+ * @example
71
+ * generateNumber() // 42
72
+ * generateNumber(1, 10) // 7
73
+ */
74
+ export declare function generateNumber(min?: number, max?: number): number;
75
+ /**
76
+ * 텍스트 생성 (한글)
77
+ * @param length - 길이 (기본값: 10)
78
+ * @returns 랜덤 한글 텍스트
79
+ *
80
+ * @example
81
+ * generateKoreanText() // "안녕하세요반갑습니다"
82
+ * generateKoreanText(5) // "테스트데이터"
83
+ */
84
+ export declare function generateKoreanText(length?: number): string;
85
+ /**
86
+ * 텍스트 생성 (영문)
87
+ * @param length - 길이 (기본값: 10)
88
+ * @returns 랜덤 영문 텍스트
89
+ *
90
+ * @example
91
+ * generateEnglishText() // "loremipsum"
92
+ * generateEnglishText(5) // "testd"
93
+ */
94
+ export declare function generateEnglishText(length?: number): string;
95
+ /**
96
+ * 불린 값 생성
97
+ * @returns 랜덤 boolean
98
+ *
99
+ * @example
100
+ * generateBoolean() // true
101
+ */
102
+ export declare function generateBoolean(): boolean;
103
+ /**
104
+ * 배열에서 랜덤 선택
105
+ * @param array - 선택할 배열
106
+ * @returns 랜덤으로 선택된 요소
107
+ *
108
+ * @example
109
+ * generateFromArray(['red', 'blue', 'green']) // "blue"
110
+ */
111
+ export declare function generateFromArray<T>(array: T[]): T;
112
+ //# sourceMappingURL=generators.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"generators.d.ts","sourceRoot":"","sources":["../../src/mock/generators.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAEH;;;;;;;;GAQG;AACH,wBAAgB,kBAAkB,CAAC,MAAM,GAAE,MAAM,GAAG,QAAQ,GAAG,QAAmB,GAAG,MAAM,CAY1F;AAED;;;;;;;;GAQG;AACH,wBAAgB,mBAAmB,CAAC,MAAM,GAAE,MAAM,GAAG,QAAQ,GAAG,QAAmB,GAAG,MAAM,CAY3F;AAED;;;;;;;;;GASG;AACH,wBAAgB,aAAa,CAAC,IAAI,CAAC,EAAE,MAAM,EAAE,MAAM,GAAE,MAAsB,GAAG,MAAM,CAqBnF;AAED;;;;;;GAMG;AACH,wBAAgB,mBAAmB,IAAI,MAAM,CAM5C;AAED;;;;;;GAMG;AACH,wBAAgB,eAAe,IAAI,MAAM,CAWxC;AAED;;;;;;;;;GASG;AACH,wBAAgB,YAAY,CAAC,SAAS,CAAC,EAAE,MAAM,EAAE,OAAO,CAAC,EAAE,MAAM,GAAG,MAAM,CAWzE;AAED;;;;;;;;;GASG;AACH,wBAAgB,cAAc,CAAC,GAAG,GAAE,MAAU,EAAE,GAAG,GAAE,MAAY,GAAG,MAAM,CAEzE;AAED;;;;;;;;GAQG;AACH,wBAAgB,kBAAkB,CAAC,MAAM,GAAE,MAAW,GAAG,MAAM,CAO9D;AAED;;;;;;;;GAQG;AACH,wBAAgB,mBAAmB,CAAC,MAAM,GAAE,MAAW,GAAG,MAAM,CAO/D;AAED;;;;;;GAMG;AACH,wBAAgB,eAAe,IAAI,OAAO,CAEzC;AAED;;;;;;;GAOG;AACH,wBAAgB,iBAAiB,CAAC,CAAC,EAAE,KAAK,EAAE,CAAC,EAAE,GAAG,CAAC,CAElD"}
@@ -0,0 +1,195 @@
1
+ /**
2
+ * Mock Data Generators
3
+ *
4
+ * 폼 필드 타입에 맞는 mock 데이터 생성 유틸리티
5
+ */
6
+ /**
7
+ * 한국어 이름 생성
8
+ * @param gender - 성별 ('male' | 'female' | 'random', 기본값: 'random')
9
+ * @returns 한국어 이름
10
+ *
11
+ * @example
12
+ * generateKoreanName() // "김민수"
13
+ * generateKoreanName('female') // "이지은"
14
+ */
15
+ export function generateKoreanName(gender = 'random') {
16
+ const surnames = ['김', '이', '박', '최', '정', '강', '조', '윤', '장', '임', '한', '오', '서', '신', '권', '황', '안', '송', '전', '홍'];
17
+ const maleNames = ['민수', '준호', '성호', '지훈', '현우', '동현', '민준', '건우', '준영', '상우', '영수', '성민', '준혁', '태현', '승현'];
18
+ const femaleNames = ['지은', '수진', '미영', '혜진', '은지', '서연', '민지', '예은', '지원', '유진', '소영', '지현', '수빈', '하은', '서윤'];
19
+ const surname = surnames[Math.floor(Math.random() * surnames.length)];
20
+ const selectedGender = gender === 'random' ? (Math.random() > 0.5 ? 'male' : 'female') : gender;
21
+ const name = selectedGender === 'male'
22
+ ? maleNames[Math.floor(Math.random() * maleNames.length)]
23
+ : femaleNames[Math.floor(Math.random() * femaleNames.length)];
24
+ return `${surname}${name}`;
25
+ }
26
+ /**
27
+ * 영어 이름 생성
28
+ * @param gender - 성별 ('male' | 'female' | 'random', 기본값: 'random')
29
+ * @returns 영어 이름
30
+ *
31
+ * @example
32
+ * generateEnglishName() // "John Smith"
33
+ * generateEnglishName('female') // "Emily Johnson"
34
+ */
35
+ export function generateEnglishName(gender = 'random') {
36
+ const maleFirstNames = ['James', 'John', 'Robert', 'Michael', 'William', 'David', 'Richard', 'Joseph', 'Thomas', 'Christopher'];
37
+ const femaleFirstNames = ['Mary', 'Patricia', 'Jennifer', 'Linda', 'Elizabeth', 'Barbara', 'Susan', 'Jessica', 'Sarah', 'Karen'];
38
+ const lastNames = ['Smith', 'Johnson', 'Williams', 'Brown', 'Jones', 'Garcia', 'Miller', 'Davis', 'Rodriguez', 'Martinez'];
39
+ const selectedGender = gender === 'random' ? (Math.random() > 0.5 ? 'male' : 'female') : gender;
40
+ const firstName = selectedGender === 'male'
41
+ ? maleFirstNames[Math.floor(Math.random() * maleFirstNames.length)]
42
+ : femaleFirstNames[Math.floor(Math.random() * femaleFirstNames.length)];
43
+ const lastName = lastNames[Math.floor(Math.random() * lastNames.length)];
44
+ return `${firstName} ${lastName}`;
45
+ }
46
+ /**
47
+ * 이메일 생성
48
+ * @param name - 이름 (선택, 없으면 랜덤 생성)
49
+ * @param domain - 도메인 (기본값: 'example.com')
50
+ * @returns 이메일 주소
51
+ *
52
+ * @example
53
+ * generateEmail() // "user123@example.com"
54
+ * generateEmail('홍길동') // "honggildong@example.com"
55
+ */
56
+ export function generateEmail(name, domain = 'example.com') {
57
+ if (name) {
58
+ // 한글 이름을 영문으로 변환 (간단한 변환)
59
+ const nameMap = {
60
+ '김': 'kim', '이': 'lee', '박': 'park', '최': 'choi', '정': 'jung',
61
+ '강': 'kang', '조': 'cho', '윤': 'yoon', '장': 'jang', '임': 'lim'
62
+ };
63
+ let emailName = name.toLowerCase()
64
+ .replace(/[가-힣]/g, (char) => {
65
+ // 간단한 로마자 변환 (실제로는 더 복잡한 변환이 필요)
66
+ return nameMap[char] || 'user';
67
+ })
68
+ .replace(/[^a-z0-9]/g, '')
69
+ .substring(0, 20) || 'user';
70
+ if (emailName.length < 3)
71
+ emailName = 'user';
72
+ return `${emailName}${Math.floor(Math.random() * 1000)}@${domain}`;
73
+ }
74
+ const randomString = Math.random().toString(36).substring(2, 10);
75
+ return `${randomString}@${domain}`;
76
+ }
77
+ /**
78
+ * 전화번호 생성 (한국 형식)
79
+ * @returns 전화번호 (010-1234-5678 형식)
80
+ *
81
+ * @example
82
+ * generatePhoneNumber() // "010-1234-5678"
83
+ */
84
+ export function generatePhoneNumber() {
85
+ const prefixes = ['010', '011', '016', '017', '018', '019'];
86
+ const prefix = prefixes[Math.floor(Math.random() * prefixes.length)];
87
+ const middle = Math.floor(Math.random() * 9000) + 1000;
88
+ const last = Math.floor(Math.random() * 9000) + 1000;
89
+ return `${prefix}-${middle}-${last}`;
90
+ }
91
+ /**
92
+ * 주소 생성 (한국 형식)
93
+ * @returns 한국 주소
94
+ *
95
+ * @example
96
+ * generateAddress() // "서울특별시 강남구 테헤란로 123"
97
+ */
98
+ export function generateAddress() {
99
+ const cities = ['서울특별시', '부산광역시', '대구광역시', '인천광역시', '광주광역시', '대전광역시', '울산광역시'];
100
+ const districts = ['강남구', '강동구', '강북구', '강서구', '관악구', '광진구', '구로구', '금천구', '노원구', '도봉구'];
101
+ const streets = ['테헤란로', '강남대로', '서초대로', '올림픽대로', '반포대로', '한강대로', '을지로', '종로', '명동길', '이태원로'];
102
+ const city = cities[Math.floor(Math.random() * cities.length)];
103
+ const district = districts[Math.floor(Math.random() * districts.length)];
104
+ const street = streets[Math.floor(Math.random() * streets.length)];
105
+ const number = Math.floor(Math.random() * 999) + 1;
106
+ return `${city} ${district} ${street} ${number}`;
107
+ }
108
+ /**
109
+ * 날짜 생성 (범위 내)
110
+ * @param startDate - 시작 날짜 (기본값: 1년 전)
111
+ * @param endDate - 종료 날짜 (기본값: 오늘)
112
+ * @returns 날짜 문자열 (YYYY-MM-DD)
113
+ *
114
+ * @example
115
+ * generateDate() // "2023-06-15"
116
+ * generateDate('2024-01-01', '2024-12-31') // "2024-08-20"
117
+ */
118
+ export function generateDate(startDate, endDate) {
119
+ const start = startDate ? new Date(startDate).getTime() : Date.now() - 365 * 24 * 60 * 60 * 1000;
120
+ const end = endDate ? new Date(endDate).getTime() : Date.now();
121
+ const randomTime = start + Math.random() * (end - start);
122
+ const date = new Date(randomTime);
123
+ const year = date.getFullYear();
124
+ const month = String(date.getMonth() + 1).padStart(2, '0');
125
+ const day = String(date.getDate()).padStart(2, '0');
126
+ return `${year}-${month}-${day}`;
127
+ }
128
+ /**
129
+ * 숫자 생성 (범위 내)
130
+ * @param min - 최소값 (기본값: 0)
131
+ * @param max - 최대값 (기본값: 100)
132
+ * @returns 랜덤 숫자
133
+ *
134
+ * @example
135
+ * generateNumber() // 42
136
+ * generateNumber(1, 10) // 7
137
+ */
138
+ export function generateNumber(min = 0, max = 100) {
139
+ return Math.floor(Math.random() * (max - min + 1)) + min;
140
+ }
141
+ /**
142
+ * 텍스트 생성 (한글)
143
+ * @param length - 길이 (기본값: 10)
144
+ * @returns 랜덤 한글 텍스트
145
+ *
146
+ * @example
147
+ * generateKoreanText() // "안녕하세요반갑습니다"
148
+ * generateKoreanText(5) // "테스트데이터"
149
+ */
150
+ export function generateKoreanText(length = 10) {
151
+ const chars = '가나다라마바사아자차카타파하거너더러머버서어저처커터퍼허고노도로모보소오조초코토포호구누두루무부수우주추쿠투푸후그느드르므브스으즈츠크트프흐기니디리미비시이지치키티피히';
152
+ let result = '';
153
+ for (let i = 0; i < length; i++) {
154
+ result += chars[Math.floor(Math.random() * chars.length)];
155
+ }
156
+ return result;
157
+ }
158
+ /**
159
+ * 텍스트 생성 (영문)
160
+ * @param length - 길이 (기본값: 10)
161
+ * @returns 랜덤 영문 텍스트
162
+ *
163
+ * @example
164
+ * generateEnglishText() // "loremipsum"
165
+ * generateEnglishText(5) // "testd"
166
+ */
167
+ export function generateEnglishText(length = 10) {
168
+ const chars = 'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ';
169
+ let result = '';
170
+ for (let i = 0; i < length; i++) {
171
+ result += chars[Math.floor(Math.random() * chars.length)];
172
+ }
173
+ return result;
174
+ }
175
+ /**
176
+ * 불린 값 생성
177
+ * @returns 랜덤 boolean
178
+ *
179
+ * @example
180
+ * generateBoolean() // true
181
+ */
182
+ export function generateBoolean() {
183
+ return Math.random() > 0.5;
184
+ }
185
+ /**
186
+ * 배열에서 랜덤 선택
187
+ * @param array - 선택할 배열
188
+ * @returns 랜덤으로 선택된 요소
189
+ *
190
+ * @example
191
+ * generateFromArray(['red', 'blue', 'green']) // "blue"
192
+ */
193
+ export function generateFromArray(array) {
194
+ return array[Math.floor(Math.random() * array.length)];
195
+ }
@@ -0,0 +1,8 @@
1
+ /**
2
+ * Mock Data Utilities
3
+ *
4
+ * 폼 필드 타입에 맞는 mock 데이터 생성 유틸리티
5
+ */
6
+ export * from './generators';
7
+ export * from './form';
8
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/mock/index.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAGH,cAAc,cAAc,CAAC;AAG7B,cAAc,QAAQ,CAAC"}
@@ -0,0 +1,9 @@
1
+ /**
2
+ * Mock Data Utilities
3
+ *
4
+ * 폼 필드 타입에 맞는 mock 데이터 생성 유틸리티
5
+ */
6
+ // Generators
7
+ export * from './generators';
8
+ // Form mock data generators
9
+ export * from './form';
@@ -0,0 +1,116 @@
1
+ /**
2
+ * Number Formatting Utilities
3
+ *
4
+ * 숫자 및 가격 포맷팅 관련 유틸리티 함수들
5
+ */
6
+ /**
7
+ * 숫자를 천 단위 구분자로 포맷팅
8
+ * @param value - 포맷팅할 숫자
9
+ * @param decimals - 소수점 자릿수 (기본값: 0)
10
+ * @param separator - 천 단위 구분자 (기본값: ',')
11
+ * @returns 포맷팅된 문자열
12
+ *
13
+ * @example
14
+ * formatNumber(1234567) // "1,234,567"
15
+ * formatNumber(1234.56, 2) // "1,234.56"
16
+ * formatNumber(1234.56, 2, '.') // "1.234.56"
17
+ */
18
+ export declare function formatNumber(value: number | string, decimals?: number, separator?: string): string;
19
+ /**
20
+ * 가격을 포맷팅 (천 단위 구분자 + 통화 기호)
21
+ * @param value - 포맷팅할 가격
22
+ * @param currency - 통화 기호 (기본값: '₩')
23
+ * @param decimals - 소수점 자릿수 (기본값: 0)
24
+ * @param separator - 천 단위 구분자 (기본값: ',')
25
+ * @param position - 통화 기호 위치 (기본값: 'before')
26
+ * @returns 포맷팅된 가격 문자열
27
+ *
28
+ * @example
29
+ * formatPrice(1234567) // "₩1,234,567"
30
+ * formatPrice(1234.56, '₩', 2) // "₩1,234.56"
31
+ * formatPrice(1234, '$', 0, ',', 'after') // "1,234$"
32
+ */
33
+ export declare function formatPrice(value: number | string, currency?: string, decimals?: number, separator?: string, position?: 'before' | 'after'): string;
34
+ /**
35
+ * 국제화 통화 포맷팅 (Intl.NumberFormat 사용)
36
+ * @param value - 포맷팅할 금액
37
+ * @param locale - 로케일 (기본값: 'ko-KR')
38
+ * @param currency - 통화 코드 (기본값: 'KRW')
39
+ * @param style - 표시 스타일 (기본값: 'currency')
40
+ * @returns 포맷팅된 통화 문자열
41
+ *
42
+ * @example
43
+ * formatCurrencyIntl(1234567) // "₩1,234,567"
44
+ * formatCurrencyIntl(1234.56, 'en-US', 'USD') // "$1,234.56"
45
+ * formatCurrencyIntl(1234.56, 'ja-JP', 'JPY') // "¥1,235"
46
+ */
47
+ export declare function formatCurrencyIntl(value: number | string, locale?: string, currency?: string, style?: 'currency' | 'decimal'): string;
48
+ /**
49
+ * 가격 문자열을 숫자로 변환
50
+ * @param priceString - 가격 문자열 (예: "₩1,234,567" 또는 "1,234.56")
51
+ * @returns 변환된 숫자
52
+ *
53
+ * @example
54
+ * parsePrice("₩1,234,567") // 1234567
55
+ * parsePrice("1,234.56") // 1234.56
56
+ * parsePrice("$1,234.56") // 1234.56
57
+ */
58
+ export declare function parsePrice(priceString: string): number;
59
+ /**
60
+ * 할인율 계산
61
+ * @param originalPrice - 원래 가격
62
+ * @param discountedPrice - 할인된 가격
63
+ * @returns 할인율 (퍼센트)
64
+ *
65
+ * @example
66
+ * calculateDiscount(10000, 8000) // 20
67
+ * calculateDiscount(100, 75) // 25
68
+ */
69
+ export declare function calculateDiscount(originalPrice: number, discountedPrice: number): number;
70
+ /**
71
+ * 할인 가격 계산
72
+ * @param originalPrice - 원래 가격
73
+ * @param discountPercent - 할인율 (퍼센트)
74
+ * @returns 할인된 가격
75
+ *
76
+ * @example
77
+ * calculateDiscountedPrice(10000, 20) // 8000
78
+ * calculateDiscountedPrice(100, 25) // 75
79
+ */
80
+ export declare function calculateDiscountedPrice(originalPrice: number, discountPercent: number): number;
81
+ /**
82
+ * 세금 포함 가격 계산
83
+ * @param price - 세전 가격
84
+ * @param taxRate - 세율 (퍼센트, 기본값: 10)
85
+ * @returns 세금 포함 가격
86
+ *
87
+ * @example
88
+ * calculatePriceWithTax(10000, 10) // 11000
89
+ * calculatePriceWithTax(100, 20) // 120
90
+ */
91
+ export declare function calculatePriceWithTax(price: number, taxRate?: number): number;
92
+ /**
93
+ * 세금 제외 가격 계산
94
+ * @param priceWithTax - 세금 포함 가격
95
+ * @param taxRate - 세율 (퍼센트, 기본값: 10)
96
+ * @returns 세전 가격
97
+ *
98
+ * @example
99
+ * calculatePriceWithoutTax(11000, 10) // 10000
100
+ * calculatePriceWithoutTax(120, 20) // 100
101
+ */
102
+ export declare function calculatePriceWithoutTax(priceWithTax: number, taxRate?: number): number;
103
+ /**
104
+ * 백분율 포맷팅
105
+ * @param value - 포맷팅할 값 (0-1 또는 0-100)
106
+ * @param decimals - 소수점 자릿수 (기본값: 1)
107
+ * @param isDecimal - 값이 0-1 범위인지 여부 (기본값: false, 0-100 범위)
108
+ * @returns 포맷팅된 퍼센트 문자열
109
+ *
110
+ * @example
111
+ * formatPercent(0.1234, 1, true) // "12.3%"
112
+ * formatPercent(12.34, 1, false) // "12.3%"
113
+ * formatPercent(50) // "50.0%"
114
+ */
115
+ export declare function formatPercent(value: number, decimals?: number, isDecimal?: boolean): string;
116
+ //# sourceMappingURL=format.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"format.d.ts","sourceRoot":"","sources":["../../src/number/format.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAEH;;;;;;;;;;;GAWG;AACH,wBAAgB,YAAY,CAC1B,KAAK,EAAE,MAAM,GAAG,MAAM,EACtB,QAAQ,GAAE,MAAU,EACpB,SAAS,GAAE,MAAY,GACtB,MAAM,CAaR;AAED;;;;;;;;;;;;;GAaG;AACH,wBAAgB,WAAW,CACzB,KAAK,EAAE,MAAM,GAAG,MAAM,EACtB,QAAQ,GAAE,MAAY,EACtB,QAAQ,GAAE,MAAU,EACpB,SAAS,GAAE,MAAY,EACvB,QAAQ,GAAE,QAAQ,GAAG,OAAkB,GACtC,MAAM,CAIR;AAED;;;;;;;;;;;;GAYG;AACH,wBAAgB,kBAAkB,CAChC,KAAK,EAAE,MAAM,GAAG,MAAM,EACtB,MAAM,GAAE,MAAgB,EACxB,QAAQ,GAAE,MAAc,EACxB,KAAK,GAAE,UAAU,GAAG,SAAsB,GACzC,MAAM,CAaR;AAED;;;;;;;;;GASG;AACH,wBAAgB,UAAU,CAAC,WAAW,EAAE,MAAM,GAAG,MAAM,CAUtD;AAED;;;;;;;;;GASG;AACH,wBAAgB,iBAAiB,CAAC,aAAa,EAAE,MAAM,EAAE,eAAe,EAAE,MAAM,GAAG,MAAM,CAOxF;AAED;;;;;;;;;GASG;AACH,wBAAgB,wBAAwB,CAAC,aAAa,EAAE,MAAM,EAAE,eAAe,EAAE,MAAM,GAAG,MAAM,CAG/F;AAED;;;;;;;;;GASG;AACH,wBAAgB,qBAAqB,CAAC,KAAK,EAAE,MAAM,EAAE,OAAO,GAAE,MAAW,GAAG,MAAM,CAGjF;AAED;;;;;;;;;GASG;AACH,wBAAgB,wBAAwB,CAAC,YAAY,EAAE,MAAM,EAAE,OAAO,GAAE,MAAW,GAAG,MAAM,CAG3F;AAED;;;;;;;;;;;GAWG;AACH,wBAAgB,aAAa,CAC3B,KAAK,EAAE,MAAM,EACb,QAAQ,GAAE,MAAU,EACpB,SAAS,GAAE,OAAe,GACzB,MAAM,CAGR"}
@@ -0,0 +1,165 @@
1
+ /**
2
+ * Number Formatting Utilities
3
+ *
4
+ * 숫자 및 가격 포맷팅 관련 유틸리티 함수들
5
+ */
6
+ /**
7
+ * 숫자를 천 단위 구분자로 포맷팅
8
+ * @param value - 포맷팅할 숫자
9
+ * @param decimals - 소수점 자릿수 (기본값: 0)
10
+ * @param separator - 천 단위 구분자 (기본값: ',')
11
+ * @returns 포맷팅된 문자열
12
+ *
13
+ * @example
14
+ * formatNumber(1234567) // "1,234,567"
15
+ * formatNumber(1234.56, 2) // "1,234.56"
16
+ * formatNumber(1234.56, 2, '.') // "1.234.56"
17
+ */
18
+ export function formatNumber(value, decimals = 0, separator = ',') {
19
+ const num = typeof value === 'string' ? parseFloat(value) : value;
20
+ if (isNaN(num)) {
21
+ return '0';
22
+ }
23
+ const fixed = num.toFixed(decimals);
24
+ const parts = fixed.split('.');
25
+ const integerPart = parts[0].replace(/\B(?=(\d{3})+(?!\d))/g, separator);
26
+ const decimalPart = parts[1] ? `.${parts[1]}` : '';
27
+ return integerPart + decimalPart;
28
+ }
29
+ /**
30
+ * 가격을 포맷팅 (천 단위 구분자 + 통화 기호)
31
+ * @param value - 포맷팅할 가격
32
+ * @param currency - 통화 기호 (기본값: '₩')
33
+ * @param decimals - 소수점 자릿수 (기본값: 0)
34
+ * @param separator - 천 단위 구분자 (기본값: ',')
35
+ * @param position - 통화 기호 위치 (기본값: 'before')
36
+ * @returns 포맷팅된 가격 문자열
37
+ *
38
+ * @example
39
+ * formatPrice(1234567) // "₩1,234,567"
40
+ * formatPrice(1234.56, '₩', 2) // "₩1,234.56"
41
+ * formatPrice(1234, '$', 0, ',', 'after') // "1,234$"
42
+ */
43
+ export function formatPrice(value, currency = '₩', decimals = 0, separator = ',', position = 'before') {
44
+ const formatted = formatNumber(value, decimals, separator);
45
+ return position === 'before' ? `${currency}${formatted}` : `${formatted}${currency}`;
46
+ }
47
+ /**
48
+ * 국제화 통화 포맷팅 (Intl.NumberFormat 사용)
49
+ * @param value - 포맷팅할 금액
50
+ * @param locale - 로케일 (기본값: 'ko-KR')
51
+ * @param currency - 통화 코드 (기본값: 'KRW')
52
+ * @param style - 표시 스타일 (기본값: 'currency')
53
+ * @returns 포맷팅된 통화 문자열
54
+ *
55
+ * @example
56
+ * formatCurrencyIntl(1234567) // "₩1,234,567"
57
+ * formatCurrencyIntl(1234.56, 'en-US', 'USD') // "$1,234.56"
58
+ * formatCurrencyIntl(1234.56, 'ja-JP', 'JPY') // "¥1,235"
59
+ */
60
+ export function formatCurrencyIntl(value, locale = 'ko-KR', currency = 'KRW', style = 'currency') {
61
+ const num = typeof value === 'string' ? parseFloat(value) : value;
62
+ if (isNaN(num)) {
63
+ return '0';
64
+ }
65
+ return new Intl.NumberFormat(locale, {
66
+ style,
67
+ currency,
68
+ minimumFractionDigits: currency === 'KRW' || currency === 'JPY' ? 0 : 2,
69
+ maximumFractionDigits: currency === 'KRW' || currency === 'JPY' ? 0 : 2,
70
+ }).format(num);
71
+ }
72
+ /**
73
+ * 가격 문자열을 숫자로 변환
74
+ * @param priceString - 가격 문자열 (예: "₩1,234,567" 또는 "1,234.56")
75
+ * @returns 변환된 숫자
76
+ *
77
+ * @example
78
+ * parsePrice("₩1,234,567") // 1234567
79
+ * parsePrice("1,234.56") // 1234.56
80
+ * parsePrice("$1,234.56") // 1234.56
81
+ */
82
+ export function parsePrice(priceString) {
83
+ if (!priceString || typeof priceString !== 'string') {
84
+ return 0;
85
+ }
86
+ // 통화 기호, 공백, 쉼표 제거
87
+ const cleaned = priceString.replace(/[₩$€£¥,\s]/g, '');
88
+ const parsed = parseFloat(cleaned);
89
+ return isNaN(parsed) ? 0 : parsed;
90
+ }
91
+ /**
92
+ * 할인율 계산
93
+ * @param originalPrice - 원래 가격
94
+ * @param discountedPrice - 할인된 가격
95
+ * @returns 할인율 (퍼센트)
96
+ *
97
+ * @example
98
+ * calculateDiscount(10000, 8000) // 20
99
+ * calculateDiscount(100, 75) // 25
100
+ */
101
+ export function calculateDiscount(originalPrice, discountedPrice) {
102
+ if (originalPrice <= 0) {
103
+ return 0;
104
+ }
105
+ const discount = ((originalPrice - discountedPrice) / originalPrice) * 100;
106
+ return Math.round(discount * 100) / 100; // 소수점 2자리까지
107
+ }
108
+ /**
109
+ * 할인 가격 계산
110
+ * @param originalPrice - 원래 가격
111
+ * @param discountPercent - 할인율 (퍼센트)
112
+ * @returns 할인된 가격
113
+ *
114
+ * @example
115
+ * calculateDiscountedPrice(10000, 20) // 8000
116
+ * calculateDiscountedPrice(100, 25) // 75
117
+ */
118
+ export function calculateDiscountedPrice(originalPrice, discountPercent) {
119
+ const discount = (originalPrice * discountPercent) / 100;
120
+ return Math.round((originalPrice - discount) * 100) / 100;
121
+ }
122
+ /**
123
+ * 세금 포함 가격 계산
124
+ * @param price - 세전 가격
125
+ * @param taxRate - 세율 (퍼센트, 기본값: 10)
126
+ * @returns 세금 포함 가격
127
+ *
128
+ * @example
129
+ * calculatePriceWithTax(10000, 10) // 11000
130
+ * calculatePriceWithTax(100, 20) // 120
131
+ */
132
+ export function calculatePriceWithTax(price, taxRate = 10) {
133
+ const tax = (price * taxRate) / 100;
134
+ return Math.round((price + tax) * 100) / 100;
135
+ }
136
+ /**
137
+ * 세금 제외 가격 계산
138
+ * @param priceWithTax - 세금 포함 가격
139
+ * @param taxRate - 세율 (퍼센트, 기본값: 10)
140
+ * @returns 세전 가격
141
+ *
142
+ * @example
143
+ * calculatePriceWithoutTax(11000, 10) // 10000
144
+ * calculatePriceWithoutTax(120, 20) // 100
145
+ */
146
+ export function calculatePriceWithoutTax(priceWithTax, taxRate = 10) {
147
+ const price = priceWithTax / (1 + taxRate / 100);
148
+ return Math.round(price * 100) / 100;
149
+ }
150
+ /**
151
+ * 백분율 포맷팅
152
+ * @param value - 포맷팅할 값 (0-1 또는 0-100)
153
+ * @param decimals - 소수점 자릿수 (기본값: 1)
154
+ * @param isDecimal - 값이 0-1 범위인지 여부 (기본값: false, 0-100 범위)
155
+ * @returns 포맷팅된 퍼센트 문자열
156
+ *
157
+ * @example
158
+ * formatPercent(0.1234, 1, true) // "12.3%"
159
+ * formatPercent(12.34, 1, false) // "12.3%"
160
+ * formatPercent(50) // "50.0%"
161
+ */
162
+ export function formatPercent(value, decimals = 1, isDecimal = false) {
163
+ const percent = isDecimal ? value * 100 : value;
164
+ return `${percent.toFixed(decimals)}%`;
165
+ }
@@ -0,0 +1,7 @@
1
+ /**
2
+ * Number Utilities
3
+ *
4
+ * 숫자 및 가격 관련 유틸리티 함수들
5
+ */
6
+ export * from './format';
7
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/number/index.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAGH,cAAc,UAAU,CAAC"}
@@ -0,0 +1,7 @@
1
+ /**
2
+ * Number Utilities
3
+ *
4
+ * 숫자 및 가격 관련 유틸리티 함수들
5
+ */
6
+ // Formatting utilities
7
+ export * from './format';
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "goodchuck-utils",
3
- "version": "1.8.0",
3
+ "version": "1.9.0",
4
4
  "type": "module",
5
5
  "main": "dist/index.js",
6
6
  "types": "dist/index.d.ts",