diff --git a/src/sidebar/search/AddressInput.module.css b/src/sidebar/search/AddressInput.module.css index 297eed18..a9dc9e87 100644 --- a/src/sidebar/search/AddressInput.module.css +++ b/src/sidebar/search/AddressInput.module.css @@ -5,6 +5,17 @@ scale: 0.7; } +.btnCurrentLocation { + padding: 0 7px 0 5px; + color: grey; + width: 32px; +} + +.btnCurrentLocation > svg { + height: 100%; + width: 100%; +} + .btnClose { display: none; } diff --git a/src/sidebar/search/AddressInput.tsx b/src/sidebar/search/AddressInput.tsx index 12711c11..5c83fe41 100644 --- a/src/sidebar/search/AddressInput.tsx +++ b/src/sidebar/search/AddressInput.tsx @@ -1,17 +1,13 @@ import { ReactNode, useCallback, useEffect, useRef, useState } from 'react' import { Coordinate, getBBoxFromCoord, QueryPoint, QueryPointType } from '@/stores/QueryStore' import { Bbox, GeocodingHit, ReverseGeocodingHit } from '@/api/graphhopper' -import Autocomplete, { - AutocompleteItem, - GeocodingItem, - POIQueryItem, - SelectCurrentLocationItem, -} from '@/sidebar/search/AddressInputAutocomplete' +import Autocomplete, { AutocompleteItem, GeocodingItem, POIQueryItem } from '@/sidebar/search/AddressInputAutocomplete' import ArrowBack from './arrow_back.svg' import Cross from '@/sidebar/times-solid-thin.svg' +import CurrentLocationIcon from './current-location.svg' import styles from './AddressInput.module.css' -import Api, { ApiImpl, getApi } from '@/api/Api' +import Api, { getApi } from '@/api/Api' import { tr } from '@/translation/Translation' import { coordinateToText, hitToItem, nominatimHitToItem, textToCoordinate } from '@/Converters' import { useMediaQuery } from 'react-responsive' @@ -19,7 +15,6 @@ import PopUp from '@/sidebar/search/PopUp' import PlainButton from '@/PlainButton' import { onCurrentLocationSelected } from '@/map/MapComponent' import { toLonLat, transformExtent } from 'ol/proj' -import { calcDist } from '@/distUtils' import { Map } from 'ol' import { AddressParseResult } from '@/pois/AddressParseResult' import { getMap } from '@/map/map' @@ -79,17 +74,6 @@ export default function AddressInput(props: AddressInputProps) { // if item is selected we need to clear the autocompletion list useEffect(() => setAutocompleteItems([]), [props.point]) - // if no items but input is selected show current location item - useEffect(() => { - if (hasFocus && text.length == 0 && autocompleteItems.length === 0) - setAutocompleteItems([new SelectCurrentLocationItem()]) - }, [autocompleteItems, hasFocus]) - - function hideSuggestions() { - geocoder.cancel() - setOrigAutocompleteItems(autocompleteItems) - setAutocompleteItems([]) - } // highlighted result of geocoding results. Keep track which index is highlighted and change things on ArrowUp and Down // on Enter select highlighted result or the 0th if nothing is highlighted @@ -104,12 +88,9 @@ export default function AddressInput(props: AddressInputProps) { const onKeypress = useCallback( (event: React.KeyboardEvent) => { - const inputElement = event.target as HTMLInputElement if (event.key === 'Escape') { - // onBlur is deactivated for mobile so force: - setHasFocus(false) setText(origText) - hideSuggestions() + searchInput.current!.blur() return } @@ -146,7 +127,7 @@ export default function AddressInput(props: AddressInputProps) { if (item instanceof POIQueryItem) { handlePoiSearch(poiSearch, item.result, props.map) props.onAddressSelected(item.result.text(item.result.poi), undefined) - } else if (highlightedResult < 0) { + } else if (highlightedResult < 0 && !props.point.isInitialized) { // by default use the first result, otherwise the highlighted one getApi() .geocode(text, 'nominatim') @@ -163,9 +144,8 @@ export default function AddressInput(props: AddressInputProps) { props.onAddressSelected(item.toText(), item.point) } } - // onBlur is deactivated for mobile so force: - setHasFocus(false) - hideSuggestions() + // do not disturb 'tab' cycle + if (event.key == 'Enter') searchInput.current!.blur() break } }, @@ -180,6 +160,9 @@ export default function AddressInput(props: AddressInputProps) { const lonlat = toLonLat(getMap().getView().getCenter()!) const biasCoord = { lng: lonlat[0], lat: lonlat[1] } + // do not focus on mobile as we would hide the map with the "input"-view + const focusFirstInput = props.index == 0 && !isSmallScreen + return (
{ - setHasFocus(false) - hideSuggestions() - }} + onMouseDown={ + e => e.preventDefault() // prevents that input->onBlur is called when just "mouse down" event (lose focus only for onClick) + } + onClick={() => searchInput.current!.blur()} > @@ -207,6 +190,7 @@ export default function AddressInput(props: AddressInputProps) { style={props.moveStartIndex == props.index ? { borderWidth: '2px', margin: '-1px' } : {}} className={styles.input} type="text" + autoFocus={focusFirstInput} ref={searchInput} autoComplete="off" onChange={e => { @@ -223,7 +207,10 @@ export default function AddressInput(props: AddressInputProps) { if (origAutocompleteItems.length > 0) setAutocompleteItems(origAutocompleteItems) }} onBlur={() => { - if (!isSmallScreen) hideSuggestions() // see #398 + setHasFocus(false) + geocoder.cancel() + setOrigAutocompleteItems(autocompleteItems) + setAutocompleteItems([]) }} value={text} placeholder={tr( @@ -232,17 +219,38 @@ export default function AddressInput(props: AddressInputProps) { /> { + onMouseDown={ + e => e.preventDefault() // prevents that input->onBlur is called when clicking the button (would hide this button and prevent onClick) + } + onClick={e => { setText('') props.onChange('') + // if we clear the text without focus then explicitly request it to improve usability: searchInput.current!.focus() }} > + e.preventDefault() // prevents that input->onBlur is called when clicking the button (would hide this button and prevent onClick) + } + onClick={() => { + onCurrentLocationSelected(props.onAddressSelected) + // but when clicked => we want to lose the focus e.g. to close mobile-input view + searchInput.current!.blur() + }} + > + + + {autocompleteItems.length > 0 && ( { - setHasFocus(false) if (item instanceof GeocodingItem) { - hideSuggestions() props.onAddressSelected(item.toText(), item.point) - } else if (item instanceof SelectCurrentLocationItem) { - hideSuggestions() - onCurrentLocationSelected(props.onAddressSelected) } else if (item instanceof POIQueryItem) { - hideSuggestions() handlePoiSearch(poiSearch, item.result, props.map) setText(item.result.text(item.result.poi)) } - searchInput.current!.blur() + searchInput.current!.blur() // see also AutocompleteEntry->onMouseDown }} /> diff --git a/src/sidebar/search/AddressInputAutocomplete.module.css b/src/sidebar/search/AddressInputAutocomplete.module.css index c3ffe51c..6fa88ed9 100644 --- a/src/sidebar/search/AddressInputAutocomplete.module.css +++ b/src/sidebar/search/AddressInputAutocomplete.module.css @@ -30,23 +30,6 @@ background-color: #c6c6c6; } -.currentLocationEntry { - display: flex; - flex-direction: row; - align-items: center; - gap: 0.5rem; - margin: 0.5rem 0.5rem; -} - -.currentLocationIcon { - width: 1.2rem; -} - -.currentLocationIcon > svg { - height: 100%; - width: 100%; -} - .poiEntry { padding: 0.5em 0; display: flex; diff --git a/src/sidebar/search/AddressInputAutocomplete.tsx b/src/sidebar/search/AddressInputAutocomplete.tsx index 7f0e0329..292a8cf7 100644 --- a/src/sidebar/search/AddressInputAutocomplete.tsx +++ b/src/sidebar/search/AddressInputAutocomplete.tsx @@ -1,6 +1,4 @@ import styles from './AddressInputAutocomplete.module.css' -import CurrentLocationIcon from './current-location.svg' -import { tr } from '@/translation/Translation' import { Bbox } from '@/api/graphhopper' import { AddressParseResult } from '@/pois/AddressParseResult' @@ -24,8 +22,6 @@ export class GeocodingItem implements AutocompleteItem { } } -export class SelectCurrentLocationItem implements AutocompleteItem {} - export class POIQueryItem implements AutocompleteItem { result: AddressParseResult @@ -55,8 +51,6 @@ export default function Autocomplete({ items, highlightedItem, onSelect }: Autoc function mapToComponent(item: AutocompleteItem, isHighlighted: boolean, onSelect: (hit: AutocompleteItem) => void) { if (item instanceof GeocodingItem) return - else if (item instanceof SelectCurrentLocationItem) - return else if (item instanceof POIQueryItem) return else throw Error('Unsupported item type: ' + typeof item) @@ -82,27 +76,6 @@ export function POIQueryEntry({ ) } -export function SelectCurrentLocation({ - item, - isHighlighted, - onSelect, -}: { - item: SelectCurrentLocationItem - isHighlighted: boolean - onSelect: (item: SelectCurrentLocationItem) => void -}) { - return ( - onSelect(item)}> -
-
- -
- {tr('current_location')} -
-
- ) -} - function GeocodingEntry({ item, isHighlighted, @@ -137,12 +110,15 @@ function AutocompleteEntry({ className={className} // using click events for mouse interaction and touch end to select an entry. onClick={() => onSelect()} + // minor workaround to improve success rate for click even if start and end location on screen are slightly different onTouchEnd={e => { e.preventDefault() // do not forward click to underlying component onSelect() }} onMouseDown={e => { - e.preventDefault() // prevent blur event for our input, see #398 + // prevents that input->onBlur is called when clicking the autocomplete item (focus would be lost and autocomplete items would disappear before they can be clicked) + // See also the onMouseDown calls in the buttons in AddressInput.tsx created for the same reason. + e.preventDefault() }} > {children} diff --git a/src/sidebar/search/Search.tsx b/src/sidebar/search/Search.tsx index d2d8bf8a..9b3b7037 100644 --- a/src/sidebar/search/Search.tsx +++ b/src/sidebar/search/Search.tsx @@ -92,13 +92,6 @@ const SearchBox = ({ }) => { const point = points[index] - // With this ref and tabIndex=-1 we ensure that the first 'TAB' gives the focus the first input but the marker won't be included in the TAB sequence, #194 - const myMarkerRef = useRef(null) - - useEffect(() => { - if (index == 0) myMarkerRef.current?.focus() - }, []) - function onClickOrDrop() { onDropPreviewSelect(-1) const newIndex = moveStartIndex < index ? index + 1 : index @@ -113,8 +106,6 @@ const SearchBox = ({ <> {(moveStartIndex < 0 || moveStartIndex == index) && (