/* eslint-disable max-lines-per-function */ import React, { forwardRef } from "react"; import { Flex } from "../flex"; import { typographyVariants } from "../text"; import { pxToRem } from "../../utils"; import { useInput, isTextArea } from "./use-input"; import { StyledFieldSetContent, StyledFieldSet, StyledInfoText, StyledInput, StyledLabel, StyledLegend, StyledRoot, StyledRootContent, StyledTextArea, InputFieldContainer, LEGEND_PADDING, } from "./input.styled"; import type * as Stitches from "@stitches/react"; import type { WithTestId } from "../../types"; import type { ForwardedRef } from "react"; type SingleLineNumberInputProps = { type: "number"; /** * Clears the value on blur if it's invalid and blocks entering `e` notation numbers into. * This works only with `number` type fields. * For example, by default, user can type i.e.: `1e` or just `+` into the field, and such values will be considered * invalid value, and the read value will be an empty string. * This can break floating label (which on empty value is rendered inside the input) and data in forms. * This prop allows you to work around this issue by sacrificing the `e` notation and clearing some invalid inputs. */ clearScientificNotation?: boolean; }; type SingleLineGeneralInputProps = { type?: Exclude["type"], "number">; clearScientificNotation?: never; }; type SingleLineInputPropsType = SingleLineNumberInputProps | SingleLineGeneralInputProps; export type SingleLineInputProps = Omit< WithTestId>, "type" > & SingleLineInputPropsType & { multiLine?: false; }; export interface TextAreaProps extends WithTestId> { /** * If true, will render a textarea element */ multiLine: true; /** * If there is a value, will render the textarea in fix height. Only applicable when multiLine set to `true` */ height?: number; type?: never; } export interface CommonProps extends Stitches.VariantProps { /** * Label Element */ label: string; /** * Informative text displayed under the input */ info?: string; /** * If true, will apply ellipsis text overflow to info text */ infoEllipsis?: boolean; /** * Renders rightmost inside the input */ rightElement?: React.ReactNode; /** * If true, will keep the label in a raised position */ raisedLabel?: boolean; } export type InputProps = (SingleLineInputProps | TextAreaProps) & CommonProps; // export const isTextArea = (props: InputProps): props is TextAreaProps & CommonProps => { // return Boolean(props.multiLine); // }; /** * A component that displays an `input` or `textarea` element. */ export const Input = forwardRef( (props, ref) => { const { label, inputId, type, restProps, inputRef, rightElement, rightElementContainer, isMaxHeight, isTextareaFixHeight, isFocused, isFocusedOrHasValue, info, error, fieldsetRef, onChange, handleOnChange, onPaste, onKeyDown, onBlur, } = useInput(props, ref); const { css, raisedLabel, infoEllipsis, ...rest } = restProps; const input = isTextArea(props) ? ( } id={inputId} css={{ ...(!isMaxHeight && { overflow: "hidden" }), ...(isTextareaFixHeight && { height: pxToRem(props.height), maxHeight: pxToRem(props.height), }), }} onChange={handleOnChange} raisedLabel={raisedLabel} {...(error && { "aria-invalid": true })} {...(rest as TextAreaProps)} /> ) : ( } type={type} id={inputId} onChange={onChange as SingleLineInputProps["onChange"]} onKeyDown={onKeyDown} onPaste={onPaste} onBlur={onBlur} raisedLabel={raisedLabel} {...(error && { "aria-invalid": true })} {...(rest as Omit)} /> ); return ( {label} {label} {input} {rightElement} {info && ( {info} )} ); } ); Input.displayName = "Input";