forked from MapComplete/MapComplete
UX: add helper for gps selector
This commit is contained in:
parent
c215051ec5
commit
886b25c0b4
4 changed files with 108 additions and 6 deletions
|
@ -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)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
56
src/UI/BigComponents/GpsElementHelper.svelte
Normal file
56
src/UI/BigComponents/GpsElementHelper.svelte
Normal 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>
|
|
@ -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">
|
||||||
|
|
36
src/Utils/dragDetection.ts
Normal file
36
src/Utils/dragDetection.ts
Normal 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: () => {
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
Loading…
Add table
Add a link
Reference in a new issue