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";