Files
dotfiles/scaffolds/nextjs/components/atoms/ControlledAutocomplete.tsx
2022-09-01 23:52:07 +03:00

77 lines
2.6 KiB
TypeScript

import Autocomplete, { AutocompleteProps } from '@mui/material/Autocomplete'
import TextField from '@mui/material/TextField'
import React from 'react'
import { Controller, Control, Path } from 'react-hook-form'
import { CustomComponent, Option } from '../../core/types'
import { optionFrom } from '../../core/utils/object_utils'
export interface ControlledAutocompleteProps<T, K extends Path<T>, Multiple extends boolean = false>
extends CustomComponent<Partial<AutocompleteProps<Option, Multiple, false, false>>> {
control: Control<T>
name: K
options: Option[]
helperText?: React.ReactNode
placeholder?: string
required?: boolean
multiple?: Multiple
}
export const ControlledAutocomplete = <T, K extends Path<T>, Multiple extends boolean = false>(
props: ControlledAutocompleteProps<T, K, Multiple>,
) => {
const { control, name, options, helperText, placeholder, onChange, multiple, ...rest } = props
const _value = React.useMemo(
(): Multiple extends true ? Option<string>[] : Option<string> | null =>
control._formValues[name]
? multiple
? control._formValues[name].map(optionFrom)
: optionFrom(control._formValues[name])
: multiple
? []
: null,
// eslint-disable-next-line react-hooks/exhaustive-deps
[control._formValues[name]],
)
// eslint-disable-next-line @typescript-eslint/no-explicit-any
const [value, setValue] = React.useState(_value ?? (multiple ? ([] as string[]) : ('' as any)))
React.useEffect(
// eslint-disable-next-line @typescript-eslint/no-explicit-any
() => setValue(_value ?? (multiple ? ([] as string[]) : ('' as any))),
[_value, multiple],
)
return (
<Controller<T, K>
control={control}
name={name}
rules={{ required: props.required }}
render={({ field, formState: { errors } }) => (
<Autocomplete
onChange={(e, d, r) => {
setValue(d as typeof _value)
field.onChange(Array.isArray(d) ? d.map((d) => d?.value) : d?.value)
onChange?.(e, d, r)
}}
value={value}
options={options}
getOptionLabel={(opt) => opt?.label}
isOptionEqualToValue={(opt, value) => opt.value === value.value}
multiple={multiple}
renderInput={(params) => (
<TextField
{...params}
fullWidth
placeholder={placeholder}
helperText={helperText}
// eslint-disable-next-line @typescript-eslint/no-explicit-any
error={Boolean((errors as any)[name])}
/>
)}
{...rest}
/>
)}
/>
)
}