forked from MapComplete/MapComplete
		
	UX: add crosshair to map center
This commit is contained in:
		
							parent
							
								
									a464877629
								
							
						
					
					
						commit
						4da5261db5
					
				
					 5 changed files with 132 additions and 82 deletions
				
			
		|  | @ -331,6 +331,30 @@ | |||
|         } | ||||
|       ] | ||||
|     }, | ||||
|     { | ||||
|       "id": "show_crosshair", | ||||
|       "question": { | ||||
|         "en":"Should a crosshair be shown in the center of the display?" | ||||
|       }, | ||||
|       "questionHint": { | ||||
|         "en":"This can help to accurately position a new element" | ||||
|       }, | ||||
|       "mappings": [ | ||||
|         { | ||||
|           "if": "mapcomplete-show_crosshair=yes", | ||||
|           "then": "Show a crosshair in the center of the map (when zoomed in above level 17)" | ||||
|         }, | ||||
|         { | ||||
|           "if": "mapcomplete-show_crosshair=no", | ||||
|           "then": "Do not show a crosshair in the center of the map" | ||||
|         }, | ||||
|         { | ||||
|           "if": "mapcomplete-show_crosshair=", | ||||
|           "then": "Do not show a crosshair in the center of the map", | ||||
|           "hideInAnswer": true | ||||
|         } | ||||
|       ] | ||||
|     }, | ||||
|     { | ||||
|       "id": "fixate-north", | ||||
|       "question": { | ||||
|  |  | |||
|  | @ -249,6 +249,14 @@ | |||
|     ], | ||||
|     "sources": [] | ||||
|   }, | ||||
|   { | ||||
|     "path": "cross.svg", | ||||
|     "license": "CC0-1.0", | ||||
|     "authors": [ | ||||
|       "Pieter Vander Vennet" | ||||
|     ], | ||||
|     "sources": [] | ||||
|   }, | ||||
|   { | ||||
|     "path": "cross_bottom_right.svg", | ||||
|     "license": "TRIVIAL", | ||||
|  |  | |||
|  | @ -39,6 +39,7 @@ export default class UserRelatedState { | |||
|     public readonly installedUserThemes: Store<string[]> | ||||
|     public readonly showAllQuestionsAtOnce: UIEventSource<boolean> | ||||
|     public readonly showTags: UIEventSource<"no" | undefined | "always" | "yes" | "full"> | ||||
|     public readonly showCrosshair: UIEventSource<"yes" | undefined> | ||||
|     public readonly fixateNorth: UIEventSource<undefined | "yes"> | ||||
|     public readonly homeLocation: FeatureSource | ||||
|     public readonly language: UIEventSource<string> | ||||
|  | @ -102,6 +103,7 @@ export default class UserRelatedState { | |||
|         ) | ||||
|         this.language = this.osmConnection.GetPreference("language") | ||||
|         this.showTags = <UIEventSource<any>>this.osmConnection.GetPreference("show_tags") | ||||
|         this.showCrosshair = <UIEventSource<any>>this.osmConnection.GetPreference("show_crosshair") | ||||
|         this.fixateNorth = <UIEventSource<"yes">>this.osmConnection.GetPreference("fixate-north") | ||||
|         this.mangroveIdentity = new MangroveIdentity( | ||||
|             this.osmConnection.GetLongPreference("identity", "mangrove") | ||||
|  |  | |||
|  | @ -4,6 +4,7 @@ | |||
|   import { Translation } from "../i18n/Translation" | ||||
|   import Tr from "../Base/Tr.svelte" | ||||
|   import type { SpecialVisualizationState } from "../SpecialVisualization" | ||||
|   import Translations from "../i18n/Translations" | ||||
| 
 | ||||
|   /** | ||||
|    * A 'TagHint' will show the given tags in a human readable form. | ||||
|  | @ -25,7 +26,7 @@ | |||
| {#if $userDetails.loggedIn} | ||||
|   <div> | ||||
|     {#if tags === undefined} | ||||
|       <slot name="no-tags">No tags</slot> | ||||
|       <slot name="no-tags"><Tr cls="subtle" t={Translations.t.general.noTagsSelected}></Tr></slot> | ||||
|     {:else if embedIn === undefined} | ||||
|       <FromHtml src={tagsExplanation} /> | ||||
|     {:else} | ||||
|  |  | |||
|  | @ -1,114 +1,114 @@ | |||
| <script lang="ts"> | ||||
|     import { Store, UIEventSource } from "../Logic/UIEventSource"; | ||||
|     import { Map as MlMap } from "maplibre-gl"; | ||||
|     import MaplibreMap from "./Map/MaplibreMap.svelte"; | ||||
|     import FeatureSwitchState from "../Logic/State/FeatureSwitchState"; | ||||
|     import MapControlButton from "./Base/MapControlButton.svelte"; | ||||
|     import ToSvelte from "./Base/ToSvelte.svelte"; | ||||
|     import If from "./Base/If.svelte"; | ||||
|     import { GeolocationControl } from "./BigComponents/GeolocationControl"; | ||||
|     import type { Feature } from "geojson"; | ||||
|     import SelectedElementView from "./BigComponents/SelectedElementView.svelte"; | ||||
|     import LayerConfig from "../Models/ThemeConfig/LayerConfig"; | ||||
|     import Filterview from "./BigComponents/Filterview.svelte"; | ||||
|     import ThemeViewState from "../Models/ThemeViewState"; | ||||
|     import type { MapProperties } from "../Models/MapProperties"; | ||||
|     import Geosearch from "./BigComponents/Geosearch.svelte"; | ||||
|     import Translations from "./i18n/Translations"; | ||||
|     import { CogIcon, EyeIcon, MenuIcon, XCircleIcon } from "@rgossiaux/svelte-heroicons/solid"; | ||||
|     import { Store, UIEventSource } from "../Logic/UIEventSource" | ||||
|     import { Map as MlMap } from "maplibre-gl" | ||||
|     import MaplibreMap from "./Map/MaplibreMap.svelte" | ||||
|     import FeatureSwitchState from "../Logic/State/FeatureSwitchState" | ||||
|     import MapControlButton from "./Base/MapControlButton.svelte" | ||||
|     import ToSvelte from "./Base/ToSvelte.svelte" | ||||
|     import If from "./Base/If.svelte" | ||||
|     import { GeolocationControl } from "./BigComponents/GeolocationControl" | ||||
|     import type { Feature } from "geojson" | ||||
|     import SelectedElementView from "./BigComponents/SelectedElementView.svelte" | ||||
|     import LayerConfig from "../Models/ThemeConfig/LayerConfig" | ||||
|     import Filterview from "./BigComponents/Filterview.svelte" | ||||
|     import ThemeViewState from "../Models/ThemeViewState" | ||||
|     import type { MapProperties } from "../Models/MapProperties" | ||||
|     import Geosearch from "./BigComponents/Geosearch.svelte" | ||||
|     import Translations from "./i18n/Translations" | ||||
|     import { CogIcon, EyeIcon, MenuIcon, XCircleIcon } from "@rgossiaux/svelte-heroicons/solid" | ||||
| 
 | ||||
|     import Tr from "./Base/Tr.svelte"; | ||||
|     import CommunityIndexView from "./BigComponents/CommunityIndexView.svelte"; | ||||
|     import FloatOver from "./Base/FloatOver.svelte"; | ||||
|     import PrivacyPolicy from "./BigComponents/PrivacyPolicy"; | ||||
|     import Constants from "../Models/Constants"; | ||||
|     import TabbedGroup from "./Base/TabbedGroup.svelte"; | ||||
|     import UserRelatedState from "../Logic/State/UserRelatedState"; | ||||
|     import LoginToggle from "./Base/LoginToggle.svelte"; | ||||
|     import LoginButton from "./Base/LoginButton.svelte"; | ||||
|     import CopyrightPanel from "./BigComponents/CopyrightPanel"; | ||||
|     import DownloadPanel from "./DownloadFlow/DownloadPanel.svelte"; | ||||
|     import ModalRight from "./Base/ModalRight.svelte"; | ||||
|     import { Utils } from "../Utils"; | ||||
|     import Hotkeys from "./Base/Hotkeys"; | ||||
|     import { VariableUiElement } from "./Base/VariableUIElement"; | ||||
|     import SvelteUIElement from "./Base/SvelteUIElement"; | ||||
|     import OverlayToggle from "./BigComponents/OverlayToggle.svelte"; | ||||
|     import LevelSelector from "./BigComponents/LevelSelector.svelte"; | ||||
|     import ExtraLinkButton from "./BigComponents/ExtraLinkButton"; | ||||
|     import SelectedElementTitle from "./BigComponents/SelectedElementTitle.svelte"; | ||||
|     import Svg from "../Svg"; | ||||
|     import ThemeIntroPanel from "./BigComponents/ThemeIntroPanel.svelte"; | ||||
|     import type { RasterLayerPolygon } from "../Models/RasterLayers"; | ||||
|     import { AvailableRasterLayers } from "../Models/RasterLayers"; | ||||
|     import RasterLayerOverview from "./Map/RasterLayerOverview.svelte"; | ||||
|     import IfHidden from "./Base/IfHidden.svelte"; | ||||
|     import { onDestroy } from "svelte"; | ||||
|     import { OpenJosm } from "./BigComponents/OpenJosm"; | ||||
|     import MapillaryLink from "./BigComponents/MapillaryLink.svelte"; | ||||
|     import OpenIdEditor from "./BigComponents/OpenIdEditor.svelte"; | ||||
|     import OpenBackgroundSelectorButton from "./BigComponents/OpenBackgroundSelectorButton.svelte"; | ||||
|     import StateIndicator from "./BigComponents/StateIndicator.svelte"; | ||||
|     import LanguagePicker from "./LanguagePicker"; | ||||
|     import Locale from "./i18n/Locale"; | ||||
|     import ShareScreen from "./BigComponents/ShareScreen.svelte"; | ||||
|     import Tr from "./Base/Tr.svelte" | ||||
|     import CommunityIndexView from "./BigComponents/CommunityIndexView.svelte" | ||||
|     import FloatOver from "./Base/FloatOver.svelte" | ||||
|     import PrivacyPolicy from "./BigComponents/PrivacyPolicy" | ||||
|     import Constants from "../Models/Constants" | ||||
|     import TabbedGroup from "./Base/TabbedGroup.svelte" | ||||
|     import UserRelatedState from "../Logic/State/UserRelatedState" | ||||
|     import LoginToggle from "./Base/LoginToggle.svelte" | ||||
|     import LoginButton from "./Base/LoginButton.svelte" | ||||
|     import CopyrightPanel from "./BigComponents/CopyrightPanel" | ||||
|     import DownloadPanel from "./DownloadFlow/DownloadPanel.svelte" | ||||
|     import ModalRight from "./Base/ModalRight.svelte" | ||||
|     import { Utils } from "../Utils" | ||||
|     import Hotkeys from "./Base/Hotkeys" | ||||
|     import { VariableUiElement } from "./Base/VariableUIElement" | ||||
|     import SvelteUIElement from "./Base/SvelteUIElement" | ||||
|     import OverlayToggle from "./BigComponents/OverlayToggle.svelte" | ||||
|     import LevelSelector from "./BigComponents/LevelSelector.svelte" | ||||
|     import ExtraLinkButton from "./BigComponents/ExtraLinkButton" | ||||
|     import SelectedElementTitle from "./BigComponents/SelectedElementTitle.svelte" | ||||
|     import Svg from "../Svg" | ||||
|     import ThemeIntroPanel from "./BigComponents/ThemeIntroPanel.svelte" | ||||
|     import type { RasterLayerPolygon } from "../Models/RasterLayers" | ||||
|     import { AvailableRasterLayers } from "../Models/RasterLayers" | ||||
|     import RasterLayerOverview from "./Map/RasterLayerOverview.svelte" | ||||
|     import IfHidden from "./Base/IfHidden.svelte" | ||||
|     import { onDestroy } from "svelte" | ||||
|     import { OpenJosm } from "./BigComponents/OpenJosm" | ||||
|     import MapillaryLink from "./BigComponents/MapillaryLink.svelte" | ||||
|     import OpenIdEditor from "./BigComponents/OpenIdEditor.svelte" | ||||
|     import OpenBackgroundSelectorButton from "./BigComponents/OpenBackgroundSelectorButton.svelte" | ||||
|     import StateIndicator from "./BigComponents/StateIndicator.svelte" | ||||
|     import LanguagePicker from "./LanguagePicker" | ||||
|     import Locale from "./i18n/Locale" | ||||
|     import ShareScreen from "./BigComponents/ShareScreen.svelte" | ||||
| 
 | ||||
|     export let state: ThemeViewState; | ||||
|     let layout = state.layout; | ||||
|     export let state: ThemeViewState | ||||
|     let layout = state.layout | ||||
| 
 | ||||
|     let maplibremap: UIEventSource<MlMap> = state.map; | ||||
|     let selectedElement: UIEventSource<Feature> = state.selectedElement; | ||||
|     let selectedLayer: UIEventSource<LayerConfig> = state.selectedLayer; | ||||
|     let maplibremap: UIEventSource<MlMap> = state.map | ||||
|     let selectedElement: UIEventSource<Feature> = state.selectedElement | ||||
|     let selectedLayer: UIEventSource<LayerConfig> = state.selectedLayer | ||||
| 
 | ||||
|     const selectedElementView = selectedElement.map( | ||||
|         (selectedElement) => { | ||||
|             // Svelte doesn't properly reload some of the legacy UI-elements | ||||
|             // As such, we _reconstruct_ the selectedElementView every time a new feature is selected | ||||
|             // This is a bit wasteful, but until everything is a svelte-component, this should do the trick | ||||
|             const layer = selectedLayer.data; | ||||
|             const layer = selectedLayer.data | ||||
|             if (selectedElement === undefined || layer === undefined) { | ||||
|                 return undefined; | ||||
|                 return undefined | ||||
|             } | ||||
| 
 | ||||
|             if (!(layer.tagRenderings?.length > 0) || layer.title === undefined) { | ||||
|                 return undefined; | ||||
|                 return undefined | ||||
|             } | ||||
| 
 | ||||
|             const tags = state.featureProperties.getStore(selectedElement.properties.id); | ||||
|             return new SvelteUIElement(SelectedElementView, { state, layer, selectedElement, tags }); | ||||
|             const tags = state.featureProperties.getStore(selectedElement.properties.id) | ||||
|             return new SvelteUIElement(SelectedElementView, { state, layer, selectedElement, tags }) | ||||
|         }, | ||||
|         [selectedLayer] | ||||
|     ); | ||||
|         [selectedLayer], | ||||
|     ) | ||||
| 
 | ||||
|     const selectedElementTitle = selectedElement.map( | ||||
|         (selectedElement) => { | ||||
|             // Svelte doesn't properly reload some of the legacy UI-elements | ||||
|             // As such, we _reconstruct_ the selectedElementView every time a new feature is selected | ||||
|             // This is a bit wasteful, but until everything is a svelte-component, this should do the trick | ||||
|             const layer = selectedLayer.data; | ||||
|             const layer = selectedLayer.data | ||||
|             if (selectedElement === undefined || layer === undefined) { | ||||
|                 return undefined; | ||||
|                 return undefined | ||||
|             } | ||||
| 
 | ||||
|             const tags = state.featureProperties.getStore(selectedElement.properties.id); | ||||
|             return new SvelteUIElement(SelectedElementTitle, { state, layer, selectedElement, tags }); | ||||
|             const tags = state.featureProperties.getStore(selectedElement.properties.id) | ||||
|             return new SvelteUIElement(SelectedElementTitle, { state, layer, selectedElement, tags }) | ||||
|         }, | ||||
|         [selectedLayer] | ||||
|     ); | ||||
|         [selectedLayer], | ||||
|     ) | ||||
| 
 | ||||
|     let mapproperties: MapProperties = state.mapProperties; | ||||
|     let featureSwitches: FeatureSwitchState = state.featureSwitches; | ||||
|     let availableLayers = state.availableLayers; | ||||
|     let userdetails = state.osmConnection.userDetails; | ||||
|     let currentViewLayer = layout.layers.find((l) => l.id === "current_view"); | ||||
|     let rasterLayer: Store<RasterLayerPolygon> = state.mapProperties.rasterLayer; | ||||
|     let mapproperties: MapProperties = state.mapProperties | ||||
|     let featureSwitches: FeatureSwitchState = state.featureSwitches | ||||
|     let availableLayers = state.availableLayers | ||||
|     let userdetails = state.osmConnection.userDetails | ||||
|     let currentViewLayer = layout.layers.find((l) => l.id === "current_view") | ||||
|     let rasterLayer: Store<RasterLayerPolygon> = state.mapProperties.rasterLayer | ||||
|     let rasterLayerName = | ||||
|         rasterLayer.data?.properties?.name ?? AvailableRasterLayers.maptilerDefaultLayer.properties.name; | ||||
|         rasterLayer.data?.properties?.name ?? AvailableRasterLayers.maptilerDefaultLayer.properties.name | ||||
|     onDestroy( | ||||
|         rasterLayer.addCallbackAndRunD((l) => { | ||||
|             rasterLayerName = l.properties.name; | ||||
|         }) | ||||
|     ); | ||||
|             rasterLayerName = l.properties.name | ||||
|         }), | ||||
|     ) | ||||
| </script> | ||||
| 
 | ||||
| <div class="absolute top-0 left-0 h-screen w-screen overflow-hidden"> | ||||
|  | @ -233,6 +233,17 @@ | |||
|   </div> | ||||
| </div> | ||||
| 
 | ||||
| <LoginToggle ignoreLoading={true} {state }> | ||||
|   <If condition={state.userRelatedState.showCrosshair.map(s => s === "yes")}> | ||||
|     <If condition={state.mapProperties.zoom.map(z  => z >= 17)}> | ||||
|       <div class="absolute top-0 left-0 flex items-center justify-center pointer-events-none w-full h-full"> | ||||
|         <ToSvelte construct={Svg.cross_svg()} /> | ||||
|       </div> | ||||
|     </If> | ||||
|   </If> | ||||
| </LoginToggle> | ||||
| 
 | ||||
| 
 | ||||
| <If | ||||
|   condition={selectedElementView.map( | ||||
|     (v) => | ||||
|  | @ -257,6 +268,7 @@ | |||
|   </ModalRight> | ||||
| </If> | ||||
| 
 | ||||
| 
 | ||||
| <If | ||||
|   condition={selectedElementView.map( | ||||
|     (v) => | ||||
|  | @ -364,7 +376,8 @@ | |||
|   <!-- Menu page --> | ||||
|   <FloatOver on:close={() =>      state.guistate.menuIsOpened.setData(false)    }> | ||||
|     <span slot="close-button"><!-- Hide the default close button --></span> | ||||
|     <TabbedGroup condition1={featureSwitches.featureSwitchEnableLogin} condition2={state.featureSwitches. featureSwitchCommunityIndex} | ||||
|     <TabbedGroup condition1={featureSwitches.featureSwitchEnableLogin} | ||||
|                  condition2={state.featureSwitches. featureSwitchCommunityIndex} | ||||
|                  tab={state.guistate.menuViewTabIndex}> | ||||
|       <div slot="post-tablist"> | ||||
|         <XCircleIcon | ||||
|  | @ -464,3 +477,5 @@ | |||
|     </TabbedGroup> | ||||
|   </FloatOver> | ||||
| </If> | ||||
| 
 | ||||
| 
 | ||||
|  |  | |||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue