Skip to content

Instantly share code, notes, and snippets.

@DanCouper
Created January 23, 2020 12:00
Show Gist options
  • Save DanCouper/01a27a0d9bfa187bd1904dc9bb1ca8e4 to your computer and use it in GitHub Desktop.
Save DanCouper/01a27a0d9bfa187bd1904dc9bb1ca8e4 to your computer and use it in GitHub Desktop.
Rough draft of a generic code input component
import React from "react";
import { StyleSheet, Text, TextInput, View } from "react-native";
/* --------------------------------------- *\
* Local versions of imported modules,
* just for isolated testing.
\* --------------------------------------- */
const s = StyleSheet.create({
white: { color: "#ffffff" },
bg_mid: { backgroundColor: "#1A5163" },
text_sm: { fontSize: 12 },
text_xl: { fontSize: 20, },
text_bold: { fontWeight: "bold" },
text_center: { textAlign: "center" },
flex_1: { flex: 1 },
flex_row: { flexDirection: "row" },
align_center: { alignItems: "center" },
align_stretch: { alignItems: "stretch" },
justify_center: { justifyContent: "center" },
p_sm: { padding: 8 },
m_sm: {margin: 8 },
fullwidth: { width: "100%" },
width_36: { width: 36 },
});
/* --------------------------------------- */
export function useCodeInput(numCharacters) {
const [codeValues, updateCodeValues] = React.useState(() =>
Array.from(Array(numCharacters), (_, i) => ({
id: `codeinput-${i}`,
value: "",
isFocussed: false,
ref: React.createRef(),
}))
);
const updateCodeValue = (id, value) =>
updateCodeValues((inputFields) => {
return inputFields.map((currentInputField, i) => {
switch (true) {
case id === currentInputField.id:
// Set the value and the focussed property on a matching input field:
return { ...currentInputField, value, isFocussed: false };
case value === "" && (inputFields[i + 1] && id === inputFields[i + 1].id):
case inputFields[i - 1] && id === inputFields[i - 1].id:
// If the incoming value is an empty string, user has deleted what
// was there before, and we set the focus to the previous input field
// Otherwise, go forwards, switching focus to the next input field:
currentInputField.ref.current.focus();
return { ...currentInputField, isFocussed: true };
default:
// If on first input field & going backwards, or on last input field
// going forwards, previous two conditions will be false: just return
// as is, nothing more to do:
return currentInputField;
}
});
});
const focusCodeInput = (id) =>
updateCodeValues((inputFields) => {
return inputFields.map((currentInputField) => {
if (id === currentInputField.id) {
return { ...currentInputField, isFocussed: true };
} else {
return { ...currentInputField, isFocussed: false };
}
});
});
const clearCodeValues = () =>
updateCodeValues((inputFields) => {
return inputFields.map((currentInputField) => ({ ...currentInputField, value: "", isFocussed: false }));
});
return { codeValues, updateCodeValue, focusCodeInput, clearCodeValues };
}
export const CodeInput = ({
codeValues,
errorText,
focusCodeInput,
hintText,
updateCodeValue,
...props
}) => (
<View style={[s.align_stretch, s.fullwidth, props.style]}>
{hintText && <Text style={[s.text_sm]}>{hintText}</Text>}
<View style={[s.flex_row]}>
{codeValues.map(({ id, value, ref }) => (
<TextInput
key={id}
ref={ref}
style={[s.width_36, s.p_sm, s.bg_mid, s.text_xl, s.white, s.m_sm, s.text_bold, s.align_center, s.text_center]}
maxLength={1}
onChangeText={(val) => updateCodeValue(id, val)}
onFocus={() => focusCodeInput(id)}
value={value}
/>
))}
</View>
{errorText && <Text style={[s.text_sm]}>{errorText}</Text>}
</View>
);
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment