react-native-keypad-component
Version:
Customizable keypad component for react native
224 lines (223 loc) • 7.48 kB
JavaScript
"use strict";
import React, { Fragment, useCallback, useEffect, useState } from 'react';
import { StyleSheet, Text, TouchableOpacity, View } from 'react-native';
import Animated, { LinearTransition, useAnimatedStyle, useSharedValue, withRepeat, withSequence, withTiming, ZoomIn, ZoomOut } from 'react-native-reanimated';
import { ANIMATION_VALUES, DEFAULTS } from "./constants.js";
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
export default function Keypad({
onPinEntered,
onPinErrored,
errorMessageComponent,
pinLength = DEFAULTS.pinLength,
containerStyle,
buttonStyle,
buttonTextStyle,
keypadTextSize = DEFAULTS.keypadTextSize,
disableKeypadBackground = DEFAULTS.displayKeypadBg,
usesFaceId = DEFAULTS.useFaceId,
keypadRadius = DEFAULTS.borderRadius,
theme = DEFAULTS.theme,
activeDotColor,
emptyDotColor = DEFAULTS.emptyDotColor,
keypadColor,
textColor,
dotWidth = DEFAULTS.dotWidth,
dotHeight = DEFAULTS.dotHeight,
gridGap = DEFAULTS.gridGap,
renderFaceIdIcon,
applyBackgroundToFaceIdButton = DEFAULTS.applyFaceIdButtonBackground
}) {
const isDarkTheme = theme === 'dark';
const dotColor = isDarkTheme ? DEFAULTS.dotColorDark : DEFAULTS.dotColorLight;
const defaultTextColor = isDarkTheme ? DEFAULTS.textColorDark : DEFAULTS.textColorLight;
const defaultKeypadColor = isDarkTheme ? DEFAULTS.keyboardColorDark : DEFAULTS.keyboardColorLight;
const [pin, setPin] = useState('');
const offset = useSharedValue(0);
const animatedStyle = useAnimatedStyle(() => {
return {
transform: [{
translateX: offset.value
}]
};
});
const dotScales = [useSharedValue(1), useSharedValue(1), useSharedValue(1), useSharedValue(1)];
const animatedDotStyles = [useAnimatedStyle(() => ({
transform: [{
scale: dotScales[0]?.value ?? 1
}]
})), useAnimatedStyle(() => ({
transform: [{
scale: dotScales[1]?.value ?? 2
}]
})), useAnimatedStyle(() => ({
transform: [{
scale: dotScales[2]?.value ?? 3
}]
})), useAnimatedStyle(() => ({
transform: [{
scale: dotScales[3]?.value ?? 4
}]
}))];
const applyShakeAnimation = useCallback(() => {
offset.value = withSequence(withTiming(-ANIMATION_VALUES.offset, {
duration: ANIMATION_VALUES.timing / 2
}), withRepeat(withTiming(ANIMATION_VALUES.offset, {
duration: ANIMATION_VALUES.timing
}), 4, true), withTiming(0, {
duration: ANIMATION_VALUES.timing / 2
}));
}, [offset]);
useEffect(() => {
if (onPinErrored) {
applyShakeAnimation();
}
}, [onPinErrored, applyShakeAnimation]);
function handlePress(digit) {
const newPin = pin + digit;
if (newPin.length <= pinLength) {
setPin(newPin);
const nextIndex = newPin.length - 1;
const dot = dotScales[nextIndex];
if (dot) {
dot.value = withSequence(withTiming(1.2, {
duration: 100
}), withTiming(1, {
duration: 100
}));
}
if (newPin.length === pinLength) {
onPinEntered(newPin);
setTimeout(() => setPin(''), 200);
}
}
}
function handleDelete() {
if (pin.length > 0) {
const indexToAnimate = pin.length - 1;
const dot = dotScales[indexToAnimate];
if (dot) {
dot.value = withSequence(withTiming(1.2, {
duration: 100
}), withTiming(1, {
duration: 100
}));
}
setPin(prev => prev.slice(0, -1));
}
}
return /*#__PURE__*/_jsxs(View, {
style: [styles.container, containerStyle],
children: [/*#__PURE__*/_jsx(Animated.View, {
style: [styles.dotsContainer, animatedStyle],
children: Array.from({
length: pinLength
}).map((_, index) => {
return /*#__PURE__*/_jsx(Animated.View, {
testID: index < pin.length ? 'pin-dot-filled' : 'pin-dot',
style: [styles.dot, {
backgroundColor: index < pin.length ? activeDotColor ?? dotColor : emptyDotColor,
borderRadius: keypadRadius,
width: dotWidth,
height: dotHeight
}, animatedDotStyles[index]]
}, index);
})
}), onPinErrored && errorMessageComponent && /*#__PURE__*/_jsx(Animated.View, {
layout: LinearTransition.springify().damping(ANIMATION_VALUES.damping),
entering: ZoomIn,
exiting: ZoomOut,
style: {
marginBottom: 10
},
children: errorMessageComponent()
}), /*#__PURE__*/_jsx(Animated.View, {
layout: LinearTransition.springify().damping(ANIMATION_VALUES.damping),
style: [styles.grid, {
gap: gridGap
}],
children: ['1', '2', '3', '4', '5', '6', '7', '8', '9', 'face', '0', 'del'].map((key, index) => {
if (key === 'face') {
return usesFaceId ? /*#__PURE__*/_jsx(Fragment, {
children: /*#__PURE__*/_jsx(TouchableOpacity, {
activeOpacity: 0.6,
style: [styles.button, {
borderRadius: keypadRadius,
backgroundColor: disableKeypadBackground ? 'transparent' : applyBackgroundToFaceIdButton ? buttonStyle && buttonStyle.backgroundColor || keypadColor || defaultKeypadColor : 'transparent'
}, buttonStyle],
children: renderFaceIdIcon ? renderFaceIdIcon() : /*#__PURE__*/_jsx(Text, {
children: "\uD83D\uDD10"
})
})
}, key) : /*#__PURE__*/_jsx(View, {
style: [styles.button, {
backgroundColor: disableKeypadBackground ? 'transparent' : keypadColor ?? defaultKeypadColor,
borderRadius: keypadRadius,
opacity: 0
}]
}, index);
}
if (key === 'del') {
return /*#__PURE__*/_jsx(TouchableOpacity, {
activeOpacity: 0.6,
style: [styles.button, {
backgroundColor: disableKeypadBackground ? 'transparent' : keypadColor ?? defaultKeypadColor,
borderRadius: keypadRadius
}, buttonStyle],
onPress: handleDelete,
children: /*#__PURE__*/_jsx(Text, {
style: [{
color: textColor ?? defaultTextColor,
fontSize: keypadTextSize
}, buttonTextStyle],
children: "\u232B"
})
}, key);
}
return /*#__PURE__*/_jsx(TouchableOpacity, {
activeOpacity: 0.6,
testID: `key-${key}`,
style: [styles.button, {
backgroundColor: disableKeypadBackground ? 'transparent' : keypadColor ?? defaultKeypadColor,
borderRadius: keypadRadius
}, buttonStyle],
onPress: () => handlePress(key),
children: /*#__PURE__*/_jsx(Text, {
style: [{
color: textColor ?? defaultTextColor,
fontSize: keypadTextSize
}, buttonTextStyle],
children: key
})
}, index);
})
})]
});
}
const styles = StyleSheet.create({
container: {
alignItems: 'center'
},
dotsContainer: {
flexDirection: 'row',
marginBottom: 20,
gap: 5
},
dot: {
margin: 8,
borderRadius: 8
},
grid: {
flexDirection: 'row',
flexWrap: 'wrap',
width: 240,
justifyContent: 'center'
},
button: {
width: 60,
height: 60,
margin: 5,
justifyContent: 'center',
alignItems: 'center'
}
});
//# sourceMappingURL=index.js.map