1414import React , { useState , useEffect , useMemo } from "react" ;
1515import PropTypes from "prop-types" ;
1616import TextField from "@mui/material/TextField" ;
17- import Autocomplete from "@mui/material/Autocomplete" ;
17+ import Autocomplete , { createFilterOptions } from "@mui/material/Autocomplete" ;
1818import CircularProgress from "@mui/material/CircularProgress" ;
1919import ExpandMoreIcon from "@mui/icons-material/ExpandMore" ;
2020import { useField } from "formik" ;
2121import { queryCompanies } from "../../../actions/company-actions" ;
2222import { DEBOUNCE_WAIT_250 } from "../../../utils/constants" ;
2323
24+ const filter = createFilterOptions ( ) ;
25+
2426const CompanyInputMUI = ( {
2527 id,
2628 name,
2729 placeholder,
2830 plainValue,
2931 isMulti = false ,
32+ allowCreate = false ,
3033 ...rest
3134} ) => {
3235 const [ field , meta , helpers ] = useField ( name ) ;
3336 const [ options , setOptions ] = useState ( [ ] ) ;
3437 const [ open , setOpen ] = useState ( false ) ;
3538 const [ inputValue , setInputValue ] = useState ( "" ) ;
3639 const [ loading , setLoading ] = useState ( false ) ;
40+ const [ isDebouncing , setIsDebouncing ] = useState ( false ) ;
3741
3842 const { value } = field ;
3943 const error = meta . touched && meta . error ;
@@ -44,6 +48,7 @@ const CompanyInputMUI = ({
4448 return ;
4549 }
4650
51+ setIsDebouncing ( false ) ;
4752 setLoading ( true ) ;
4853
4954 const normalize = ( results ) =>
@@ -60,11 +65,13 @@ const CompanyInputMUI = ({
6065
6166 useEffect ( ( ) => {
6267 if ( inputValue ) {
68+ setIsDebouncing ( true ) ;
6369 const delayDebounce = setTimeout ( ( ) => {
6470 fetchOptions ( inputValue ) ;
6571 } , DEBOUNCE_WAIT_250 ) ;
6672 return ( ) => clearTimeout ( delayDebounce ) ;
6773 }
74+ setIsDebouncing ( false ) ;
6875 } , [ inputValue ] ) ;
6976
7077 const selectedValue = useMemo ( ( ) => {
@@ -96,30 +103,66 @@ const CompanyInputMUI = ({
96103 } ) ) ;
97104 } else {
98105 theValue = plainValue
99- ? newValue . label
100- : { id : parseInt ( newValue . value ) , name : newValue . label } ;
106+ ? newValue . inputValue || newValue . label
107+ : {
108+ id : newValue . inputValue ? 0 : parseInt ( newValue . value ) ,
109+ name : newValue . inputValue || newValue . label
110+ } ;
101111 }
102112
103113 helpers . setValue ( theValue ) ;
104114 } ;
105115
116+ const handleFilterOptions = ( options , params ) => {
117+ const filtered = filter ( options , params ) ;
118+
119+ if ( ! allowCreate || loading || isDebouncing ) return filtered ;
120+
121+ const { inputValue } = params ;
122+ const isExisting = options . some (
123+ ( option ) => inputValue . toLowerCase ( ) === option . label . toLowerCase ( )
124+ ) ;
125+
126+ if ( inputValue !== "" && ! isExisting ) {
127+ filtered . push ( {
128+ inputValue,
129+ value : null ,
130+ label : `Create "${ inputValue } "`
131+ } ) ;
132+ }
133+ return filtered ;
134+ } ;
135+
136+ const getOptionLabel = ( option ) => {
137+ if ( option . inputValue ) {
138+ return option . inputValue ;
139+ }
140+ return option . label || "" ;
141+ } ;
142+
106143 return (
107144 < Autocomplete
108145 open = { open }
109146 onOpen = { ( ) => setOpen ( true ) }
110147 onClose = { ( ) => setOpen ( false ) }
111148 options = { options }
112149 value = { selectedValue }
113- getOptionLabel = { ( option ) => option . label }
150+ getOptionLabel = { getOptionLabel }
114151 isOptionEqualToValue = { ( option , value ) => option . value === value . value }
115152 onInputChange = { ( _ , newInputValue ) => {
116153 setInputValue ( newInputValue ) ;
117154 } }
155+ filterOptions = { handleFilterOptions }
118156 multiple = { isMulti }
119157 onChange = { handleChange }
120158 loading = { loading }
121159 fullWidth
122160 popupIcon = { < ExpandMoreIcon /> }
161+ renderOption = { ( props , option ) => (
162+ < li { ...props } key = { option . value ?? `create-${ option . inputValue } ` } >
163+ { option . label }
164+ </ li >
165+ ) }
123166 renderInput = { ( params ) => (
124167 < TextField
125168 { ...params }
@@ -156,7 +199,8 @@ CompanyInputMUI.propTypes = {
156199 name : PropTypes . string . isRequired ,
157200 placeholder : PropTypes . string ,
158201 plainValue : PropTypes . bool ,
159- isMulti : PropTypes . bool
202+ isMulti : PropTypes . bool ,
203+ allowCreate : PropTypes . bool
160204} ;
161205
162206export default CompanyInputMUI ;
0 commit comments