todo/js/components/TodoTextInput.js (63 lines of code) (raw):
// @flow
import * as React from 'react';
import {useEffect, useRef, useState} from 'react';
type Props = {|
  +className: string,
  +commitOnBlur?: boolean,
  +initialValue?: string,
  +onCancel?: () => void,
  +onDelete?: () => void,
  +onSave: (string) => void,
  +placeholder?: string,
|};
const ENTER_KEY_CODE = 13;
const ESC_KEY_CODE = 27;
const TodoTextInput = ({
  className,
  commitOnBlur,
  initialValue,
  onCancel,
  onDelete,
  onSave,
  placeholder,
}: Props): React$Element<'input'> => {
  const [text, setText] = useState<string>(initialValue || '');
  const inputRef = useRef();
  useEffect(() => {
    if (inputRef.current) {
      inputRef.current.focus();
    }
  }, [inputRef]);
  const commitChanges = () => {
    const newText = text.trim();
    if (onDelete && newText === '') {
      onDelete();
    } else if (onCancel && newText === initialValue) {
      onCancel();
    } else if (newText !== '') {
      onSave(newText);
      setText('');
    }
  };
  const handleBlur = () => {
    if (commitOnBlur) {
      commitChanges();
    }
  };
  const handleChange = (e: SyntheticEvent<HTMLInputElement>) =>
    setText(e.currentTarget.value);
  const handleKeyDown = (e: SyntheticKeyboardEvent<HTMLInputElement>) => {
    if (onCancel && e.keyCode === ESC_KEY_CODE) {
      onCancel();
    } else if (e.keyCode === ENTER_KEY_CODE) {
      commitChanges();
    }
  };
  return (
    <input
      className={className}
      onBlur={handleBlur}
      onChange={handleChange}
      onKeyDown={handleKeyDown}
      placeholder={placeholder}
      ref={inputRef}
      value={text}
    />
  );
};
export default TodoTextInput;