Skip to content

Commit 41003f9

Browse files
committed
feat: add PasswordInput
1 parent ea3eee1 commit 41003f9

File tree

5 files changed

+266
-11
lines changed

5 files changed

+266
-11
lines changed

App.tsx

Lines changed: 7 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,20 +1,21 @@
1-
import { StatusBar } from 'expo-status-bar';
2-
import { StyleSheet, Text, View } from 'react-native';
1+
import { StatusBar } from "expo-status-bar";
2+
import { StyleSheet, Text, View } from "react-native";
3+
import { PasswordInput } from "./src/components/PasswordInput";
34

45
export default function App() {
56
return (
67
<View style={styles.container}>
7-
<Text>Open up App.tsx to start working on your app!</Text>
88
<StatusBar style="auto" />
9+
<View style={{ flex: 2, justifyContent: "flex-end", alignItems: "center" }}>
10+
<PasswordInput />
11+
</View>
12+
<View style={{ flex: 3 }} />
913
</View>
1014
);
1115
}
1216

1317
const styles = StyleSheet.create({
1418
container: {
1519
flex: 1,
16-
backgroundColor: '#fff',
17-
alignItems: 'center',
18-
justifyContent: 'center',
1920
},
2021
});

babel.config.js

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
1-
module.exports = function(api) {
1+
module.exports = function (api) {
22
api.cache(true);
33
return {
4-
presets: ['babel-preset-expo'],
4+
presets: ["babel-preset-expo"],
5+
plugins: ["react-native-reanimated/plugin"],
56
};
67
};

package.json

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,9 @@
1212
"expo": "~49.0.8",
1313
"expo-status-bar": "~1.6.0",
1414
"react": "18.2.0",
15-
"react-native": "0.72.4"
15+
"react-native": "0.72.4",
16+
"react-native-reanimated": "~3.3.0",
17+
"react-native-svg": "13.9.0"
1618
},
1719
"devDependencies": {
1820
"@babel/core": "^7.20.0",

src/components/PasswordInput.tsx

Lines changed: 145 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,145 @@
1+
import React, { useState } from "react";
2+
import { Dimensions, TextInput, View } from "react-native";
3+
import Animated, {
4+
FadeInDown,
5+
FadeOutDown,
6+
interpolateColor,
7+
useAnimatedProps,
8+
useSharedValue,
9+
withSpring,
10+
} from "react-native-reanimated";
11+
import { Path, Svg } from "react-native-svg";
12+
13+
/**
14+
* inpriration: https://twitter.com/nitishkmrk/status/1697583481294078136
15+
*/
16+
17+
const SCREEN_WIDTH = Dimensions.get("window").width;
18+
19+
const inputWidth = SCREEN_WIDTH * 0.8;
20+
const inputHeight = 40;
21+
const inputBorderWidth = 3;
22+
const inputBorderRadius = 5;
23+
24+
const AnimtedPath = Animated.createAnimatedComponent(Path);
25+
26+
const MAX_PASSWORD_LENGTH = 8;
27+
const STEP_COLORS = [
28+
"#2e5fce",
29+
"#2e5fce",
30+
"#ae49d2",
31+
"#ae49d2",
32+
"#ae49d2",
33+
"#ae49d2",
34+
"#ae49d2",
35+
"#2fb96c",
36+
];
37+
38+
const path = `
39+
M 2 ${inputHeight / 2 + 3}
40+
v ${-inputHeight / 2 + inputBorderRadius}
41+
a ${inputBorderRadius} ${inputBorderRadius} 0 0 1 ${inputBorderRadius} ${-inputBorderRadius}
42+
h ${inputWidth - 2.9 * inputBorderRadius}
43+
a ${inputBorderRadius} ${inputBorderRadius} 0 0 1 ${inputBorderRadius} ${inputBorderRadius}
44+
v ${inputHeight - 0.4 * inputBorderRadius}
45+
a ${inputBorderRadius} ${inputBorderRadius} 0 0 1 ${-inputBorderRadius} ${inputBorderRadius}
46+
h ${-inputWidth + 2.9 * inputBorderRadius}
47+
a ${inputBorderRadius} ${inputBorderRadius} 0 0 1 ${-inputBorderRadius} ${-inputBorderRadius}
48+
Z
49+
`;
50+
51+
const pathLength =
52+
2 * inputWidth + 2 * inputHeight - 4 * inputBorderRadius + 25;
53+
54+
export function PasswordInput() {
55+
const [password, setPassword] = useState("");
56+
const progress = useSharedValue(0);
57+
58+
const pathAnimatedProps = useAnimatedProps(() => {
59+
return {
60+
strokeDashoffset: pathLength * (1 - progress.value),
61+
stroke: interpolateColor(
62+
progress.value,
63+
STEP_COLORS.map((_, i) => i / (STEP_COLORS.length - 1)),
64+
STEP_COLORS
65+
),
66+
};
67+
}, []);
68+
69+
const onChangePassword = (text: string) => {
70+
if (text.length <= MAX_PASSWORD_LENGTH) {
71+
setPassword(text);
72+
progress.value = withSpring(text.length / MAX_PASSWORD_LENGTH, {
73+
damping: 20,
74+
stiffness: 100,
75+
});
76+
}
77+
};
78+
79+
return (
80+
<View style={{ position: "relative" }}>
81+
{password.length > 0 && (
82+
<Animated.Text
83+
entering={FadeInDown}
84+
exiting={FadeOutDown}
85+
style={{
86+
position: "absolute",
87+
top: -27,
88+
left: 0,
89+
color: "#aeaeae",
90+
fontSize: 16,
91+
}}
92+
>
93+
Password
94+
</Animated.Text>
95+
)}
96+
97+
<View
98+
style={{
99+
shadowColor: "#ccc",
100+
shadowOffset: {
101+
width: 0,
102+
height: 1,
103+
},
104+
shadowOpacity: 0.22,
105+
shadowRadius: 2.22,
106+
backgroundColor: "white",
107+
borderRadius: inputBorderRadius,
108+
padding: 0,
109+
}}
110+
>
111+
<Svg width={inputWidth} height={inputHeight + 13}>
112+
<Path
113+
d={path}
114+
stroke="#ccc"
115+
fill="none"
116+
strokeWidth={inputBorderWidth}
117+
/>
118+
<AnimtedPath
119+
d={path}
120+
animatedProps={pathAnimatedProps}
121+
fill="none"
122+
strokeWidth={inputBorderWidth}
123+
strokeDasharray={pathLength}
124+
/>
125+
</Svg>
126+
<TextInput
127+
style={{
128+
position: "absolute",
129+
top: 3,
130+
left: 0,
131+
width: inputWidth,
132+
height: inputHeight + 10,
133+
paddingHorizontal: 10,
134+
fontSize: 16,
135+
color: "black",
136+
}}
137+
secureTextEntry
138+
value={password}
139+
placeholder="Password"
140+
onChangeText={onChangePassword}
141+
/>
142+
</View>
143+
</View>
144+
);
145+
}

yarn.lock

Lines changed: 108 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -787,6 +787,13 @@
787787
"@babel/helper-plugin-utils" "^7.22.5"
788788
"@babel/plugin-syntax-numeric-separator" "^7.10.4"
789789

790+
"@babel/plugin-transform-object-assign@^7.16.7":
791+
version "7.22.5"
792+
resolved "https://registry.yarnpkg.com/@babel/plugin-transform-object-assign/-/plugin-transform-object-assign-7.22.5.tgz#290c1b9555dcea48bb2c29ad94237777600d04f9"
793+
integrity sha512-iDhx9ARkXq4vhZ2CYOSnQXkmxkDgosLi3J8Z17mKz7LyzthtkdVchLD7WZ3aXeCuvJDOW3+1I5TpJmwIbF9MKQ==
794+
dependencies:
795+
"@babel/helper-plugin-utils" "^7.22.5"
796+
790797
"@babel/plugin-transform-object-rest-spread@^7.22.11":
791798
version "7.22.11"
792799
resolved "https://registry.yarnpkg.com/@babel/plugin-transform-object-rest-spread/-/plugin-transform-object-rest-spread-7.22.11.tgz#dbbb06ce783cd994a8f430d8cefa553e9b42ca62"
@@ -1095,7 +1102,7 @@
10951102
"@babel/types" "^7.4.4"
10961103
esutils "^2.0.2"
10971104

1098-
"@babel/preset-typescript@^7.13.0":
1105+
"@babel/preset-typescript@^7.13.0", "@babel/preset-typescript@^7.16.7":
10991106
version "7.22.11"
11001107
resolved "https://registry.yarnpkg.com/@babel/preset-typescript/-/preset-typescript-7.22.11.tgz#f218cd0345524ac888aa3dc32f029de5b064b575"
11011108
integrity sha512-tWY5wyCZYBGY7IlalfKI1rLiGlIfnwsRHZqlky0HVv8qviwQ1Uo/05M6+s+TcTCVa6Bmoo2uJW5TMFX6Wa4qVg==
@@ -2343,6 +2350,11 @@ body-parser@^1.20.1:
23432350
type-is "~1.6.18"
23442351
unpipe "1.0.0"
23452352

2353+
boolbase@^1.0.0:
2354+
version "1.0.0"
2355+
resolved "https://registry.yarnpkg.com/boolbase/-/boolbase-1.0.0.tgz#68dff5fbe60c51eb37725ea9e3ed310dcc1e776e"
2356+
integrity sha512-JZOSA7Mo9sNGB8+UjSgzdLtokWAky1zbztM3WRLCbZ70/3cTANmQmOdR7y2g+J0e2WXywy1yS468tY+IruqEww==
2357+
23462358
23472359
version "0.1.0"
23482360
resolved "https://registry.yarnpkg.com/bplist-creator/-/bplist-creator-0.1.0.tgz#018a2d1b587f769e379ef5519103730f8963ba1e"
@@ -2739,6 +2751,11 @@ convert-source-map@^1.7.0:
27392751
resolved "https://registry.yarnpkg.com/convert-source-map/-/convert-source-map-1.9.0.tgz#7faae62353fb4213366d0ca98358d22e8368b05f"
27402752
integrity sha512-ASFBup0Mz1uyiIjANan1jzLQami9z1PoYSZCiiYW2FczPbenXc45FZdBZLzOT+r6+iciuEModtmCti+hjaAk0A==
27412753

2754+
convert-source-map@^2.0.0:
2755+
version "2.0.0"
2756+
resolved "https://registry.yarnpkg.com/convert-source-map/-/convert-source-map-2.0.0.tgz#4b560f649fc4e918dd0ab75cf4961e8bc882d82a"
2757+
integrity sha512-Kvp459HrV2FEJ1CAsi1Ku+MY3kasH19TFykTz2xWmMeq6bk2NU3XXvfJ+Q61m0xktWwt+1HSYf3JZsTms3aRJg==
2758+
27422759
core-js-compat@^3.31.0:
27432760
version "3.32.1"
27442761
resolved "https://registry.yarnpkg.com/core-js-compat/-/core-js-compat-3.32.1.tgz#55f9a7d297c0761a8eb1d31b593e0f5b6ffae964"
@@ -2803,6 +2820,30 @@ crypto-random-string@^2.0.0:
28032820
resolved "https://registry.yarnpkg.com/crypto-random-string/-/crypto-random-string-2.0.0.tgz#ef2a7a966ec11083388369baa02ebead229b30d5"
28042821
integrity sha512-v1plID3y9r/lPhviJ1wrXpLeyUIGAZ2SHNYTEapm7/8A9nLPoyvVp3RK/EPFqn5kEznyWgYZNsRtYYIWbuG8KA==
28052822

2823+
css-select@^5.1.0:
2824+
version "5.1.0"
2825+
resolved "https://registry.yarnpkg.com/css-select/-/css-select-5.1.0.tgz#b8ebd6554c3637ccc76688804ad3f6a6fdaea8a6"
2826+
integrity sha512-nwoRF1rvRRnnCqqY7updORDsuqKzqYJ28+oSMaJMMgOauh3fvwHqMS7EZpIPqK8GL+g9mKxF1vP/ZjSeNjEVHg==
2827+
dependencies:
2828+
boolbase "^1.0.0"
2829+
css-what "^6.1.0"
2830+
domhandler "^5.0.2"
2831+
domutils "^3.0.1"
2832+
nth-check "^2.0.1"
2833+
2834+
css-tree@^1.1.3:
2835+
version "1.1.3"
2836+
resolved "https://registry.yarnpkg.com/css-tree/-/css-tree-1.1.3.tgz#eb4870fb6fd7707327ec95c2ff2ab09b5e8db91d"
2837+
integrity sha512-tRpdppF7TRazZrjJ6v3stzv93qxRcSsFmW6cX0Zm2NVKpxE1WV1HblnghVv9TreireHkqI/VDEsfolRF1p6y7Q==
2838+
dependencies:
2839+
mdn-data "2.0.14"
2840+
source-map "^0.6.1"
2841+
2842+
css-what@^6.1.0:
2843+
version "6.1.0"
2844+
resolved "https://registry.yarnpkg.com/css-what/-/css-what-6.1.0.tgz#fb5effcf76f1ddea2c81bdfaa4de44e79bac70f4"
2845+
integrity sha512-HTUrgRJ7r4dsZKU6GjmpfRK1O76h97Z8MfS1G0FozR+oF2kG6Vfe8JE6zwrkbxigziPHinCJ+gCPjA9EaBDtRw==
2846+
28062847
csstype@^3.0.2:
28072848
version "3.1.2"
28082849
resolved "https://registry.yarnpkg.com/csstype/-/csstype-3.1.2.tgz#1d4bf9d572f11c14031f0436e1c10bc1f571f50b"
@@ -2929,6 +2970,36 @@ dir-glob@^3.0.1:
29292970
dependencies:
29302971
path-type "^4.0.0"
29312972

2973+
dom-serializer@^2.0.0:
2974+
version "2.0.0"
2975+
resolved "https://registry.yarnpkg.com/dom-serializer/-/dom-serializer-2.0.0.tgz#e41b802e1eedf9f6cae183ce5e622d789d7d8e53"
2976+
integrity sha512-wIkAryiqt/nV5EQKqQpo3SToSOV9J0DnbJqwK7Wv/Trc92zIAYZ4FlMu+JPFW1DfGFt81ZTCGgDEabffXeLyJg==
2977+
dependencies:
2978+
domelementtype "^2.3.0"
2979+
domhandler "^5.0.2"
2980+
entities "^4.2.0"
2981+
2982+
domelementtype@^2.3.0:
2983+
version "2.3.0"
2984+
resolved "https://registry.yarnpkg.com/domelementtype/-/domelementtype-2.3.0.tgz#5c45e8e869952626331d7aab326d01daf65d589d"
2985+
integrity sha512-OLETBj6w0OsagBwdXnPdN0cnMfF9opN69co+7ZrbfPGrdpPVNBUj02spi6B1N7wChLQiPn4CSH/zJvXw56gmHw==
2986+
2987+
domhandler@^5.0.2, domhandler@^5.0.3:
2988+
version "5.0.3"
2989+
resolved "https://registry.yarnpkg.com/domhandler/-/domhandler-5.0.3.tgz#cc385f7f751f1d1fc650c21374804254538c7d31"
2990+
integrity sha512-cgwlv/1iFQiFnU96XXgROh8xTeetsnJiDsTc7TYCLFd9+/WNkIqPTxiM/8pSd8VIrhXGTf1Ny1q1hquVqDJB5w==
2991+
dependencies:
2992+
domelementtype "^2.3.0"
2993+
2994+
domutils@^3.0.1:
2995+
version "3.1.0"
2996+
resolved "https://registry.yarnpkg.com/domutils/-/domutils-3.1.0.tgz#c47f551278d3dc4b0b1ab8cbb42d751a6f0d824e"
2997+
integrity sha512-H78uMmQtI2AhgDJjWeQmHwJJ2bLPD3GMmO7Zja/ZZh84wkm+4ut+IUnUdRa8uCGX88DiVx1j6FRe1XfxEgjEZA==
2998+
dependencies:
2999+
dom-serializer "^2.0.0"
3000+
domelementtype "^2.3.0"
3001+
domhandler "^5.0.3"
3002+
29323003
dotenv-expand@~10.0.0:
29333004
version "10.0.0"
29343005
resolved "https://registry.yarnpkg.com/dotenv-expand/-/dotenv-expand-10.0.0.tgz#12605d00fb0af6d0a592e6558585784032e4ef37"
@@ -2966,6 +3037,11 @@ end-of-stream@^1.1.0:
29663037
dependencies:
29673038
once "^1.4.0"
29683039

3040+
entities@^4.2.0:
3041+
version "4.5.0"
3042+
resolved "https://registry.yarnpkg.com/entities/-/entities-4.5.0.tgz#5d268ea5e7113ec74c4d033b79ea5a35a488fb48"
3043+
integrity sha512-V0hjH4dGPh9Ao5p0MoRY6BVqtwCjhz6vI5LT8AJ55H+4g9/4vbHx1I54fS0XuclLhDHArPQCiMjDxjaL8fPxhw==
3044+
29693045
env-editor@^0.4.1:
29703046
version "0.4.2"
29713047
resolved "https://registry.yarnpkg.com/env-editor/-/env-editor-0.4.2.tgz#4e76568d0bd8f5c2b6d314a9412c8fe9aa3ae861"
@@ -4261,6 +4337,11 @@ md5hex@^1.0.0:
42614337
resolved "https://registry.yarnpkg.com/md5hex/-/md5hex-1.0.0.tgz#ed74b477a2ee9369f75efee2f08d5915e52a42e8"
42624338
integrity sha512-c2YOUbp33+6thdCUi34xIyOU/a7bvGKj/3DB1iaPMTuPHf/Q2d5s4sn1FaCOO43XkXggnb08y5W2PU8UNYNLKQ==
42634339

4340+
4341+
version "2.0.14"
4342+
resolved "https://registry.yarnpkg.com/mdn-data/-/mdn-data-2.0.14.tgz#7113fc4281917d63ce29b43446f701e68c25ba50"
4343+
integrity sha512-dn6wd0uw5GsdswPFfsgMp5NSB0/aDe6fK94YJV/AJDYXL6HVLWBsxeq7js7Ad+mU2K9LAlwpk6kN2D5mwCPVow==
4344+
42644345
42654346
version "0.3.0"
42664347
resolved "https://registry.yarnpkg.com/media-typer/-/media-typer-0.3.0.tgz#8710d7af0aa626f8fffa1ce00168545263255748"
@@ -4886,6 +4967,13 @@ npm-run-path@^4.0.1:
48864967
dependencies:
48874968
path-key "^3.0.0"
48884969

4970+
nth-check@^2.0.1:
4971+
version "2.1.1"
4972+
resolved "https://registry.yarnpkg.com/nth-check/-/nth-check-2.1.1.tgz#c9eab428effce36cd6b92c924bdb000ef1f1ed1d"
4973+
integrity sha512-lqjrjmaOoAnWfMmBPL+XNnynZh2+swxiX3WUE0s4yEHI6m+AwrK2UZOimIRl3X/4QctVqS8AiZjFqyOGrMXb/w==
4974+
dependencies:
4975+
boolbase "^1.0.0"
4976+
48894977
nullthrows@^1.1.1:
48904978
version "1.1.1"
48914979
resolved "https://registry.yarnpkg.com/nullthrows/-/nullthrows-1.1.1.tgz#7818258843856ae971eae4208ad7d7eb19a431b1"
@@ -5344,6 +5432,24 @@ react-is@^17.0.1:
53445432
resolved "https://registry.yarnpkg.com/react-is/-/react-is-17.0.2.tgz#e691d4a8e9c789365655539ab372762b0efb54f0"
53455433
integrity sha512-w2GsyukL62IJnlaff/nRegPQR94C/XXamvMWmSHRJ4y7Ts/4ocGRmTHvOs8PSE6pB3dWOrD/nueuU5sduBsQ4w==
53465434

5435+
react-native-reanimated@~3.3.0:
5436+
version "3.3.0"
5437+
resolved "https://registry.yarnpkg.com/react-native-reanimated/-/react-native-reanimated-3.3.0.tgz#80f9d58e28fddf62fe4c1bc792337b8ab57936ab"
5438+
integrity sha512-LzfpPZ1qXBGy5BcUHqw3pBC0qSd22qXS3t8hWSbozXNrBkzMhhOrcILE/nEg/PHpNNp1xvGOW8NwpAMF006roQ==
5439+
dependencies:
5440+
"@babel/plugin-transform-object-assign" "^7.16.7"
5441+
"@babel/preset-typescript" "^7.16.7"
5442+
convert-source-map "^2.0.0"
5443+
invariant "^2.2.4"
5444+
5445+
5446+
version "13.9.0"
5447+
resolved "https://registry.yarnpkg.com/react-native-svg/-/react-native-svg-13.9.0.tgz#8df8a690dd00362601f074dec5d3a86dd0f99c7f"
5448+
integrity sha512-Ey18POH0dA0ob/QiwCBVrxIiwflhYuw0P0hBlOHeY4J5cdbs8ngdKHeWC/Kt9+ryP6fNoEQ1PUgPYw2Bs/rp5Q==
5449+
dependencies:
5450+
css-select "^5.1.0"
5451+
css-tree "^1.1.3"
5452+
53475453
53485454
version "0.72.4"
53495455
resolved "https://registry.yarnpkg.com/react-native/-/react-native-0.72.4.tgz#97b57e22e4d7657eaf4d1f62a678511fcf9bdda7"
@@ -5830,7 +5936,7 @@ source-map@^0.5.6:
58305936
resolved "https://registry.yarnpkg.com/source-map/-/source-map-0.5.7.tgz#8a039d2d1021d22d1ea14c80d8ea468ba2ef3fcc"
58315937
integrity sha512-LbrmJOMUSdEVxIKvdcJzQC+nQhe8FUZQTXQy6+I75skNgn3OoQ0DZA8YnFa7gp8tqtL3KPf1kmo0R5DoApeSGQ==
58325938

5833-
source-map@^0.6.0, source-map@~0.6.1:
5939+
source-map@^0.6.0, source-map@^0.6.1, source-map@~0.6.1:
58345940
version "0.6.1"
58355941
resolved "https://registry.yarnpkg.com/source-map/-/source-map-0.6.1.tgz#74722af32e9614e9c287a8d0bbde48b5e2f1a263"
58365942
integrity sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==

0 commit comments

Comments
 (0)