From 916e95329a434898456e1b307d80e29984ca18c9 Mon Sep 17 00:00:00 2001 From: Peter Date: Wed, 6 Sep 2023 16:43:27 +0200 Subject: [PATCH 1/4] input: avoid outline --- src/sidebar/search/AddressInput.module.css | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/sidebar/search/AddressInput.module.css b/src/sidebar/search/AddressInput.module.css index 169f4f71..7ba35b97 100644 --- a/src/sidebar/search/AddressInput.module.css +++ b/src/sidebar/search/AddressInput.module.css @@ -22,6 +22,10 @@ -webkit-appearance: none; } +.input:focus { + outline: none; +} + @media (max-width: 44rem) { .fullscreen { position: fixed; From f6edc9e6ddc294cbb6bf928c82359abd446707b1 Mon Sep 17 00:00:00 2001 From: Peter Date: Wed, 6 Sep 2023 16:57:45 +0200 Subject: [PATCH 2/4] for first 5 inputs the suggestion list should never appear above the input --- src/sidebar/search/AddressInput.tsx | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/sidebar/search/AddressInput.tsx b/src/sidebar/search/AddressInput.tsx index 6c158d10..81194bfa 100644 --- a/src/sidebar/search/AddressInput.tsx +++ b/src/sidebar/search/AddressInput.tsx @@ -161,7 +161,7 @@ export default function AddressInput(props: AddressInputProps) { {autocompleteItems.length > 0 && ( - + {isSmallScreen ? ( children ) : ( - + 5 ? 270 : 0}> {children} )} From 79ab4f17981a7f8a01a265dbf5b076885d7c55cb Mon Sep 17 00:00:00 2001 From: Peter Date: Wed, 6 Sep 2023 18:25:20 +0200 Subject: [PATCH 3/4] cleaner slope and towerslope (#347) --- src/pathDetails/PathDetails.tsx | 17 +++++++++++------ 1 file changed, 11 insertions(+), 6 deletions(-) diff --git a/src/pathDetails/PathDetails.tsx b/src/pathDetails/PathDetails.tsx index 20897330..88b04d60 100644 --- a/src/pathDetails/PathDetails.tsx +++ b/src/pathDetails/PathDetails.tsx @@ -9,6 +9,7 @@ import QueryStore, { Coordinate, QueryPointType } from '@/stores/QueryStore' import { Position } from 'geojson' import { calcDist } from '@/distUtils' import { ShowDistanceInMilesContext } from '@/ShowDistanceInMilesContext' +import {toFixed} from "ol/math"; interface PathDetailsProps { selectedPath: Path @@ -112,8 +113,9 @@ function buildPathDetailsData(selectedPath: Path) { const from = coordinates[i] const to = coordinates[i + 1] const distance = calcDistLonLat(from, to) - const slope = (100.0 * (to[2] - from[2])) / distance - slopeFeatures.push(createFeature([from, to], slope)) + const slope = distance == 0 ? 0 : (100.0 * (to[2] - from[2])) / distance + const slopeRounded = Math.round(slope / 3) * 3 + slopeFeatures.push(createFeature([from, to], slopeRounded)) } const slopeCollection = createFeatureCollection('Slope', slopeFeatures) result.data.push(slopeCollection) @@ -128,7 +130,8 @@ function buildPathDetailsData(selectedPath: Path) { const from = featurePoints[0] const to = featurePoints[featurePoints.length - 1] const distance = calcDistLonLat(from, to) - const slope = (100.0 * (to[2] - from[2])) / distance + const slope = distance == 0 ? 0 : (100.0 * (to[2] - from[2])) / distance + const slopeRounded = Math.round(slope / 3) * 3 // for the elevations in tower slope diagram we do linear interpolation between the tower nodes. note that // we cannot simply leave out the pillar nodes, because otherwise the total distance would change let tmpDistance = 0 @@ -142,7 +145,7 @@ function buildPathDetailsData(selectedPath: Path) { featurePoints[j] = [featurePoints[j][0], featurePoints[j][1], ele] if (j < featurePoints.length - 1) tmpDistance += calcDistLonLat(featurePoints[j], featurePoints[j + 1]) } - towerSlopeFeatures.push(createFeature(featurePoints, slope)) + towerSlopeFeatures.push(createFeature(featurePoints, slopeRounded)) } const towerSlopeCollection = createFeatureCollection('Towerslope', towerSlopeFeatures) result.data.push(towerSlopeCollection) @@ -213,8 +216,10 @@ function slope2color(slope: number): object { const factor = absSlope / 25 const color = [0, 1, 2].map(i => colorMin[i] + factor * (colorMax[i] - colorMin[i])) return { - text: slope.toFixed(2), - color: 'rgb(' + color[0] + ', ' + color[1] + ', ' + color[2] + ')', + text: slope < 0 + ? "↘ " + (-slope - 3).toFixed(0) + "-" + (-slope).toFixed(0) + "%" + : "↗ " + slope.toFixed(0) + "-" + (slope + 3).toFixed(0) + "%", + color: Number.isNaN(slope) ? 'red' : 'rgb(' + color[0] + ', ' + color[1] + ', ' + color[2] + ')', } } From b24086cf2d65ce2aae3bd57dea3f4e31c9f246d0 Mon Sep 17 00:00:00 2001 From: Peter Date: Thu, 7 Sep 2023 18:13:36 +0200 Subject: [PATCH 4/4] disable blur for smallscreen only (#354) --- src/sidebar/search/AddressInput.tsx | 59 ++++++++++++++----- .../search/AddressInputAutocomplete.tsx | 2 +- 2 files changed, 46 insertions(+), 15 deletions(-) diff --git a/src/sidebar/search/AddressInput.tsx b/src/sidebar/search/AddressInput.tsx index 81194bfa..f9d52200 100644 --- a/src/sidebar/search/AddressInput.tsx +++ b/src/sidebar/search/AddressInput.tsx @@ -1,14 +1,12 @@ import { ReactNode, useCallback, useEffect, useRef, useState } from 'react' import { Coordinate, getBBoxFromCoord, QueryPoint, QueryPointType } from '@/stores/QueryStore' import { Bbox, GeocodingHit } from '@/api/graphhopper' -import { ErrorAction } from '@/actions/Actions' import Autocomplete, { AutocompleteItem, GeocodingItem, MoreResultsItem, SelectCurrentLocationItem, } from '@/sidebar/search/AddressInputAutocomplete' -import Dispatcher from '@/stores/Dispatcher' import styles from './AddressInput.module.css' import Api, { getApi } from '@/api/Api' @@ -37,6 +35,7 @@ export default function AddressInput(props: AddressInputProps) { // keep track of focus and toggle fullscreen display on small screens const [hasFocus, setHasFocus] = useState(false) + const isSmallScreen = useMediaQuery({ query: '(max-width: 44rem)' }) // container for geocoding results which gets set by the geocoder class and set to empty if the underlying query point gets changed from outside // also gets filled with an item to select the current location as input if input has focus and geocoding results are @@ -73,6 +72,18 @@ export default function AddressInput(props: AddressInputProps) { setAutocompleteItems([new SelectCurrentLocationItem()]) }, [autocompleteItems, hasFocus]) + function blur() { + searchInput.current!.blur() + // onBlur is a no-op for smallscreen so force: + hideSuggestions() + } + + function hideSuggestions() { + geocoder.cancel() + setHasFocus(false) + 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 const [highlightedResult, setHighlightedResult] = useState(-1) @@ -81,7 +92,7 @@ export default function AddressInput(props: AddressInputProps) { const onKeypress = useCallback( (event: React.KeyboardEvent) => { if (event.key === 'Escape') { - searchInput.current!.blur() + blur() return } @@ -104,7 +115,7 @@ export default function AddressInput(props: AddressInputProps) { const item = autocompleteItems[index] if (item instanceof GeocodingItem) props.onAddressSelected(item.toText(), item.point, item.bbox) } - searchInput.current!.blur() + blur() break } }, @@ -112,7 +123,6 @@ export default function AddressInput(props: AddressInputProps) { ) const containerClass = hasFocus ? styles.container + ' ' + styles.fullscreen : styles.container - const type = props.point.type return ( @@ -133,6 +143,7 @@ export default function AddressInput(props: AddressInputProps) { className={styles.input} type="text" ref={searchInput} + autoComplete="off" onChange={e => { setText(e.target.value) const coordinate = textToCoordinate(e.target.value) @@ -146,34 +157,44 @@ export default function AddressInput(props: AddressInputProps) { event.target.select() }} onBlur={() => { - geocoder.cancel() - setHasFocus(false) - setAutocompleteItems([]) + // leave the fullscreen only when clicking on the back button + if (isSmallScreen) return + + hideSuggestions() }} value={text} placeholder={tr( type == QueryPointType.From ? 'from_hint' : type == QueryPointType.To ? 'to_hint' : 'via_hint' )} /> - setHasFocus(false)}> + { + hideSuggestions() + }} + > {tr('back_to_map')} {autocompleteItems.length > 0 && ( - + { if (item instanceof GeocodingItem) { - searchInput.current!.blur() + blur() props.onAddressSelected(item.toText(), item.point, item.bbox) } else if (item instanceof SelectCurrentLocationItem) { - searchInput.current!.blur() + blur() onCurrentLocationSelected(props.onAddressSelected) } else if (item instanceof MoreResultsItem) { - // do not blur + // do not hide autocomplete items const coordinate = textToCoordinate(item.search) if (!coordinate) geocoder.request(item.search, 'nominatim') } @@ -185,7 +206,16 @@ export default function AddressInput(props: AddressInputProps) { ) } -function ResponsiveAutocomplete({ inputRef, children, index }: { inputRef: HTMLElement; children: ReactNode; index:number }): JSX.Element { +function ResponsiveAutocomplete({ + inputRef, + children, + index, +}: { + inputRef: HTMLElement + children: ReactNode + isSmallScreen: boolean + index: number +}): JSX.Element { const isSmallScreen = useMediaQuery({ query: '(max-width: 44rem)' }) return ( <> @@ -276,6 +306,7 @@ class Timout { this.handle = window.setTimeout(resolve, this.delay) }) } + cancel() { clearTimeout(this.handle) } diff --git a/src/sidebar/search/AddressInputAutocomplete.tsx b/src/sidebar/search/AddressInputAutocomplete.tsx index 2ec1b960..f638794f 100644 --- a/src/sidebar/search/AddressInputAutocomplete.tsx +++ b/src/sidebar/search/AddressInputAutocomplete.tsx @@ -1,8 +1,8 @@ import styles from './AddressInputAutocomplete.module.css' -import { useState } from 'react' import CurrentLocationIcon from './current-location.svg' import { tr } from '@/translation/Translation' import { Bbox } from '@/api/graphhopper' +import { useState } from 'react' export interface AutocompleteItem {}