|
|
@@ -1,117 +1,164 @@ |
|
|
|
"use client" |
|
|
|
"use client"; |
|
|
|
|
|
|
|
import { Autocomplete, MenuItem, TextField, Checkbox, Chip } from "@mui/material"; |
|
|
|
import { Controller, FieldValues, Path, Control, RegisterOptions } from "react-hook-form"; |
|
|
|
import { |
|
|
|
Autocomplete, |
|
|
|
MenuItem, |
|
|
|
TextField, |
|
|
|
Checkbox, |
|
|
|
Chip, |
|
|
|
} from "@mui/material"; |
|
|
|
import { |
|
|
|
Controller, |
|
|
|
FieldValues, |
|
|
|
Path, |
|
|
|
Control, |
|
|
|
RegisterOptions, |
|
|
|
} from "react-hook-form"; |
|
|
|
import { useTranslation } from "react-i18next"; |
|
|
|
import CheckBoxOutlineBlankIcon from '@mui/icons-material/CheckBoxOutlineBlank'; |
|
|
|
import CheckBoxIcon from '@mui/icons-material/CheckBox'; |
|
|
|
import CheckBoxOutlineBlankIcon from "@mui/icons-material/CheckBoxOutlineBlank"; |
|
|
|
import CheckBoxIcon from "@mui/icons-material/CheckBox"; |
|
|
|
|
|
|
|
const icon = <CheckBoxOutlineBlankIcon fontSize="medium" />; |
|
|
|
const checkedIcon = <CheckBoxIcon fontSize="medium" />; |
|
|
|
// label -> e.g. code - name -> 001 - WL |
|
|
|
// name -> WL |
|
|
|
interface Props<T extends { id?: number | string | null; label?: string; name?: string }, TField extends FieldValues> { |
|
|
|
control: Control<TField>, |
|
|
|
options: T[], |
|
|
|
name: Path<TField>, // register name |
|
|
|
label?: string, // display label |
|
|
|
noOptionsText?: string, |
|
|
|
isMultiple?: boolean, |
|
|
|
rules?: RegisterOptions<FieldValues> |
|
|
|
disabled?: boolean, |
|
|
|
interface Props< |
|
|
|
T extends { id?: number | string | null; label?: string; name?: string }, |
|
|
|
TField extends FieldValues, |
|
|
|
> { |
|
|
|
control: Control<TField>; |
|
|
|
options: T[]; |
|
|
|
name: Path<TField>; // register name |
|
|
|
label?: string; // display label |
|
|
|
noOptionsText?: string; |
|
|
|
isMultiple?: boolean; |
|
|
|
rules?: RegisterOptions<FieldValues>; |
|
|
|
disabled?: boolean; |
|
|
|
} |
|
|
|
|
|
|
|
function ControlledAutoComplete< |
|
|
|
T extends { id?: number | string; label?: string; name?: string }, |
|
|
|
TField extends FieldValues |
|
|
|
>( |
|
|
|
props: Props<T, TField> |
|
|
|
) { |
|
|
|
const { t } = useTranslation() |
|
|
|
const { control, options, name, label, noOptionsText, isMultiple, rules, disabled } = props; |
|
|
|
T extends { id?: number | string; label?: string; name?: string }, |
|
|
|
TField extends FieldValues, |
|
|
|
>(props: Props<T, TField>) { |
|
|
|
const { t } = useTranslation(); |
|
|
|
const { |
|
|
|
control, |
|
|
|
options, |
|
|
|
name, |
|
|
|
label, |
|
|
|
noOptionsText, |
|
|
|
isMultiple, |
|
|
|
rules, |
|
|
|
disabled, |
|
|
|
} = props; |
|
|
|
|
|
|
|
// set default value if value is null |
|
|
|
if (!Boolean(isMultiple) && !Boolean(control._formValues[name])) { |
|
|
|
control._formValues[name] = options[0]?.id ?? undefined |
|
|
|
} else if (Boolean(isMultiple) && !Boolean(control._formValues[name])) { |
|
|
|
control._formValues[name] = [] |
|
|
|
} |
|
|
|
// set default value if value is null |
|
|
|
if (!Boolean(isMultiple) && !Boolean(control._formValues[name])) { |
|
|
|
control._formValues[name] = options[0]?.id ?? undefined; |
|
|
|
} else if (Boolean(isMultiple) && !Boolean(control._formValues[name])) { |
|
|
|
control._formValues[name] = []; |
|
|
|
} |
|
|
|
|
|
|
|
return ( |
|
|
|
<Controller |
|
|
|
name={name} |
|
|
|
control={control} |
|
|
|
rules={rules} |
|
|
|
render={({ field, fieldState, formState }) => { |
|
|
|
|
|
|
|
return ( |
|
|
|
isMultiple ? |
|
|
|
<Autocomplete |
|
|
|
multiple |
|
|
|
disableClearable |
|
|
|
disableCloseOnSelect |
|
|
|
// disablePortal |
|
|
|
disabled={disabled} |
|
|
|
noOptionsText={noOptionsText ?? t("No Options")} |
|
|
|
value={options.filter(option => { |
|
|
|
return field.value?.includes(option.id) |
|
|
|
})} |
|
|
|
options={options} |
|
|
|
getOptionLabel={(option) => option.label ?? option.name!!} |
|
|
|
isOptionEqualToValue={(option, value) => option.id === value.id} |
|
|
|
renderOption={(params, option, { selected }) => { |
|
|
|
return ( |
|
|
|
<li {...params} key={option?.id}> |
|
|
|
<Checkbox |
|
|
|
icon={icon} |
|
|
|
checkedIcon={checkedIcon} |
|
|
|
checked={selected} |
|
|
|
style={{ marginRight: 8 }} |
|
|
|
/> |
|
|
|
{option.label ?? option.name} |
|
|
|
</li> |
|
|
|
); |
|
|
|
}} |
|
|
|
renderTags={(tagValue, getTagProps) => { |
|
|
|
return tagValue.map((option, index) => ( |
|
|
|
<Chip {...getTagProps({ index })} key={option?.id} label={option.label ?? option.name} /> |
|
|
|
)) |
|
|
|
}} |
|
|
|
onChange={(event, value) => { |
|
|
|
field.onChange(value?.map(v => v.id)) |
|
|
|
}} |
|
|
|
renderInput={(params) => <TextField {...params} error={Boolean(formState.errors[name])} variant="outlined" label={label} />} |
|
|
|
/> |
|
|
|
: |
|
|
|
<Autocomplete |
|
|
|
disableClearable |
|
|
|
// disablePortal |
|
|
|
disabled={disabled} |
|
|
|
noOptionsText={noOptionsText ?? t("No Options")} |
|
|
|
value={options.find(option => option.id === field.value) ?? options[0]} |
|
|
|
options={options} |
|
|
|
getOptionLabel={(option) => option.label ?? option.name!!} |
|
|
|
isOptionEqualToValue={(option, value) => option?.id === value?.id} |
|
|
|
renderOption={(params, option) => { |
|
|
|
return ( |
|
|
|
<MenuItem {...params} key={option?.id} value={option.id}> |
|
|
|
{option.label ?? option.name} |
|
|
|
</MenuItem> |
|
|
|
); |
|
|
|
}} |
|
|
|
renderTags={(tagValue, getTagProps) => { |
|
|
|
return tagValue.map((option, index) => ( |
|
|
|
<Chip {...getTagProps({ index })} key={option?.id} label={option.label ?? option.name} /> |
|
|
|
)) |
|
|
|
}} |
|
|
|
onChange={(event, value) => { |
|
|
|
field.onChange(value?.id ?? null) |
|
|
|
}} |
|
|
|
renderInput={(params) => <TextField {...params} error={Boolean(formState.errors[name])} variant="outlined" label={label} />} |
|
|
|
/>) |
|
|
|
return ( |
|
|
|
<Controller |
|
|
|
name={name} |
|
|
|
control={control} |
|
|
|
rules={rules} |
|
|
|
render={({ field, fieldState, formState }) => { |
|
|
|
return isMultiple ? ( |
|
|
|
<Autocomplete |
|
|
|
multiple |
|
|
|
disableClearable |
|
|
|
disableCloseOnSelect |
|
|
|
// disablePortal |
|
|
|
disabled={disabled} |
|
|
|
noOptionsText={noOptionsText ?? t("No Options")} |
|
|
|
value={options.filter((option) => { |
|
|
|
return field.value?.includes(option.id); |
|
|
|
})} |
|
|
|
options={options} |
|
|
|
getOptionLabel={(option) => option.label ?? option.name!} |
|
|
|
isOptionEqualToValue={(option, value) => option.id === value.id} |
|
|
|
renderOption={(params, option, { selected }) => { |
|
|
|
return ( |
|
|
|
<li {...params} key={option?.id}> |
|
|
|
<Checkbox |
|
|
|
icon={icon} |
|
|
|
checkedIcon={checkedIcon} |
|
|
|
checked={selected} |
|
|
|
style={{ marginRight: 8 }} |
|
|
|
/> |
|
|
|
{option.label ?? option.name} |
|
|
|
</li> |
|
|
|
); |
|
|
|
}} |
|
|
|
renderTags={(tagValue, getTagProps) => { |
|
|
|
return tagValue.map((option, index) => ( |
|
|
|
<Chip |
|
|
|
{...getTagProps({ index })} |
|
|
|
key={option?.id} |
|
|
|
label={option.label ?? option.name} |
|
|
|
/> |
|
|
|
)); |
|
|
|
}} |
|
|
|
onChange={(event, value) => { |
|
|
|
field.onChange(value?.map((v) => v.id)); |
|
|
|
}} |
|
|
|
onBlur={field.onBlur} |
|
|
|
renderInput={(params) => ( |
|
|
|
<TextField |
|
|
|
{...params} |
|
|
|
error={Boolean(formState.errors[name])} |
|
|
|
variant="outlined" |
|
|
|
label={label} |
|
|
|
/> |
|
|
|
)} |
|
|
|
/> |
|
|
|
) : ( |
|
|
|
<Autocomplete |
|
|
|
disableClearable |
|
|
|
// disablePortal |
|
|
|
disabled={disabled} |
|
|
|
noOptionsText={noOptionsText ?? t("No Options")} |
|
|
|
value={ |
|
|
|
options.find((option) => option.id === field.value) ?? options[0] |
|
|
|
} |
|
|
|
options={options} |
|
|
|
getOptionLabel={(option) => option.label ?? option.name!} |
|
|
|
isOptionEqualToValue={(option, value) => option?.id === value?.id} |
|
|
|
renderOption={(params, option) => { |
|
|
|
return ( |
|
|
|
<MenuItem {...params} key={option?.id} value={option.id}> |
|
|
|
{option.label ?? option.name} |
|
|
|
</MenuItem> |
|
|
|
); |
|
|
|
}} |
|
|
|
renderTags={(tagValue, getTagProps) => { |
|
|
|
return tagValue.map((option, index) => ( |
|
|
|
<Chip |
|
|
|
{...getTagProps({ index })} |
|
|
|
key={option?.id} |
|
|
|
label={option.label ?? option.name} |
|
|
|
/> |
|
|
|
)); |
|
|
|
}} |
|
|
|
onChange={(event, value) => { |
|
|
|
field.onChange(value?.id ?? null); |
|
|
|
}} |
|
|
|
/> |
|
|
|
) |
|
|
|
onBlur={field.onBlur} |
|
|
|
renderInput={(params) => ( |
|
|
|
<TextField |
|
|
|
{...params} |
|
|
|
error={Boolean(formState.errors[name])} |
|
|
|
variant="outlined" |
|
|
|
label={label} |
|
|
|
/> |
|
|
|
)} |
|
|
|
/> |
|
|
|
); |
|
|
|
}} |
|
|
|
/> |
|
|
|
); |
|
|
|
} |
|
|
|
|
|
|
|
export default ControlledAutoComplete; |
|
|
|
export default ControlledAutoComplete; |