Skip to content

Instantly share code, notes, and snippets.

@schriker
Created September 4, 2022 06:54
Show Gist options
  • Save schriker/9ecd3d1130773f98f0d84e1413ea1003 to your computer and use it in GitHub Desktop.
Save schriker/9ecd3d1130773f98f0d84e1413ea1003 to your computer and use it in GitHub Desktop.

Revisions

  1. schriker created this gist Sep 4, 2022.
    94 changes: 94 additions & 0 deletions OTPInput.tsx
    Original file line number Diff line number Diff line change
    @@ -0,0 +1,94 @@
    import React, { useRef } from 'react'
    import { NativeSyntheticEvent, TextInput, TextInputKeyPressEventData, View } from 'react-native'
    import { useStyles } from 'lib/hooks'
    import { createStyles } from 'lib/styles'
    import { Nullable } from 'lib/types'

    type OTPInputProps = {
    length: number,
    value: Array<string>,
    disabled: boolean,
    onChange(value: Array<string>): void
    }

    export const OTPInput: React.FunctionComponent<OTPInputProps> = ({
    length,
    disabled,
    value,
    onChange
    }) => {
    const { styles } = useStyles(stylesheet)
    const inputRefs = useRef<Array<Nullable<TextInput>>>([])

    const onChangeValue = (text: string, index: number) => {
    const newValue = value.map((item, valueIndex) => {
    if (valueIndex === index) {
    return text
    }

    return item
    })

    onChange(newValue)
    }

    const handleChange = (text: string, index: number) => {
    onChangeValue(text, index)

    if (text.length !== 0) {
    return inputRefs?.current[index + 1]?.focus()
    }

    return inputRefs?.current[index - 1]?.focus()
    }

    const handleBackspace = (event: NativeSyntheticEvent<TextInputKeyPressEventData>, index: number) => {
    const { nativeEvent } = event

    if (nativeEvent.key === 'Backspace') {
    handleChange('', index)
    }
    }

    return (
    <View style={styles.container}>
    {[...new Array(length)].map((item, index) => (
    <TextInput
    ref={ref => {
    if (ref && !inputRefs.current.includes(ref)) {
    inputRefs.current = [...inputRefs.current, ref]
    }
    }}
    key={index}
    maxLength={1}
    contextMenuHidden
    selectTextOnFocus
    editable={!disabled}
    style={styles.input}
    keyboardType="decimal-pad"
    testID={`OTPInput-${index}`}
    onChangeText={text => handleChange(text, index)}
    onKeyPress={event => handleBackspace(event, index)}
    />
    ))}
    </View>
    )
    }

    const stylesheet = createStyles(template => ({
    container: {
    width: '100%',
    flexDirection: 'row',
    justifyContent: 'space-between'
    },
    input: {
    fontSize: 24,
    color: template.typography.regular,
    textAlign: 'center',
    width: 45,
    height: 55,
    backgroundColor: template.input.background,
    borderRadius: template.input.borderRadius,
    ...template.ui.createShadow()
    }
    }))