import { getNestedError } from '@components/common/form/utils/getNestedError.js'; import { Field, FieldLabel } from '@components/common/ui/Field.js'; import { _ } from '@evershop/evershop/lib/locale/translate/_'; import React from 'react'; import { Controller, FieldPath, FieldValues, RegisterOptions, useFormContext } from 'react-hook-form'; import CreatableSelect, { CreatableProps } from 'react-select/creatable'; interface SelectOption { value: any; label: string; [key: string]: unknown; } interface ReactSelectCreatableFieldProps extends Omit< CreatableProps, 'name' | 'value' | 'onChange' > { name: FieldPath; label?: string; error?: string; helperText?: string; required?: boolean; validation?: RegisterOptions; options: SelectOption[]; className?: string; wrapperClassName?: string; defaultValue?: any; onCreateOption?: (inputValue: string) => void; formatCreateLabel?: (inputValue: string) => string; } export function ReactSelectCreatableField({ name, label, error, wrapperClassName = 'form-field', helperText, required, validation, options, className, isMulti = false, defaultValue, onCreateOption, formatCreateLabel = (inputValue: string) => `Create "${inputValue}"`, ...selectProps }: ReactSelectCreatableFieldProps) { const { control, unregister, formState: { errors } } = useFormContext(); const fieldId = `field-${name}`; const [dynamicOptions, setDynamicOptions] = React.useState(options); React.useEffect(() => { setDynamicOptions(options); }, [options]); React.useEffect(() => { return () => { unregister(name); }; }, [name, unregister]); const validationRules = { ...validation, ...(required && { required: _('${field} is required', { field: label || name }) }) }; const fieldError = getNestedError(name, errors, error); return ( { const handleCreateOption = (inputValue: string) => { const newOption = { value: inputValue.toLowerCase().replace(/\W/g, ''), label: inputValue }; const optionExists = dynamicOptions.some( (option) => option.value === newOption.value || option.label === newOption.label ); if (!optionExists) { setDynamicOptions((prev) => { const updated = [...prev, newOption]; return updated; }); } if (onCreateOption) { onCreateOption(inputValue); } if (isMulti) { const currentValues = (field.value as any[]) || []; if (!currentValues.includes(newOption.value)) { const newValues = [...currentValues, newOption.value]; field.onChange(newValues); } } else { field.onChange(newOption.value); } }; return ( {label && ( {label} {required && *} )} field.value?.includes(option.value) ) : dynamicOptions.find( (option) => option.value === field.value ) || null } onChange={(selectedOption) => { if (isMulti) { const values = selectedOption ? (selectedOption as SelectOption[]).map( (option) => option.value ) : []; field.onChange(values); } else { field.onChange( selectedOption ? (selectedOption as SelectOption).value : null ); } }} classNamePrefix="react-select" styles={{ control: (base, state) => ({ ...base, minHeight: 'auto', border: '1px solid #d1d5db', borderRadius: '0.375rem', boxShadow: 'none', transition: 'border-color 0.15s ease-in-out', '&:hover': { borderColor: '#d1d5db' }, ...(state.isFocused && { borderColor: '#3b82f6', boxShadow: '0 0 0 1px rgb(59, 130, 246)' }) }), input: (base) => ({ ...base, '& input': { boxShadow: 'none !important', outline: 'none !important' } }) }} /> {fieldError && (

{fieldError}

)} {helperText && !fieldError && (

{helperText}

)}
); }} /> ); }