rn-floating-input 0.1.0 → 0.1.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (46) hide show
  1. package/README.md +7 -7
  2. package/lib/commonjs/ErrorText.js +26 -0
  3. package/lib/commonjs/ErrorText.js.map +1 -0
  4. package/lib/commonjs/FloatingInput.js +79 -118
  5. package/lib/commonjs/FloatingInput.js.map +1 -1
  6. package/lib/commonjs/FloatingLabel.js +27 -0
  7. package/lib/commonjs/FloatingLabel.js.map +1 -0
  8. package/lib/commonjs/defaults.js +31 -26
  9. package/lib/commonjs/defaults.js.map +1 -1
  10. package/lib/commonjs/index.js +0 -13
  11. package/lib/commonjs/index.js.map +1 -1
  12. package/lib/commonjs/useFloatingLabel.js +86 -0
  13. package/lib/commonjs/useFloatingLabel.js.map +1 -0
  14. package/lib/module/ErrorText.js +21 -0
  15. package/lib/module/ErrorText.js.map +1 -0
  16. package/lib/module/FloatingInput.js +82 -121
  17. package/lib/module/FloatingInput.js.map +1 -1
  18. package/lib/module/FloatingLabel.js +22 -0
  19. package/lib/module/FloatingLabel.js.map +1 -0
  20. package/lib/module/defaults.js +31 -26
  21. package/lib/module/defaults.js.map +1 -1
  22. package/lib/module/index.js +1 -2
  23. package/lib/module/index.js.map +1 -1
  24. package/lib/module/useFloatingLabel.js +82 -0
  25. package/lib/module/useFloatingLabel.js.map +1 -0
  26. package/lib/typescript/ErrorText.d.ts +14 -0
  27. package/lib/typescript/ErrorText.d.ts.map +1 -0
  28. package/lib/typescript/FloatingInput.d.ts.map +1 -1
  29. package/lib/typescript/FloatingLabel.d.ts +17 -0
  30. package/lib/typescript/FloatingLabel.d.ts.map +1 -0
  31. package/lib/typescript/defaults.d.ts +2 -1
  32. package/lib/typescript/defaults.d.ts.map +1 -1
  33. package/lib/typescript/index.d.ts +2 -3
  34. package/lib/typescript/index.d.ts.map +1 -1
  35. package/lib/typescript/types.d.ts +3 -5
  36. package/lib/typescript/types.d.ts.map +1 -1
  37. package/lib/typescript/useFloatingLabel.d.ts +43 -0
  38. package/lib/typescript/useFloatingLabel.d.ts.map +1 -0
  39. package/package.json +2 -2
  40. package/src/ErrorText.tsx +37 -0
  41. package/src/FloatingInput.tsx +91 -161
  42. package/src/FloatingLabel.tsx +41 -0
  43. package/src/defaults.ts +34 -29
  44. package/src/index.tsx +2 -3
  45. package/src/types.ts +50 -49
  46. package/src/useFloatingLabel.ts +111 -0
