Skip to content

Commit 7ace38f

Browse files
committed
merged master
2 parents 674a473 + f7d073f commit 7ace38f

File tree

13 files changed

+497
-500
lines changed

13 files changed

+497
-500
lines changed

config.js

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -31,7 +31,6 @@ const config = {
3131
'track_type',
3232
'country',
3333
],
34-
snapPreventions: ['ferry'],
3534
},
3635

3736
// Use 'profiles' to define which profiles are visible and how. Useful if the /info endpoint contains too many or too "ugly" profile

package-lock.json

Lines changed: 432 additions & 394 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

package.json

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -21,8 +21,8 @@
2121
"custom-model-editor": "github:graphhopper/custom-model-editor#3a46b6981d170b7eb70d621bbb92caed149e5a97",
2222
"geojson": "^0.5.0",
2323
"heightgraph": "github:easbar/Leaflet.Heightgraph#5f4f0b1fff3646aa071981381f5955c9e6f111f0",
24-
"ol": "9.2.3",
25-
"ol-mapbox-style": "12.3.2",
24+
"ol": "10.2.1",
25+
"ol-mapbox-style": "12.3.5",
2626
"react": "^18.2.0",
2727
"react-dom": "^18.2.0",
2828
"react-responsive": "^9.0.0"

src/api/Api.ts

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -283,11 +283,12 @@ export class ApiImpl implements Api {
283283
locale: getTranslation().getLang(),
284284
points_encoded: true,
285285
points_encoded_multiplier: 1e6,
286-
snap_preventions: config.request?.snapPreventions ? config.request.snapPreventions : [],
287286
...profileConfig,
288287
details: details,
289288
}
290289

