forked from MapComplete/MapComplete
		
	UX: map in 'add new point' now takes the full screen
This commit is contained in:
		
							parent
							
								
									5c692fb11a
								
							
						
					
					
						commit
						4219b23af1
					
				
					 8 changed files with 281 additions and 266 deletions
				
			
		|  | @ -21,3 +21,5 @@ The participant has extensive OpenStreetMap-knowledge but only used MapComplete | |||
| - [x] This user had an expression with two tags in an AND. There was some confusion if the taginfo-count gave the totals for the tags individually or for the entire expression. | ||||
|         Fix: play with padding and wording | ||||
| - [x] BUG: having a complex expression for tags (e.g. with `and: [key=value, key0=value0]`) fails as the JSON would be stringified | ||||
| - [x] In MapComplete (not in studio): creating a new point: the buttons might dissapear under scroll if zoomed in a lot | ||||
| - [x] If a layer does not have a title and a tagRenderings, it is not interpreted as 'standalone' theme | ||||
|  |  | |||
|  | @ -156,6 +156,7 @@ | |||
|   "tagRenderings": [ | ||||
|     { | ||||
|       "id": "add_new", | ||||
|       "classes": "h-full flex", | ||||
|       "condition": "has_presets=yes", | ||||
|       "render": { | ||||
|         "*": "{add_new_point()}" | ||||
|  |  | |||
|  | @ -483,6 +483,9 @@ export class AddQuestionBox extends DesugaringStep<LayerConfigJson> { | |||
|         ) { | ||||
|             return json | ||||
|         } | ||||
|         if (json.source === "special") { | ||||
|             return json | ||||
|         } | ||||
|         json = { ...json } | ||||
|         json.tagRenderings = [...json.tagRenderings] | ||||
|         const allSpecials: Exclude<RenderingSpecification, string>[] = <any>( | ||||
|  |  | |||
|  | @ -28,10 +28,11 @@ | |||
|     <Tr t={Translations.t.general.returnToTheMap} /> | ||||
|   </button> | ||||
| {:else} | ||||
|   <div class="flex flex-col gap-y-2 overflow-y-auto p-1 px-2"> | ||||
|   <div class="flex flex-col gap-y-2 overflow-y-auto p-1 px-2 h-full"> | ||||
|     {#each layer.tagRenderings as config (config.id)} | ||||
|       {#if (config.condition?.matchesProperties($tags) ?? true) && config.metacondition?.matchesProperties({ ...$tags, ..._metatags } ?? true)} | ||||
|         {#if config.IsKnown($tags)} | ||||
|           {config.id} | ||||
|           <TagRenderingEditable | ||||
|             {tags} | ||||
|             {config} | ||||
|  |  | |||
|  | @ -3,109 +3,109 @@ | |||
|    * This component ties together all the steps that are needed to create a new point. | ||||
|    * There are many subcomponents which help with that | ||||
|    */ | ||||
|   import type { SpecialVisualizationState } from "../../SpecialVisualization" | ||||
|   import PresetList from "./PresetList.svelte" | ||||
|   import type PresetConfig from "../../../Models/ThemeConfig/PresetConfig" | ||||
|   import LayerConfig from "../../../Models/ThemeConfig/LayerConfig" | ||||
|   import Tr from "../../Base/Tr.svelte" | ||||
|   import SubtleButton from "../../Base/SubtleButton.svelte" | ||||
|   import FromHtml from "../../Base/FromHtml.svelte" | ||||
|   import Translations from "../../i18n/Translations.js" | ||||
|   import TagHint from "../TagHint.svelte" | ||||
|   import { And } from "../../../Logic/Tags/And.js" | ||||
|   import LoginToggle from "../../Base/LoginToggle.svelte" | ||||
|   import Constants from "../../../Models/Constants.js" | ||||
|   import FilteredLayer from "../../../Models/FilteredLayer" | ||||
|   import { Store, UIEventSource } from "../../../Logic/UIEventSource" | ||||
|   import { EyeIcon, EyeOffIcon } from "@rgossiaux/svelte-heroicons/solid" | ||||
|   import LoginButton from "../../Base/LoginButton.svelte" | ||||
|   import NewPointLocationInput from "../../BigComponents/NewPointLocationInput.svelte" | ||||
|   import CreateNewNodeAction from "../../../Logic/Osm/Actions/CreateNewNodeAction" | ||||
|   import { OsmWay } from "../../../Logic/Osm/OsmObject" | ||||
|   import { Tag } from "../../../Logic/Tags/Tag" | ||||
|   import type { WayId } from "../../../Models/OsmFeature" | ||||
|   import Loading from "../../Base/Loading.svelte" | ||||
|   import type { GlobalFilter } from "../../../Models/GlobalFilter" | ||||
|   import { onDestroy } from "svelte" | ||||
|   import NextButton from "../../Base/NextButton.svelte" | ||||
|   import BackButton from "../../Base/BackButton.svelte" | ||||
|   import ToSvelte from "../../Base/ToSvelte.svelte" | ||||
|   import Svg from "../../../Svg" | ||||
|   import OpenBackgroundSelectorButton from "../../BigComponents/OpenBackgroundSelectorButton.svelte" | ||||
|   import { twJoin } from "tailwind-merge" | ||||
|   import type { SpecialVisualizationState } from "../../SpecialVisualization"; | ||||
|   import PresetList from "./PresetList.svelte"; | ||||
|   import type PresetConfig from "../../../Models/ThemeConfig/PresetConfig"; | ||||
|   import LayerConfig from "../../../Models/ThemeConfig/LayerConfig"; | ||||
|   import Tr from "../../Base/Tr.svelte"; | ||||
|   import SubtleButton from "../../Base/SubtleButton.svelte"; | ||||
|   import FromHtml from "../../Base/FromHtml.svelte"; | ||||
|   import Translations from "../../i18n/Translations.js"; | ||||
|   import TagHint from "../TagHint.svelte"; | ||||
|   import { And } from "../../../Logic/Tags/And.js"; | ||||
|   import LoginToggle from "../../Base/LoginToggle.svelte"; | ||||
|   import Constants from "../../../Models/Constants.js"; | ||||
|   import FilteredLayer from "../../../Models/FilteredLayer"; | ||||
|   import { Store, UIEventSource } from "../../../Logic/UIEventSource"; | ||||
|   import { EyeIcon, EyeOffIcon } from "@rgossiaux/svelte-heroicons/solid"; | ||||
|   import LoginButton from "../../Base/LoginButton.svelte"; | ||||
|   import NewPointLocationInput from "../../BigComponents/NewPointLocationInput.svelte"; | ||||
|   import CreateNewNodeAction from "../../../Logic/Osm/Actions/CreateNewNodeAction"; | ||||
|   import { OsmWay } from "../../../Logic/Osm/OsmObject"; | ||||
|   import { Tag } from "../../../Logic/Tags/Tag"; | ||||
|   import type { WayId } from "../../../Models/OsmFeature"; | ||||
|   import Loading from "../../Base/Loading.svelte"; | ||||
|   import type { GlobalFilter } from "../../../Models/GlobalFilter"; | ||||
|   import { onDestroy } from "svelte"; | ||||
|   import NextButton from "../../Base/NextButton.svelte"; | ||||
|   import BackButton from "../../Base/BackButton.svelte"; | ||||
|   import ToSvelte from "../../Base/ToSvelte.svelte"; | ||||
|   import Svg from "../../../Svg"; | ||||
|   import OpenBackgroundSelectorButton from "../../BigComponents/OpenBackgroundSelectorButton.svelte"; | ||||
|   import { twJoin } from "tailwind-merge"; | ||||
| 
 | ||||
|   export let coordinate: { lon: number; lat: number } | ||||
|   export let state: SpecialVisualizationState | ||||
|   export let coordinate: { lon: number; lat: number }; | ||||
|   export let state: SpecialVisualizationState; | ||||
| 
 | ||||
|   let selectedPreset: { | ||||
|     preset: PresetConfig | ||||
|     layer: LayerConfig | ||||
|     icon: string | ||||
|     tags: Record<string, string> | ||||
|   } = undefined | ||||
|   let checkedOfGlobalFilters: number = 0 | ||||
|   let confirmedCategory = false | ||||
|   } = undefined; | ||||
|   let checkedOfGlobalFilters: number = 0; | ||||
|   let confirmedCategory = false; | ||||
|   $: if (selectedPreset === undefined) { | ||||
|     confirmedCategory = false | ||||
|     creating = false | ||||
|     checkedOfGlobalFilters = 0 | ||||
|     confirmedCategory = false; | ||||
|     creating = false; | ||||
|     checkedOfGlobalFilters = 0; | ||||
|   } | ||||
| 
 | ||||
|   let flayer: FilteredLayer = undefined | ||||
|   let layerIsDisplayed: UIEventSource<boolean> | undefined = undefined | ||||
|   let layerHasFilters: Store<boolean> | undefined = undefined | ||||
|   let globalFilter: UIEventSource<GlobalFilter[]> = state.layerState.globalFilters | ||||
|   let _globalFilter: GlobalFilter[] = [] | ||||
|   let flayer: FilteredLayer = undefined; | ||||
|   let layerIsDisplayed: UIEventSource<boolean> | undefined = undefined; | ||||
|   let layerHasFilters: Store<boolean> | undefined = undefined; | ||||
|   let globalFilter: UIEventSource<GlobalFilter[]> = state.layerState.globalFilters; | ||||
|   let _globalFilter: GlobalFilter[] = []; | ||||
|   onDestroy( | ||||
|     globalFilter.addCallbackAndRun((globalFilter) => { | ||||
|       console.log("Global filters are", globalFilter) | ||||
|       _globalFilter = globalFilter ?? [] | ||||
|       console.log("Global filters are", globalFilter); | ||||
|       _globalFilter = globalFilter ?? []; | ||||
|     }) | ||||
|   ) | ||||
|   ); | ||||
|   $: { | ||||
|     flayer = state.layerState.filteredLayers.get(selectedPreset?.layer?.id) | ||||
|     layerIsDisplayed = flayer?.isDisplayed | ||||
|     layerHasFilters = flayer?.hasFilter | ||||
|     flayer = state.layerState.filteredLayers.get(selectedPreset?.layer?.id); | ||||
|     layerIsDisplayed = flayer?.isDisplayed; | ||||
|     layerHasFilters = flayer?.hasFilter; | ||||
|   } | ||||
|   const t = Translations.t.general.add | ||||
|   const t = Translations.t.general.add; | ||||
| 
 | ||||
|   const zoom = state.mapProperties.zoom | ||||
|   const zoom = state.mapProperties.zoom; | ||||
| 
 | ||||
|   const isLoading = state.dataIsLoading | ||||
|   let preciseCoordinate: UIEventSource<{ lon: number; lat: number }> = new UIEventSource(undefined) | ||||
|   let snappedToObject: UIEventSource<string> = new UIEventSource<string>(undefined) | ||||
|   const isLoading = state.dataIsLoading; | ||||
|   let preciseCoordinate: UIEventSource<{ lon: number; lat: number }> = new UIEventSource(undefined); | ||||
|   let snappedToObject: UIEventSource<string> = new UIEventSource<string>(undefined); | ||||
| 
 | ||||
|   // Small helper variable: if the map is tapped, we should let the 'Next'-button grab some attention as users have to click _that_ to continue, not the map | ||||
|   let preciseInputIsTapped = false | ||||
|   let preciseInputIsTapped = false; | ||||
| 
 | ||||
|   let creating = false | ||||
|   let creating = false; | ||||
| 
 | ||||
|   /** | ||||
|    * Call when the user should restart the flow by clicking on the map, e.g. because they disabled filters. | ||||
|    * Will delete the lastclick-location | ||||
|    */ | ||||
|   function abort() { | ||||
|     state.selectedElement.setData(undefined) | ||||
|     state.selectedElement.setData(undefined); | ||||
|     // When aborted, we force the contributors to place the pin _again_ | ||||
|     // This is because there might be a nearby object that was disabled; this forces them to re-evaluate the map | ||||
|     state.lastClickObject.features.setData([]) | ||||
|     preciseInputIsTapped = false | ||||
|     state.lastClickObject.features.setData([]); | ||||
|     preciseInputIsTapped = false; | ||||
|   } | ||||
| 
 | ||||
|   async function confirm() { | ||||
|     creating = true | ||||
|     const location: { lon: number; lat: number } = preciseCoordinate.data | ||||
|     const snapTo: WayId | undefined = <WayId>snappedToObject.data | ||||
|     creating = true; | ||||
|     const location: { lon: number; lat: number } = preciseCoordinate.data; | ||||
|     const snapTo: WayId | undefined = <WayId>snappedToObject.data; | ||||
|     const tags: Tag[] = selectedPreset.preset.tags.concat( | ||||
|       ..._globalFilter.map((f) => f?.onNewPoint?.tags ?? []) | ||||
|     ) | ||||
|     console.log("Creating new point at", location, "snapped to", snapTo, "with tags", tags) | ||||
|     ); | ||||
|     console.log("Creating new point at", location, "snapped to", snapTo, "with tags", tags); | ||||
| 
 | ||||
|     let snapToWay: undefined | OsmWay = undefined | ||||
|     let snapToWay: undefined | OsmWay = undefined; | ||||
|     if (snapTo !== undefined && snapTo !== null) { | ||||
|       const downloaded = await state.osmObjectDownloader.DownloadObjectAsync(snapTo, 0) | ||||
|       const downloaded = await state.osmObjectDownloader.DownloadObjectAsync(snapTo, 0); | ||||
|       if (downloaded !== "deleted") { | ||||
|         snapToWay = downloaded | ||||
|         snapToWay = downloaded; | ||||
|       } | ||||
|     } | ||||
| 
 | ||||
|  | @ -113,44 +113,44 @@ | |||
|       theme: state.layout?.id ?? "unkown", | ||||
|       changeType: "create", | ||||
|       snapOnto: snapToWay, | ||||
|       reusePointWithinMeters: 1, | ||||
|     }) | ||||
|     await state.changes.applyAction(newElementAction) | ||||
|     state.newFeatures.features.ping() | ||||
|       reusePointWithinMeters: 1 | ||||
|     }); | ||||
|     await state.changes.applyAction(newElementAction); | ||||
|     state.newFeatures.features.ping(); | ||||
|     // The 'changes' should have created a new point, which added this into the 'featureProperties' | ||||
|     const newId = newElementAction.newElementId | ||||
|     console.log("Applied pending changes, fetching store for", newId) | ||||
|     const tagsStore = state.featureProperties.getStore(newId) | ||||
|     const newId = newElementAction.newElementId; | ||||
|     console.log("Applied pending changes, fetching store for", newId); | ||||
|     const tagsStore = state.featureProperties.getStore(newId); | ||||
|     if (!tagsStore) { | ||||
|       console.error("Bug: no tagsStore found for", newId) | ||||
|       console.error("Bug: no tagsStore found for", newId); | ||||
|     } | ||||
|     { | ||||
|       // Set some metainfo | ||||
|       const properties = tagsStore.data | ||||
|       const properties = tagsStore.data; | ||||
|       if (snapTo) { | ||||
|         // metatags (starting with underscore) are not uploaded, so we can safely mark this | ||||
|         delete properties["_referencing_ways"] | ||||
|         properties["_referencing_ways"] = `["${snapTo}"]` | ||||
|         delete properties["_referencing_ways"]; | ||||
|         properties["_referencing_ways"] = `["${snapTo}"]`; | ||||
|       } | ||||
|       properties["_backend"] = state.osmConnection.Backend() | ||||
|       properties["_last_edit:timestamp"] = new Date().toISOString() | ||||
|       const userdetails = state.osmConnection.userDetails.data | ||||
|       properties["_last_edit:contributor"] = userdetails.name | ||||
|       properties["_last_edit:uid"] = "" + userdetails.uid | ||||
|       tagsStore.ping() | ||||
|       properties["_backend"] = state.osmConnection.Backend(); | ||||
|       properties["_last_edit:timestamp"] = new Date().toISOString(); | ||||
|       const userdetails = state.osmConnection.userDetails.data; | ||||
|       properties["_last_edit:contributor"] = userdetails.name; | ||||
|       properties["_last_edit:uid"] = "" + userdetails.uid; | ||||
|       tagsStore.ping(); | ||||
|     } | ||||
|     const feature = state.indexedFeatures.featuresById.data.get(newId) | ||||
|     console.log("Selecting feature", feature, "and opening their popup") | ||||
|     abort() | ||||
|     state.selectedLayer.setData(selectedPreset.layer) | ||||
|     state.selectedElement.setData(feature) | ||||
|     tagsStore.ping() | ||||
|     const feature = state.indexedFeatures.featuresById.data.get(newId); | ||||
|     console.log("Selecting feature", feature, "and opening their popup"); | ||||
|     abort(); | ||||
|     state.selectedLayer.setData(selectedPreset.layer); | ||||
|     state.selectedElement.setData(feature); | ||||
|     tagsStore.ping(); | ||||
|   } | ||||
| 
 | ||||