@@ -1 +1 @@
1
- {"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../src/types.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../src/types.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EACV,mBAAmB,EACnB,SAAS,EACT,cAAc,EACd,SAAS,EACT,SAAS,EACV,MAAM,cAAc,CAAC;AAEtB,MAAM,WAAW,kBAAkB;IACjC,eAAe,CAAC,EAAE,MAAM,CAAC;IACzB,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,cAAc,CAAC,EAAE,MAAM,CAAC;IACxB,gBAAgB,CAAC,EAAE,MAAM,CAAC;IAC1B,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,mBAAmB,CAAC,EAAE,MAAM,CAAC;IAC7B,UAAU,CAAC,EAAE,MAAM,CAAC;CACrB;AAED,MAAM,WAAW,mBAAmB;IAClC,SAAS,CAAC,EAAE,SAAS,CAAC,SAAS,CAAC,CAAC;IACjC,cAAc,CAAC,EAAE,SAAS,CAAC,SAAS,CAAC,CAAC;IACtC,iBAAiB,CAAC,EAAE,SAAS,CAAC,SAAS,CAAC,CAAC;IACzC,KAAK,CAAC,EAAE,SAAS,CAAC,SAAS,CAAC,CAAC;IAC7B,KAAK,CAAC,EAAE,SAAS,CAAC,SAAS,CAAC,CAAC;IAC7B,KAAK,CAAC,EAAE,SAAS,CAAC,SAAS,CAAC,CAAC;IAC7B,KAAK,CAAC,EAAE,SAAS,CAAC,SAAS,CAAC,CAAC;CAC9B;AAED,MAAM,WAAW,4BAA4B;IAC3C,aAAa,CAAC,EAAE,MAAM,CAAC;IACvB,cAAc,CAAC,EAAE,MAAM,CAAC;IACxB,aAAa,CAAC,EAAE,MAAM,CAAC;CACxB;AAED,MAAM,WAAW,gBAAgB;IAC/B,KAAK,EAAE,MAAM,IAAI,CAAC;IAClB,IAAI,EAAE,MAAM,IAAI,CAAC;IACjB,KAAK,EAAE,MAAM,IAAI,CAAC;IAClB,SAAS,EAAE,MAAM,OAAO,CAAC;CAC1B;AAED,MAAM,WAAW,kBAAkB;IAEjC,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,YAAY,CAAC,EAAE,CAAC,IAAI,EAAE,MAAM,KAAK,IAAI,CAAC;IACtC,MAAM,CAAC,EAAE,MAAM,IAAI,CAAC;IACpB,OAAO,CAAC,EAAE,MAAM,IAAI,CAAC;IACrB,OAAO,CAAC,EAAE,MAAM,IAAI,CAAC;IACrB,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,OAAO,CAAC,EAAE,OAAO,CAAC;IAGlB,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,YAAY,CAAC,EAAE,mBAAmB,CAAC;IACnC,SAAS,CAAC,EAAE,OAAO,CAAC;IACpB,QAAQ,CAAC,EAAE,OAAO,CAAC;IACnB,cAAc,CAAC,EAAE,MAAM,GAAG,WAAW,GAAG,OAAO,GAAG,YAAY,CAAC;IAC/D,eAAe,CAAC,EAAE,OAAO,CAAC;IAG1B,KAAK,CAAC,EAAE,KAAK,CAAC,SAAS,CAAC;IACxB,WAAW,CAAC,EAAE,CAAC,KAAK,EAAE,cAAc,KAAK,KAAK,CAAC,SAAS,CAAC;IAGzD,KAAK,CAAC,EAAE,kBAAkB,CAAC;IAC3B,MAAM,CAAC,EAAE,mBAAmB,CAAC;IAC7B,KAAK,CAAC,EAAE,SAAS,CAAC,SAAS,CAAC,CAAC;IAC7B,eAAe,CAAC,EAAE,4BAA4B,CAAC;IAG/C,cAAc,CAAC,EAAE,IAAI,CACnB,cAAc,EACd,OAAO,GAAG,cAAc,GAAG,QAAQ,GAAG,SAAS,GAAG,WAAW,CAC9D,CAAC;CACH"}
@@ -0,0 +1,43 @@
1
+ import type { LayoutChangeEvent } from "react-native";
2
+ import type { FloatingInputAnimationConfig, FloatingInputTheme } from "./types";
3
+ interface UseFloatingLabelOptions {
4
+ value?: string;
5
+ onFocus?: () => void;
6
+ onBlur?: () => void;
7
+ touched?: boolean;
8
+ error?: string;
9
+ theme?: FloatingInputTheme;
10
+ animationConfig?: FloatingInputAnimationConfig;
11
+ }
12
+ export declare function useFloatingLabel({ value, onFocus, onBlur, touched, error, theme: themeProp, animationConfig: animationProp, }: UseFloatingLabelOptions): {
13
+ isFocused: boolean;
14
+ hasError: boolean;
15
+ shouldBeActive: boolean;
16
+ theme: {
17
+ backgroundColor: string;
18
+ labelColor: string;
19
+ inputColor: string;
20
+ errorColor: string;
21
+ selectionColor: string;
22
+ placeholderColor: string;
23
+ borderRadius: number;
24
+ fontSize: number;
25
+ labelActiveFontSize: number;
26
+ fontFamily: string;
27
+ };
28
+ labelAnimatedStyle: {
29
+ transform: ({
30
+ translateX: number;
31
+ translateY?: undefined;
32
+ } | {
33
+ translateY: number;
34
+ translateX?: undefined;
35
+ })[];
36
+ fontSize: number;
37
+ };
38
+ handleFocus: () => void;
39
+ handleBlur: () => void;
40
+ onContainerLayout: (e: LayoutChangeEvent) => void;
41
+ };
42
+ export {};
43
+ //# sourceMappingURL=useFloatingLabel.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"useFloatingLabel.d.ts","sourceRoot":"","sources":["../../src/useFloatingLabel.ts"],"names":[],"mappings":"AAQA,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,cAAc,CAAC;AAOtD,OAAO,KAAK,EAAE,4BAA4B,EAAE,kBAAkB,EAAE,MAAM,SAAS,CAAC;AAEhF,UAAU,uBAAuB;IAC/B,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,OAAO,CAAC,EAAE,MAAM,IAAI,CAAC;IACrB,MAAM,CAAC,EAAE,MAAM,IAAI,CAAC;IACpB,OAAO,CAAC,EAAE,OAAO,CAAC;IAClB,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,KAAK,CAAC,EAAE,kBAAkB,CAAC;IAC3B,eAAe,CAAC,EAAE,4BAA4B,CAAC;CAChD;AAED,wBAAgB,gBAAgB,CAAC,EAC/B,KAAK,EACL,OAAO,EACP,MAAM,EACN,OAAO,EACP,KAAK,EACL,KAAK,EAAE,SAAS,EAChB,eAAe,EAAE,aAAa,GAC/B,EAAE,uBAAuB;;;;;;;;;;;;;;;;;;;;;;;;;;;;2BA6DM,iBAAiB;EAchD"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "rn-floating-input",
3
- "version": "0.1.0",
3
+ "version": "0.1.2",
4
4
  "description": "A performant floating label TextInput for React Native with animated label, error shake, and full customization",
