Created
January 23, 2020 12:00
-
-
Save DanCouper/01a27a0d9bfa187bd1904dc9bb1ca8e4 to your computer and use it in GitHub Desktop.
Rough draft of a generic code input component
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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