UI: fix behaviour of hotkey "escape" when closing previews and nearby images popups

This commit is contained in:
Pieter Vander Vennet 2025-03-13 16:53:12 +01:00
parent ae84207555
commit e22929bb35
6 changed files with 73 additions and 49 deletions

View file

@ -4,6 +4,7 @@ import UserRelatedState from "../Logic/State/UserRelatedState"
import { Utils } from "../Utils" import { Utils } from "../Utils"
import Zoomcontrol from "../UI/Zoomcontrol" import Zoomcontrol from "../UI/Zoomcontrol"
import { LocalStorageSource } from "../Logic/Web/LocalStorageSource" import { LocalStorageSource } from "../Logic/Web/LocalStorageSource"
import { ProvidedImage } from "../Logic/ImageProviders/ImageProvider"
export type PageType = (typeof MenuState.pageNames)[number] export type PageType = (typeof MenuState.pageNames)[number]
@ -27,7 +28,7 @@ export class MenuState {
"favourites", "favourites",
"usersettings", "usersettings",
"share", "share",
"menu", "menu"
] as const ] as const
/** /**
@ -38,6 +39,9 @@ export class MenuState {
undefined undefined
) )
public static readonly nearbyImagesFeature: UIEventSource<object> = new UIEventSource<object>(
undefined
)
public readonly pageStates: Record<PageType, UIEventSource<boolean>> public readonly pageStates: Record<PageType, UIEventSource<boolean>>
public readonly highlightedLayerInFilters: UIEventSource<string> = new UIEventSource<string>( public readonly highlightedLayerInFilters: UIEventSource<string> = new UIEventSource<string>(
@ -45,6 +49,7 @@ export class MenuState {
) )
public highlightedUserSetting: UIEventSource<string> = new UIEventSource<string>(undefined) public highlightedUserSetting: UIEventSource<string> = new UIEventSource<string>(undefined)
private readonly _selectedElement: UIEventSource<any> | undefined private readonly _selectedElement: UIEventSource<any> | undefined
private isClosingAll = false
constructor(selectedElement: UIEventSource<any> | undefined) { constructor(selectedElement: UIEventSource<any> | undefined) {
this._selectedElement = selectedElement this._selectedElement = selectedElement
@ -129,8 +134,13 @@ export class MenuState {
* Returns 'true' if at least one menu was opened * Returns 'true' if at least one menu was opened
*/ */
public closeAll(): boolean { public closeAll(): boolean {
console.log("Closing all") if (this.isClosingAll) {
return true
}
this.isClosingAll = true
const ps = this.pageStates const ps = this.pageStates
try {
if (ps.menu.data) { if (ps.menu.data) {
ps.menu.set(false) ps.menu.set(false)
return true return true
@ -141,6 +151,10 @@ export class MenuState {
return true return true
} }
if (MenuState.nearbyImagesFeature.data !== undefined) {
MenuState.nearbyImagesFeature.setData(undefined)
return true
}
for (const key in ps) { for (const key in ps) {
const toggle = ps[key] const toggle = ps[key]
const wasOpen = toggle.data const wasOpen = toggle.data
@ -153,5 +167,16 @@ export class MenuState {
this._selectedElement.setData(undefined) this._selectedElement.setData(undefined)
return true return true
} }
} finally {
this.isClosingAll = false
}
}
public setPreviewedImage(img?: Partial<ProvidedImage>) {
if (img === undefined && !this.isClosingAll) {
return
}
MenuState.previewedImage.setData(img)
} }
} }

View file