290+
if (config.request?.snapPreventions) request.snap_preventions = config.request?.snapPreventions
291+
291292
if (args.customModel) {
292293
request['ch.disable'] = true
293294
request.custom_model = args.customModel

src/layers/UseQueryPointsLayer.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -85,7 +85,7 @@ function removeDragInteractions(map: Map) {
8585
.forEach(i => map.removeInteraction(i))
8686
}
8787

88-
function addDragInteractions(map: Map, queryPointsLayer: VectorLayer<Feature<Geometry>>) {
88+
function addDragInteractions(map: Map, queryPointsLayer: VectorLayer<VectorSource<Feature<Geometry>>>) {
8989
let tmp = queryPointsLayer.getSource()
9090
if (tmp == null) throw new Error('source must not be null') // typescript requires this
9191
const modify = new Modify({

src/sidebar/search/AddressInput.module.css

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,17 @@
55
scale: 0.7;
66
}
77

8+
.btnCurrentLocation {
9+
padding: 0 7px 0 5px;
10+
color: grey;
11+
width: 32px;
12+
}
13+
14+
.btnCurrentLocation > svg {
15+
height: 100%;
16+
width: 100%;
17+
}
18+
819
.btnClose {
920
display: none;
1021
}

src/sidebar/search/AddressInput.tsx

Lines changed: 42 additions & 41 deletions
Original file line numberDiff line numberDiff line change
@@ -1,17 +1,13 @@
11
import { ReactNode, useCallback, useEffect, useRef, useState } from 'react'
22
import { Coordinate, getBBoxFromCoord, QueryPoint, QueryPointType } from '@/stores/QueryStore'
33
import { Bbox, GeocodingHit, ReverseGeocodingHit } from '@/api/graphhopper'
4-
import Autocomplete, {
5-
AutocompleteItem,
6-
GeocodingItem,
7-
POIQueryItem,
8-
SelectCurrentLocationItem,
9-
} from '@/sidebar/search/AddressInputAutocomplete'
4+
import Autocomplete, { AutocompleteItem, GeocodingItem, POIQueryItem } from '@/sidebar/search/AddressInputAutocomplete'
105

116
import ArrowBack from './arrow_back.svg'
127
import Cross from '@/sidebar/times-solid-thin.svg'
8+
import CurrentLocationIcon from './current-location.svg'
139
import styles from './AddressInput.module.css'
14-
import Api, { ApiImpl, getApi } from '@/api/Api'
10+
import Api, { getApi } from '@/api/Api'
1511
import { tr } from '@/translation/Translation'
1612
import { coordinateToText, hitToItem, nominatimHitToItem, textToCoordinate } from '@/Converters'
1713
import { useMediaQuery } from 'react-responsive'
@@ -78,17 +74,6 @@ export default function AddressInput(props: AddressInputProps) {
7874

7975
// if item is selected we need to clear the autocompletion list
8076
useEffect(() => setAutocompleteItems([]), [props.point])
81-
// if no items but input is selected show current location item
82-
useEffect(() => {
83-
if (hasFocus && text.length == 0 && autocompleteItems.length === 0)
84-
setAutocompleteItems([new SelectCurrentLocationItem()])
85-
}, [autocompleteItems, hasFocus])
86-
87-
function hideSuggestions() {
88-
geocoder.cancel()
89-
setOrigAutocompleteItems(autocompleteItems)
90-
setAutocompleteItems([])
91-
}
9277

9378
// highlighted result of geocoding results. Keep track which index is highlighted and change things on ArrowUp and Down
9479
// on Enter select highlighted result or the 0th if nothing is highlighted
@@ -103,13 +88,9 @@ export default function AddressInput(props: AddressInputProps) {
10388

10489
const onKeypress = useCallback(
10590
(event: React.KeyboardEvent<HTMLInputElement>) => {
106-
const inputElement = event.target as HTMLInputElement
10791
if (event.key === 'Escape') {
108-
inputElement.blur()
109-
// onBlur is deactivated for mobile so force:
110-
setHasFocus(false)
11192
setText(origText)
112-
hideSuggestions()
93+
searchInput.current!.blur()
11394
return
11495
}
11596

@@ -146,7 +127,7 @@ export default function AddressInput(props: AddressInputProps) {
146127
if (item instanceof POIQueryItem) {
147128
handlePoiSearch(poiSearch, item.result, props.map)
148129
props.onAddressSelected(item.result.text(item.result.poi), undefined)
149-
} else if (highlightedResult < 0) {
130+
} else if (highlightedResult < 0 && !props.point.isInitialized) {
150131
// by default use the first result, otherwise the highlighted one
151132
getApi()
152133
.geocode(text, 'nominatim')
@@ -163,10 +144,8 @@ export default function AddressInput(props: AddressInputProps) {
163144
props.onAddressSelected(item.toText(), item.point)
164145
}
165146
}
166-
inputElement.blur()
167-
// onBlur is deactivated for mobile so force:
168-
setHasFocus(false)
169-
hideSuggestions()
147+
// do not disturb 'tab' cycle
148+
if (event.key == 'Enter') searchInput.current!.blur()
170149
break
171150
}
172151
},
@@ -181,6 +160,9 @@ export default function AddressInput(props: AddressInputProps) {
181160
const lonlat = toLonLat(getMap().getView().getCenter()!)
182161
const biasCoord = { lng: lonlat[0], lat: lonlat[1] }
183162

163+
// do not focus on mobile as we would hide the map with the "input"-view
164+
const focusFirstInput = props.index == 0 && !isSmallScreen
165+
184166
return (
185167
<div className={containerClass}>
186168
<div
@@ -197,17 +179,18 @@ export default function AddressInput(props: AddressInputProps) {
197179
>
198180
<PlainButton
199181
className={styles.btnClose}
200-
onClick={() => {
201-
setHasFocus(false)
202-
hideSuggestions()
203-
}}
182+
onMouseDown={
183+
e => e.preventDefault() // prevents that input->onBlur is called when just "mouse down" event (lose focus only for onClick)
184+
}
185+
onClick={() => searchInput.current!.blur()}
204186
>
205187
<ArrowBack />
206188
</PlainButton>
207189
<input
208190
style={props.moveStartIndex == props.index ? { borderWidth: '2px', margin: '-1px' } : {}}
209191
className={styles.input}
210192
type="text"
193+
autoFocus={focusFirstInput}
211194
ref={searchInput}
212195
autoComplete="off"
213196
onChange={e => {
@@ -224,7 +207,10 @@ export default function AddressInput(props: AddressInputProps) {
224207
if (origAutocompleteItems.length > 0) setAutocompleteItems(origAutocompleteItems)
225208
}}
226209
onBlur={() => {
227-
if (!isSmallScreen) hideSuggestions() // see #398
210+
setHasFocus(false)
211+
geocoder.cancel()
212+
setOrigAutocompleteItems(autocompleteItems)
213+
setAutocompleteItems([])
228214
}}
229215
value={text}
230216
placeholder={tr(
@@ -233,17 +219,38 @@ export default function AddressInput(props: AddressInputProps) {
233219
/>
234220

235221
<PlainButton
222+
tabIndex={-1}
236223
style={text.length == 0 ? { display: 'none' } : {}}
237224
className={styles.btnInputClear}
238-
onClick={() => {
225+
onMouseDown={
226+
e => e.preventDefault() // prevents that input->onBlur is called when clicking the button (would hide this button and prevent onClick)
227+
}
228+
onClick={e => {
239229
setText('')
240230
props.onChange('')
231+
// if we clear the text without focus then explicitly request it to improve usability:
241232
searchInput.current!.focus()
242233
}}
243234
>
244235
<Cross />
245236
</PlainButton>
246237

238+
<PlainButton
239+
tabIndex={-1}
240+
style={text.length == 0 && hasFocus ? {} : { display: 'none' }}
241+
className={styles.btnCurrentLocation}
242+
onMouseDown={
243+
e => e.preventDefault() // prevents that input->onBlur is called when clicking the button (would hide this button and prevent onClick)
244+
}
245+
onClick={() => {
246+
onCurrentLocationSelected(props.onAddressSelected)
247+
// but when clicked => we want to lose the focus e.g. to close mobile-input view
248+
searchInput.current!.blur()
249+
}}
250+
>
251+
<CurrentLocationIcon />
252+
</PlainButton>
253+
247254
{autocompleteItems.length > 0 && (
248255
<ResponsiveAutocomplete
249256
inputRef={searchInputContainer.current!}
@@ -254,19 +261,13 @@ export default function AddressInput(props: AddressInputProps) {
254261
items={autocompleteItems}
255262
highlightedItem={autocompleteItems[highlightedResult]}
256263
onSelect={item => {
257-
setHasFocus(false)
258264
if (item instanceof GeocodingItem) {
259-
hideSuggestions()
260265
props.onAddressSelected(item.toText(), item.point)
261-
} else if (item instanceof SelectCurrentLocationItem) {
262-
hideSuggestions()
263-
onCurrentLocationSelected(props.onAddressSelected)
264266
} else if (item instanceof POIQueryItem) {
265-
hideSuggestions()
266267
handlePoiSearch(poiSearch, item.result, props.map)
267268
setText(item.result.text(item.result.poi))
268269
}
269-
searchInput.current!.blur()
270+
searchInput.current!.blur() // see also AutocompleteEntry->onMouseDown
270271
}}
271272
/>
272273
</ResponsiveAutocomplete>

src/sidebar/search/AddressInputAutocomplete.module.css

Lines changed: 0 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -30,23 +30,6 @@
3030
background-color: #c6c6c6;
3131
}
3232

33-
.currentLocationEntry {
34-
display: flex;
35-
flex-direction: row;
36-
align-items: center;
37-
gap: 0.5rem;
38-
margin: 0.5rem 0.5rem;
39-
}
40-
41-
.currentLocationIcon {
42-
width: 1.2rem;
43-
}
44-
45-
.currentLocationIcon > svg {
46-
height: 100%;
47-
width: 100%;
48-
}
49-
5033
.poiEntry {
5134
padding: 0.5em 0;
5235
display: flex;

src/sidebar/search/AddressInputAutocomplete.tsx

Lines changed: 4 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,4 @@
11
import styles from './AddressInputAutocomplete.module.css'
2-
import CurrentLocationIcon from './current-location.svg'
3-
import { tr } from '@/translation/Translation'
42
import { Bbox } from '@/api/graphhopper'
53
import { AddressParseResult } from '@/pois/AddressParseResult'
64

@@ -24,8 +22,6 @@ export class GeocodingItem implements AutocompleteItem {
2422
}
2523
}
2624

27-
export class SelectCurrentLocationItem implements AutocompleteItem {}
28-
2925
export class POIQueryItem implements AutocompleteItem {
3026
result: AddressParseResult
3127

@@ -55,8 +51,6 @@ export default function Autocomplete({ items, highlightedItem, onSelect }: Autoc
5551
function mapToComponent(item: AutocompleteItem, isHighlighted: boolean, onSelect: (hit: AutocompleteItem) => void) {
5652
if (item instanceof GeocodingItem)
5753
return <GeocodingEntry item={item} isHighlighted={isHighlighted} onSelect={onSelect} />
58-
else if (item instanceof SelectCurrentLocationItem)
59-
return <SelectCurrentLocation item={item} isHighlighted={isHighlighted} onSelect={onSelect} />
6054
else if (item instanceof POIQueryItem)
6155
return <POIQueryEntry item={item} isHighlighted={isHighlighted} onSelect={onSelect} />
6256
else throw Error('Unsupported item type: ' + typeof item)
@@ -82,27 +76,6 @@ export function POIQueryEntry({
8276
)
8377
}
8478

85-
export function SelectCurrentLocation({
86-
item,
87-
isHighlighted,
88-
onSelect,
89-
}: {
90-
item: SelectCurrentLocationItem
91-
isHighlighted: boolean
92-
onSelect: (item: SelectCurrentLocationItem) => void
93-
}) {
94-
return (
95-
<AutocompleteEntry isHighlighted={isHighlighted} onSelect={() => onSelect(item)}>
96-
<div className={styles.currentLocationEntry}>
97-
<div className={styles.currentLocationIcon}>
98-
<CurrentLocationIcon />
99-
</div>
100-
<span className={styles.mainText}>{tr('current_location')}</span>
101-
</div>
102-
</AutocompleteEntry>
103-
)
104-
}
105-
10679
function GeocodingEntry({
10780
item,
10881
isHighlighted,
@@ -137,12 +110,15 @@ function AutocompleteEntry({
137110
className={className}
138111
// using click events for mouse interaction and touch end to select an entry.
139112
onClick={() => onSelect()}
113+
// minor workaround to improve success rate for click even if start and end location on screen are slightly different
140114
onTouchEnd={e => {
141115
e.preventDefault() // do not forward click to underlying component
142116
onSelect()
143117
}}
144118
onMouseDown={e => {
145-
e.preventDefault() // prevent blur event for our input, see #398
119+
// 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)
120+
// See also the onMouseDown calls in the buttons in AddressInput.tsx created for the same reason.
121+
e.preventDefault()
146122
}}
147123
>
148124
{children}

src/sidebar/search/Search.tsx

Lines changed: 0 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -101,13 +101,6 @@ const SearchBox = ({
101101
}) => {
102102
const point = points[index]
103103

104-
// 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
105-
const myMarkerRef = useRef<HTMLDivElement>(null)
106-
107-
useEffect(() => {
108-
if (index == 0) myMarkerRef.current?.focus()
109-
}, [])
110-
111104
function onClickOrDrop() {
112105
onDropPreviewSelect(-1)
113106
const newIndex = moveStartIndex < index ? index + 1 : index
@@ -122,8 +115,6 @@ const SearchBox = ({
122115
<>
123116
{(moveStartIndex < 0 || moveStartIndex == index) && (
124117
<div
125-
ref={myMarkerRef}
126-
tabIndex={-1}
127118
title={tr('drag_to_reorder')}
128119
className={styles.markerContainer}
129120
draggable

0 commit comments

Comments
 (0)