71
common/resources/client/ui/forms/input-field/text-field/text-field-base.tsx
Executable file
71
common/resources/client/ui/forms/input-field/text-field/text-field-base.tsx
Executable file
@@ -0,0 +1,71 @@
|
||||
import React, {ComponentPropsWithoutRef, forwardRef, Ref} from 'react';
|
||||
import type {TextFieldProps} from './text-field';
|
||||
import {Field} from '../field';
|
||||
import {getInputFieldClassNames} from '../get-input-field-class-names';
|
||||
|
||||
interface Props extends TextFieldProps {
|
||||
labelProps?: ComponentPropsWithoutRef<'label'>;
|
||||
inputProps:
|
||||
| ComponentPropsWithoutRef<'input'>
|
||||
| ComponentPropsWithoutRef<'textarea'>;
|
||||
descriptionProps?: ComponentPropsWithoutRef<'div'>;
|
||||
errorMessageProps?: ComponentPropsWithoutRef<'div'>;
|
||||
inputRef?: Ref<HTMLInputElement>;
|
||||
isLoading?: boolean;
|
||||
rows?: number;
|
||||
}
|
||||
|
||||
export const TextFieldBase = forwardRef<HTMLDivElement, Props>((props, ref) => {
|
||||
const {
|
||||
label,
|
||||
startAdornment,
|
||||
endAdornment,
|
||||
startAppend,
|
||||
endAppend,
|
||||
errorMessage,
|
||||
description,
|
||||
labelProps,
|
||||
inputProps,
|
||||
inputRef,
|
||||
descriptionProps,
|
||||
errorMessageProps,
|
||||
inputWrapperClassName,
|
||||
className,
|
||||
inputClassName,
|
||||
disabled,
|
||||
inputElementType,
|
||||
rows,
|
||||
} = props;
|
||||
|
||||
const isTextArea = inputElementType === 'textarea';
|
||||
const ElementType: React.ElementType = isTextArea ? 'textarea' : 'input';
|
||||
const fieldClassNames = getInputFieldClassNames(props);
|
||||
|
||||
return (
|
||||
<Field
|
||||
ref={ref}
|
||||
label={label}
|
||||
labelProps={labelProps}
|
||||
startAdornment={startAdornment}
|
||||
endAdornment={endAdornment}
|
||||
startAppend={startAppend}
|
||||
endAppend={endAppend}
|
||||
errorMessage={errorMessage}
|
||||
description={description}
|
||||
descriptionProps={descriptionProps}
|
||||
errorMessageProps={errorMessageProps}
|
||||
inputWrapperClassName={inputWrapperClassName}
|
||||
className={className}
|
||||
inputClassName={inputClassName}
|
||||
fieldClassNames={fieldClassNames}
|
||||
disabled={disabled}
|
||||
>
|
||||
<ElementType
|
||||
ref={inputRef as any}
|
||||
{...(inputProps as any)}
|
||||
rows={isTextArea ? rows || 4 : undefined}
|
||||
className={fieldClassNames.input}
|
||||
/>
|
||||
</Field>
|
||||
);
|
||||
});
|
||||
89
common/resources/client/ui/forms/input-field/text-field/text-field.tsx
Executable file
89
common/resources/client/ui/forms/input-field/text-field/text-field.tsx
Executable file
@@ -0,0 +1,89 @@
|
||||
import React, {forwardRef, HTMLProps, Ref} from 'react';
|
||||
import {useController} from 'react-hook-form';
|
||||
import {mergeProps, useObjectRef} from '@react-aria/utils';
|
||||
import {BaseFieldPropsWithDom} from '../base-field-props';
|
||||
import {getInputFieldClassNames} from '../get-input-field-class-names';
|
||||
import {Field} from '../field';
|
||||
import {useField} from '../use-field';
|
||||
|
||||
export interface TextFieldProps
|
||||
extends BaseFieldPropsWithDom<HTMLInputElement> {
|
||||
rows?: number;
|
||||
inputElementType?: 'input' | 'textarea';
|
||||
inputRef?: Ref<HTMLInputElement>;
|
||||
value?: string | number;
|
||||
onChange?: (e: React.ChangeEvent<HTMLInputElement>) => void;
|
||||
}
|
||||
export const TextField = forwardRef<HTMLDivElement, TextFieldProps>(
|
||||
(
|
||||
{
|
||||
inputElementType = 'input',
|
||||
flexibleHeight,
|
||||
inputRef,
|
||||
inputTestId,
|
||||
...props
|
||||
},
|
||||
ref
|
||||
) => {
|
||||
const inputObjRef = useObjectRef(inputRef);
|
||||
|
||||
const {fieldProps, inputProps} = useField<HTMLInputElement>({
|
||||
...props,
|
||||
focusRef: inputObjRef,
|
||||
});
|
||||
|
||||
const isTextArea = inputElementType === 'textarea';
|
||||
const ElementType: React.ElementType = isTextArea ? 'textarea' : 'input';
|
||||
const inputFieldClassNames = getInputFieldClassNames({
|
||||
...props,
|
||||
flexibleHeight: flexibleHeight || inputElementType === 'textarea',
|
||||
});
|
||||
|
||||
if (inputElementType === 'textarea' && !props.unstyled) {
|
||||
inputFieldClassNames.input = `${inputFieldClassNames.input} py-12`;
|
||||
}
|
||||
|
||||
return (
|
||||
<Field ref={ref} fieldClassNames={inputFieldClassNames} {...fieldProps}>
|
||||
<ElementType
|
||||
data-testid={inputTestId}
|
||||
ref={inputObjRef}
|
||||
{...(inputProps as any)}
|
||||
rows={
|
||||
isTextArea
|
||||
? (inputProps as HTMLProps<HTMLTextAreaElement>).rows || 4
|
||||
: undefined
|
||||
}
|
||||
className={inputFieldClassNames.input}
|
||||
/>
|
||||
</Field>
|
||||
);
|
||||
}
|
||||
);
|
||||
|
||||
export interface FormTextFieldProps extends TextFieldProps {
|
||||
name: string;
|
||||
}
|
||||
export const FormTextField = React.forwardRef<
|
||||
HTMLDivElement,
|
||||
FormTextFieldProps
|
||||
>(({name, ...props}, ref) => {
|
||||
const {
|
||||
field: {onChange, onBlur, value = '', ref: inputRef},
|
||||
fieldState: {invalid, error},
|
||||
} = useController({
|
||||
name,
|
||||
});
|
||||
|
||||
const formProps: TextFieldProps = {
|
||||
onChange,
|
||||
onBlur,
|
||||
value: value == null ? '' : value, // avoid issues with "null" value when setting form defaults from backend model
|
||||
invalid,
|
||||
errorMessage: error?.message,
|
||||
inputRef,
|
||||
name,
|
||||
};
|
||||
|
||||
return <TextField ref={ref} {...mergeProps(formProps, props)} />;
|
||||
});
|
||||
Reference in New Issue
Block a user