5
5
  "main": "lib/commonjs/index",
6
6
  "module": "lib/module/index",
@@ -29,7 +29,7 @@
29
29
  ],
30
30
  "repository": {
31
31
  "type": "git",
32
- "url": "git+https://github.com/ilhambabayev/react-native-floating-input.git"
32
+ "url": "git+https://github.com/Babayev03/rn-floating-input.git"
33
33
  },
34
34
  "author": "Ilham Babayev",
35
35
  "license": "MIT",
@@ -0,0 +1,37 @@
1
+ import React from "react";
2
+ import Animated from "react-native-reanimated";
3
+ import type { StyleProp, TextStyle } from "react-native";
4
+
5
+ import { baseStyles } from "./defaults";
6
+
7
+ interface ErrorTextProps {
8
+ error?: string;
9
+ theme: {
10
+ labelActiveFontSize: number;
11
+ errorColor: string;
12
+ fontFamily: string;
13
+ };
14
+ style?: StyleProp<TextStyle>;
15
+ }
16
+
17
+ export const ErrorText = React.memo(function ErrorText({
18
+ error,
19
+ theme,
20
+ style,
21
+ }: ErrorTextProps) {
22
+ return (
23
+ <Animated.Text
24
+ style={[
25
+ baseStyles.errorText,
26
+ {
27
+ fontSize: theme.labelActiveFontSize,
28
+ color: theme.errorColor,
29
+ fontFamily: theme.fontFamily,
30
+ },
31
+ style,
32
+ ]}
33
+ >
34
+ {error}
35
+ </Animated.Text>
36
+ );
37
+ });
@@ -1,15 +1,11 @@
1
- import React, { useEffect, useImperativeHandle, useRef, useState } from "react";
1
+ import React, { useImperativeHandle, useRef } from "react";
2
2
  import { Pressable, TextInput as RNTextInput, View } from "react-native";
