UX: add helper for gps selector

This commit is contained in:
Pieter Vander Vennet 2025-06-07 04:46:43 +02:00
parent c215051ec5
commit 886b25c0b4
4 changed files with 108 additions and 6 deletions

View file

@ -131,7 +131,9 @@ export class WithLayoutSourceState extends WithSelectedElementState {
protected setSelectedElement(feature: Feature) { protected setSelectedElement(feature: Feature) {
// The given feature might be a partial one from the cache // The given feature might be a partial one from the cache
feature = this.indexedFeatures.featuresById.data?.get(feature.properties.id) ?? feature if(feature !== undefined){
feature = this.indexedFeatures.featuresById.data?.get(feature?.properties?.id) ?? feature
}
super.setSelectedElement(feature) super.setSelectedElement(feature)
} }
} }

View file

@ -0,0 +1,56 @@
<script lang="ts">
/**
* Element that labels the GPS state
*/
import { fade } from "svelte/transition"
import ThemeViewState from "../../Models/ThemeViewState"
import Tr from "../Base/Tr.svelte"
import { onDestroy } from "svelte"
import { Popover } from "flowbite-svelte"
export let state: ThemeViewState
let gpsState = state.geolocationState
export let mapIsDragged: Store<void>
let open = true
function showFor(timeoutSeconds: number = 5) {
console.trace("Showing for", timeoutSeconds)
open = true
window.setTimeout(() => {
open = false
},
timeoutSeconds * 1000)
}
mapIsDragged.addCallback(() => {
const movingIsAllowed = gpsState.allowMoving.data
if (!movingIsAllowed) {
showFor(5)
}
})
gpsState.requestMoment.stabilized(50).addCallback(() => {
if(gpsState.gpsAvailable.data && gpsState.allowMoving.data){
return
}
showFor(5)
})
let explanation = gpsState.gpsStateExplanation
onDestroy(
explanation.addCallbackD(
expl => {
if (expl) {
showFor(5)
} else {
open = false
}
},
),
)
</script>
<Popover reference={undefined} trigger=null placement="left" transition={e => fade(e, {duration: 150})} bind:open>
<div class="break-words" style="max-width: calc( 100vw - 8rem)">
<Tr t={$explanation} />
</div>
</Popover>

View file

@ -43,7 +43,7 @@
import Hash from "../Logic/Web/Hash" import Hash from "../Logic/Web/Hash"
import Searchbar from "./Base/Searchbar.svelte" import Searchbar from "./Base/Searchbar.svelte"
import ChevronRight from "@babeard/svelte-heroicons/mini/ChevronRight" import ChevronRight from "@babeard/svelte-heroicons/mini/ChevronRight"
import { Drawer } from "flowbite-svelte" import { Drawer, Popover } from "flowbite-svelte"
import { linear } from "svelte/easing" import { linear } from "svelte/easing"
import DefaultIcon from "./Map/DefaultIcon.svelte" import DefaultIcon from "./Map/DefaultIcon.svelte"
import Loading from "./Base/Loading.svelte" import Loading from "./Base/Loading.svelte"
@ -51,7 +51,8 @@
import TitleHandler from "../Logic/Actors/TitleHandler" import TitleHandler from "../Logic/Actors/TitleHandler"
import Popup from "./Base/Popup.svelte" import Popup from "./Base/Popup.svelte"
import TagRenderingAnswer from "./Popup/TagRendering/TagRenderingAnswer.svelte" import TagRenderingAnswer from "./Popup/TagRendering/TagRenderingAnswer.svelte"
import GpsElementHelper from "./BigComponents/GpsElementHelper.svelte"
import { dragDetection } from "../Utils/dragDetection"
export let state: WithSearchState export let state: WithSearchState
new TitleHandler(state.selectedElement, state) new TitleHandler(state.selectedElement, state)
@ -176,12 +177,18 @@
} }
let apiState = state?.osmConnection?.apiIsOnline ?? new ImmutableStore("online") let apiState = state?.osmConnection?.apiIsOnline ?? new ImmutableStore("online")
let mapIsDragged: UIEventSource<void> = new UIEventSource(undefined)
function onMapDragged(){
mapIsDragged.ping()
}
</script> </script>
<main> <main>
<div class="absolute left-0 top-0 h-screen w-screen" style="background-color: #f0efdd" /> <div class="absolute left-0 top-0 h-screen w-screen" style="background-color: #f0efdd" />
<!-- Main map --> <!-- Main map -->
<div class="absolute left-0 top-0 h-screen w-screen overflow-hidden"> <div class="absolute left-0 top-0 h-screen w-screen overflow-hidden" use:dragDetection={() => onMapDragged()}>
<MaplibreMap map={maplibremap} mapProperties={mapproperties} autorecovery={true} /> <MaplibreMap map={maplibremap} mapProperties={mapproperties} autorecovery={true} />
</div> </div>
@ -294,7 +301,7 @@
<Min class="h-8 w-8" /> <Min class="h-8 w-8" />
</MapControlButton> </MapControlButton>
<If condition={featureSwitches.featureSwitchGeolocation}> <If condition={featureSwitches.featureSwitchGeolocation}>
<div class="relative m-0"> <div class="relative m-0" id="gps-control-button">
<MapControlButton <MapControlButton
enabled={gpsAvailable} enabled={gpsAvailable}
arialabelDynamic={gpsButtonAriaLabel} arialabelDynamic={gpsButtonAriaLabel}
@ -308,11 +315,12 @@
<div class="absolute left-0 top-0 m-0.5 h-0 w-0 sm:m-1"> <div class="absolute left-0 top-0 m-0.5 h-0 w-0 sm:m-1">
<Compass_arrow <Compass_arrow
class="compass_arrow" class="compass_arrow"
style={`rotate: calc(${-$compass}deg + 45deg); transform-origin: 50% 50%;`} openstyle={`rotate: calc(${-$compass}deg + 45deg); transform-origin: 50% 50%;`}
/> />
</div> </div>
{/if} {/if}
</div> </div>
<GpsElementHelper reference = "#gps-control-button" {state} {mapIsDragged}/>
</If> </If>
<If condition={state.mapProperties.showScale}> <If condition={state.mapProperties.showScale}>
<div class="h-6"> <div class="h-6">

View file

@ -0,0 +1,36 @@
export function dragDetection(htmlElement: HTMLElement, callback: () => {}) {
let isDown = false
let threshold = 5
let start = null
htmlElement.addEventListener("pointerdown", (e) => {
isDown = true
start = { x: e.clientX, y: e.clientY }
})
htmlElement.addEventListener("pointermove", (e) => {
if (!isDown || !start) return
const dx = e.clientX - start.x
const dy = e.clientY - start.y
if (Math.hypot(dx, dy) > threshold) {
callback()
isDown = false
}
})
htmlElement.addEventListener("pointerup", () => {
isDown = false
start = null
})
htmlElement.addEventListener("pointerleave", () => {
isDown = false
start = null
})
return {
destroy: () => {
},
}
}