import {
Box,
InputAdornment,
InputProps,
TextField as MuiTextField,
TextFieldProps,
TextFieldSlotsAndSlotProps,
Theme,
Tooltip,
Typography
} from '@mui/material';
import { equals, isNil } from 'ramda';
import { forwardRef, useCallback } from 'react';
import { makeStyles } from 'tss-react/mui';
import { getNormalizedId } from '../../utils';
import useAutoSize from './useAutoSize';
const useStyles = makeStyles()((theme: Theme) => ({
autoSizeCompact: {
paddingRight: theme.spacing(1),
paddingTop: theme.spacing(0.6)
},
compactLabel: {
top: '-10px'
},
compactLabelShrink: {
top: '0px'
},
hiddenText: {
display: 'table',
lineHeight: 0,
transform: 'scaleY(0)'
},
input: {
fontSize: theme.typography.body1.fontSize
},
inputBase: {
display: 'inline-flex',
justifyItems: 'start',
paddingRight: theme.spacing(1)
},
noLabelInput: {
padding: theme.spacing(1)
},
textField: {
transition: theme.transitions.create(['width'], {
duration: theme.transitions.duration.shortest
})
},
transparent: {
backgroundColor: 'transparent'
}
}));
interface OptionalLabelInputAdornmentProps {
children: React.ReactNode;
label?: React.ReactNode;
position: 'end' | 'start';
}
const OptionalLabelInputAdornment = ({
label,
position,
children
}: OptionalLabelInputAdornmentProps): JSX.Element => {
const noMarginWhenNoLabel = !label && { style: { marginTop: 0 } };
return (
{children}
);
};
type SizeVariant = 'large' | 'medium' | 'small' | 'compact';
export type TextProps = {
EndAdornment?: React.FC | React.ReactNode;
StartAdornment?: React.FC;
ariaLabel?: string;
autoSize?: boolean;
autoSizeCustomPadding?: number;
autoSizeDefaultWidth?: number;
className?: string;
containerClassName?: string;
dataTestId?: string;
debounced?: boolean;
displayErrorInTooltip?: boolean;
error?: string;
externalValueForAutoSize?: string;
open?: boolean;
required?: boolean;
size?: SizeVariant;
transparent?: boolean;
value?: string;
textFieldSlotsAndSlotProps?: TextFieldSlotsAndSlotProps;
forceUncontrolled?: boolean;
} & Omit;
const TextField = forwardRef(
(
{
StartAdornment,
EndAdornment,
label,
error,
ariaLabel,
dataTestId,
transparent = false,
size,
displayErrorInTooltip = false,
className,
autoSize = false,
debounced = false,
autoSizeDefaultWidth = 0,
externalValueForAutoSize,
autoSizeCustomPadding,
defaultValue,
required = false,
containerClassName,
type,
textFieldSlotsAndSlotProps,
forceUncontrolled,
helperText,
...rest
}: TextProps,
ref: React.ForwardedRef
): JSX.Element => {
const { classes, cx } = useStyles();
const { inputRef, width, changeInputValue, innerValue } = useAutoSize({
autoSize,
autoSizeCustomPadding: equals(type, 'number')
? Math.max(6, autoSizeCustomPadding || 0)
: autoSizeCustomPadding,
autoSizeDefaultWidth,
value: externalValueForAutoSize || rest.value
});
const tooltipTitle = displayErrorInTooltip && !isNil(error) ? error : '';
const getValueProps = useCallback((): object => {
if (debounced || forceUncontrolled) {
return {};
}
if (defaultValue) {
return { defaultValue };
}
return { value: innerValue };
}, [innerValue, debounced, defaultValue, forceUncontrolled]);
return (
{EndAdornment ? (
typeof EndAdornment === 'function' ? (
) : (
EndAdornment
)
) : (
(
textFieldSlotsAndSlotProps?.slotProps?.input as
| Record
| undefined
)?.endAdornment
)}
),
startAdornment: StartAdornment && (
),
...textFieldSlotsAndSlotProps?.slotProps?.input
},
inputLabel: {
classes: {
root: cx(equals(size, 'compact') && classes.compactLabel),
shrink: cx(
equals(size, 'compact') && classes.compactLabelShrink
)
}
}
}}
sx={{
width: autoSize ? width : undefined,
...rest?.sx
}}
type={type}
{...rest}
/>
{autoSize && (
{rest.value || externalValueForAutoSize || innerValue}
)}
);
}
);
export default TextField;