3
- import Animated, {
4
- interpolate,
5
- LinearTransition,
6
- useAnimatedStyle,
7
- useSharedValue,
8
- withSequence,
9
- withTiming,
10
- } from "react-native-reanimated";
3
+ import Animated, { LinearTransition } from "react-native-reanimated";
11
4
 
12
- import { baseStyles, DEFAULT_ANIMATION, DEFAULT_THEME } from "./defaults";
5
+ import { baseStyles } from "./defaults";
6
+ import { ErrorText } from "./ErrorText";
7
+ import { FloatingLabel } from "./FloatingLabel";
8
+ import { useFloatingLabel } from "./useFloatingLabel";
13
9
  import type { FloatingInputProps, FloatingInputRef } from "./types";
14
10
 
15
11
  export const FloatingInput = React.forwardRef<
@@ -38,21 +34,30 @@ export const FloatingInput = React.forwardRef<
38
34
  theme: themeProp,
39
35
  styles: stylesProp,
40
36
  style,
41
- animationConfig: animationProp,
37
+ animationConfig,
42
38
  textInputProps,
43
39
  },
44
40
  ref
45
41
  ) => {
46
42
  const inputRef = useRef<RNTextInput>(null);
47
- const [isFocused, setIsFocused] = useState(false);
48
- const hasError = !!touched && !!error;
49
43
 
50
- const theme = { ...DEFAULT_THEME, ...themeProp };
51
- const anim = { ...DEFAULT_ANIMATION, ...animationProp };
52
-
53
- const shouldBeActive = isFocused || !!value;
54
- const focusAnim = useSharedValue(value ? 1 : 0);
55
- const shakeAnim = useSharedValue(0);
44
+ const {
45
+ hasError,
46
+ shouldBeActive,
47
+ theme,
48
+ labelAnimatedStyle,
49
+ handleFocus,
50
+ handleBlur,
51
+ onContainerLayout,
52
+ } = useFloatingLabel({
53
+ value,
54
+ onFocus,
55
+ onBlur,
56
+ touched,
57
+ error,
58
+ theme: themeProp,
59
+ animationConfig,
60
+ });
56
61
 
57
62
  useImperativeHandle(ref, () => ({
58
63
  focus: () => inputRef.current?.focus(),
@@ -61,165 +66,90 @@ export const FloatingInput = React.forwardRef<
61
66
  isFocused: () => inputRef.current?.isFocused() ?? false,
62
67
  }));
63
68
 
64
- useEffect(() => {
65
- focusAnim.value = withTiming(shouldBeActive ? 1 : 0, {
66
- duration: anim.labelDuration,
67
- });
68
- }, [shouldBeActive]);
69
-
70
- useEffect(() => {
71
- if (!hasError) return;
72
- const mag = anim.shakeMagnitude;
73
- const dur = anim.shakeDuration;
74
- shakeAnim.value = withSequence(
75
- withTiming(-mag, { duration: dur }),
76
- withTiming(mag, { duration: dur }),
77
- withTiming(-mag, { duration: dur }),
78
- withTiming(0, { duration: dur })
79
- );
80
- }, [hasError]);
81
-
82
- const labelAnimatedStyle = useAnimatedStyle(() => ({
83
- transform: [
84
- { translateX: shakeAnim.value },
69
+ const inputProps = {
70
+ ref: inputRef,
71
+ editable,
72
+ autoFocus,
73
+ maxLength,
74
+ value,
75
+ placeholder: shouldBeActive ? placeholder : undefined,
76
+ placeholderTextColor: theme.placeholderColor,
77
+ onChangeText,
78
+ onBlur: handleBlur,
79
+ onFocus: handleFocus,
80
+ keyboardType,
81
+ autoCapitalize,
82
+ secureTextEntry,
83
+ selectionColor: hasError ? theme.errorColor : theme.selectionColor,
84
+ cursorColor: hasError ? theme.errorColor : theme.selectionColor,
85
+ selectionHandleColor: hasError ? theme.errorColor : theme.selectionColor,
86
+ underlineColorAndroid: "transparent" as const,
87
+ style: [
88
+ baseStyles.input,
85
89
  {
86
- translateY: interpolate(
87
- focusAnim.value,
88
- [0, 1],
89
- [0, -anim.labelTranslateY]
90
- ),
90
+ color: theme.inputColor,
91
+ fontFamily: theme.fontFamily,
92
+ fontSize: theme.fontSize,
91
93
  },
94
+ right ? baseStyles.inputWithRight : undefined,
95
+ stylesProp?.input,
92
96
  ],
93
- fontSize: interpolate(
94
- focusAnim.value,
95
- [0, 1],
96
- [theme.fontSize, theme.labelActiveFontSize]
97
- ),
98
- }));
99
-
100
- function handleFocus() {
101
- setIsFocused(true);
102
- onFocus?.();
103
- }
97
+ ...textInputProps,
98
+ };
104
99
 
105
- function handleBlur() {
106
- setIsFocused(false);
107
- onBlur?.();
108
- }
100
+ const inputElement = renderInput ? (
101
+ renderInput(inputProps)
102
+ ) : (
103
+ <RNTextInput {...inputProps} />
104
+ );
109
105
 
110
- function renderTextInput() {
111
- const inputProps = {
112
- ref: inputRef,
113
- editable,
114
- autoFocus,
115
- maxLength,
116
- value,
117
- placeholder: shouldBeActive ? placeholder : undefined,
118
- placeholderTextColor: theme.placeholderColor,
119
- onChangeText,
120
- onBlur: handleBlur,
121
- onFocus: handleFocus,
122
- keyboardType,
123
- autoCapitalize,
124
- secureTextEntry,
125
- selectionColor: theme.selectionColor,
126
- cursorColor: theme.selectionColor,
127
- selectionHandleColor: theme.selectionColor,
128
- underlineColorAndroid: "transparent" as const,
129
- style: [
130
- baseStyles.input,
106
+ const inputContainer = (
107
+ <View
108
+ onLayout={onContainerLayout}
109
+ style={[
110
+ baseStyles.inputContainer,
131
111
  {
132
- color: theme.inputColor,
133
- fontFamily: theme.fontFamily,
134
- fontSize: theme.fontSize,
112
+ backgroundColor: theme.backgroundColor,
113
+ borderRadius: theme.borderRadius,
135
114
  },
136
- right ? baseStyles.inputWithRight : undefined,
137
- stylesProp?.input,
138
- ],
139
- ...textInputProps,
140
- };
141
-
142
- const inputElement = renderInput ? (
143
- renderInput(inputProps)
144
- ) : (
145
- <RNTextInput {...inputProps} />
146
- );
147
-
148
- return (
115
+ style,
116
+ stylesProp?.inputContainer,
117
+ ]}
118
+ >
149
119
  <View
150
- style={[
151
- baseStyles.inputContainer,
152
- {
153
- backgroundColor: theme.backgroundColor,
154
- borderRadius: theme.borderRadius,
155
- minHeight: theme.minHeight,
156
- },
157
- style,
158
- stylesProp?.inputContainer,
159
- ]}
120
+ style={[baseStyles.labelAndInputArea, stylesProp?.labelAndInputArea]}
160
121
  >
161
- <View
162
- style={[
163
- baseStyles.labelAndInputArea,
164
- stylesProp?.labelAndInputArea,
165
- ]}
166
- >
167
- <Animated.Text
168
- style={[
169
- baseStyles.label,
170
- {
171
- color: hasError ? theme.errorColor : theme.labelColor,
172
- fontFamily: theme.fontFamily,
173
- },
174
- labelAnimatedStyle,
175
- stylesProp?.label,
176
- ]}
177
- >
178
- {label}
179
- </Animated.Text>
180
- {inputElement}
181
- </View>
182
- {right && (
183
- <View style={[baseStyles.rightContainer, stylesProp?.right]}>
184
- {right}
185
- </View>
186
- )}
122
+ <FloatingLabel
123
+ label={label}
124
+ hasError={hasError}
125
+ theme={theme}
126
+ labelAnimatedStyle={labelAnimatedStyle}
127
+ style={stylesProp?.label}
128
+ />
129
+ {inputElement}
187
130
  </View>
188
- );
189
- }
190
-
191
- function renderContent() {
192
- if (onPress) {
193
- return (
194
- <Pressable onPress={onPress}>
195
- <View pointerEvents="none">{renderTextInput()}</View>
196
- </Pressable>
197
- );
198
- }
199
-
200
- return renderTextInput();
201
- }
131
+ {right && (
132
+ <View style={[baseStyles.rightContainer, stylesProp?.right]}>
133
+ {right}
134
+ </View>
135
+ )}
136
+ </View>
137
+ );
202
138
 
203
139
  return (
204
140
  <Animated.View
205
141
  layout={LinearTransition.springify()}
206
142
  style={[baseStyles.container, stylesProp?.container]}
207
143
  >
208
- {renderContent()}
144
+ {onPress ? (
145
+ <Pressable onPress={onPress}>
146
+ <View pointerEvents="none">{inputContainer}</View>
147
+ </Pressable>
148
+ ) : (
149
+ inputContainer
150
+ )}
209
151
  {hasError && (
210
- <Animated.Text
211
- style={[
212
- baseStyles.errorText,
213
- {
214
- fontSize: theme.labelActiveFontSize,
215
- color: theme.errorColor,
216
- fontFamily: theme.fontFamily,
217
- },
218
- stylesProp?.error,
219
- ]}
220
- >
221
- {error}
222
- </Animated.Text>
152
+ <ErrorText error={error} theme={theme} style={stylesProp?.error} />
223
153
  )}
224
154
  </Animated.View>
225
155
  );
@@ -0,0 +1,41 @@
1
+ import React from "react";
2
+ import Animated, { type AnimatedStyle } from "react-native-reanimated";
3
+ import type { StyleProp, TextStyle } from "react-native";
4
+
5
+ import { baseStyles } from "./defaults";
6
+
7
+ interface FloatingLabelProps {
8
+ label?: string;
9
+ hasError: boolean;
10
+ theme: {
11
+ errorColor: string;
12
+ labelColor: string;
13
+ fontFamily: string;
14
+ };
15
+ labelAnimatedStyle: AnimatedStyle;
16
+ style?: StyleProp<TextStyle>;
17
+ }
18
+
19
+ export const FloatingLabel = React.memo(function FloatingLabel({
20
+ label,
21
+ hasError,
22
+ theme,
23
+ labelAnimatedStyle,
24
+ style,
25
+ }: FloatingLabelProps) {
26
+ return (
27
+ <Animated.Text
28
+ style={[
29
+ baseStyles.label,
30
+ {
31
+ color: hasError ? theme.errorColor : theme.labelColor,
32
+ fontFamily: theme.fontFamily,
33
+ },
34
+ labelAnimatedStyle,
35
+ style,
36
+ ]}
37
+ >
38
+ {label}
39
+ </Animated.Text>
40
+ );
41
+ });
package/src/defaults.ts CHANGED
@@ -1,58 +1,63 @@
1
- import { StyleSheet } from 'react-native'
1
+ import { StyleSheet } from "react-native";
2
+ import type { FloatingInputAnimationConfig, FloatingInputTheme } from "./types";
2
3
 
3
- import type { FloatingInputAnimationConfig, FloatingInputTheme } from './types'
4
+ export const INPUT_PADDING_TOP = 24;
5
+ const INPUT_PADDING_BOTTOM = 8;
6
+ const INPUT_PADDING_HORIZONTAL = 16;
7
+ const RIGHT_CONTAINER_OFFSET = 12;
8
+ const INPUT_WITH_RIGHT_PADDING = 48;
9
+ const ERROR_MARGIN_LEFT = 8;
10
+ const ERROR_MARGIN_TOP = 6;
4
11
 
5
12
  export const DEFAULT_THEME: Required<FloatingInputTheme> = {
6
- backgroundColor: '#EDEFF2',
7
- labelColor: '#878A99',
8
- inputColor: '#36373D',
9
- errorColor: '#E3152E',
10
- selectionColor: '#31BE30',
11
- placeholderColor: '#878A99',
13
+ backgroundColor: "#EDEFF2",
14
+ labelColor: "#878A99",
15
+ inputColor: "#36373D",
16
+ errorColor: "#E3152E",
17
+ selectionColor: "#31BE30",
18
+ placeholderColor: "#878A99",
12
19
  borderRadius: 14,
13
- minHeight: 56,
14
20
  fontSize: 16,
15
21
  labelActiveFontSize: 12,
16
- fontFamily: 'System',
17
- }
22
+ fontFamily: "System",
23
+ };
18
24
 
19
25
  export const DEFAULT_ANIMATION: Required<FloatingInputAnimationConfig> = {
20
26
  labelDuration: 200,
21
27
  shakeMagnitude: 2,
22
28
  shakeDuration: 50,
23
- labelTranslateY: 10,
24
- }
29
+ };
25
30
 
