jy-headless 0.3.16 → 0.3.17
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/Autocomplete/Autocomplete.js +1 -0
- package/dist/Autocomplete/Autocomplete.js.map +1 -0
- package/dist/Input/NumberInput.js +1 -0
- package/dist/Input/NumberInput.js.map +1 -0
- package/dist/Input/TextInput.js +1 -0
- package/dist/Input/TextInput.js.map +1 -0
- package/dist/Select/Select.js +1 -0
- package/dist/Select/Select.js.map +1 -0
- package/dist/Tooltip/Tooltip.js +1 -0
- package/dist/Tooltip/Tooltip.js.map +1 -0
- package/dist/hooks/useDebounce.js +1 -0
- package/dist/hooks/useDebounce.js.map +1 -0
- package/dist/hooks/usePortal.js +1 -0
- package/dist/hooks/usePortal.js.map +1 -0
- package/dist/hooks/useThrottle.js +1 -0
- package/dist/hooks/useThrottle.js.map +1 -0
- package/dist/index.js +1 -0
- package/dist/index.js.map +1 -0
- package/package.json +4 -3
- package/dist/cjs/Autocomplete/Autocomplete.d.ts +0 -110
- package/dist/cjs/Autocomplete/Autocomplete.js +0 -404
- package/dist/cjs/Autocomplete/Autocomplete.type.d.ts +0 -37
- package/dist/cjs/Autocomplete/index.d.ts +0 -2
- package/dist/cjs/Input/NumberInput.d.ts +0 -55
- package/dist/cjs/Input/NumberInput.js +0 -93
- package/dist/cjs/Input/NumberInput.type.d.ts +0 -31
- package/dist/cjs/Input/TextInput.d.ts +0 -98
- package/dist/cjs/Input/TextInput.js +0 -174
- package/dist/cjs/Input/TextInput.type.d.ts +0 -95
- package/dist/cjs/Input/index.d.ts +0 -4
- package/dist/cjs/Select/Select.d.ts +0 -51
- package/dist/cjs/Select/Select.js +0 -179
- package/dist/cjs/Select/Select.type.d.ts +0 -52
- package/dist/cjs/Select/index.d.ts +0 -2
- package/dist/cjs/Tooltip/Tooltip.d.ts +0 -10
- package/dist/cjs/Tooltip/Tooltip.js +0 -38
- package/dist/cjs/Tooltip/Tooltip.type.d.ts +0 -20
- package/dist/cjs/Tooltip/index.d.ts +0 -2
- package/dist/cjs/hooks/index.d.ts +0 -3
- package/dist/cjs/hooks/useDebounce.d.ts +0 -1
- package/dist/cjs/hooks/useDebounce.js +0 -24
- package/dist/cjs/hooks/usePortal.d.ts +0 -23
- package/dist/cjs/hooks/usePortal.js +0 -80
- package/dist/cjs/hooks/useThrottle.d.ts +0 -1
- package/dist/cjs/hooks/useThrottle.js +0 -34
- package/dist/cjs/index.d.ts +0 -5
- package/dist/cjs/index.js +0 -23
|
@@ -1,55 +0,0 @@
|
|
|
1
|
-
import { NumberInputProps } from './NumberInput.type';
|
|
2
|
-
/**
|
|
3
|
-
* 숫자 전용 Input 컴포넌트
|
|
4
|
-
*
|
|
5
|
-
* - 숫자만 입력 가능 (콤마는 내부적으로 제거 후 처리)
|
|
6
|
-
* - max: 최대값 제한 (초과 시 자동으로 max로 보정)
|
|
7
|
-
* - useThousandsSeparator: 천단위 콤마 자동 포맷팅
|
|
8
|
-
* - 실제 onChange로 넘어가는 값은:
|
|
9
|
-
* - useThousandsSeparator=true → 콤마가 포함된 문자열
|
|
10
|
-
* - false → 순수 숫자 문자열
|
|
11
|
-
*
|
|
12
|
-
* @example
|
|
13
|
-
* // 1) 기본 숫자 입력
|
|
14
|
-
* <NumberInput
|
|
15
|
-
* placeholder="숫자 입력"
|
|
16
|
-
* onChange={(e) => console.log(e.target.value)}
|
|
17
|
-
* />
|
|
18
|
-
*
|
|
19
|
-
* @example
|
|
20
|
-
* // 2) 최대값 제한
|
|
21
|
-
* <NumberInput
|
|
22
|
-
* max={100}
|
|
23
|
-
* placeholder="최대 100"
|
|
24
|
-
* onChange={(e) => console.log(e.target.value)}
|
|
25
|
-
* />
|
|
26
|
-
*
|
|
27
|
-
* @example
|
|
28
|
-
* // 3) 천단위 콤마 포맷
|
|
29
|
-
* <NumberInput
|
|
30
|
-
* useThousandsSeparator
|
|
31
|
-
* placeholder="금액 입력"
|
|
32
|
-
* onChange={(e) => console.log(e.target.value)} // 10000 → "10,000"
|
|
33
|
-
* />
|
|
34
|
-
*
|
|
35
|
-
* @example
|
|
36
|
-
* // 4) max + 천단위 콤마 같이 사용
|
|
37
|
-
* <NumberInput
|
|
38
|
-
* max={1000000}
|
|
39
|
-
* useThousandsSeparator
|
|
40
|
-
* placeholder="최대 1,000,000"
|
|
41
|
-
* onChange={(e) => console.log(e.target.value)}
|
|
42
|
-
* />
|
|
43
|
-
*
|
|
44
|
-
* @example
|
|
45
|
-
* // 5) React Hook Form 등과 함께 사용할 때
|
|
46
|
-
* // - 저장 시에는 콤마 제거 후 숫자로 변환하는 게 일반적
|
|
47
|
-
* <NumberInput
|
|
48
|
-
* useThousandsSeparator
|
|
49
|
-
* onChange={(e) => {
|
|
50
|
-
* const numeric = Number(e.target.value.replace(/,/g, ''));
|
|
51
|
-
* console.log(numeric);
|
|
52
|
-
* }}
|
|
53
|
-
* />
|
|
54
|
-
*/
|
|
55
|
-
export declare const NumberInput: ({ max, useThousandsSeparator, onChange, ...props }: NumberInputProps) => import("react/jsx-runtime").JSX.Element;
|
|
@@ -1,93 +0,0 @@
|
|
|
1
|
-
'use strict';
|
|
2
|
-
|
|
3
|
-
var jsxRuntime = require('react/jsx-runtime');
|
|
4
|
-
var TextInput = require('./TextInput.js');
|
|
5
|
-
|
|
6
|
-
/**
|
|
7
|
-
* 숫자 전용 Input 컴포넌트
|
|
8
|
-
*
|
|
9
|
-
* - 숫자만 입력 가능 (콤마는 내부적으로 제거 후 처리)
|
|
10
|
-
* - max: 최대값 제한 (초과 시 자동으로 max로 보정)
|
|
11
|
-
* - useThousandsSeparator: 천단위 콤마 자동 포맷팅
|
|
12
|
-
* - 실제 onChange로 넘어가는 값은:
|
|
13
|
-
* - useThousandsSeparator=true → 콤마가 포함된 문자열
|
|
14
|
-
* - false → 순수 숫자 문자열
|
|
15
|
-
*
|
|
16
|
-
* @example
|
|
17
|
-
* // 1) 기본 숫자 입력
|
|
18
|
-
* <NumberInput
|
|
19
|
-
* placeholder="숫자 입력"
|
|
20
|
-
* onChange={(e) => console.log(e.target.value)}
|
|
21
|
-
* />
|
|
22
|
-
*
|
|
23
|
-
* @example
|
|
24
|
-
* // 2) 최대값 제한
|
|
25
|
-
* <NumberInput
|
|
26
|
-
* max={100}
|
|
27
|
-
* placeholder="최대 100"
|
|
28
|
-
* onChange={(e) => console.log(e.target.value)}
|
|
29
|
-
* />
|
|
30
|
-
*
|
|
31
|
-
* @example
|
|
32
|
-
* // 3) 천단위 콤마 포맷
|
|
33
|
-
* <NumberInput
|
|
34
|
-
* useThousandsSeparator
|
|
35
|
-
* placeholder="금액 입력"
|
|
36
|
-
* onChange={(e) => console.log(e.target.value)} // 10000 → "10,000"
|
|
37
|
-
* />
|
|
38
|
-
*
|
|
39
|
-
* @example
|
|
40
|
-
* // 4) max + 천단위 콤마 같이 사용
|
|
41
|
-
* <NumberInput
|
|
42
|
-
* max={1000000}
|
|
43
|
-
* useThousandsSeparator
|
|
44
|
-
* placeholder="최대 1,000,000"
|
|
45
|
-
* onChange={(e) => console.log(e.target.value)}
|
|
46
|
-
* />
|
|
47
|
-
*
|
|
48
|
-
* @example
|
|
49
|
-
* // 5) React Hook Form 등과 함께 사용할 때
|
|
50
|
-
* // - 저장 시에는 콤마 제거 후 숫자로 변환하는 게 일반적
|
|
51
|
-
* <NumberInput
|
|
52
|
-
* useThousandsSeparator
|
|
53
|
-
* onChange={(e) => {
|
|
54
|
-
* const numeric = Number(e.target.value.replace(/,/g, ''));
|
|
55
|
-
* console.log(numeric);
|
|
56
|
-
* }}
|
|
57
|
-
* />
|
|
58
|
-
*/
|
|
59
|
-
const NumberInput = ({ max, useThousandsSeparator, onChange, ...props }) => {
|
|
60
|
-
const handleChange = (e) => {
|
|
61
|
-
const rawValue = e.target.value.replace(/,/g, '');
|
|
62
|
-
if (rawValue && isNaN(Number(rawValue)))
|
|
63
|
-
return;
|
|
64
|
-
let finalValue = rawValue;
|
|
65
|
-
if (max && Number(rawValue) > max) {
|
|
66
|
-
finalValue = String(max);
|
|
67
|
-
}
|
|
68
|
-
if (useThousandsSeparator && finalValue) {
|
|
69
|
-
const formattedValue = Number(finalValue).toLocaleString();
|
|
70
|
-
const syntheticEvent = {
|
|
71
|
-
...e,
|
|
72
|
-
target: {
|
|
73
|
-
...e.target,
|
|
74
|
-
value: formattedValue,
|
|
75
|
-
},
|
|
76
|
-
};
|
|
77
|
-
onChange?.(syntheticEvent);
|
|
78
|
-
}
|
|
79
|
-
else {
|
|
80
|
-
const syntheticEvent = {
|
|
81
|
-
...e,
|
|
82
|
-
target: {
|
|
83
|
-
...e.target,
|
|
84
|
-
value: finalValue,
|
|
85
|
-
},
|
|
86
|
-
};
|
|
87
|
-
onChange?.(syntheticEvent);
|
|
88
|
-
}
|
|
89
|
-
};
|
|
90
|
-
return jsxRuntime.jsx(TextInput.TextInput, { ...props, onChange: handleChange });
|
|
91
|
-
};
|
|
92
|
-
|
|
93
|
-
exports.NumberInput = NumberInput;
|
|
@@ -1,31 +0,0 @@
|
|
|
1
|
-
import { TextInputProps } from './TextInput.type';
|
|
2
|
-
/**
|
|
3
|
-
* 숫자 전용 입력 컴포넌트 props
|
|
4
|
-
*
|
|
5
|
-
* TextInput을 기반으로 하며,
|
|
6
|
-
* 숫자 입력에 특화된 기능을 제공합니다.
|
|
7
|
-
*/
|
|
8
|
-
export interface NumberInputProps extends TextInputProps {
|
|
9
|
-
/**
|
|
10
|
-
* 최대 허용 값
|
|
11
|
-
*
|
|
12
|
-
* 초과 시:
|
|
13
|
-
* - 입력 제한
|
|
14
|
-
* - 또는 validation 에러 처리 가능
|
|
15
|
-
*
|
|
16
|
-
* @example
|
|
17
|
-
* max={100}
|
|
18
|
-
*/
|
|
19
|
-
max?: number;
|
|
20
|
-
/**
|
|
21
|
-
* 천 단위 구분자 사용 여부
|
|
22
|
-
*
|
|
23
|
-
* true일 경우:
|
|
24
|
-
* 10000 → 10,000
|
|
25
|
-
*
|
|
26
|
-
* 내부 값은 parse 단계에서 콤마 제거 권장
|
|
27
|
-
*
|
|
28
|
-
* @default false
|
|
29
|
-
*/
|
|
30
|
-
useThousandsSeparator?: boolean;
|
|
31
|
-
}
|
|
@@ -1,98 +0,0 @@
|
|
|
1
|
-
import React from 'react';
|
|
2
|
-
import { TextInputProps } from './TextInput.type';
|
|
3
|
-
/**
|
|
4
|
-
* 범용 TextInput 컴포넌트
|
|
5
|
-
*
|
|
6
|
-
* - maxLength: (조합 입력 중이 아닐 때) 최대 길이 제한
|
|
7
|
-
* - pattern: 정규식 문자열(매 입력마다 test 통과해야 반영)
|
|
8
|
-
* - disallowPattern: "허용" 정규식(현재 구현은 test가 false면 return)
|
|
9
|
-
* - validator: 값 검증 (boolean | error message string 반환 가능)
|
|
10
|
-
* - trimWhitespace: blur 시 앞뒤 공백 제거 후 onChange 호출
|
|
11
|
-
* - debounceMs / onDebouncedChange: 디바운스된 값 콜백
|
|
12
|
-
* - throttleMs / onThrottledChange: 스로틀된 값 콜백
|
|
13
|
-
*
|
|
14
|
-
* @example
|
|
15
|
-
* // 1) 기본 사용
|
|
16
|
-
* <TextInput
|
|
17
|
-
* placeholder="이름을 입력하세요"
|
|
18
|
-
* onChange={(e) => console.log(e.target.value)}
|
|
19
|
-
* />
|
|
20
|
-
*
|
|
21
|
-
* @example
|
|
22
|
-
* // 2) maxLength + trimWhitespace (blur 시 공백 제거)
|
|
23
|
-
* <TextInput
|
|
24
|
-
* maxLength={20}
|
|
25
|
-
* trimWhitespace
|
|
26
|
-
* placeholder="최대 20자, blur 시 공백 제거"
|
|
27
|
-
* onChange={(e) => console.log('change:', e.target.value)}
|
|
28
|
-
* />
|
|
29
|
-
*
|
|
30
|
-
* @example
|
|
31
|
-
* // 3) pattern(문자열)로 숫자만 허용
|
|
32
|
-
* // - 입력값이 정규식에 매번 매칭되어야 반영됨
|
|
33
|
-
* <TextInput
|
|
34
|
-
* pattern="^[0-9]*$"
|
|
35
|
-
* inputMode="numeric"
|
|
36
|
-
* placeholder="숫자만 입력"
|
|
37
|
-
* onChange={(e) => console.log(e.target.value)}
|
|
38
|
-
* />
|
|
39
|
-
*
|
|
40
|
-
* @example
|
|
41
|
-
* // 4) disallowPattern(정규식)로 "허용 규칙" 적용 (현재 구현 기준)
|
|
42
|
-
* // - test가 false면 반영되지 않음
|
|
43
|
-
* // - 예: 영문/숫자/언더스코어만 허용
|
|
44
|
-
* <TextInput
|
|
45
|
-
* disallowPattern={/^[a-zA-Z0-9_]*$/}
|
|
46
|
-
* placeholder="영문/숫자/_ 만"
|
|
47
|
-
* onChange={(e) => console.log(e.target.value)}
|
|
48
|
-
* />
|
|
49
|
-
*
|
|
50
|
-
* @example
|
|
51
|
-
* // 5) validator + onValidate
|
|
52
|
-
* // - validator가 false면 onChange가 호출되지 않음
|
|
53
|
-
* // - 문자열 반환 시 그 문자열이 error로 넘어감
|
|
54
|
-
* <TextInput
|
|
55
|
-
* placeholder="이메일"
|
|
56
|
-
* validator={(v) => {
|
|
57
|
-
* if (!v) return true; // 빈 값은 통과(원하면 false로 바꿔도 됨)
|
|
58
|
-
* const ok = /^[^\s@]+@[^\s@]+\.[^\s@]+$/.test(v);
|
|
59
|
-
* return ok || '이메일 형식이 아니에요';
|
|
60
|
-
* }}
|
|
61
|
-
* onValidate={(isValid, error) => {
|
|
62
|
-
* console.log('valid:', isValid, 'error:', error);
|
|
63
|
-
* }}
|
|
64
|
-
* onChange={(e) => console.log(e.target.value)}
|
|
65
|
-
* />
|
|
66
|
-
*
|
|
67
|
-
* @example
|
|
68
|
-
* // 6) debounce: 입력은 즉시 반영(onChange), 서버검색/자동완성은 디바운스로 처리
|
|
69
|
-
* <TextInput
|
|
70
|
-
* placeholder="검색어 입력"
|
|
71
|
-
* debounceMs={300}
|
|
72
|
-
* onDebouncedChange={(value) => {
|
|
73
|
-
* // fetch(`/api/search?q=${encodeURIComponent(value)}`)
|
|
74
|
-
* console.log('debounced:', value);
|
|
75
|
-
* }}
|
|
76
|
-
* onChange={(e) => console.log('immediate:', e.target.value)}
|
|
77
|
-
* />
|
|
78
|
-
*
|
|
79
|
-
* @example
|
|
80
|
-
* // 7) throttle: 스크롤/리사이즈처럼 자주 쏘는 이벤트에 유용한 패턴
|
|
81
|
-
* <TextInput
|
|
82
|
-
* placeholder="입력값 로깅(최대 200ms에 1번)"
|
|
83
|
-
* throttleMs={200}
|
|
84
|
-
* onThrottledChange={(value) => console.log('throttled:', value)}
|
|
85
|
-
* onChange={(e) => console.log('immediate:', e.target.value)}
|
|
86
|
-
* />
|
|
87
|
-
*
|
|
88
|
-
* @example
|
|
89
|
-
* // 8) IME(한글/일본어) 조합 이벤트 처리 예시
|
|
90
|
-
* <TextInput
|
|
91
|
-
* placeholder="한글 입력"
|
|
92
|
-
* maxLength={10}
|
|
93
|
-
* onCompositionStart={() => console.log('compose start')}
|
|
94
|
-
* onCompositionEnd={() => console.log('compose end')}
|
|
95
|
-
* onChange={(e) => console.log(e.target.value)}
|
|
96
|
-
* />
|
|
97
|
-
*/
|
|
98
|
-
export declare const TextInput: React.ForwardRefExoticComponent<TextInputProps & React.RefAttributes<HTMLInputElement>>;
|
|
@@ -1,174 +0,0 @@
|
|
|
1
|
-
'use strict';
|
|
2
|
-
|
|
3
|
-
var jsxRuntime = require('react/jsx-runtime');
|
|
4
|
-
var react = require('react');
|
|
5
|
-
require('react-dom');
|
|
6
|
-
var useDebounce = require('../hooks/useDebounce.js');
|
|
7
|
-
var useThrottle = require('../hooks/useThrottle.js');
|
|
8
|
-
|
|
9
|
-
function mergeRefs(...refs) {
|
|
10
|
-
return (value) => {
|
|
11
|
-
refs.forEach((ref) => {
|
|
12
|
-
if (!ref)
|
|
13
|
-
return;
|
|
14
|
-
if (typeof ref === 'function')
|
|
15
|
-
ref(value);
|
|
16
|
-
else
|
|
17
|
-
ref.current = value;
|
|
18
|
-
});
|
|
19
|
-
};
|
|
20
|
-
}
|
|
21
|
-
/**
|
|
22
|
-
* 범용 TextInput 컴포넌트
|
|
23
|
-
*
|
|
24
|
-
* - maxLength: (조합 입력 중이 아닐 때) 최대 길이 제한
|
|
25
|
-
* - pattern: 정규식 문자열(매 입력마다 test 통과해야 반영)
|
|
26
|
-
* - disallowPattern: "허용" 정규식(현재 구현은 test가 false면 return)
|
|
27
|
-
* - validator: 값 검증 (boolean | error message string 반환 가능)
|
|
28
|
-
* - trimWhitespace: blur 시 앞뒤 공백 제거 후 onChange 호출
|
|
29
|
-
* - debounceMs / onDebouncedChange: 디바운스된 값 콜백
|
|
30
|
-
* - throttleMs / onThrottledChange: 스로틀된 값 콜백
|
|
31
|
-
*
|
|
32
|
-
* @example
|
|
33
|
-
* // 1) 기본 사용
|
|
34
|
-
* <TextInput
|
|
35
|
-
* placeholder="이름을 입력하세요"
|
|
36
|
-
* onChange={(e) => console.log(e.target.value)}
|
|
37
|
-
* />
|
|
38
|
-
*
|
|
39
|
-
* @example
|
|
40
|
-
* // 2) maxLength + trimWhitespace (blur 시 공백 제거)
|
|
41
|
-
* <TextInput
|
|
42
|
-
* maxLength={20}
|
|
43
|
-
* trimWhitespace
|
|
44
|
-
* placeholder="최대 20자, blur 시 공백 제거"
|
|
45
|
-
* onChange={(e) => console.log('change:', e.target.value)}
|
|
46
|
-
* />
|
|
47
|
-
*
|
|
48
|
-
* @example
|
|
49
|
-
* // 3) pattern(문자열)로 숫자만 허용
|
|
50
|
-
* // - 입력값이 정규식에 매번 매칭되어야 반영됨
|
|
51
|
-
* <TextInput
|
|
52
|
-
* pattern="^[0-9]*$"
|
|
53
|
-
* inputMode="numeric"
|
|
54
|
-
* placeholder="숫자만 입력"
|
|
55
|
-
* onChange={(e) => console.log(e.target.value)}
|
|
56
|
-
* />
|
|
57
|
-
*
|
|
58
|
-
* @example
|
|
59
|
-
* // 4) disallowPattern(정규식)로 "허용 규칙" 적용 (현재 구현 기준)
|
|
60
|
-
* // - test가 false면 반영되지 않음
|
|
61
|
-
* // - 예: 영문/숫자/언더스코어만 허용
|
|
62
|
-
* <TextInput
|
|
63
|
-
* disallowPattern={/^[a-zA-Z0-9_]*$/}
|
|
64
|
-
* placeholder="영문/숫자/_ 만"
|
|
65
|
-
* onChange={(e) => console.log(e.target.value)}
|
|
66
|
-
* />
|
|
67
|
-
*
|
|
68
|
-
* @example
|
|
69
|
-
* // 5) validator + onValidate
|
|
70
|
-
* // - validator가 false면 onChange가 호출되지 않음
|
|
71
|
-
* // - 문자열 반환 시 그 문자열이 error로 넘어감
|
|
72
|
-
* <TextInput
|
|
73
|
-
* placeholder="이메일"
|
|
74
|
-
* validator={(v) => {
|
|
75
|
-
* if (!v) return true; // 빈 값은 통과(원하면 false로 바꿔도 됨)
|
|
76
|
-
* const ok = /^[^\s@]+@[^\s@]+\.[^\s@]+$/.test(v);
|
|
77
|
-
* return ok || '이메일 형식이 아니에요';
|
|
78
|
-
* }}
|
|
79
|
-
* onValidate={(isValid, error) => {
|
|
80
|
-
* console.log('valid:', isValid, 'error:', error);
|
|
81
|
-
* }}
|
|
82
|
-
* onChange={(e) => console.log(e.target.value)}
|
|
83
|
-
* />
|
|
84
|
-
*
|
|
85
|
-
* @example
|
|
86
|
-
* // 6) debounce: 입력은 즉시 반영(onChange), 서버검색/자동완성은 디바운스로 처리
|
|
87
|
-
* <TextInput
|
|
88
|
-
* placeholder="검색어 입력"
|
|
89
|
-
* debounceMs={300}
|
|
90
|
-
* onDebouncedChange={(value) => {
|
|
91
|
-
* // fetch(`/api/search?q=${encodeURIComponent(value)}`)
|
|
92
|
-
* console.log('debounced:', value);
|
|
93
|
-
* }}
|
|
94
|
-
* onChange={(e) => console.log('immediate:', e.target.value)}
|
|
95
|
-
* />
|
|
96
|
-
*
|
|
97
|
-
* @example
|
|
98
|
-
* // 7) throttle: 스크롤/리사이즈처럼 자주 쏘는 이벤트에 유용한 패턴
|
|
99
|
-
* <TextInput
|
|
100
|
-
* placeholder="입력값 로깅(최대 200ms에 1번)"
|
|
101
|
-
* throttleMs={200}
|
|
102
|
-
* onThrottledChange={(value) => console.log('throttled:', value)}
|
|
103
|
-
* onChange={(e) => console.log('immediate:', e.target.value)}
|
|
104
|
-
* />
|
|
105
|
-
*
|
|
106
|
-
* @example
|
|
107
|
-
* // 8) IME(한글/일본어) 조합 이벤트 처리 예시
|
|
108
|
-
* <TextInput
|
|
109
|
-
* placeholder="한글 입력"
|
|
110
|
-
* maxLength={10}
|
|
111
|
-
* onCompositionStart={() => console.log('compose start')}
|
|
112
|
-
* onCompositionEnd={() => console.log('compose end')}
|
|
113
|
-
* onChange={(e) => console.log(e.target.value)}
|
|
114
|
-
* />
|
|
115
|
-
*/
|
|
116
|
-
const TextInput = react.forwardRef(({ maxLength, onChange, pattern, onValidate, validator, onCompositionStart, onCompositionEnd, disallowPattern, trimWhitespace, debounceMs, throttleMs, onDebouncedChange, onThrottledChange, onBlur, ...props }, ref) => {
|
|
117
|
-
const [isComposing, setIsComposing] = react.useState(false);
|
|
118
|
-
const innerRef = react.useRef(null);
|
|
119
|
-
const combinedRef = react.useMemo(() => mergeRefs(innerRef, ref), [ref]);
|
|
120
|
-
const debouncedChange = useDebounce.useDebounce((value) => onDebouncedChange?.(value), debounceMs || 0);
|
|
121
|
-
const throttledChange = useThrottle.useThrottle((value) => onThrottledChange?.(value), throttleMs || 0);
|
|
122
|
-
const handleCompositionStart = (e) => {
|
|
123
|
-
setIsComposing(true);
|
|
124
|
-
onCompositionStart?.(e);
|
|
125
|
-
};
|
|
126
|
-
const handleCompositionEnd = (e) => {
|
|
127
|
-
setIsComposing(false);
|
|
128
|
-
onCompositionEnd?.(e);
|
|
129
|
-
};
|
|
130
|
-
const handleChange = (e) => {
|
|
131
|
-
if (maxLength && !isComposing && e.target.value.length > maxLength)
|
|
132
|
-
return;
|
|
133
|
-
if (pattern) {
|
|
134
|
-
const regex = new RegExp(pattern);
|
|
135
|
-
if (!regex.test(e.target.value))
|
|
136
|
-
return;
|
|
137
|
-
}
|
|
138
|
-
if (disallowPattern && !disallowPattern.test(e.target.value))
|
|
139
|
-
return;
|
|
140
|
-
if (debounceMs && onDebouncedChange)
|
|
141
|
-
debouncedChange(e.target.value);
|
|
142
|
-
if (throttleMs && onThrottledChange)
|
|
143
|
-
throttledChange(e.target.value);
|
|
144
|
-
if (validator) {
|
|
145
|
-
const result = validator(e.target.value);
|
|
146
|
-
const isValid = typeof result === 'boolean' ? result : true;
|
|
147
|
-
const error = typeof result === 'string' ? result : undefined;
|
|
148
|
-
onValidate?.(isValid, error);
|
|
149
|
-
if (!isValid)
|
|
150
|
-
return;
|
|
151
|
-
}
|
|
152
|
-
onChange?.(e);
|
|
153
|
-
};
|
|
154
|
-
const handleBlur = (e) => {
|
|
155
|
-
if (trimWhitespace && innerRef.current) {
|
|
156
|
-
const trimmedValue = e.target.value.trim();
|
|
157
|
-
if (trimmedValue !== e.target.value) {
|
|
158
|
-
innerRef.current.value = trimmedValue;
|
|
159
|
-
const syntheticEvent = {
|
|
160
|
-
...e,
|
|
161
|
-
target: innerRef.current,
|
|
162
|
-
currentTarget: innerRef.current,
|
|
163
|
-
type: 'change',
|
|
164
|
-
};
|
|
165
|
-
onChange?.(syntheticEvent);
|
|
166
|
-
}
|
|
167
|
-
}
|
|
168
|
-
onBlur?.(e);
|
|
169
|
-
};
|
|
170
|
-
return (jsxRuntime.jsx("input", { ref: combinedRef, ...props, onBlur: handleBlur, onChange: handleChange, onCompositionStart: handleCompositionStart, onCompositionEnd: handleCompositionEnd }));
|
|
171
|
-
});
|
|
172
|
-
TextInput.displayName = 'TextInput';
|
|
173
|
-
|
|
174
|
-
exports.TextInput = TextInput;
|
|
@@ -1,95 +0,0 @@
|
|
|
1
|
-
import { ChangeEvent, CompositionEvent, InputHTMLAttributes } from 'react';
|
|
2
|
-
/**
|
|
3
|
-
* 고급 기능을 제공하는 TextInput 컴포넌트 props
|
|
4
|
-
*
|
|
5
|
-
* 기능:
|
|
6
|
-
* - validation
|
|
7
|
-
* - formatting/parsing
|
|
8
|
-
* - debounce/throttle
|
|
9
|
-
* - IME(한글 입력) 대응
|
|
10
|
-
* - 공백 제어
|
|
11
|
-
*/
|
|
12
|
-
export interface TextInputProps extends InputHTMLAttributes<HTMLInputElement> {
|
|
13
|
-
/**
|
|
14
|
-
* 입력값 검증 함수
|
|
15
|
-
*
|
|
16
|
-
* return:
|
|
17
|
-
* - true → 유효
|
|
18
|
-
* - false → 유효하지 않음
|
|
19
|
-
* - string → 에러 메시지
|
|
20
|
-
*
|
|
21
|
-
* @example
|
|
22
|
-
* validator={(v) => v.length > 3 || "최소 3자 이상"}
|
|
23
|
-
*/
|
|
24
|
-
validator?: (value: string) => boolean | string;
|
|
25
|
-
/**
|
|
26
|
-
* 검증 결과 콜백
|
|
27
|
-
*/
|
|
28
|
-
onValidate?: (isValid: boolean, error?: string) => void;
|
|
29
|
-
/**
|
|
30
|
-
* 표시용 값 포맷팅
|
|
31
|
-
*
|
|
32
|
-
* ex) 숫자 천단위 콤마
|
|
33
|
-
*/
|
|
34
|
-
format?: (value: string) => string;
|
|
35
|
-
/**
|
|
36
|
-
* 실제 값으로 변환
|
|
37
|
-
*
|
|
38
|
-
* ex) 콤마 제거
|
|
39
|
-
*/
|
|
40
|
-
parse?: (value: string) => string;
|
|
41
|
-
/**
|
|
42
|
-
* 입력을 막을 패턴
|
|
43
|
-
*
|
|
44
|
-
* @example 숫자만 허용
|
|
45
|
-
* disallowPattern={/[^0-9]/g}
|
|
46
|
-
*/
|
|
47
|
-
disallowPattern?: RegExp;
|
|
48
|
-
/**
|
|
49
|
-
* 공백 제거 방식
|
|
50
|
-
*
|
|
51
|
-
* - true → 전체 trim
|
|
52
|
-
* - 'leading' → 앞 공백만
|
|
53
|
-
* - 'trailing' → 뒤 공백만
|
|
54
|
-
* - 'both' → 양쪽
|
|
55
|
-
*/
|
|
56
|
-
trimWhitespace?: boolean | 'leading' | 'trailing' | 'both';
|
|
57
|
-
/**
|
|
58
|
-
* IME 입력 시작 이벤트
|
|
59
|
-
*
|
|
60
|
-
* 한글/중국어 입력 시 중요
|
|
61
|
-
*/
|
|
62
|
-
onCompositionStart?: (e: CompositionEvent<HTMLInputElement>) => void;
|
|
63
|
-
/**
|
|
64
|
-
* IME 입력 종료 이벤트
|
|
65
|
-
*/
|
|
66
|
-
onCompositionEnd?: (e: CompositionEvent<HTMLInputElement>) => void;
|
|
67
|
-
/**
|
|
68
|
-
* IME 입력 중 이벤트
|
|
69
|
-
*/
|
|
70
|
-
onCompositionUpdate?: (e: CompositionEvent<HTMLInputElement>) => void;
|
|
71
|
-
/**
|
|
72
|
-
* debounce 지연(ms)
|
|
73
|
-
*
|
|
74
|
-
* @example 300
|
|
75
|
-
*/
|
|
76
|
-
debounceMs?: number;
|
|
77
|
-
/**
|
|
78
|
-
* throttle 지연(ms)
|
|
79
|
-
*/
|
|
80
|
-
throttleMs?: number;
|
|
81
|
-
/**
|
|
82
|
-
* debounce 후 값 변경 콜백
|
|
83
|
-
*/
|
|
84
|
-
onDebouncedChange?: (value: string) => void;
|
|
85
|
-
/**
|
|
86
|
-
* throttle 후 값 변경 콜백
|
|
87
|
-
*/
|
|
88
|
-
onThrottledChange?: (value: string) => void;
|
|
89
|
-
/**
|
|
90
|
-
* 기본 onChange override
|
|
91
|
-
*
|
|
92
|
-
* ⚠️ 일반 HTML onChange와 동일
|
|
93
|
-
*/
|
|
94
|
-
onChange?: (value: ChangeEvent<HTMLInputElement>) => void;
|
|
95
|
-
}
|
|
@@ -1,51 +0,0 @@
|
|
|
1
|
-
import { Dispatch, SetStateAction } from 'react';
|
|
2
|
-
import { SelectOptionProps, SelectOptionsProps, SelectProps, SelectTriggerProps } from './Select.type';
|
|
3
|
-
/**
|
|
4
|
-
* Select 내부 상태 공유용 Context
|
|
5
|
-
*/
|
|
6
|
-
type SelectContextValue = {
|
|
7
|
-
/** 드롭다운 열림 여부 */
|
|
8
|
-
open: boolean;
|
|
9
|
-
/** 드롭다운 열기/닫기 */
|
|
10
|
-
setOpen: (v: boolean) => void;
|
|
11
|
-
/** 현재 선택된 값들 */
|
|
12
|
-
value: string[];
|
|
13
|
-
/** 값 토글 */
|
|
14
|
-
toggleValue: (v: string) => void;
|
|
15
|
-
/** 다중 선택 여부 */
|
|
16
|
-
multiple: boolean;
|
|
17
|
-
/** Trigger DOM ref */
|
|
18
|
-
triggerRef: React.RefObject<HTMLDivElement | null>;
|
|
19
|
-
/** Option DOM refs 목록 */
|
|
20
|
-
optionRefs: React.MutableRefObject<HTMLDivElement[]>;
|
|
21
|
-
/** 현재 포커스된 옵션 index */
|
|
22
|
-
focusedIndex: number | null;
|
|
23
|
-
/** 포커스 index 설정 */
|
|
24
|
-
setFocusedIndex: Dispatch<SetStateAction<number>>;
|
|
25
|
-
};
|
|
26
|
-
/**
|
|
27
|
-
* Select Context 접근 훅
|
|
28
|
-
*
|
|
29
|
-
* @throws Select 외부에서 사용할 경우 에러
|
|
30
|
-
*/
|
|
31
|
-
export declare const useSelectContext: () => SelectContextValue;
|
|
32
|
-
/**
|
|
33
|
-
* Compound Select 컴포넌트
|
|
34
|
-
*
|
|
35
|
-
* 사용 예시:
|
|
36
|
-
*
|
|
37
|
-
* @example
|
|
38
|
-
* <Select value={value} onChange={setValue}>
|
|
39
|
-
* <Select.Trigger>열기</Select.Trigger>
|
|
40
|
-
* <Select.Options>
|
|
41
|
-
* <Select.Option value="a">A</Select.Option>
|
|
42
|
-
* <Select.Option value="b">B</Select.Option>
|
|
43
|
-
* </Select.Options>
|
|
44
|
-
* </Select>
|
|
45
|
-
*/
|
|
46
|
-
export declare const Select: (({ value, onChange, multiple, children }: SelectProps) => import("react/jsx-runtime").JSX.Element) & {
|
|
47
|
-
Trigger: (props: SelectTriggerProps) => import("react/jsx-runtime").JSX.Element;
|
|
48
|
-
Options: ({ children, ...props }: SelectOptionsProps) => import("react").ReactPortal | null;
|
|
49
|
-
Option: ({ value, disabled, children, ...props }: SelectOptionProps) => import("react/jsx-runtime").JSX.Element;
|
|
50
|
-
};
|
|
51
|
-
export {};
|