44 InputAdornment ,
55 MenuItem ,
66 TextField ,
7+ useTheme ,
78} from "@mui/material" ;
89import { useMapboxGeocoder } from "hooks/useMapboxGeocoder" ;
910import { useEffect , useState } from "react" ;
@@ -19,6 +20,7 @@ import {
1920import { useMapbox } from "../../hooks/useMapbox" ;
2021
2122export default function AddressDropDown ( { autoFocus } ) {
23+ const theme = useTheme ( ) ;
2224 const searchCoordinates = useSearchCoordinates ( ) ;
2325 const userCoordinates = useUserCoordinates ( ) ;
2426 const isWidget = useWidget ( ) ;
@@ -32,6 +34,39 @@ export default function AddressDropDown({ autoFocus }) {
3234 const { flyTo } = useMapbox ( ) ;
3335 const [ highlightedOption , setHighlightedOption ] = useState ( null ) ;
3436 const stakeholders = useStakeholders ( ) ;
37+
38+ const stakeholderOptions = stakeholders ?. map ( ( stakeholder ) => ( {
39+ type : "stakeholder" ,
40+ value : stakeholder ,
41+ } ) ) ;
42+ const mapboxOptions = mapboxResults . slice ( 0 , 10 ) . map ( ( result ) => ( {
43+ type : "mapbox" ,
44+ value : result ,
45+ } ) ) ;
46+
47+ const filteredStakeholders = stakeholderOptions . filter ( ( option ) => {
48+ if ( inputVal . trim ( ) . length === 0 ) return true ;
49+ return inputVal
50+ . toLowerCase ( )
51+ . split ( " " )
52+ . every ( ( word ) =>
53+ getNameAndAddress ( option . value ) . toLowerCase ( ) . includes ( word )
54+ ) ;
55+ } ) ;
56+
57+ // Only show "no match" message if user has typed input, loading is done, no results found,
58+ // AND the input doesn't match the currently selected location (to avoid showing message after selection)
59+ const showNoMatch =
60+ ! isLoading &&
61+ inputVal . trim ( ) . length > 0 &&
62+ mapboxOptions . length === 0 &&
63+ filteredStakeholders . length === 0 &&
64+ inputVal !== searchCoordinates ?. locationName ;
65+
66+ const combinedOptions = showNoMatch
67+ ? [ { type : "no-match" , value : null } ]
68+ : [ ...mapboxOptions , ...filteredStakeholders ] ;
69+
3570 useEffect ( ( ) => {
3671 if ( userCoordinates ) {
3772 setInputVal ( "" ) ;
@@ -144,6 +179,21 @@ export default function AddressDropDown({ autoFocus }) {
144179
145180 const renderOption = ( props , option ) => {
146181 const { id } = props ;
182+
183+ if ( option . type === "no-match" ) {
184+ return (
185+ < MenuItem
186+ { ...props }
187+ key = "no-match"
188+ component = "div"
189+ disabled
190+ sx = { { fontStyle : "italic" , color : "text.secondary" } }
191+ >
192+ No match found
193+ </ MenuItem >
194+ ) ;
195+ }
196+
147197 return (
148198 < MenuItem
149199 { ...props }
@@ -156,25 +206,16 @@ export default function AddressDropDown({ autoFocus }) {
156206 ) ;
157207 } ;
158208
159- const stakeholderOptions = stakeholders ?. map ( ( stakeholder ) => ( {
160- type : "stakeholder" ,
161- value : stakeholder ,
162- } ) ) ;
163- const mapboxOptions = mapboxResults . slice ( 0 , 10 ) . map ( ( result ) => ( {
164- type : "mapbox" ,
165- value : result ,
166- } ) ) ;
167-
168209 const handleKeyDown = ( event ) => {
169- if ( event . key === "Enter" && mapboxOptions . length > 0 && ! isLoading ) {
210+ if ( event . key === "Enter" && ! isLoading ) {
170211 event . preventDefault ( ) ;
171- const selected = highlightedOption ?? mapboxOptions [ 0 ] ;
172- handleAutocompleteOnChange ( selected ) ;
212+ if ( mapboxOptions . length > 0 ) {
213+ const selected = highlightedOption ?? mapboxOptions [ 0 ] ;
214+ handleAutocompleteOnChange ( selected ) ;
215+ }
173216 }
174217 } ;
175218
176- const combinedOptions = [ ...mapboxOptions , ...stakeholderOptions ] ;
177-
178219 return (
179220 < >
180221 < Autocomplete
@@ -194,22 +235,13 @@ export default function AddressDropDown({ autoFocus }) {
194235 setInputVal ( newValue ? getOptionLabel ( newValue ) : "" ) ;
195236 } }
196237 options = { combinedOptions }
197- groupBy = { ( option ) =>
198- option . type === "stakeholder" ? "Organizations" : "Addresses"
199- }
238+ groupBy = { ( option ) => {
239+ if ( option . type === "no-match" ) return "" ;
240+ return option . type === "stakeholder" ? "Organizations" : "Addresses" ;
241+ } }
200242 getOptionLabel = { getOptionLabel }
201- filterOptions = { ( options , { inputValue } ) => {
202- return options . filter ( ( option ) => {
203- if ( option . type === "stakeholder" ) {
204- return inputValue
205- . toLowerCase ( )
206- . split ( " " )
207- . every ( ( word ) =>
208- getNameAndAddress ( option . value ) . toLowerCase ( ) . includes ( word )
209- ) ;
210- }
211- return true ; // For mapbox results, we don't filter by inputValue
212- } ) ;
243+ filterOptions = { ( options ) => {
244+ return options ;
213245 } }
214246 sx = { {
215247 width : 600 ,
@@ -234,8 +266,13 @@ export default function AddressDropDown({ autoFocus }) {
234266 } ,
235267 } }
236268 ListboxProps = { {
237- style : {
269+ sx : {
238270 maxHeight : "160px" ,
271+ "& .MuiAutocomplete-groupLabel" : {
272+ backgroundColor : theme . palette . filterButtons . backgroundColor ,
273+ color : theme . palette . headingText . main ,
274+ fontWeight : 600 ,
275+ } ,
239276 } ,
240277 } }
241278 renderInput = { ( params ) => renderInput ( params ) }
@@ -255,6 +292,9 @@ function getNameAndAddress(stakeholder) {
255292}
256293
257294export function getOptionLabel ( option ) {
295+ if ( option . type === "no-match" ) {
296+ return "No match found" ;
297+ }
258298 if ( option . type === "stakeholder" ) {
259299 return getNameAndAddress ( option . value ) ;
260300 }
0 commit comments