@@ -114,17 +114,24 @@ export interface IProps<T>
114114 * Messages for the Autocomplete.
115115 */
116116 messages ?: {
117+ addCustomValue ?: string ;
117118 listCount ?: string ;
118119 listCountPlural ?: string ;
119120 loading : string ;
120121 notFound : string ;
122+ setAsHTML ?: boolean ;
121123 } ;
122124
123125 /**
124126 * Callback for when the active state changes (controlled).
125127 */
126128 onActiveChange ?: InternalDispatch < boolean > ;
127129
130+ /**
131+ * Callback called when an item is added to the autocomplete list.
132+ */
133+ onAddNewItem ?: ( val : T ) => void ;
134+
128135 /**
129136 * Callback called when input value changes (controlled).
130137 */
@@ -189,10 +196,12 @@ function hasItem<T extends Item>(
189196const ESCAPE_REGEXP = / [ . * + ? ^ $ { } ( ) | [ \] \\ ] / g;
190197
191198const defaultMessages = {
199+ addCustomValue : 'Add {0}' ,
192200 listCount : '{0} option available.' ,
193201 listCountPlural : '{0} options available.' ,
194202 loading : 'Loading...' ,
195203 notFound : 'No results found' ,
204+ setAsHTML : false ,
196205} ;
197206
198207function AutocompleteInner < T extends Item > (
@@ -214,6 +223,7 @@ function AutocompleteInner<T extends Item>(
214223 menuTrigger = 'input' ,
215224 messages,
216225 onActiveChange,
226+ onAddNewItem,
217227 onChange,
218228 onItemsChange,
219229 onLoadMore,
@@ -227,7 +237,7 @@ function AutocompleteInner<T extends Item>(
227237 ...( messages ?? { } ) ,
228238 } ;
229239
230- const [ items , , isItemsUncontrolled ] = useControlledState ( {
240+ const [ items , setItems , isItemsUncontrolled ] = useControlledState ( {
231241 defaultName : 'defaultItems' ,
232242 defaultValue : defaultItems ,
233243 handleName : 'onItemsChange' ,
@@ -301,9 +311,8 @@ function AutocompleteInner<T extends Item>(
301311 } , [ items ] ) ;
302312
303313 useEffect ( ( ) => {
304- // Does not update state on first render, if the custom value is allowed
305- // or if the value is empty.
306- if ( isFirst || allowsCustomValue || ! value ) {
314+ // Does not update state on first render, if the value is empty.
315+ if ( isFirst || ! value ) {
307316 return ;
308317 }
309318
@@ -352,6 +361,8 @@ function AutocompleteInner<T extends Item>(
352361 } ) ;
353362 } , [ debouncedLoadingChange , isItemsUncontrolled , items , filterFn ] ) ;
354363
364+ const [ activeDescendant , setActiveDescendant ] = useState < React . Key > ( '' ) ;
365+
355366 const virtualizer = useVirtual ( {
356367 estimateSize : 37 ,
357368 items : filteredItems ,
@@ -392,6 +403,7 @@ function AutocompleteInner<T extends Item>(
392403 setActive ( false ) ;
393404
394405 currentItemSelected . current = itemValue ;
406+
395407 setValue ( itemValue ) ;
396408
397409 shouldIgnoreOpenMenuOnFocus . current = true ;
@@ -405,19 +417,24 @@ function AutocompleteInner<T extends Item>(
405417 items : filteredItems ,
406418 notFound : (
407419 < DropDown . Item
408- aria-disabled = "true"
409- className = "disabled"
420+ disabled = { allowsCustomValue ? false : true }
410421 roleItem = "option"
411422 >
412- { messages . notFound }
423+ { allowsCustomValue && messages . addCustomValue ? (
424+ < span
425+ dangerouslySetInnerHTML = { {
426+ __html : sub ( messages . addCustomValue , [ value ] ) ,
427+ } }
428+ />
429+ ) : (
430+ messages . notFound
431+ ) }
413432 </ DropDown . Item >
414433 ) ,
415434 suppressTextValueWarning : false ,
416435 virtualizer : items ? virtualizer : undefined ,
417436 } ) ;
418437
419- const [ activeDescendant , setActiveDescendant ] = useState < React . Key > ( '' ) ;
420-
421438 useOverlayPosition (
422439 {
423440 alignmentByViewport : true ,
@@ -554,6 +571,16 @@ function AutocompleteInner<T extends Item>(
554571 break ;
555572 }
556573 case Keys . Enter : {
574+ if ( allowsCustomValue && items && onAddNewItem ) {
575+ if ( value !== '' ) {
576+ if ( ! hasItem ( items , value , filterKey ) && isItemsUncontrolled ) {
577+ setItems ( [ ...items , value ] . sort ( ) ) ;
578+ }
579+
580+ onAddNewItem ( value as T ) ;
581+ }
582+ }
583+
557584 setActive ( false ) ;
558585
559586 if ( active && activeDescendant ) {
@@ -563,6 +590,7 @@ function AutocompleteInner<T extends Item>(
563590 if ( ! active && event . key === Keys . Esc ) {
564591 setValue ( '' ) ;
565592 }
593+
566594 break ;
567595 }
568596 case Keys . Home :
@@ -607,6 +635,7 @@ function AutocompleteInner<T extends Item>(
607635 }
608636
609637 navigationProps . onKeyDown ( event ) ;
638+
610639 break ;
611640 }
612641 default :
@@ -657,6 +686,7 @@ function AutocompleteInner<T extends Item>(
657686 onLoadMore = { onLoadMore }
658687 role = "listbox"
659688 >
689+ { activeDescendant }
660690 { debouncedLoadingChange ? (
661691 < DropDown . Item
662692 aria-disabled = "true"
0 commit comments