Source

components/input/InputText.tsx

import { DEFAULT_INPUT_STATE_STRING, InputWrapper, stateClassName } from "./InputBase";
import type { InputProps, InputState } from "./InputBase";

import type { FunctionComponent } from "react";
import { useState } from "react";

/**
 * @interface
 * @augments BaseProps
 * @property [type] {"text" | "email" | "password"} The type of input tag to use.
 * @property [validation] {RegExp} The regex validation for the user input.
 *
 * @see {@link <https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/RegExp>|MDN Docs}
 * @author Giancarlo Pernudi Segura <gino@neuralberta.tech>
 */
export interface InputTextProps extends InputProps<string> {
  type?: "text" | "email" | "password";
  validation?: RegExp;
}

/**
 * InputText Component
 * @component
 * @param name {string} The html name attribute for the input tag.
 * @param label {string} The text for the html label tag.
 * @param placeholder {string} The input placeholder.
 * @param [icon] {ReactElement} The icon to display inside the input field.
 * @param [disabled] {disabled} Whether the input tag is disabled or not.
 * @param [type="text"] {"text" | "email" | "password"} The type of input tag to use.
 * @param [validation] {RegExp} The regex validation for the user input.
 * @param [required=false] {boolean} If this field is required or not.
 *
 * @see {@link <https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/RegExp>|MDN Docs}
 * @todo add some hover tooltip to explain why input might be invalid (new prop field)
 * @author Giancarlo Pernudi Segura <gino@neuralberta.tech>
 */
const InputText: FunctionComponent<InputTextProps> = ({ name, label, placeholder, icon, disabled, type = "text", validation, required = false, onChange }) => {
  const [state, setState] = useState<InputState<string>>(DEFAULT_INPUT_STATE_STRING);

  const innerOnChange = (event: React.ChangeEvent<HTMLInputElement>) => {
    const newState = {
      value: event.target.value,
      valid: validation?.test(event.target.value)
    };
    if (onChange) {
      onChange(newState.value, newState.valid);
    }
    setState(newState);
  };

  return (
    <InputWrapper label={label} icon={icon} required={required}>
      <input
        type={type}
        id={name}
        name={name}
        className={`input ${stateClassName(state.valid)} ${disabled ? "disabled" : ""}`}
        placeholder={placeholder}
        required={required}
        value={state.value}
        onChange={innerOnChange}
        aria-invalid={state.valid === false}
      />
    </InputWrapper>
  );
};

export default InputText;