@ -14,6 +14,8 @@ export default class Hotkeys {
}[] }[]
> = new UIEventSource([]) > = new UIEventSource([])
private static readonly seenKeys: Set<string> = new Set()
/** /**
* Register a hotkey * Register a hotkey
* @param key * @param key
@ -51,6 +53,9 @@ export default class Hotkeys {
} }
} }
const keyString = JSON.stringify(key)
this.seenKeys.add(keyString)
this._docs.data.push({ key, documentation, alsoTriggeredBy }) this._docs.data.push({ key, documentation, alsoTriggeredBy })
this._docs.ping() this._docs.ping()
if (Utils.runningFromConsole) { if (Utils.runningFromConsole) {

View file

@ -56,6 +56,7 @@
<slot name="header" /> <slot name="header" />
</h1> </h1>
{/if} {/if}
<slot name="closebutton" />
</svelte:fragment> </svelte:fragment>
<slot /> <slot />
{#if $$slots.footer} {#if $$slots.footer}

View file

@ -11,7 +11,6 @@
import ImageOperations from "./ImageOperations.svelte" import ImageOperations from "./ImageOperations.svelte"
import Popup from "../Base/Popup.svelte" import Popup from "../Base/Popup.svelte"
import { onDestroy } from "svelte" import { onDestroy } from "svelte"
import type { SpecialVisualizationState } from "../SpecialVisualization"
import type { Feature, Point } from "geojson" import type { Feature, Point } from "geojson"
import Loading from "../Base/Loading.svelte" import Loading from "../Base/Loading.svelte"
import Translations from "../i18n/Translations" import Translations from "../i18n/Translations"
@ -19,8 +18,9 @@
import DotMenu from "../Base/DotMenu.svelte" import DotMenu from "../Base/DotMenu.svelte"
import LoadingPlaceholder from "../Base/LoadingPlaceholder.svelte" import LoadingPlaceholder from "../Base/LoadingPlaceholder.svelte"
import { MenuState } from "../../Models/MenuState" import { MenuState } from "../../Models/MenuState"
import ThemeViewState from "../../Models/ThemeViewState"
export let image: Partial<ProvidedImage> export let image: Partial<ProvidedImage> & { id: string; url: string }
let fallbackImage: string = undefined let fallbackImage: string = undefined
if (image.provider === Mapillary.singleton) { if (image.provider === Mapillary.singleton) {
fallbackImage = "./assets/svg/blocked.svg" fallbackImage = "./assets/svg/blocked.svg"
@ -28,7 +28,7 @@
let imgEl: HTMLImageElement let imgEl: HTMLImageElement
export let imgClass: string = undefined export let imgClass: string = undefined
export let state: SpecialVisualizationState = undefined export let state: ThemeViewState = undefined
export let attributionFormat: "minimal" | "medium" | "large" = "medium" export let attributionFormat: "minimal" | "medium" | "large" = "medium"
let previewedImage: UIEventSource<Partial<ProvidedImage>> = MenuState.previewedImage let previewedImage: UIEventSource<Partial<ProvidedImage>> = MenuState.previewedImage
export let canZoom = previewedImage !== undefined export let canZoom = previewedImage !== undefined
@ -36,9 +36,7 @@
let showBigPreview = new UIEventSource(false) let showBigPreview = new UIEventSource(false)
onDestroy( onDestroy(
showBigPreview.addCallbackAndRun((shown) => { showBigPreview.addCallbackAndRun((shown) => {
if (!shown) { state.guistate.setPreviewedImage(shown ? image : undefined)
previewedImage?.set(undefined)
}
}) })
) )
if (previewedImage) { if (previewedImage) {

View file

@ -89,17 +89,6 @@
imgClass="max-h-64 w-auto sm:h-32 md:h-64" imgClass="max-h-64 w-auto sm:h-32 md:h-64"
attributionFormat="minimal" attributionFormat="minimal"
> >
<!--
<div slot="preview-action" class="self-center" >
<LoginToggle {state} silentFail={true}>
{#if linkable}
<label class="normal-background p-2 rounded-full pointer-events-auto">
<input bind:checked={$isLinked} type="checkbox" />
<SpecialTranslation t={t.link} {tags} {state} {layer} {feature} />
</label>
{/if}
</LoginToggle>
</div>-->
</AttributedImage> </AttributedImage>
<LoginToggle {state} silentFail={true}> <LoginToggle {state} silentFail={true}>
{#if linkable} {#if linkable}

View file

@ -1,22 +1,19 @@
<script lang="ts"> <script lang="ts">
import { UIEventSource } from "../../Logic/UIEventSource" import { UIEventSource } from "../../Logic/UIEventSource"
import type { OsmTags } from "../../Models/OsmFeature" import type { OsmTags } from "../../Models/OsmFeature"
import type { SpecialVisualizationState } from "../SpecialVisualization"
import type { Feature } from "geojson" import type { Feature } from "geojson"
import LayerConfig from "../../Models/ThemeConfig/LayerConfig" import LayerConfig from "../../Models/ThemeConfig/LayerConfig"
import Translations from "../i18n/Translations" import Translations from "../i18n/Translations"
import Tr from "../Base/Tr.svelte" import Tr from "../Base/Tr.svelte"
import NearbyImages from "./NearbyImages.svelte" import NearbyImages from "./NearbyImages.svelte"
import { XCircleIcon } from "@babeard/svelte-heroicons/solid"
import Camera_plus from "../../assets/svg/Camera_plus.svelte"
import LoginToggle from "../Base/LoginToggle.svelte"
import { ariaLabel } from "../../Utils/ariaLabel"
import { Accordion, AccordionItem, Modal } from "flowbite-svelte"
import AccordionSingle from "../Flowbite/AccordionSingle.svelte"
import Popup from "../Base/Popup.svelte" import Popup from "../Base/Popup.svelte"
import ThemeViewState from "../../Models/ThemeViewState"
import { onDestroy } from "svelte"
import { MenuState } from "../../Models/MenuState"
import { CloseButton } from "flowbite-svelte"
export let tags: UIEventSource<OsmTags> export let tags: UIEventSource<OsmTags>
export let state: SpecialVisualizationState export let state: ThemeViewState
export let lon: number export let lon: number
export let lat: number export let lat: number
export let feature: Feature export let feature: Feature
@ -27,6 +24,16 @@
let enableLogin = state.featureSwitches.featureSwitchEnableLogin let enableLogin = state.featureSwitches.featureSwitchEnableLogin
export let shown = new UIEventSource(false) export let shown = new UIEventSource(false)
onDestroy(MenuState.nearbyImagesFeature.addCallback(something => {
if (something !== feature) {
shown.set(false)
}
}))
onDestroy(shown.addCallbackAndRun(isShown => {
if (isShown) {
MenuState.nearbyImagesFeature.set(feature)
}
}))
</script> </script>
{#if enableLogin.data} {#if enableLogin.data}
@ -37,10 +44,9 @@
> >
<Tr t={t.seeNearby} /> <Tr t={t.seeNearby} />
</button> </button>
<Popup {shown} bodyPadding="p-4"> <Popup {shown} bodyPadding="p-4" dismissable={false}>
<span slot="header"> <Tr slot="header" t={t.seeNearby} />
<Tr t={t.seeNearby} /> <CloseButton slot="closebutton" on:click={() => shown?.set(false)} />
</span>
<NearbyImages {tags} {state} {lon} {lat} {feature} {linkable} {layer} /> <NearbyImages {tags} {state} {lon} {lat} {feature} {linkable} {layer} />
</Popup> </Popup>
{/if} {/if}