import { useCallback , isValidElement } from 'react' ; import set from 'lodash/set' ; import useChoices , { OptionText , UseChoicesOptions } from './useChoices' ; import { useTranslate } from '../i18n' ; const useSuggestions = ( { allowCreate , allowDuplicates , allowEmpty , choices , createText = 'ra.action.create' , createValue = '@@create' , emptyText = '' , emptyValue = null , limitChoicesToValue , matchSuggestion , optionText , optionValue , selectedItem , suggestionLimit = 0 , translateChoice , } ) => { const translate = useTranslate ( ) ; const { getChoiceText , getChoiceValue } = useChoices ( { optionText , optionValue , translateChoice , } ) ; const getSuggestions = useCallback ( getSuggestionsFactory ( { allowCreate , allowDuplicates , allowEmpty , choices , createText , createValue , emptyText : translate ( emptyText , { _ : emptyText } ) , emptyValue , getChoiceText , getChoiceValue , limitChoicesToValue , matchSuggestion , optionText , optionValue , selectedItem , suggestionLimit , } ) , [ allowCreate , allowDuplicates , allowEmpty , choices , createText , createValue , emptyText , emptyValue , getChoiceText , getChoiceValue , limitChoicesToValue , matchSuggestion , optionText , optionValue , selectedItem , suggestionLimit , translate , ] ) ; return { getChoiceText , getChoiceValue , getSuggestions , } ; } ; export default useSuggestions ; const escapeRegExp = value => value ? value . replace ( /[.*+?^${}()|[\]\\]/g , '\\$&' ) : '' ; export interface UseSuggestionsOptions extends UseChoicesOptions { allowCreate ? : boolean ; allowDuplicates ? : boolean ; allowEmpty ? : boolean ; choices : any [ ] ; createText ? : string ; createValue ? : any ; emptyText ? : string ; emptyValue ? : any ; limitChoicesToValue ? : boolean ; matchSuggestion ? : ( filter : string , suggestion : any , exact ? : boolean ) => boolean ; suggestionLimit ? : number ; selectedItem ? : any | any [ ] ; } /**
 * Default matcher implementation which check whether the suggestion text matches the filter.
 */ const defaultMatchSuggestion = getChoiceText => ( filter , suggestion , exact = false ) => { const suggestionText = getChoiceText ( suggestion ) ; const isReactElement = isValidElement ( suggestionText ) ; const regex = escapeRegExp ( filter ) ; return isReactElement ? false : suggestionText && suggestionText . match ( new RegExp ( exact ? `^${ regex }$` : regex , 'i' ) ) ; } ; example export const getSuggestionsFactory = ( { allowCreate = false , allowDuplicates = false , allowEmpty = false , choices = [ ] , createText = 'ra.action.create' , createValue = '@@create' , emptyText = '' , emptyValue = null , optionText = 'name' , optionValue = 'id' , getChoiceText , getChoiceValue , limitChoicesToValue = false , matchSuggestion = defaultMatchSuggestion ( getChoiceText ) , selectedItem , suggestionLimit = 0 , } ) => filter => { let suggestions = [ ] ; if ( selectedItem && ! Array . isArray ( selectedItem ) && matchSuggestion ( filter , selectedItem ) ) { if ( limitChoicesToValue ) { suggestions = choices . filter ( choice => getChoiceValue ( choice ) === getChoiceValue ( selectedItem ) ) ; } else if ( ! allowDuplicates ) { suggestions = removeAlreadySelectedSuggestions ( choices , selectedItem , getChoiceValue ) ; } else { suggestions = choices ; } } else { suggestions = choices . filter ( choice => matchSuggestion ( filter , choice ) ) ; if ( ! allowDuplicates ) { suggestions = removeAlreadySelectedSuggestions ( suggestions , selectedItem , getChoiceValue ) ; } } suggestions = limitSuggestions ( suggestions , suggestionLimit ) ; const hasExactMatch = suggestions . some ( suggestion => matchSuggestion ( filter , suggestion , true ) ) ; if ( allowCreate ) { const filterIsSelectedItem = ! ! selectedItem && ! Array . isArray ( selectedItem ) ? matchSuggestion ( filter , selectedItem , true ) : false ; if ( ! hasExactMatch && ! filterIsSelectedItem ) { suggestions . push ( getSuggestion ( { optionText , optionValue , text : createText , value : createValue , } ) ) ; } } if ( allowEmpty ) { suggestions . unshift ( getSuggestion ( { optionText , optionValue , text : emptyText , value : emptyValue , } ) ) ; } return suggestions ; } ; example param suggestions param selectedItems param getChoiceValue const removeAlreadySelectedSuggestions = ( suggestions , selectedItems , getChoiceValue ) => { if ( ! selectedItems ) { return suggestions ; } const selectedValues = Array . isArray ( selectedItems ) ? selectedItems . map ( getChoiceValue ) : [ getChoiceValue ( selectedItems ) ] ; return suggestions . filter ( suggestion => ! selectedValues . includes ( getChoiceValue ( suggestion ) ) ) ; } ; example param suggestions param limit const limitSuggestions = ( suggestions , limit = 0 ) => Number . isInteger ( limit ) && limit > 0 ? suggestions . slice ( 0 , limit ) : suggestions ; param suggestions param options param options . optionText const getSuggestion = ( { optionText = 'name' , optionValue = 'id' , text = '' , value = null , } ) => { const suggestion = { } ; set ( suggestion , optionValue , value ) ; if ( typeof optionText === 'string' ) { set ( suggestion , optionText , text ) ; } return suggestion ; } ;