26
31
  export const baseStyles = StyleSheet.create({
27
32
  container: {},
28
33
  inputContainer: {
29
- flexDirection: 'row',
30
- alignItems: 'center',
34
+ flexDirection: "row",
35
+ alignItems: "center",
31
36
  },
32
37
  labelAndInputArea: {
33
38
  flex: 1,
34
- justifyContent: 'center',
39
+ justifyContent: "center",
35
40
  },
36
41
  label: {
37
- position: 'absolute',
38
- left: 16,
42
+ position: "absolute",
43
+ left: INPUT_PADDING_HORIZONTAL,
39
44
  },
40
45
  input: {
41
- paddingTop: 24,
42
- paddingBottom: 8,
43
- paddingLeft: 16,
44
- paddingRight: 16,
46
+ paddingTop: INPUT_PADDING_TOP,
47
+ paddingBottom: INPUT_PADDING_BOTTOM,
48
+ paddingLeft: INPUT_PADDING_HORIZONTAL,
49
+ paddingRight: INPUT_PADDING_HORIZONTAL,
45
50
  },
46
51
  inputWithRight: {
47
- paddingRight: 48,
52
+ paddingRight: INPUT_WITH_RIGHT_PADDING,
48
53
  },
49
54
  rightContainer: {
50
- position: 'absolute',
51
- right: 12,
52
- alignSelf: 'center',
55
+ position: "absolute",
56
+ right: RIGHT_CONTAINER_OFFSET,
57
+ alignSelf: "center",
53
58
  },
54
59
  errorText: {
55
- marginLeft: 8,
56
- marginTop: 6,
60
+ marginLeft: ERROR_MARGIN_LEFT,
61
+ marginTop: ERROR_MARGIN_TOP,
57
62
  },
58
- })
63
+ });
package/src/index.tsx CHANGED
@@ -1,9 +1,8 @@
1
- export { FloatingInput } from './FloatingInput'
2
- export { DEFAULT_ANIMATION, DEFAULT_THEME } from './defaults'
1
+ export { FloatingInput } from "./FloatingInput";
3
2
  export type {
4
3
  FloatingInputAnimationConfig,
5
4
  FloatingInputProps,
6
5
  FloatingInputRef,
7
6
  FloatingInputStyles,
8
7
  FloatingInputTheme,
9
- } from './types'
8
+ } from "./types";