jy-headless 0.3.11 → 0.3.13
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.d.ts +1 -1
- package/dist/Autocomplete/Autocomplete.js +17 -29
- package/dist/Autocomplete/Autocomplete.type.d.ts +3 -2
- package/dist/Autocomplete/index.d.ts +1 -0
- package/dist/Input/TextInput.d.ts +2 -1
- package/dist/Input/TextInput.js +77 -0
- package/dist/Input/index.d.ts +2 -0
- package/dist/Select/index.d.ts +1 -0
- package/dist/cjs/Autocomplete/Autocomplete.d.ts +1 -1
- package/dist/cjs/Autocomplete/Autocomplete.js +17 -29
- package/dist/cjs/Autocomplete/Autocomplete.type.d.ts +3 -2
- package/dist/cjs/Autocomplete/index.d.ts +1 -0
- package/dist/cjs/Input/TextInput.d.ts +2 -1
- package/dist/cjs/Input/TextInput.js +79 -0
- package/dist/cjs/Input/index.d.ts +2 -0
- package/dist/cjs/Select/index.d.ts +1 -0
- package/dist/cjs/index.js +1 -0
- package/dist/index.js +1 -0
- package/package.json +1 -1
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import React from 'react';
|
|
2
2
|
import type { AutocompleteInputProps, AutocompleteOptionProps, AutocompleteOptionsProps, AutocompleteProps } from './Autocomplete.type';
|
|
3
3
|
declare const Autocomplete: (({ value, onChange, inputValue, onInputChange, disabled, filterFn, children, }: AutocompleteProps) => import("react/jsx-runtime").JSX.Element) & {
|
|
4
|
-
Input: ({ onKeyDown, onFocus, onChange, ...props }: AutocompleteInputProps) => import("react/jsx-runtime").JSX.Element;
|
|
4
|
+
Input: ({ onKeyDown, onFocus, onChange, onCompositionStart, onCompositionEnd, ...props }: AutocompleteInputProps) => import("react/jsx-runtime").JSX.Element;
|
|
5
5
|
Options: ({ items, renderItem, itemHeight, maxVisibleItems, overscan, children, ...props }: AutocompleteOptionsProps) => React.ReactPortal | null;
|
|
6
6
|
Option: ({ value, label, disabled, children, ...props }: AutocompleteOptionProps) => import("react/jsx-runtime").JSX.Element | null;
|
|
7
7
|
};
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { jsxs, jsx, Fragment } from 'react/jsx-runtime';
|
|
2
2
|
import { createContext, useState, useRef, useId, useMemo, useEffect, useContext } from 'react';
|
|
3
3
|
import usePortal from '../hooks/usePortal.js';
|
|
4
|
+
import TextInput from '../Input/TextInput.js';
|
|
4
5
|
|
|
5
6
|
const AutocompleteContext = createContext(null);
|
|
6
7
|
const useAutocomplete = () => {
|
|
@@ -104,8 +105,11 @@ const useItemsBridge = () => useContext(ItemsBridgeContext);
|
|
|
104
105
|
* - 포커스는 input 유지
|
|
105
106
|
* - aria-activedescendant로 active option을 알려줌
|
|
106
107
|
*/
|
|
107
|
-
|
|
108
|
+
// ...생략
|
|
109
|
+
const Input = ({ onKeyDown, onFocus, onChange, onCompositionStart, onCompositionEnd, ...props }) => {
|
|
108
110
|
const { open, setOpen, query, setQuery, disabled, listboxId, activeIndex, setActiveIndex, filtered, commitByIndex, close, inputRef, } = useAutocomplete();
|
|
111
|
+
// ✅ IME 조합 중에는 방향키/엔터로 옵션 선택하지 않게 막기
|
|
112
|
+
const composingRef = useRef(false);
|
|
109
113
|
const activeId = activeIndex >= 0 ? `${listboxId}-opt-${activeIndex}` : undefined;
|
|
110
114
|
const move = (delta) => {
|
|
111
115
|
if (!filtered.length)
|
|
@@ -113,15 +117,7 @@ const Input = ({ onKeyDown, onFocus, onChange, ...props }) => {
|
|
|
113
117
|
setOpen(true);
|
|
114
118
|
setActiveIndex(activeIndex < 0 ? 0 : (activeIndex + delta + filtered.length) % filtered.length);
|
|
115
119
|
};
|
|
116
|
-
|
|
117
|
-
if (!filtered.length)
|
|
118
|
-
return;
|
|
119
|
-
setOpen(true);
|
|
120
|
-
// 10개 단위 이동(관례). 필요하면 props로 빼도 됨
|
|
121
|
-
const jump = 10 * deltaPages;
|
|
122
|
-
setActiveIndex(Math.max(0, Math.min(filtered.length - 1, (activeIndex < 0 ? 0 : activeIndex) + jump)));
|
|
123
|
-
};
|
|
124
|
-
return (jsxs(Fragment, { children: [jsx("input", { ref: inputRef, role: 'combobox', "aria-autocomplete": 'list', "aria-expanded": open, "aria-controls": listboxId, "aria-activedescendant": activeId, disabled: disabled, value: query, onFocus: (e) => {
|
|
120
|
+
return (jsxs(Fragment, { children: [jsx(TextInput, { ref: inputRef, role: 'combobox', "aria-autocomplete": 'list', "aria-expanded": open, "aria-controls": listboxId, "aria-activedescendant": activeId, disabled: disabled, value: query, onFocus: (e) => {
|
|
125
121
|
if (!disabled) {
|
|
126
122
|
setOpen(true);
|
|
127
123
|
setActiveIndex(filtered.length ? 0 : -1);
|
|
@@ -134,9 +130,20 @@ const Input = ({ onKeyDown, onFocus, onChange, ...props }) => {
|
|
|
134
130
|
setOpen(true);
|
|
135
131
|
setActiveIndex(0);
|
|
136
132
|
onChange?.(e);
|
|
133
|
+
}, onCompositionStart: (e) => {
|
|
134
|
+
composingRef.current = true;
|
|
135
|
+
onCompositionStart?.(e);
|
|
136
|
+
}, onCompositionEnd: (e) => {
|
|
137
|
+
composingRef.current = false;
|
|
138
|
+
onCompositionEnd?.(e);
|
|
137
139
|
}, onKeyDown: (e) => {
|
|
138
140
|
if (disabled)
|
|
139
141
|
return;
|
|
142
|
+
// ✅ 조합중이면 Autocomplete 키처리 하지 않음
|
|
143
|
+
if (composingRef.current) {
|
|
144
|
+
onKeyDown?.(e);
|
|
145
|
+
return;
|
|
146
|
+
}
|
|
140
147
|
switch (e.key) {
|
|
141
148
|
case 'ArrowDown':
|
|
142
149
|
e.preventDefault();
|
|
@@ -146,24 +153,6 @@ const Input = ({ onKeyDown, onFocus, onChange, ...props }) => {
|
|
|
146
153
|
e.preventDefault();
|
|
147
154
|
move(-1);
|
|
148
155
|
break;
|
|
149
|
-
case 'Home':
|
|
150
|
-
e.preventDefault();
|
|
151
|
-
setOpen(true);
|
|
152
|
-
setActiveIndex(filtered.length ? 0 : -1);
|
|
153
|
-
break;
|
|
154
|
-
case 'End':
|
|
155
|
-
e.preventDefault();
|
|
156
|
-
setOpen(true);
|
|
157
|
-
setActiveIndex(filtered.length ? filtered.length - 1 : -1);
|
|
158
|
-
break;
|
|
159
|
-
case 'PageDown':
|
|
160
|
-
e.preventDefault();
|
|
161
|
-
pageMove(1);
|
|
162
|
-
break;
|
|
163
|
-
case 'PageUp':
|
|
164
|
-
e.preventDefault();
|
|
165
|
-
pageMove(-1);
|
|
166
|
-
break;
|
|
167
156
|
case 'Enter':
|
|
168
157
|
if (open && activeIndex >= 0) {
|
|
169
158
|
e.preventDefault();
|
|
@@ -175,7 +164,6 @@ const Input = ({ onKeyDown, onFocus, onChange, ...props }) => {
|
|
|
175
164
|
close();
|
|
176
165
|
break;
|
|
177
166
|
case 'Tab':
|
|
178
|
-
// 관례: 탭 이동 시 팝오버 닫기
|
|
179
167
|
close();
|
|
180
168
|
break;
|
|
181
169
|
}
|
|
@@ -1,4 +1,5 @@
|
|
|
1
|
-
import { HTMLAttributes,
|
|
1
|
+
import { HTMLAttributes, ReactNode } from 'react';
|
|
2
|
+
import { TextInputProps } from '../Input/TextInput.type';
|
|
2
3
|
export type AutocompleteItem = {
|
|
3
4
|
value: string;
|
|
4
5
|
label: string;
|
|
@@ -14,7 +15,7 @@ export interface AutocompleteProps {
|
|
|
14
15
|
filterFn?: (item: AutocompleteItem, query: string) => boolean;
|
|
15
16
|
children: ReactNode;
|
|
16
17
|
}
|
|
17
|
-
export interface AutocompleteInputProps extends
|
|
18
|
+
export interface AutocompleteInputProps extends TextInputProps {
|
|
18
19
|
/** label 연결용 (없으면 aria-label 필수) */
|
|
19
20
|
'aria-label'?: string;
|
|
20
21
|
}
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import React from 'react';
|
|
1
2
|
import { TextInputProps } from './TextInput.type';
|
|
2
|
-
declare const TextInput:
|
|
3
|
+
declare const TextInput: React.ForwardRefExoticComponent<TextInputProps & React.RefAttributes<HTMLInputElement>>;
|
|
3
4
|
export default TextInput;
|
|
@@ -0,0 +1,77 @@
|
|
|
1
|
+
import { jsx } from 'react/jsx-runtime';
|
|
2
|
+
import { forwardRef, useState, useRef, useMemo } from 'react';
|
|
3
|
+
import 'react-dom';
|
|
4
|
+
import { useDebounce } from '../hooks/useDebounce.js';
|
|
5
|
+
import { useThrottle } from '../hooks/useThrottle.js';
|
|
6
|
+
|
|
7
|
+
function mergeRefs(...refs) {
|
|
8
|
+
return (value) => {
|
|
9
|
+
refs.forEach((ref) => {
|
|
10
|
+
if (!ref)
|
|
11
|
+
return;
|
|
12
|
+
if (typeof ref === 'function')
|
|
13
|
+
ref(value);
|
|
14
|
+
else
|
|
15
|
+
ref.current = value;
|
|
16
|
+
});
|
|
17
|
+
};
|
|
18
|
+
}
|
|
19
|
+
const TextInput = forwardRef(({ maxLength, onChange, pattern, onValidate, validator, onCompositionStart, onCompositionEnd, disallowPattern, trimWhitespace, debounceMs, throttleMs, onDebouncedChange, onThrottledChange, onBlur, ...props }, ref) => {
|
|
20
|
+
const [isComposing, setIsComposing] = useState(false);
|
|
21
|
+
const innerRef = useRef(null);
|
|
22
|
+
const combinedRef = useMemo(() => mergeRefs(innerRef, ref), [ref]);
|
|
23
|
+
const debouncedChange = useDebounce((value) => onDebouncedChange?.(value), debounceMs || 0);
|
|
24
|
+
const throttledChange = useThrottle((value) => onThrottledChange?.(value), throttleMs || 0);
|
|
25
|
+
const handleCompositionStart = (e) => {
|
|
26
|
+
setIsComposing(true);
|
|
27
|
+
onCompositionStart?.(e);
|
|
28
|
+
};
|
|
29
|
+
const handleCompositionEnd = (e) => {
|
|
30
|
+
setIsComposing(false);
|
|
31
|
+
onCompositionEnd?.(e);
|
|
32
|
+
};
|
|
33
|
+
const handleChange = (e) => {
|
|
34
|
+
if (maxLength && !isComposing && e.target.value.length > maxLength)
|
|
35
|
+
return;
|
|
36
|
+
if (pattern) {
|
|
37
|
+
const regex = new RegExp(pattern);
|
|
38
|
+
if (!regex.test(e.target.value))
|
|
39
|
+
return;
|
|
40
|
+
}
|
|
41
|
+
if (disallowPattern && !disallowPattern.test(e.target.value))
|
|
42
|
+
return;
|
|
43
|
+
if (debounceMs && onDebouncedChange)
|
|
44
|
+
debouncedChange(e.target.value);
|
|
45
|
+
if (throttleMs && onThrottledChange)
|
|
46
|
+
throttledChange(e.target.value);
|
|
47
|
+
if (validator) {
|
|
48
|
+
const result = validator(e.target.value);
|
|
49
|
+
const isValid = typeof result === 'boolean' ? result : true;
|
|
50
|
+
const error = typeof result === 'string' ? result : undefined;
|
|
51
|
+
onValidate?.(isValid, error);
|
|
52
|
+
if (!isValid)
|
|
53
|
+
return;
|
|
54
|
+
}
|
|
55
|
+
onChange?.(e);
|
|
56
|
+
};
|
|
57
|
+
const handleBlur = (e) => {
|
|
58
|
+
if (trimWhitespace && innerRef.current) {
|
|
59
|
+
const trimmedValue = e.target.value.trim();
|
|
60
|
+
if (trimmedValue !== e.target.value) {
|
|
61
|
+
innerRef.current.value = trimmedValue;
|
|
62
|
+
const syntheticEvent = {
|
|
63
|
+
...e,
|
|
64
|
+
target: innerRef.current,
|
|
65
|
+
currentTarget: innerRef.current,
|
|
66
|
+
type: 'change',
|
|
67
|
+
};
|
|
68
|
+
onChange?.(syntheticEvent);
|
|
69
|
+
}
|
|
70
|
+
}
|
|
71
|
+
onBlur?.(e);
|
|
72
|
+
};
|
|
73
|
+
return (jsx("input", { ref: combinedRef, ...props, onBlur: handleBlur, onChange: handleChange, onCompositionStart: handleCompositionStart, onCompositionEnd: handleCompositionEnd }));
|
|
74
|
+
});
|
|
75
|
+
TextInput.displayName = 'TextInput';
|
|
76
|
+
|
|
77
|
+
export { TextInput as default };
|
package/dist/Input/index.d.ts
CHANGED
package/dist/Select/index.d.ts
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import React from 'react';
|
|
2
2
|
import type { AutocompleteInputProps, AutocompleteOptionProps, AutocompleteOptionsProps, AutocompleteProps } from './Autocomplete.type';
|
|
3
3
|
declare const Autocomplete: (({ value, onChange, inputValue, onInputChange, disabled, filterFn, children, }: AutocompleteProps) => import("react/jsx-runtime").JSX.Element) & {
|
|
4
|
-
Input: ({ onKeyDown, onFocus, onChange, ...props }: AutocompleteInputProps) => import("react/jsx-runtime").JSX.Element;
|
|
4
|
+
Input: ({ onKeyDown, onFocus, onChange, onCompositionStart, onCompositionEnd, ...props }: AutocompleteInputProps) => import("react/jsx-runtime").JSX.Element;
|
|
5
5
|
Options: ({ items, renderItem, itemHeight, maxVisibleItems, overscan, children, ...props }: AutocompleteOptionsProps) => React.ReactPortal | null;
|
|
6
6
|
Option: ({ value, label, disabled, children, ...props }: AutocompleteOptionProps) => import("react/jsx-runtime").JSX.Element | null;
|
|
7
7
|
};
|
|
@@ -3,6 +3,7 @@
|
|
|
3
3
|
var jsxRuntime = require('react/jsx-runtime');
|
|
4
4
|
var react = require('react');
|
|
5
5
|
var usePortal = require('../hooks/usePortal.js');
|
|
6
|
+
var TextInput = require('../Input/TextInput.js');
|
|
6
7
|
|
|
7
8
|
const AutocompleteContext = react.createContext(null);
|
|
8
9
|
const useAutocomplete = () => {
|
|
@@ -106,8 +107,11 @@ const useItemsBridge = () => react.useContext(ItemsBridgeContext);
|
|
|
106
107
|
* - 포커스는 input 유지
|
|
107
108
|
* - aria-activedescendant로 active option을 알려줌
|
|
108
109
|
*/
|
|
109
|
-
|
|
110
|
+
// ...생략
|
|
111
|
+
const Input = ({ onKeyDown, onFocus, onChange, onCompositionStart, onCompositionEnd, ...props }) => {
|
|
110
112
|
const { open, setOpen, query, setQuery, disabled, listboxId, activeIndex, setActiveIndex, filtered, commitByIndex, close, inputRef, } = useAutocomplete();
|
|
113
|
+
// ✅ IME 조합 중에는 방향키/엔터로 옵션 선택하지 않게 막기
|
|
114
|
+
const composingRef = react.useRef(false);
|
|
111
115
|
const activeId = activeIndex >= 0 ? `${listboxId}-opt-${activeIndex}` : undefined;
|
|
112
116
|
const move = (delta) => {
|
|
113
117
|
if (!filtered.length)
|
|
@@ -115,15 +119,7 @@ const Input = ({ onKeyDown, onFocus, onChange, ...props }) => {
|
|
|
115
119
|
setOpen(true);
|
|
116
120
|
setActiveIndex(activeIndex < 0 ? 0 : (activeIndex + delta + filtered.length) % filtered.length);
|
|
117
121
|
};
|
|
118
|
-
|
|
119
|
-
if (!filtered.length)
|
|
120
|
-
return;
|
|
121
|
-
setOpen(true);
|
|
122
|
-
// 10개 단위 이동(관례). 필요하면 props로 빼도 됨
|
|
123
|
-
const jump = 10 * deltaPages;
|
|
124
|
-
setActiveIndex(Math.max(0, Math.min(filtered.length - 1, (activeIndex < 0 ? 0 : activeIndex) + jump)));
|
|
125
|
-
};
|
|
126
|
-
return (jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [jsxRuntime.jsx("input", { ref: inputRef, role: 'combobox', "aria-autocomplete": 'list', "aria-expanded": open, "aria-controls": listboxId, "aria-activedescendant": activeId, disabled: disabled, value: query, onFocus: (e) => {
|
|
122
|
+
return (jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [jsxRuntime.jsx(TextInput, { ref: inputRef, role: 'combobox', "aria-autocomplete": 'list', "aria-expanded": open, "aria-controls": listboxId, "aria-activedescendant": activeId, disabled: disabled, value: query, onFocus: (e) => {
|
|
127
123
|
if (!disabled) {
|
|
128
124
|
setOpen(true);
|
|
129
125
|
setActiveIndex(filtered.length ? 0 : -1);
|
|
@@ -136,9 +132,20 @@ const Input = ({ onKeyDown, onFocus, onChange, ...props }) => {
|
|
|
136
132
|
setOpen(true);
|
|
137
133
|
setActiveIndex(0);
|
|
138
134
|
onChange?.(e);
|
|
135
|
+
}, onCompositionStart: (e) => {
|
|
136
|
+
composingRef.current = true;
|
|
137
|
+
onCompositionStart?.(e);
|
|
138
|
+
}, onCompositionEnd: (e) => {
|
|
139
|
+
composingRef.current = false;
|
|
140
|
+
onCompositionEnd?.(e);
|
|
139
141
|
}, onKeyDown: (e) => {
|
|
140
142
|
if (disabled)
|
|
141
143
|
return;
|
|
144
|
+
// ✅ 조합중이면 Autocomplete 키처리 하지 않음
|
|
145
|
+
if (composingRef.current) {
|
|
146
|
+
onKeyDown?.(e);
|
|
147
|
+
return;
|
|
148
|
+
}
|
|
142
149
|
switch (e.key) {
|
|
143
150
|
case 'ArrowDown':
|
|
144
151
|
e.preventDefault();
|
|
@@ -148,24 +155,6 @@ const Input = ({ onKeyDown, onFocus, onChange, ...props }) => {
|
|
|
148
155
|
e.preventDefault();
|
|
149
156
|
move(-1);
|
|
150
157
|
break;
|
|
151
|
-
case 'Home':
|
|
152
|
-
e.preventDefault();
|
|
153
|
-
setOpen(true);
|
|
154
|
-
setActiveIndex(filtered.length ? 0 : -1);
|
|
155
|
-
break;
|
|
156
|
-
case 'End':
|
|
157
|
-
e.preventDefault();
|
|
158
|
-
setOpen(true);
|
|
159
|
-
setActiveIndex(filtered.length ? filtered.length - 1 : -1);
|
|
160
|
-
break;
|
|
161
|
-
case 'PageDown':
|
|
162
|
-
e.preventDefault();
|
|
163
|
-
pageMove(1);
|
|
164
|
-
break;
|
|
165
|
-
case 'PageUp':
|
|
166
|
-
e.preventDefault();
|
|
167
|
-
pageMove(-1);
|
|
168
|
-
break;
|
|
169
158
|
case 'Enter':
|
|
170
159
|
if (open && activeIndex >= 0) {
|
|
171
160
|
e.preventDefault();
|
|
@@ -177,7 +166,6 @@ const Input = ({ onKeyDown, onFocus, onChange, ...props }) => {
|
|
|
177
166
|
close();
|
|
178
167
|
break;
|
|
179
168
|
case 'Tab':
|
|
180
|
-
// 관례: 탭 이동 시 팝오버 닫기
|
|
181
169
|
close();
|
|
182
170
|
break;
|
|
183
171
|
}
|
|
@@ -1,4 +1,5 @@
|
|
|
1
|
-
import { HTMLAttributes,
|
|
1
|
+
import { HTMLAttributes, ReactNode } from 'react';
|
|
2
|
+
import { TextInputProps } from '../Input/TextInput.type';
|
|
2
3
|
export type AutocompleteItem = {
|
|
3
4
|
value: string;
|
|
4
5
|
label: string;
|
|
@@ -14,7 +15,7 @@ export interface AutocompleteProps {
|
|
|
14
15
|
filterFn?: (item: AutocompleteItem, query: string) => boolean;
|
|
15
16
|
children: ReactNode;
|
|
16
17
|
}
|
|
17
|
-
export interface AutocompleteInputProps extends
|
|
18
|
+
export interface AutocompleteInputProps extends TextInputProps {
|
|
18
19
|
/** label 연결용 (없으면 aria-label 필수) */
|
|
19
20
|
'aria-label'?: string;
|
|
20
21
|
}
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import React from 'react';
|
|
1
2
|
import { TextInputProps } from './TextInput.type';
|
|
2
|
-
declare const TextInput:
|
|
3
|
+
declare const TextInput: React.ForwardRefExoticComponent<TextInputProps & React.RefAttributes<HTMLInputElement>>;
|
|
3
4
|
export default TextInput;
|
|
@@ -0,0 +1,79 @@
|
|
|
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
|
+
const TextInput = react.forwardRef(({ maxLength, onChange, pattern, onValidate, validator, onCompositionStart, onCompositionEnd, disallowPattern, trimWhitespace, debounceMs, throttleMs, onDebouncedChange, onThrottledChange, onBlur, ...props }, ref) => {
|
|
22
|
+
const [isComposing, setIsComposing] = react.useState(false);
|
|
23
|
+
const innerRef = react.useRef(null);
|
|
24
|
+
const combinedRef = react.useMemo(() => mergeRefs(innerRef, ref), [ref]);
|
|
25
|
+
const debouncedChange = useDebounce.useDebounce((value) => onDebouncedChange?.(value), debounceMs || 0);
|
|
26
|
+
const throttledChange = useThrottle.useThrottle((value) => onThrottledChange?.(value), throttleMs || 0);
|
|
27
|
+
const handleCompositionStart = (e) => {
|
|
28
|
+
setIsComposing(true);
|
|
29
|
+
onCompositionStart?.(e);
|
|
30
|
+
};
|
|
31
|
+
const handleCompositionEnd = (e) => {
|
|
32
|
+
setIsComposing(false);
|
|
33
|
+
onCompositionEnd?.(e);
|
|
34
|
+
};
|
|
35
|
+
const handleChange = (e) => {
|
|
36
|
+
if (maxLength && !isComposing && e.target.value.length > maxLength)
|
|
37
|
+
return;
|
|
38
|
+
if (pattern) {
|
|
39
|
+
const regex = new RegExp(pattern);
|
|
40
|
+
if (!regex.test(e.target.value))
|
|
41
|
+
return;
|
|
42
|
+
}
|
|
43
|
+
if (disallowPattern && !disallowPattern.test(e.target.value))
|
|
44
|
+
return;
|
|
45
|
+
if (debounceMs && onDebouncedChange)
|
|
46
|
+
debouncedChange(e.target.value);
|
|
47
|
+
if (throttleMs && onThrottledChange)
|
|
48
|
+
throttledChange(e.target.value);
|
|
49
|
+
if (validator) {
|
|
50
|
+
const result = validator(e.target.value);
|
|
51
|
+
const isValid = typeof result === 'boolean' ? result : true;
|
|
52
|
+
const error = typeof result === 'string' ? result : undefined;
|
|
53
|
+
onValidate?.(isValid, error);
|
|
54
|
+
if (!isValid)
|
|
55
|
+
return;
|
|
56
|
+
}
|
|
57
|
+
onChange?.(e);
|
|
58
|
+
};
|
|
59
|
+
const handleBlur = (e) => {
|
|
60
|
+
if (trimWhitespace && innerRef.current) {
|
|
61
|
+
const trimmedValue = e.target.value.trim();
|
|
62
|
+
if (trimmedValue !== e.target.value) {
|
|
63
|
+
innerRef.current.value = trimmedValue;
|
|
64
|
+
const syntheticEvent = {
|
|
65
|
+
...e,
|
|
66
|
+
target: innerRef.current,
|
|
67
|
+
currentTarget: innerRef.current,
|
|
68
|
+
type: 'change',
|
|
69
|
+
};
|
|
70
|
+
onChange?.(syntheticEvent);
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
onBlur?.(e);
|
|
74
|
+
};
|
|
75
|
+
return (jsxRuntime.jsx("input", { ref: combinedRef, ...props, onBlur: handleBlur, onChange: handleChange, onCompositionStart: handleCompositionStart, onCompositionEnd: handleCompositionEnd }));
|
|
76
|
+
});
|
|
77
|
+
TextInput.displayName = 'TextInput';
|
|
78
|
+
|
|
79
|
+
module.exports = TextInput;
|
package/dist/cjs/index.js
CHANGED
|
@@ -6,6 +6,7 @@ require('react-dom');
|
|
|
6
6
|
var useDebounce = require('./hooks/useDebounce.js');
|
|
7
7
|
var useThrottle = require('./hooks/useThrottle.js');
|
|
8
8
|
var Tooltip = require('./Tooltip/Tooltip.js');
|
|
9
|
+
require('./Input/TextInput.js');
|
|
9
10
|
var Select = require('./Select/Select.js');
|
|
10
11
|
require('./Autocomplete/Autocomplete.js');
|
|
11
12
|
|
package/dist/index.js
CHANGED
|
@@ -4,5 +4,6 @@ import 'react-dom';
|
|
|
4
4
|
export { useDebounce } from './hooks/useDebounce.js';
|
|
5
5
|
export { useThrottle } from './hooks/useThrottle.js';
|
|
6
6
|
export { Tooltip } from './Tooltip/Tooltip.js';
|
|
7
|
+
import './Input/TextInput.js';
|
|
7
8
|
export { useSelectContext } from './Select/Select.js';
|
|
8
9
|
import './Autocomplete/Autocomplete.js';
|