UNPKG

10.4 kBJavaScriptView Raw
1function _extends() { _extends = Object.assign || function (target) { for (var i = 1; i < arguments.length; i++) { var source = arguments[i]; for (var key in source) { if (Object.prototype.hasOwnProperty.call(source, key)) { target[key] = source[key]; } } } return target; }; return _extends.apply(this, arguments); }
2
3import * as React from 'react';
4import { Animated, TextInput as NativeTextInput } from 'react-native';
5import TextInputOutlined from './TextInputOutlined';
6import TextInputFlat from './TextInputFlat';
7import TextInputIcon from './Adornment/TextInputIcon';
8import TextInputAffix from './Adornment/TextInputAffix';
9import { withTheme } from '../../core/theming';
10const BLUR_ANIMATION_DURATION = 180;
11const FOCUS_ANIMATION_DURATION = 150;
12
13/**
14 * A component to allow users to input text.
15 *
16 * <div class="screenshots">
17 * <figure>
18 * <img class="medium" src="screenshots/textinput-flat.focused.png" />
19 * <figcaption>Flat (focused)</figcaption>
20 * </figure>
21 * <figure>
22 * <img class="medium" src="screenshots/textinput-flat.disabled.png" />
23 * <figcaption>Flat (disabled)</figcaption>
24 * </figure>
25 * <figure>
26 * <img class="medium" src="screenshots/textinput-outlined.focused.png" />
27 * <figcaption>Outlined (focused)</figcaption>
28 * </figure>
29 * <figure>
30 * <img class="medium" src="screenshots/textinput-outlined.disabled.png" />
31 * <figcaption>Outlined (disabled)</figcaption>
32 * </figure>
33 * </div>
34 *
35 * ## Usage
36 * ```js
37 * import * as React from 'react';
38 * import { TextInput } from 'react-native-paper';
39 *
40 * const MyComponent = () => {
41 * const [text, setText] = React.useState("");
42 *
43 * return (
44 * <TextInput
45 * label="Email"
46 * value={text}
47 * onChangeText={text => setText(text)}
48 * />
49 * );
50 * };
51 *
52 * export default MyComponent;
53 * ```
54 *
55 * @extends TextInput props https://reactnative.dev/docs/textinput#props
56 */
57const TextInput = /*#__PURE__*/React.forwardRef((_ref, ref) => {
58 let {
59 mode = 'flat',
60 dense = false,
61 disabled = false,
62 error: errorProp = false,
63 multiline = false,
64 editable = true,
65 render = props => /*#__PURE__*/React.createElement(NativeTextInput, props),
66 ...rest
67 } = _ref;
68 const isControlled = rest.value !== undefined;
69 const validInputValue = isControlled ? rest.value : rest.defaultValue;
70 const {
71 current: labeled
72 } = React.useRef(new Animated.Value(validInputValue ? 0 : 1));
73 const {
74 current: error
75 } = React.useRef(new Animated.Value(errorProp ? 1 : 0));
76 const [focused, setFocused] = React.useState(false);
77 const [placeholder, setPlaceholder] = React.useState('');
78 const [uncontrolledValue, setUncontrolledValue] = React.useState(validInputValue); // Use value from props instead of local state when input is controlled
79
80 const value = isControlled ? rest.value : uncontrolledValue;
81 const [labelLayout, setLabelLayout] = React.useState({
82 measured: false,
83 width: 0,
84 height: 0
85 });
86 const [leftLayout, setLeftLayout] = React.useState({
87 width: null,
88 height: null
89 });
90 const [rightLayout, setRightLayout] = React.useState({
91 width: null,
92 height: null
93 });
94 const timer = React.useRef();
95 const root = React.useRef();
96 const {
97 scale
98 } = rest.theme.animation;
99 React.useImperativeHandle(ref, () => ({
100 focus: () => {
101 var _root$current;
102
103 return (_root$current = root.current) === null || _root$current === void 0 ? void 0 : _root$current.focus();
104 },
105 clear: () => {
106 var _root$current2;
107
108 return (_root$current2 = root.current) === null || _root$current2 === void 0 ? void 0 : _root$current2.clear();
109 },
110 setNativeProps: args => {
111 var _root$current3;
112
113 return (_root$current3 = root.current) === null || _root$current3 === void 0 ? void 0 : _root$current3.setNativeProps(args);
114 },
115 isFocused: () => {
116 var _root$current4;
117
118 return ((_root$current4 = root.current) === null || _root$current4 === void 0 ? void 0 : _root$current4.isFocused()) || false;
119 },
120 blur: () => {
121 var _root$current5;
122
123 return (_root$current5 = root.current) === null || _root$current5 === void 0 ? void 0 : _root$current5.blur();
124 },
125 forceFocus: () => {
126 var _root$current6;
127
128 return (_root$current6 = root.current) === null || _root$current6 === void 0 ? void 0 : _root$current6.focus();
129 }
130 }));
131 React.useEffect(() => {
132 // When the input has an error, we wiggle the label and apply error styles
133 if (errorProp) {
134 // show error
135 Animated.timing(error, {
136 toValue: 1,
137 duration: FOCUS_ANIMATION_DURATION * scale,
138 // To prevent this - https://github.com/callstack/react-native-paper/issues/941
139 useNativeDriver: true
140 }).start();
141 } else {
142 // hide error
143 {
144 Animated.timing(error, {
145 toValue: 0,
146 duration: BLUR_ANIMATION_DURATION * scale,
147 // To prevent this - https://github.com/callstack/react-native-paper/issues/941
148 useNativeDriver: true
149 }).start();
150 }
151 }
152 }, [errorProp, scale, error]);
153 React.useEffect(() => {
154 // Show placeholder text only if the input is focused, or there's no label
155 // We don't show placeholder if there's a label because the label acts as placeholder
156 // When focused, the label moves up, so we can show a placeholder
157 if (focused || !rest.label) {
158 // Set the placeholder in a delay to offset the label animation
159 // If we show it immediately, they'll overlap and look ugly
160 timer.current = setTimeout(() => setPlaceholder(rest.placeholder), 50);
161 } else {
162 // hidePlaceholder
163 setPlaceholder('');
164 }
165
166 return () => {
167 if (timer.current) {
168 clearTimeout(timer.current);
169 }
170 };
171 }, [focused, rest.label, rest.placeholder]);
172 React.useEffect(() => {
173 // The label should be minimized if the text input is focused, or has text
174 // In minimized mode, the label moves up and becomes small
175 // workaround for animated regression for react native > 0.61
176 // https://github.com/callstack/react-native-paper/pull/1440
177 if (value || focused) {
178 // minimize label
179 Animated.timing(labeled, {
180 toValue: 0,
181 duration: BLUR_ANIMATION_DURATION * scale,
182 // To prevent this - https://github.com/callstack/react-native-paper/issues/941
183 useNativeDriver: true
184 }).start();
185 } else {
186 // restore label
187 {
188 Animated.timing(labeled, {
189 toValue: 1,
190 duration: FOCUS_ANIMATION_DURATION * scale,
191 // To prevent this - https://github.com/callstack/react-native-paper/issues/941
192 useNativeDriver: true
193 }).start();
194 }
195 }
196 }, [focused, value, labeled, scale]);
197
198 const onLeftAffixLayoutChange = event => {
199 setLeftLayout({
200 height: event.nativeEvent.layout.height,
201 width: event.nativeEvent.layout.width
202 });
203 };
204
205 const onRightAffixLayoutChange = event => {
206 setRightLayout({
207 width: event.nativeEvent.layout.width,
208 height: event.nativeEvent.layout.height
209 });
210 };
211
212 const handleFocus = args => {
213 var _rest$onFocus;
214
215 if (disabled || !editable) {
216 return;
217 }
218
219 setFocused(true);
220 (_rest$onFocus = rest.onFocus) === null || _rest$onFocus === void 0 ? void 0 : _rest$onFocus.call(rest, args);
221 };
222
223 const handleBlur = args => {
224 var _rest$onBlur;
225
226 if (!editable) {
227 return;
228 }
229
230 setFocused(false);
231 (_rest$onBlur = rest.onBlur) === null || _rest$onBlur === void 0 ? void 0 : _rest$onBlur.call(rest, args);
232 };
233
234 const handleChangeText = value => {
235 var _rest$onChangeText;
236
237 if (!editable || disabled) {
238 return;
239 }
240
241 if (!isControlled) {
242 // Keep track of value in local state when input is not controlled
243 setUncontrolledValue(value);
244 }
245
246 (_rest$onChangeText = rest.onChangeText) === null || _rest$onChangeText === void 0 ? void 0 : _rest$onChangeText.call(rest, value);
247 };
248
249 const handleLayoutAnimatedText = e => {
250 setLabelLayout({
251 width: e.nativeEvent.layout.width,
252 height: e.nativeEvent.layout.height,
253 measured: true
254 });
255 };
256
257 const forceFocus = () => {
258 var _root$current7;
259
260 return (_root$current7 = root.current) === null || _root$current7 === void 0 ? void 0 : _root$current7.focus();
261 };
262
263 const {
264 maxFontSizeMultiplier = 1.5
265 } = rest;
266
267 if (mode === 'outlined') {
268 return /*#__PURE__*/React.createElement(TextInputOutlined, _extends({
269 dense: dense,
270 disabled: disabled,
271 error: errorProp,
272 multiline: multiline,
273 editable: editable,
274 render: render
275 }, rest, {
276 value: value,
277 parentState: {
278 labeled,
279 error,
280 focused,
281 placeholder,
282 value,
283 labelLayout,
284 leftLayout,
285 rightLayout
286 },
287 innerRef: ref => {
288 root.current = ref;
289 },
290 onFocus: handleFocus,
291 forceFocus: forceFocus,
292 onBlur: handleBlur,
293 onChangeText: handleChangeText,
294 onLayoutAnimatedText: handleLayoutAnimatedText,
295 onLeftAffixLayoutChange: onLeftAffixLayoutChange,
296 onRightAffixLayoutChange: onRightAffixLayoutChange,
297 maxFontSizeMultiplier: maxFontSizeMultiplier
298 }));
299 }
300
301 return /*#__PURE__*/React.createElement(TextInputFlat, _extends({
302 dense: dense,
303 disabled: disabled,
304 error: errorProp,
305 multiline: multiline,
306 editable: editable,
307 render: render
308 }, rest, {
309 value: value,
310 parentState: {
311 labeled,
312 error,
313 focused,
314 placeholder,
315 value,
316 labelLayout,
317 leftLayout,
318 rightLayout
319 },
320 innerRef: ref => {
321 root.current = ref;
322 },
323 onFocus: handleFocus,
324 forceFocus: forceFocus,
325 onBlur: handleBlur,
326 onChangeText: handleChangeText,
327 onLayoutAnimatedText: handleLayoutAnimatedText,
328 onLeftAffixLayoutChange: onLeftAffixLayoutChange,
329 onRightAffixLayoutChange: onRightAffixLayoutChange,
330 maxFontSizeMultiplier: maxFontSizeMultiplier
331 }));
332}); // @component ./Adornment/TextInputIcon.tsx
333
334TextInput.Icon = TextInputIcon; // @component ./Adornment/TextInputAffix.tsx
335// @ts-ignore Types of property 'theme' are incompatible.
336
337TextInput.Affix = TextInputAffix;
338export default withTheme(TextInput);
339//# sourceMappingURL=TextInput.js.map
\No newline at end of file