migration wip
This commit is contained in:
218
components/forms/FormField.tsx
Normal file
218
components/forms/FormField.tsx
Normal file
@@ -0,0 +1,218 @@
|
||||
import React from 'react';
|
||||
import { cn } from '@/lib/utils';
|
||||
import FormLabel from './FormLabel';
|
||||
import FormError from './FormError';
|
||||
import FormInput from './FormInput';
|
||||
import FormTextarea from './FormTextarea';
|
||||
import FormSelect from './FormSelect';
|
||||
import FormCheckbox from './FormCheckbox';
|
||||
import FormRadio from './FormRadio';
|
||||
|
||||
/**
|
||||
* FormField Component
|
||||
* Wrapper for form fields with label, input, and error
|
||||
* Supports different input types and provides consistent form experience
|
||||
*/
|
||||
|
||||
export type FormFieldType =
|
||||
| 'text'
|
||||
| 'email'
|
||||
| 'tel'
|
||||
| 'textarea'
|
||||
| 'select'
|
||||
| 'checkbox'
|
||||
| 'radio'
|
||||
| 'number'
|
||||
| 'password'
|
||||
| 'date'
|
||||
| 'time'
|
||||
| 'url';
|
||||
|
||||
export interface FormFieldProps {
|
||||
type?: FormFieldType;
|
||||
label?: string;
|
||||
name: string;
|
||||
value?: any;
|
||||
error?: string | string[];
|
||||
helpText?: string;
|
||||
required?: boolean;
|
||||
disabled?: boolean;
|
||||
placeholder?: string;
|
||||
className?: string;
|
||||
containerClassName?: string;
|
||||
|
||||
// For select, checkbox, radio
|
||||
options?: any[];
|
||||
|
||||
// For select
|
||||
multiple?: boolean;
|
||||
showSearch?: boolean;
|
||||
|
||||
// For checkbox/radio
|
||||
layout?: 'vertical' | 'horizontal';
|
||||
|
||||
// For textarea
|
||||
rows?: number;
|
||||
showCharCount?: boolean;
|
||||
autoResize?: boolean;
|
||||
maxLength?: number;
|
||||
|
||||
// For input
|
||||
prefix?: React.ReactNode;
|
||||
suffix?: React.ReactNode;
|
||||
showClear?: boolean;
|
||||
iconPosition?: 'prefix' | 'suffix';
|
||||
|
||||
// Callbacks
|
||||
onChange?: (value: any) => void;
|
||||
onBlur?: () => void;
|
||||
onClear?: () => void;
|
||||
|
||||
// Additional props
|
||||
[key: string]: any;
|
||||
}
|
||||
|
||||
export const FormField: React.FC<FormFieldProps> = ({
|
||||
type = 'text',
|
||||
label,
|
||||
name,
|
||||
value,
|
||||
error,
|
||||
helpText,
|
||||
required = false,
|
||||
disabled = false,
|
||||
placeholder,
|
||||
className,
|
||||
containerClassName,
|
||||
options = [],
|
||||
multiple = false,
|
||||
showSearch = false,
|
||||
layout = 'vertical',
|
||||
rows = 4,
|
||||
showCharCount = false,
|
||||
autoResize = false,
|
||||
maxLength,
|
||||
prefix,
|
||||
suffix,
|
||||
showClear = false,
|
||||
iconPosition = 'prefix',
|
||||
onChange,
|
||||
onBlur,
|
||||
onClear,
|
||||
...props
|
||||
}) => {
|
||||
const commonProps = {
|
||||
name,
|
||||
value,
|
||||
onChange,
|
||||
onBlur,
|
||||
disabled,
|
||||
required,
|
||||
placeholder,
|
||||
'aria-label': label,
|
||||
};
|
||||
|
||||
const renderInput = () => {
|
||||
switch (type) {
|
||||
case 'textarea':
|
||||
return (
|
||||
<FormTextarea
|
||||
{...commonProps}
|
||||
error={error}
|
||||
helpText={helpText}
|
||||
rows={rows}
|
||||
showCharCount={showCharCount}
|
||||
autoResize={autoResize}
|
||||
maxLength={maxLength}
|
||||
className={className}
|
||||
/>
|
||||
);
|
||||
|
||||
case 'select':
|
||||
return (
|
||||
<FormSelect
|
||||
{...commonProps}
|
||||
error={error}
|
||||
helpText={helpText}
|
||||
options={options}
|
||||
multiple={multiple}
|
||||
showSearch={showSearch}
|
||||
placeholder={placeholder}
|
||||
className={className}
|
||||
/>
|
||||
);
|
||||
|
||||
case 'checkbox':
|
||||
return (
|
||||
<FormCheckbox
|
||||
label={label}
|
||||
error={error}
|
||||
helpText={helpText}
|
||||
required={required}
|
||||
checked={Array.isArray(value) ? value.length > 0 : !!value}
|
||||
options={options}
|
||||
value={Array.isArray(value) ? value : []}
|
||||
onChange={onChange}
|
||||
disabled={disabled}
|
||||
containerClassName={className}
|
||||
/>
|
||||
);
|
||||
|
||||
case 'radio':
|
||||
return (
|
||||
<FormRadio
|
||||
label={label}
|
||||
error={error}
|
||||
helpText={helpText}
|
||||
required={required}
|
||||
options={options}
|
||||
value={value}
|
||||
onChange={onChange}
|
||||
disabled={disabled}
|
||||
layout={layout}
|
||||
containerClassName={className}
|
||||
/>
|
||||
);
|
||||
|
||||
default:
|
||||
return (
|
||||
<FormInput
|
||||
{...commonProps}
|
||||
type={type}
|
||||
error={error}
|
||||
helpText={helpText}
|
||||
label={label}
|
||||
prefix={prefix}
|
||||
suffix={suffix}
|
||||
showClear={showClear}
|
||||
iconPosition={iconPosition}
|
||||
onClear={onClear}
|
||||
className={className}
|
||||
/>
|
||||
);
|
||||
}
|
||||
};
|
||||
|
||||
// For checkbox and radio, the label is handled internally
|
||||
const showExternalLabel = type !== 'checkbox' && type !== 'radio';
|
||||
|
||||
return (
|
||||
<div className={cn('flex flex-col gap-1.5', containerClassName)}>
|
||||
{showExternalLabel && label && (
|
||||
<FormLabel htmlFor={name} required={required}>
|
||||
{label}
|
||||
</FormLabel>
|
||||
)}
|
||||
|
||||
{renderInput()}
|
||||
|
||||
{!showExternalLabel && error && (
|
||||
<FormError errors={error} />
|
||||
)}
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
FormField.displayName = 'FormField';
|
||||
|
||||
export default FormField;
|
||||
Reference in New Issue
Block a user