|   function confirmSync() { | ||||
|     confirm() | ||||
|       .then((_) => console.debug("New point successfully handled")) | ||||
|       .catch((e) => console.error("Handling the new point went wrong due to", e)) | ||||
|       .catch((e) => console.error("Handling the new point went wrong due to", e)); | ||||
|   } | ||||
| </script> | ||||
| 
 | ||||
|  | @ -162,206 +162,212 @@ | |||
|   <LoginButton osmConnection={state.osmConnection} slot="not-logged-in"> | ||||
|     <Tr slot="message" t={Translations.t.general.add.pleaseLogin} /> | ||||
|   </LoginButton> | ||||
|   {#if $zoom < Constants.minZoomLevelToAddNewPoint} | ||||
|     <div class="alert"> | ||||
|       <Tr t={Translations.t.general.add.zoomInFurther} /> | ||||
|     </div> | ||||
|   {:else if $isLoading} | ||||
|     <div class="alert"> | ||||
|       <Loading> | ||||
|         <Tr t={Translations.t.general.add.stillLoading} /> | ||||
|       </Loading> | ||||
|     </div> | ||||
|   {:else if selectedPreset === undefined} | ||||
|     <!-- First, select the correct preset --> | ||||
|     <PresetList | ||||
|       {state} | ||||
|       on:select={(event) => { | ||||
|   <div class="h-full w-full"> | ||||
| 
 | ||||
|     {#if $zoom < Constants.minZoomLevelToAddNewPoint} | ||||
|       <div class="alert"> | ||||
|         <Tr t={Translations.t.general.add.zoomInFurther} /> | ||||
|       </div> | ||||
|     {:else if $isLoading} | ||||
|       <div class="alert"> | ||||
|         <Loading> | ||||
|           <Tr t={Translations.t.general.add.stillLoading} /> | ||||
|         </Loading> | ||||
|       </div> | ||||
|     {:else if selectedPreset === undefined} | ||||
|       <!-- First, select the correct preset --> | ||||
|       <PresetList | ||||
|         {state} | ||||
|         on:select={(event) => { | ||||
|         selectedPreset = event.detail | ||||
|       }} | ||||
|     /> | ||||
|   {:else if !$layerIsDisplayed} | ||||
|     <!-- Check that the layer is enabled, so that we don't add a duplicate --> | ||||
|     <div class="alert flex items-center justify-center"> | ||||
|       <EyeOffIcon class="w-8" /> | ||||
|       <Tr | ||||
|         t={Translations.t.general.add.layerNotEnabled.Subs({ layer: selectedPreset.layer.name })} | ||||
|       /> | ||||
|     </div> | ||||
|     {:else if !$layerIsDisplayed} | ||||
|       <!-- Check that the layer is enabled, so that we don't add a duplicate --> | ||||
|       <div class="alert flex items-center justify-center"> | ||||
|         <EyeOffIcon class="w-8" /> | ||||
|         <Tr | ||||
|           t={Translations.t.general.add.layerNotEnabled.Subs({ layer: selectedPreset.layer.name })} | ||||
|         /> | ||||
|       </div> | ||||
| 
 | ||||
|     <div class="flex flex-wrap-reverse md:flex-nowrap"> | ||||
|       <button | ||||
|         class="flex w-full gap-x-1" | ||||
|         on:click={() => { | ||||
|       <div class="flex flex-wrap-reverse md:flex-nowrap"> | ||||
|         <button | ||||
|           class="flex w-full gap-x-1" | ||||
|           on:click={() => { | ||||
|           abort() | ||||
|           state.guistate.openFilterView(selectedPreset.layer) | ||||
|         }} | ||||
|       > | ||||
|         <ToSvelte construct={Svg.layers_svg().SetClass("w-12")} /> | ||||
|         <Tr t={Translations.t.general.add.openLayerControl} /> | ||||
|       </button> | ||||
|         > | ||||
|           <ToSvelte construct={Svg.layers_svg().SetClass("w-12")} /> | ||||
|           <Tr t={Translations.t.general.add.openLayerControl} /> | ||||
|         </button> | ||||
| 
 | ||||
|       <button | ||||
|         class="primary flex w-full gap-x-1" | ||||
|         on:click={() => { | ||||
|         <button | ||||
|           class="primary flex w-full gap-x-1" | ||||
|           on:click={() => { | ||||
|           layerIsDisplayed.setData(true) | ||||
|           abort() | ||||
|         }} | ||||
|       > | ||||
|         <EyeIcon class="w-12" /> | ||||
|         <Tr t={Translations.t.general.add.enableLayer.Subs({ name: selectedPreset.layer.name })} /> | ||||
|       </button> | ||||
|     </div> | ||||
|   {:else if $layerHasFilters} | ||||
|     <!-- Some filters are enabled. The feature to add might already be mapped, but hidden --> | ||||
|     <div class="alert flex items-center justify-center"> | ||||
|       <EyeOffIcon class="w-8" /> | ||||
|       <Tr t={Translations.t.general.add.disableFiltersExplanation} /> | ||||
|     </div> | ||||
|     <div class="flex flex-wrap-reverse md:flex-nowrap"> | ||||
|       <button | ||||
|         class="primary flex w-full gap-x-1" | ||||
|         on:click={() => { | ||||
|         > | ||||
|           <EyeIcon class="w-12" /> | ||||
|           <Tr t={Translations.t.general.add.enableLayer.Subs({ name: selectedPreset.layer.name })} /> | ||||
|         </button> | ||||
|       </div> | ||||
|     {:else if $layerHasFilters} | ||||
|       <!-- Some filters are enabled. The feature to add might already be mapped, but hidden --> | ||||
|       <div class="alert flex items-center justify-center"> | ||||
|         <EyeOffIcon class="w-8" /> | ||||
|         <Tr t={Translations.t.general.add.disableFiltersExplanation} /> | ||||
|       </div> | ||||
|       <div class="flex flex-wrap-reverse md:flex-nowrap"> | ||||
|         <button | ||||
|           class="primary flex w-full gap-x-1" | ||||
|           on:click={() => { | ||||
|           abort() | ||||
|           state.layerState.filteredLayers.get(selectedPreset.layer.id).disableAllFilters() | ||||
|         }} | ||||
|       > | ||||
|         <EyeOffIcon class="w-12" /> | ||||
|         <Tr t={Translations.t.general.add.disableFilters} /> | ||||
|       </button> | ||||
|       <button | ||||
|         class="flex w-full gap-x-1" | ||||
|         on:click={() => { | ||||
|         > | ||||
|           <EyeOffIcon class="w-12" /> | ||||
|           <Tr t={Translations.t.general.add.disableFilters} /> | ||||
|         </button> | ||||
|         <button | ||||
|           class="flex w-full gap-x-1" | ||||
|           on:click={() => { | ||||
|           abort() | ||||
|           state.guistate.openFilterView(selectedPreset.layer) | ||||
|         }} | ||||
|       > | ||||
|         <ToSvelte construct={Svg.layers_svg().SetClass("w-12")} /> | ||||
|         <Tr t={Translations.t.general.add.openLayerControl} /> | ||||
|       </button> | ||||
|     </div> | ||||
|   {:else if !confirmedCategory} | ||||
|     <!-- Second, confirm the category --> | ||||
|     <h2 class="mr-12"> | ||||
|       <Tr | ||||
|         t={Translations.t.general.add.confirmTitle.Subs({ title: selectedPreset.preset.title })} | ||||
|       /> | ||||
|     </h2> | ||||
|         > | ||||
|           <ToSvelte construct={Svg.layers_svg().SetClass("w-12")} /> | ||||
|           <Tr t={Translations.t.general.add.openLayerControl} /> | ||||
|         </button> | ||||
|       </div> | ||||
|     {:else if !confirmedCategory} | ||||
|       <!-- Second, confirm the category --> | ||||
|       <h2 class="mr-12"> | ||||
|         <Tr | ||||
|           t={Translations.t.general.add.confirmTitle.Subs({ title: selectedPreset.preset.title })} | ||||
|         /> | ||||
|       </h2> | ||||
| 
 | ||||
|     <Tr t={Translations.t.general.add.confirmIntro} /> | ||||
|       <Tr t={Translations.t.general.add.confirmIntro} /> | ||||
| 
 | ||||
|     {#if selectedPreset.preset.description} | ||||
|       <Tr t={selectedPreset.preset.description} /> | ||||
|     {/if} | ||||
|       {#if selectedPreset.preset.description} | ||||
|         <Tr t={selectedPreset.preset.description} /> | ||||
|       {/if} | ||||
| 
 | ||||
|     {#if selectedPreset.preset.exampleImages} | ||||
|       <h3> | ||||
|         {#if selectedPreset.preset.exampleImages.length === 1} | ||||
|           <Tr t={Translations.t.general.example} /> | ||||
|         {:else} | ||||
|           <Tr t={Translations.t.general.examples} /> | ||||
|         {/if} | ||||
|       </h3> | ||||
|       <span class="flex flex-wrap items-stretch"> | ||||
|       {#if selectedPreset.preset.exampleImages} | ||||
|         <h3> | ||||
|           {#if selectedPreset.preset.exampleImages.length === 1} | ||||
|             <Tr t={Translations.t.general.example} /> | ||||
|           {:else} | ||||
|             <Tr t={Translations.t.general.examples} /> | ||||
|           {/if} | ||||
|         </h3> | ||||
|         <span class="flex flex-wrap items-stretch"> | ||||
|         {#each selectedPreset.preset.exampleImages as src} | ||||
|           <img {src} class="m-1 h-64 w-auto rounded-lg" /> | ||||
|         {/each} | ||||
|       </span> | ||||
|     {/if} | ||||
|     <TagHint | ||||
|       embedIn={(tags) => t.presetInfo.Subs({ tags })} | ||||
|       {state} | ||||
|       tags={new And(selectedPreset.preset.tags)} | ||||
|     /> | ||||
| 
 | ||||
|     <div class="flex w-full flex-wrap-reverse md:flex-nowrap"> | ||||
|       <BackButton on:click={() => (selectedPreset = undefined)} clss="w-full"> | ||||
|         <Tr t={t.backToSelect} /> | ||||
|       </BackButton> | ||||
| 
 | ||||
|       <NextButton on:click={() => (confirmedCategory = true)} clss="primary w-full"> | ||||
|         <div slot="image" class="relative"> | ||||
|           <FromHtml src={selectedPreset.icon} /> | ||||
|           <img class="absolute bottom-0 right-0 h-4 w-4" src="./assets/svg/confirm.svg" /> | ||||
|         </div> | ||||
|         <div class="w-full"> | ||||
|           <Tr t={selectedPreset.text} /> | ||||
|         </div> | ||||
|       </NextButton> | ||||
|     </div> | ||||
|   {:else if _globalFilter?.length > 0 && _globalFilter?.length > checkedOfGlobalFilters} | ||||
|     <Tr t={_globalFilter[checkedOfGlobalFilters].onNewPoint?.safetyCheck} cls="mx-12" /> | ||||
|     <SubtleButton | ||||
|       on:click={() => { | ||||
|         checkedOfGlobalFilters = checkedOfGlobalFilters + 1 | ||||
|       }} | ||||
|     > | ||||
|       <img | ||||
|         slot="image" | ||||
|         src={_globalFilter[checkedOfGlobalFilters].onNewPoint?.icon ?? "./assets/svg/confirm.svg"} | ||||
|         class="h-12 w-12" | ||||
|       {/if} | ||||
|       <TagHint | ||||
|         embedIn={(tags) => t.presetInfo.Subs({ tags })} | ||||
|         {state} | ||||
|         tags={new And(selectedPreset.preset.tags)} | ||||
|       /> | ||||
|       <Tr | ||||
|         slot="message" | ||||
|         t={_globalFilter[checkedOfGlobalFilters].onNewPoint?.confirmAddNew.Subs({ | ||||
|           preset: selectedPreset.preset, | ||||
|         })} | ||||
|       /> | ||||
|     </SubtleButton> | ||||
|     <SubtleButton | ||||
|       on:click={() => { | ||||
|         globalFilter.setData([]) | ||||
|         abort() | ||||
|       }} | ||||
|     > | ||||
|       <img slot="image" src="./assets/svg/close.svg" class="h-8 w-8" /> | ||||
|       <Tr slot="message" t={Translations.t.general.cancel} /> | ||||
|     </SubtleButton> | ||||
|   {:else if !creating} | ||||
|     <div class="relative w-full p-1"> | ||||
|       <div class="h-96 max-h-screen w-full overflow-hidden rounded-xl"> | ||||
|         <NewPointLocationInput | ||||
|           on:click={() => { | ||||
|             preciseInputIsTapped = true | ||||
|           }} | ||||
|           value={preciseCoordinate} | ||||
|           snappedTo={snappedToObject} | ||||
|           {state} | ||||
|           {coordinate} | ||||
|           targetLayer={selectedPreset.layer} | ||||
|           snapToLayers={selectedPreset.preset.preciseInput.snapToLayers} | ||||
|         /> | ||||
|       </div> | ||||
| 
 | ||||
|       <div | ||||
|         class={twJoin( | ||||
|           !preciseInputIsTapped && "hidden", | ||||
|           "absolute top-0 flex w-full justify-center p-12" | ||||
|         )} | ||||
|       > | ||||
|         <NextButton on:click={confirmSync} clss="primary w-fit"> | ||||
|           <div class="flex w-full justify-end gap-x-2"> | ||||
|             <Tr t={Translations.t.general.add.confirmLocation} /> | ||||
|       <div class="flex w-full flex-wrap-reverse md:flex-nowrap"> | ||||
|         <BackButton on:click={() => (selectedPreset = undefined)} clss="w-full"> | ||||
|           <Tr t={t.backToSelect} /> | ||||
|         </BackButton> | ||||
| 
 | ||||
|         <NextButton on:click={() => (confirmedCategory = true)} clss="primary w-full"> | ||||
|           <div slot="image" class="relative"> | ||||
|             <FromHtml src={selectedPreset.icon} /> | ||||
|             <img class="absolute bottom-0 right-0 h-4 w-4" src="./assets/svg/confirm.svg" /> | ||||
|           </div> | ||||
|           <div class="w-full"> | ||||
|             <Tr t={selectedPreset.text} /> | ||||
|           </div> | ||||
|         </NextButton> | ||||
|       </div> | ||||
|     {:else if _globalFilter?.length > 0 && _globalFilter?.length > checkedOfGlobalFilters} | ||||
|       <Tr t={_globalFilter[checkedOfGlobalFilters].onNewPoint?.safetyCheck} cls="mx-12" /> | ||||
|       <SubtleButton | ||||
|         on:click={() => { | ||||
|         checkedOfGlobalFilters = checkedOfGlobalFilters + 1 | ||||
|       }} | ||||
|       > | ||||
|         <img | ||||
|           slot="image" | ||||
|           src={_globalFilter[checkedOfGlobalFilters].onNewPoint?.icon ?? "./assets/svg/confirm.svg"} | ||||
|           class="h-12 w-12" | ||||
|         /> | ||||
|         <Tr | ||||
|           slot="message" | ||||
|           t={_globalFilter[checkedOfGlobalFilters].onNewPoint?.confirmAddNew.Subs({ | ||||
|           preset: selectedPreset.preset, | ||||
|         })} | ||||
|         /> | ||||
|       </SubtleButton> | ||||
|       <SubtleButton | ||||
|         on:click={() => { | ||||
|         globalFilter.setData([]) | ||||
|         abort() | ||||
|       }} | ||||
|       > | ||||
|         <img slot="image" src="./assets/svg/close.svg" class="h-8 w-8" /> | ||||
|         <Tr slot="message" t={Translations.t.general.cancel} /> | ||||
|       </SubtleButton> | ||||
|     {:else if !creating} | ||||
|       <div class="flex flex-col h-full"> | ||||
|         <div class="relative min-h-20 h-full w-full p-1 "> | ||||
|           <div class="h-full w-full overflow-hidden rounded-xl"> | ||||
|             <NewPointLocationInput | ||||
|               on:click={() => { | ||||
|             preciseInputIsTapped = true | ||||
|           }} | ||||
|               value={preciseCoordinate} | ||||
|               snappedTo={snappedToObject} | ||||
|               {state} | ||||
|               {coordinate} | ||||
|               targetLayer={selectedPreset.layer} | ||||
|               snapToLayers={selectedPreset.preset.preciseInput.snapToLayers} | ||||
|             /> | ||||
|           </div> | ||||
| 
 | ||||
|       <div class="absolute bottom-0 left-0 p-4"> | ||||
|         <OpenBackgroundSelectorButton {state} /> | ||||
|       </div> | ||||
|     </div> | ||||
|     <div class="flex flex-wrap-reverse md:flex-nowrap"> | ||||
|       <BackButton on:click={() => (selectedPreset = undefined)} clss="w-full"> | ||||
|         <Tr t={t.backToSelect} /> | ||||
|       </BackButton> | ||||
|           <div | ||||
|             class={twJoin( | ||||
|           !preciseInputIsTapped && "hidden", | ||||
|           "absolute top-0 flex w-full justify-center p-12" | ||||
|         )} | ||||
|           > | ||||
|             <!-- This is an _extra_ button that appears when the map is tapped - see usertest 2023-01-07 --> | ||||
|             <NextButton on:click={confirmSync} clss="primary w-fit"> | ||||
|               <div class="flex w-full justify-end gap-x-2"> | ||||
|                 <Tr t={Translations.t.general.add.confirmLocation} /> | ||||
|               </div> | ||||
|             </NextButton> | ||||
|           </div> | ||||
| 
 | ||||
|       <NextButton on:click={confirm} clss={"primary w-full"}> | ||||
|         <div class="flex w-full justify-end gap-x-2"> | ||||
|           <Tr t={Translations.t.general.add.confirmLocation} /> | ||||
|           <div class="absolute bottom-0 left-0 p-4"> | ||||
|             <OpenBackgroundSelectorButton {state} /> | ||||
|           </div> | ||||
|         </div> | ||||
|       </NextButton> | ||||
|     </div> | ||||
|   {:else} | ||||
|     <Loading>Creating point...</Loading> | ||||
|   {/if} | ||||
|         <div class="flex flex-wrap-reverse md:flex-nowrap"> | ||||
|           <BackButton on:click={() => (selectedPreset = undefined)} clss="w-full"> | ||||
|             <Tr t={t.backToSelect} /> | ||||
|           </BackButton> | ||||
| 
 | ||||
|           <NextButton on:click={confirm} clss={"primary w-full"}> | ||||
|             <div class="flex w-full justify-end gap-x-2"> | ||||
|               <Tr t={Translations.t.general.add.confirmLocation} /> | ||||
|             </div> | ||||
|           </NextButton> | ||||
|         </div> | ||||
|       </div> | ||||
|     {:else} | ||||
|       <Loading>Creating point...</Loading> | ||||
|     {/if} | ||||
|   </div> | ||||
| </LoginToggle> | ||||
|  |  | |||
|  | @ -95,7 +95,7 @@ | |||
|   } | ||||
| </script> | ||||
| 
 | ||||
| <div bind:this={questionboxElem}> | ||||
| <div bind:this={questionboxElem} class="marker-questionbox-root" class:hidden={_questionsToAsk.length === 0 && skipped === 0 && answered === 0}> | ||||
|   {#if _questionsToAsk.length === 0} | ||||
|     {#if skipped + answered > 0} | ||||
|       <div class="thanks"> | ||||
|  |  | |||
|  | @ -20,6 +20,8 @@ | |||
| 
 | ||||
|   export let editingEnabled: Store<boolean> | undefined = state?.featureSwitchUserbadge; | ||||
|    | ||||
|   export let clss = config.classes.join(" ") | ||||
|    | ||||
|   export let highlightedRendering: UIEventSource<string> = undefined; | ||||
|   export let showQuestionIfUnknown: boolean = false; | ||||
|   /** | ||||
|  | @ -71,7 +73,7 @@ | |||
|   } | ||||
| </script> | ||||
| 
 | ||||
| <div bind:this={htmlElem} class=""> | ||||
| <div bind:this={htmlElem} class={clss}> | ||||
|   {#if config.question && (!editingEnabled || $editingEnabled)} | ||||
|     {#if editMode} | ||||
|       <TagRenderingQuestion {config} {tags} {selectedElement} {state} {layer}> | ||||
|  | @ -106,7 +108,7 @@ | |||
|       </div> | ||||
|     {/if} | ||||
|   {:else} | ||||
|     <div class="overflow-hidden p-2"> | ||||
|     <div class="overflow-hidden p-2 w-full"> | ||||
|       <TagRenderingAnswer {config} {tags} {selectedElement} {state} {layer} /> | ||||
|     </div> | ||||
|   {/if} | ||||
|  |  | |||
							
								
								
									
										0
									
								
								src/UI/Studio/ShowConversionMessage.svelte
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										0
									
								
								src/UI/Studio/ShowConversionMessage.svelte
									
										
									
									
									
										Normal file
									
								
							
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue