pakenfit / react-native-pin-input

React Native Pin Input made easy🔥
https://www.npmjs.com/package/@pakenfit/react-native-pin-input
MIT License
2 stars 2 forks source link

Brainstorm & New features #12

Open Psycarlo opened 2 months ago

Psycarlo commented 2 months ago

Hi again @pakenfit

Wouldn't be better to actually set the value prop on the TextInput? At the moment, when I backspace one of the Inputs, the value still there: inputValues.current[index] equals the previous value instead of being set to null since there is no number on the input

Is it better to receive the pin as prop to PinInput? Could we manage more easily this way instead of passing refs?

How we would implement the following feature: When clicking on any Input, if it's full (all 4 filled), focus should be on the last. When clicking on any Input, if it's not full, focus should be on the next empty

What do you think? What are your thoughts?

Psycarlo commented 2 months ago

Example

type SSPinInput2Props = {
  pin: string[]
  setPin: Dispatch<SetStateAction<string[]>>
  autoFocus?: boolean
  onFillEnded(): void
}

export default function SSPinInput2({
  pin,
  setPin,
  autoFocus,
  onFillEnded
}: SSPinInput2Props) {
  const inputRefs = useRef<TextInput[]>([])
  const [isBackspace, setIsBackspace] = useState(false)

  function handleOnChangeText(text: string, index: number) {
    const newPin = [...pin]
    newPin[index] = text
    setPin(newPin)

    if (text === '') return

    if (index + 1 < PIN_SIZE) {
      inputRefs.current[index + 1]?.focus()
    } else {
      onFillEnded()
      Keyboard.dismiss()
    }
  }

  function handleOnKeyPress(
    event: NativeSyntheticEvent<TextInputKeyPressEventData>,
    index: number
  ) {
    if (event.nativeEvent.key === 'Backspace') {
      setIsBackspace(true)
      if (index - 1 >= 0) {
        inputRefs.current[index - 1]?.focus()
      }
    }
  }

  function handleOnFocus() {
    const lastFilledIndex = pin.findIndex((text) => text === '')

    if (lastFilledIndex === 0) {
      inputRefs.current[0]?.focus()
      return
    }

    if (isBackspace) {
      setIsBackspace(false)
      return
    }

    const finalIndex = lastFilledIndex === -1 ? PIN_SIZE - 1 : lastFilledIndex
    inputRefs.current[finalIndex]?.focus()
  }

  return (
    <SSHStack gap="sm">
      {[...Array(PIN_SIZE)].map((_, index) => (
        <TextInput
          key={index}
          ref={(input) => inputRefs.current.push(input as TextInput)}
          style={styles.pinInputBase}
          autoFocus={autoFocus && index === 0}
          value={pin[index]}
          keyboardType="numeric"
          maxLength={1}
          secureTextEntry
          onChangeText={(text) => handleOnChangeText(text, index)}
          onKeyPress={(event) => handleOnKeyPress(event, index)}
          onFocus={() => handleOnFocus()}
        />
      ))}
    </SSHStack>
  )
}

const styles = StyleSheet.create({
  pinInputBase: {
    borderRadius: Sizes.pinInput.borderRadius,
    height: Sizes.pinInput.height,
    width: Sizes.pinInput.width,
    textAlign: 'center',
    backgroundColor: Colors.gray[850],
    color: Colors.white,
    fontSize: Sizes.textInput.fontSize
  }
})
pakenfit commented 2 months ago

Hi @Psycarlo thanks again for this suggestion, I'm working on a feature for a product today, I'll look into it as soon as I can, in the meantime feel free to make a PR