import * as stylex from "@stylexjs/stylex"; import { type ChangeEvent, type KeyboardEvent, type Ref, memo, useCallback, } from "react"; import { controlColor } from "./theme.stylex"; import { font, size } from "./tokens.stylex"; // Properties and some behavior of this textbox is based on the Text Field of MUI: // https://mui.com/material-ui/react-text-field/ export interface TextBoxProps { // Commonly used text props name?: string; /** * Try to avoid using placeholders as labels: * - They are not accessible to screen readers. * - They offer bad contrast. * - They disappear when you start typing. * See: https://www.nngroup.com/articles/form-design-placeholders/ * @example Consider using this instead: * ```tsx * * * * ``` */ placeholder?: string; "aria-label"?: string; disabled?: boolean; required?: boolean; autoComplete?: TextBoxAutoCompleteValue; autoFocus?: boolean; maxLength?: number; minLength?: number; pattern?: string; title?: string; defaultValue?: string; form?: string; /** * Hints at the type of data that might be entered by the user while editing the element or its contents * @see https://html.spec.whatwg.org/multipage/interaction.html#input-modalities:-the-inputmode-attribute */ inputMode?: | "none" | "text" | "tel" | "url" | "email" | "numeric" | "decimal" | "search" | undefined; /** `value` and `onChange` are optional because we want to be able to use the control in an uncontrolled manner as well (for example, in forms) */ value?: string; onChange?: (value: string, e: ChangeEvent) => void; onKeyDown?: (e: KeyboardEvent) => void; /** * @remarks - Don't use `date` or `datetime-local`. Use the `DateInput` component instead. */ type?: // | "number" TODO: We don't support "number" yet because that is more complicated and most likely will be in a different component. | "text" | "password" | "email" | "tel" | "url" | "search" | "date" | "datetime-local"; /** @remarks Don't use this. It is intended for internal use for {@link DateInput}. */ min?: string; /** @remarks Don't use this. It is intended for internal use for {@link DateInput}. */ max?: string; size?: "small" | "medium" | "large"; textTransform?: "uppercase"; /** * Used for datalists. May be removed in the future. We still need to evaluate if this is a good way to do it. */ list?: string; /** * @remarks For internal use only. * @internal * */ ref?: Ref; } export default memo(TextBox); const styles = stylex.create({ box: { background: { default: controlColor.inputBackground, ":focus": controlColor.inputActiveBackground, ":disabled": controlColor.inputDisabledBackground, }, borderWidth: "1px", borderStyle: "solid", borderColor: { default: controlColor.inputBorder, ":focus": controlColor.inputActiveBorder, // https://web.dev/articles/user-valid-and-user-invalid-pseudo-classes ":user-invalid": controlColor.inputInvalidBorder, ":disabled": controlColor.inputDisabledBorder, }, borderRadius: 0, color: { default: controlColor.inputColor, ":focus": controlColor.inputActiveColor, ":disabled": controlColor.inputDisabledColor, }, width: "100%", fontFamily: font.main, "::placeholder": { color: controlColor.inputPlaceholderColor, }, outline: { ":focus": "none", }, cursor: { ":disabled": "not-allowed", }, }, upperCase: { textTransform: "uppercase", }, }); const variants = stylex.create({ small: { padding: `${size.rem1} ${size.rem2}`, fontSize: size.rem4, }, medium: { padding: `${size.px2} ${size.px3}`, fontSize: size.rem4, fontWeight: 400, }, large: { padding: `${size.rem1} ${size.rem2}`, fontSize: size.rem6, // TODO: ASK-UX:So we have small/large text fields? }, }); function TextBox(props: TextBoxProps) { const onChange = useCallback( (e: ChangeEvent) => { props.onChange?.(e.currentTarget.value, e); }, [props.onChange], ); return ( ); } /** * See: https://html.spec.whatwg.org/multipage/form-control-infrastructure.html#autofill * MDN link: https://developer.mozilla.org/en-US/docs/Web/HTML/Attributes/autocomplete */ export type TextBoxAutoCompleteValue = | "off" | "name" | "email" | "honorific-prefix" | "given-name" | "additional-name" | "family-name" | "honorific-suffix" | "nickname" | "username" | "new-password" | "current-password" | "one-time-code" | "organization-title" | "organization" | "street-address" | "address-line1" | "address-line2" | "address-line3" | "address-level4" | "address-level3" | "address-level2" | "address-level1" | "country" | "country-name" | "postal-code" | "cc-name" | "cc-given-name" | "cc-additional-name" | "cc-family-name" | "cc-number" | "cc-exp" | "cc-exp-month" | "cc-exp-year" | "cc-csc" | "cc-type" | "transaction-currency" | "transaction-amount" | "language" | "bday" | "bday-day" | "bday-month" | "bday-year" | "sex" | "url" | "photo";