forked from MapComplete/MapComplete
		
	Merge develop
This commit is contained in:
		
						commit
						3be286c2b1
					
				
					 30 changed files with 3315 additions and 2458 deletions
				
			
		|  | @ -1,48 +0,0 @@ | |||
| <script lang="ts"> | ||||
|   import { Store } from "../../Logic/UIEventSource" | ||||
|   import { onDestroy, onMount } from "svelte" | ||||
| 
 | ||||
|   let elem: HTMLElement | ||||
|   let targetOuter: HTMLElement | ||||
|   export let isOpened: Store<boolean> | ||||
|   export let moveTo: Store<HTMLElement> | ||||
| 
 | ||||
|   export let debug: string | ||||
|   function copySizeOf(htmlElem: HTMLElement) { | ||||
|     const target = htmlElem.getBoundingClientRect() | ||||
|     elem.style.left = target.x + "px" | ||||
|     elem.style.top = target.y + "px" | ||||
|     elem.style.width = target.width + "px" | ||||
|     elem.style.height = target.height + "px" | ||||
|   } | ||||
| 
 | ||||
|   function animate(opened: boolean) { | ||||
|     const moveToElem = moveTo.data | ||||
|     if (opened) { | ||||
|       copySizeOf(targetOuter) | ||||
|       elem.style.background = "var(--background-color)" | ||||
|     } else if (moveToElem !== undefined) { | ||||
|       copySizeOf(moveToElem) | ||||
|       elem.style.background = "#ffffff00" | ||||
|     } else { | ||||
|       elem.style.left = "0px" | ||||
|       elem.style.top = "0px" | ||||
|       elem.style.background = "#ffffff00" | ||||
|     } | ||||
|   } | ||||
| 
 | ||||
|   onDestroy(isOpened.addCallback((opened) => animate(opened))) | ||||
|   onMount(() => requestAnimationFrame(() => animate(isOpened.data))) | ||||
| </script> | ||||
| 
 | ||||
| <div class={"pointer-events-none invisible absolute bottom-0 right-0 h-full w-screen p-4 md:p-6"}> | ||||
|   <div class="content h-full" bind:this={targetOuter} style="background: red" /> | ||||
| </div> | ||||
| 
 | ||||
| <div | ||||
|   bind:this={elem} | ||||
|   class="low-interaction pointer-events-none absolute bottom-0 right-0 rounded-2xl" | ||||
|   style="transition: all 0.5s ease-out, background-color 1.4s ease-out; background: var(--background-color);" | ||||
| > | ||||
|   <!-- Classes should be the same as the 'floatoaver' --> | ||||
| </div> | ||||
							
								
								
									
										30
									
								
								src/UI/Base/DrawerLeft.svelte
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										30
									
								
								src/UI/Base/DrawerLeft.svelte
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,30 @@ | |||
| <script lang="ts"> | ||||
|   import { Drawer } from "flowbite-svelte" | ||||
|   import { sineIn } from "svelte/easing" | ||||
|   import { UIEventSource } from "../../Logic/UIEventSource.js" | ||||
| 
 | ||||
|   export let shown: UIEventSource<boolean>; | ||||
|   let transitionParams = { | ||||
|     x: -320, | ||||
|     duration: 200, | ||||
|     easing: sineIn | ||||
|   }; | ||||
|   let hidden = !shown.data | ||||
|   $: { | ||||
|     shown.setData(!hidden) | ||||
|   } | ||||
|   shown.addCallback(sh => { | ||||
|     hidden = !sh | ||||
|   }) | ||||
| </script> | ||||
| 
 | ||||
| 
 | ||||
| <Drawer placement="left" | ||||
|         transitionType="fly" {transitionParams} | ||||
|         divClass = "overflow-y-auto z-50 " | ||||
|         bind:hidden={hidden}> | ||||
|   <slot> | ||||
|     CONTENTS | ||||
|   </slot> | ||||
| </Drawer> | ||||
| 
 | ||||
|  | @ -13,7 +13,7 @@ | |||
| </script> | ||||
| 
 | ||||
| <a | ||||
|   href={Utils.prepareHref(href)} | ||||
|   href={Utils.prepareHref(href) } | ||||
|   aria-label={ariaLabel} | ||||
|   title={ariaLabel} | ||||
|   target={newTab ? "_blank" : undefined} | ||||
|  |  | |||
|  | @ -1,18 +1,19 @@ | |||
| <script lang="ts"> | ||||
|   import { OsmConnection } from "../../Logic/Osm/OsmConnection" | ||||
|   import Logout from "../../assets/svg/Logout.svelte" | ||||
|   import Translations from "../i18n/Translations" | ||||
|   import Tr from "./Tr.svelte" | ||||
|   import ArrowRightOnRectangle from "@babeard/svelte-heroicons/solid/ArrowRightOnRectangle" | ||||
| 
 | ||||
|   export let osmConnection: OsmConnection | ||||
|   export let clss = "" | ||||
| </script> | ||||
| 
 | ||||
| <button | ||||
|   class={clss} | ||||
|   on:click={() => { | ||||
|     osmConnection.LogOut() | ||||
|   }} | ||||
| > | ||||
|   <ArrowRightOnRectangle class="h-6 w-6" /> | ||||
|   <ArrowRightOnRectangle class="h-6 w-6 max-h-full" /> | ||||
|   <Tr t={Translations.t.general.logout} /> | ||||
| </button> | ||||
|  |  | |||
|  | @ -14,15 +14,10 @@ | |||
|   export let arialabel: Translation = undefined | ||||
|   export let arialabelDynamic: Store<Translation> = new ImmutableStore(arialabel) | ||||
|   let arialabelString = arialabelDynamic.bind((tr) => tr?.current) | ||||
|   export let htmlElem: UIEventSource<HTMLElement> = undefined | ||||
|   let _htmlElem: HTMLElement | ||||
|   $: { | ||||
|     htmlElem?.setData(_htmlElem) | ||||
|   } | ||||
| 
 | ||||
| </script> | ||||
| 
 | ||||
| <button | ||||
|   bind:this={_htmlElem} | ||||
|   on:click={(e) => dispatch("click", e)} | ||||
|   on:keydown | ||||
|   use:ariaLabelStore={arialabelString} | ||||
|  |  | |||
|  | @ -35,8 +35,8 @@ | |||
| 
 | ||||
| {#if $showButton} | ||||
|   <div class="flex flex-col"> | ||||
|     <button class="as-link" on:click={openJosm}> | ||||
|       <Josm_logo class="h-6 w-6 pr-2" /> | ||||
|     <button class="as-link sidebar-button" on:click={openJosm}> | ||||
|       <Josm_logo class="h-6 w-6" /> | ||||
|       <Tr t={t.editJosm} /> | ||||
|     </button> | ||||
| 
 | ||||
|  |  | |||
							
								
								
									
										46
									
								
								src/UI/Base/Page.svelte
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										46
									
								
								src/UI/Base/Page.svelte
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,46 @@ | |||
| <script lang="ts"> | ||||
|   // A fake 'page' which can be shown; kind of a modal | ||||
|   import { UIEventSource } from "../../Logic/UIEventSource" | ||||
|   import { Modal } from "flowbite-svelte" | ||||
| 
 | ||||
|   export let shown: UIEventSource<boolean> | ||||
|   let _shown = false | ||||
|   export let onlyLink: boolean = false | ||||
|   shown.addCallbackAndRun(sh => { | ||||
|     _shown = sh | ||||
|   }) | ||||
|   export let fullscreen: boolean = false | ||||
| 
 | ||||
|   const shared = "in-page normal-background dark:bg-gray-800 rounded-lg border-gray-200 dark:border-gray-700 border-gray-200 dark:border-gray-700 divide-gray-200 dark:divide-gray-700 shadow-md" | ||||
|   let defaultClass = "relative flex flex-col mx-auto w-full divide-y " + shared | ||||
|   if (fullscreen) { | ||||
|     defaultClass = shared | ||||
|   } | ||||
|   let dialogClass = "fixed top-0 start-0 end-0 h-modal inset-0 z-50 w-full p-4 flex" | ||||
|   if (fullscreen) { | ||||
|     dialogClass += " h-full-child" | ||||
|   } | ||||
|   let bodyClass = "h-full p-4 md:p-5 space-y-4 flex-1 overflow-y-auto overscroll-contain" | ||||
|   let headerClass = "flex justify-between items-center p-2 px-4 md:px-5 rounded-t-lg"; | ||||
| </script> | ||||
| 
 | ||||
| {#if !onlyLink} | ||||
|   <Modal open={_shown} on:close={() => shown.set(false)} outsideclose | ||||
|          size="xl" | ||||
|          {defaultClass} {bodyClass} {dialogClass} {headerClass} | ||||
|          color="none"> | ||||
|     <h1 slot="header" class="w-full"> | ||||
|       <slot name="header" /> | ||||
|     </h1> | ||||
|     <slot /> | ||||
|     {#if $$slots.footer} | ||||
|       <slot name="footer" /> | ||||
|     {/if} | ||||
|   </Modal> | ||||
| {:else} | ||||
|   <button class="as-link sidebar-button" on:click={() => shown.setData(true)}> | ||||
|     <slot name="link"> | ||||
|     <slot name="header" /> | ||||
|     </slot> | ||||
|   </button> | ||||
| {/if} | ||||
|  | @ -6,4 +6,7 @@ | |||
|   <div class="flex h-full flex-col overflow-auto border-b-2 p-4"> | ||||
|     <slot /> | ||||
|   </div> | ||||
| 
 | ||||
| 
 | ||||
|   <slot class="border-t-gray-300 mt-1" name="footer" /> | ||||
| </div> | ||||
|  |  | |||
|  | @ -1,103 +0,0 @@ | |||
| <script lang="ts"> | ||||
|   import Translations from "../i18n/Translations" | ||||
|   import { Utils } from "../../Utils" | ||||
|   import Constants from "../../Models/Constants" | ||||
|   import Tr from "../Base/Tr.svelte" | ||||
|   import Add from "../../assets/svg/Add.svelte" | ||||
|   import Github from "../../assets/svg/Github.svelte" | ||||
|   import Mastodon from "../../assets/svg/Mastodon.svelte" | ||||
|   import Liberapay from "../../assets/svg/Liberapay.svelte" | ||||
|   import { EyeIcon } from "@rgossiaux/svelte-heroicons/solid" | ||||
|   import MapillaryLink from "./MapillaryLink.svelte" | ||||
|   import OpenJosm from "../Base/OpenJosm.svelte" | ||||
|   import OpenIdEditor from "./OpenIdEditor.svelte" | ||||
|   import If from "../Base/If.svelte" | ||||
|   import Community from "../../assets/svg/Community.svelte" | ||||
|   import Bug from "../../assets/svg/Bug.svelte" | ||||
|   import ThemeViewState from "../../Models/ThemeViewState" | ||||
|   import DocumentChartBar from "@babeard/svelte-heroicons/outline/DocumentChartBar" | ||||
|   import DocumentMagnifyingGlass from "@babeard/svelte-heroicons/outline/DocumentMagnifyingGlass" | ||||
| 
 | ||||
|   export let state: ThemeViewState | ||||
| 
 | ||||
|   let layout = state.layout | ||||
|   let featureSwitches = state.featureSwitches | ||||
|   let showHome = featureSwitches.featureSwitchBackToThemeOverview | ||||
| </script> | ||||
| 
 | ||||
| <div class="link-underline links-w-full m-2 flex flex-col gap-y-1"> | ||||
|   <Tr t={Translations.t.general.aboutMapComplete.intro} /> | ||||
| 
 | ||||
|   {#if $showHome} | ||||
|     <a class="flex" href={Utils.HomepageLink()}> | ||||
|       <Add class="h-6 w-6" /> | ||||
|       {#if Utils.isIframe} | ||||
|         <Tr t={Translations.t.general.seeIndex} /> | ||||
|       {:else} | ||||
|         <Tr t={Translations.t.general.backToIndex} /> | ||||
|       {/if} | ||||
|     </a> | ||||
|   {/if} | ||||
| 
 | ||||
|   <a class="flex" href="https://github.com/pietervdvn/MapComplete/" target="_blank"> | ||||
|     <Github class="h-6 w-6" /> | ||||
|     <Tr t={Translations.t.general.attribution.gotoSourceCode} /> | ||||
|   </a> | ||||
| 
 | ||||
|   <a class="flex" href="https://github.com/pietervdvn/MapComplete/issues" target="_blank"> | ||||
|     <Bug class="h-6 w-6" /> | ||||
|     <Tr t={Translations.t.general.attribution.openIssueTracker} /> | ||||
|   </a> | ||||
| 
 | ||||
|   {#if layout.official} | ||||
|     <a | ||||
|       class="flex" | ||||
|       href={"https://github.com/pietervdvn/MapComplete/blob/develop/Docs/Themes/" + | ||||
|         layout.id + | ||||
|         ".md"} | ||||
|       target="_blank" | ||||
|     > | ||||
|       <DocumentMagnifyingGlass class="h-6 w-6" /> | ||||
|       <Tr | ||||
|         t={Translations.t.general.attribution.openThemeDocumentation.Subs({ | ||||
|           name: layout.title, | ||||
|         })} | ||||
|       /> | ||||
|     </a> | ||||
| 
 | ||||
|     <a class="flex" href={Utils.OsmChaLinkFor(31, layout.id)}> | ||||
|       <DocumentChartBar class="h-6 w-6" /> | ||||
|       <Tr t={Translations.t.general.attribution.openOsmcha.Subs({ theme: layout.title })} /> | ||||
|     </a> | ||||
|   {/if} | ||||
| 
 | ||||
|   <a class="flex" href="https://en.osm.town/@MapComplete" target="_blank"> | ||||
|     <Mastodon class="h-6 w-6" /> | ||||
|     <Tr t={Translations.t.general.attribution.followOnMastodon} /> | ||||
|   </a> | ||||
| 
 | ||||
|   <a class="flex" href="https://liberapay.com/pietervdvn/" target="_blank"> | ||||
|     <Liberapay class="h-6 w-6" /> | ||||
|     <Tr t={Translations.t.general.attribution.donate} /> | ||||
|   </a> | ||||
| 
 | ||||
|   <button class="as-link" on:click={() => state.guistate.communityIndexPanelIsOpened.setData(true)}> | ||||
|     <Community class="h-6 w-6" /> | ||||
|     <Tr t={Translations.t.communityIndex.title} /> | ||||
|   </button> | ||||
| 
 | ||||
|   <If condition={featureSwitches.featureSwitchEnableLogin}> | ||||
|     <OpenIdEditor mapProperties={state.mapProperties} /> | ||||
|     <OpenJosm {state} /> | ||||
|     <MapillaryLink large={false} mapProperties={state.mapProperties} /> | ||||
|   </If> | ||||
| 
 | ||||
|   <button class="as-link" on:click={() => state.guistate.privacyPanelIsOpened.setData(true)}> | ||||
|     <EyeIcon class="h-6 w-6 pr-1" /> | ||||
|     <Tr t={Translations.t.privacy.title} /> | ||||
|   </button> | ||||
| 
 | ||||
|   <div class="subtle"> | ||||
|     {Constants.vNumber} | ||||
|   </div> | ||||
| </div> | ||||
							
								
								
									
										22
									
								
								src/UI/BigComponents/CopyrightAllIcons.svelte
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										22
									
								
								src/UI/BigComponents/CopyrightAllIcons.svelte
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,22 @@ | |||
| <script lang="ts"> | ||||
|   import type { SpecialVisualizationState } from "../SpecialVisualization" | ||||
|   import IconCopyrightPanel from "./CopyrightSingleIcon.svelte" | ||||
|   import licenses from "../../assets/generated/license_info.json" | ||||
|   import type SmallLicense from "../../Models/smallLicense" | ||||
| 
 | ||||
|   export let state: SpecialVisualizationState | ||||
| 
 | ||||
|   let layoutToUse = state.layout | ||||
|   let iconAttributions: string[] = layoutToUse.getUsedImages() | ||||
| 
 | ||||
|   const allLicenses = {} | ||||
|   for (const key in licenses) { | ||||
|     const license: SmallLicense = licenses[key] | ||||
|     allLicenses[license.path] = license | ||||
|   } | ||||
| 
 | ||||
| </script> | ||||
| 
 | ||||
| {#each iconAttributions as iconAttribution} | ||||
|   <IconCopyrightPanel iconPath={iconAttribution} license={allLicenses[iconAttribution]} /> | ||||
| {/each} | ||||
|  | @ -4,11 +4,7 @@ | |||
|   import contributors from "../../assets/contributors.json" | ||||
|   import translators from "../../assets/translators.json" | ||||
|   import { Translation, TypedTranslation } from "../i18n/Translation" | ||||
|   import AccordionSingle from "../Flowbite/AccordionSingle.svelte" | ||||
|   import Tr from "../Base/Tr.svelte" | ||||
|   import IconCopyrightPanel from "./IconCopyrightPanel.svelte" | ||||
|   import licenses from "../../assets/generated/license_info.json" | ||||
|   import type SmallLicense from "../../Models/smallLicense" | ||||
|   import Constants from "../../Models/Constants" | ||||
|   import ContributorCount from "../../Logic/ContributorCount" | ||||
|   import BaseUIElement from "../BaseUIElement" | ||||
|  | @ -24,7 +20,6 @@ | |||
|   const t = Translations.t.general.attribution | ||||
|   const layoutToUse = state.layout | ||||
| 
 | ||||
|   const iconAttributions: string[] = layoutToUse.getUsedImages() | ||||
| 
 | ||||
|   let maintainer: Translation = undefined | ||||
|   if (layoutToUse.credits !== undefined && layoutToUse.credits !== "") { | ||||
|  | @ -53,11 +48,7 @@ | |||
|     return Translations.t.general.attribution.attributionBackgroundLayer.Subs(props) | ||||
|   }) | ||||
| 
 | ||||
|   const allLicenses = {} | ||||
|   for (const key in licenses) { | ||||
|     const license: SmallLicense = licenses[key] | ||||
|     allLicenses[license.path] = license | ||||
|   } | ||||
| 
 | ||||
| 
 | ||||
|   function calculateDataContributions(contributions: Map<string, number>): Translation { | ||||
|     if (contributions === undefined) { | ||||
|  | @ -121,9 +112,6 @@ | |||
| </script> | ||||
| 
 | ||||
| <div class="link-underline flex flex-col gap-y-4"> | ||||
|   <h3> | ||||
|     <Tr t={t.attributionTitle} /> | ||||
|   </h3> | ||||
|   <div class="flex items-center gap-x-2"> | ||||
|     <Osm_logo class="h-8 w-8 shrink-0" /> | ||||
|     <Tr t={t.attributionContent} /> | ||||
|  | @ -159,14 +147,6 @@ | |||
|     <Tr t={codeContributors(translators, t.translatedBy)} /> | ||||
|   </div> | ||||
| 
 | ||||
|   <AccordionSingle> | ||||
|     <div slot="header"> | ||||
|       <Tr t={t.iconAttribution.title} /> | ||||
|     </div> | ||||
|     {#each iconAttributions as iconAttribution} | ||||
|       <IconCopyrightPanel iconPath={iconAttribution} license={allLicenses[iconAttribution]} /> | ||||
|     {/each} | ||||
|   </AccordionSingle> | ||||
| 
 | ||||
|   <div class="self-end"> | ||||
|     MapComplete {Constants.vNumber} | ||||
|  |  | |||
|  | @ -9,9 +9,11 @@ | |||
|   import Translations from "../i18n/Translations" | ||||
|   import Tr from "../Base/Tr.svelte" | ||||
|   import Filter from "../../assets/svg/Filter.svelte" | ||||
|   import TitledPanel from "../Base/TitledPanel.svelte" | ||||
|   import Page from "../Base/Page.svelte" | ||||
| 
 | ||||
|   export let state: ThemeViewState | ||||
|   export let onlyLink: boolean | ||||
| 
 | ||||
|   let layout = state.layout | ||||
| 
 | ||||
|   let allEnabled: boolean | ||||
|  | @ -47,8 +49,12 @@ | |||
|   } | ||||
| </script> | ||||
| 
 | ||||
| <TitledPanel> | ||||
|   <div class="mr-10 flex w-full flex-wrap items-center justify-between" slot="title"> | ||||
| <Page {onlyLink} shown={state.guistate.pageStates.filter}> | ||||
|   <div class="flex" slot="link"> | ||||
|     <Filter class="h-6 w-6" /> | ||||
|     <Tr t={Translations.t.general.menu.filter} /> | ||||
|   </div> | ||||
|   <div class="mr-16 flex w-full flex-wrap items-center justify-between" slot="header"> | ||||
|     <div class="flex"> | ||||
|       <Filter class="h-6 w-6 pr-2" /> | ||||
|       <Tr t={Translations.t.general.menu.filter} /> | ||||
|  | @ -80,4 +86,4 @@ | |||
|       zoomlevel={state.mapProperties.zoom} | ||||
|     /> | ||||
|   {/each} | ||||
| </TitledPanel> | ||||
| </Page> | ||||
|  | @ -15,10 +15,6 @@ | |||
|   } | ||||
| </script> | ||||
| 
 | ||||
| <AccordionSingle> | ||||
|   <div slot="header"> | ||||
|     <Tr t={t.title} /> | ||||
|   </div> | ||||
|   <Tr t={t.intro} /> | ||||
|   <table> | ||||
|     <tr> | ||||
|  | @ -47,4 +43,3 @@ | |||
|       </tr> | ||||
|     {/each} | ||||
|   </table> | ||||
| </AccordionSingle> | ||||
|  |  | |||
							
								
								
									
										361
									
								
								src/UI/BigComponents/MenuDrawer.svelte
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										361
									
								
								src/UI/BigComponents/MenuDrawer.svelte
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,361 @@ | |||
| <script lang="ts"> | ||||
| 
 | ||||
|   // All the relevant links | ||||
|   import ThemeViewState from "../../Models/ThemeViewState" | ||||
|   import Translations from "../i18n/Translations" | ||||
|   import { CogIcon, EyeIcon, HeartIcon } from "@rgossiaux/svelte-heroicons/solid" | ||||
|   import Page from "../Base/Page.svelte" | ||||
|   import PrivacyPolicy from "./PrivacyPolicy.svelte" | ||||
|   import Tr from "../Base/Tr.svelte" | ||||
|   import If from "../Base/If.svelte" | ||||
|   import CommunityIndexView from "./CommunityIndexView.svelte" | ||||
|   import Community from "../../assets/svg/Community.svelte" | ||||
|   import LoginToggle from "../Base/LoginToggle.svelte" | ||||
|   import { Sidebar } from "flowbite-svelte" | ||||
|   import HotkeyTable from "./HotkeyTable.svelte" | ||||
|   import { Utils } from "../../Utils" | ||||
|   import Constants from "../../Models/Constants" | ||||
|   import Mastodon from "../../assets/svg/Mastodon.svelte" | ||||
|   import Liberapay from "../../assets/svg/Liberapay.svelte" | ||||
|   import DocumentMagnifyingGlass from "@babeard/svelte-heroicons/outline/DocumentMagnifyingGlass" | ||||
|   import DocumentChartBar from "@babeard/svelte-heroicons/outline/DocumentChartBar" | ||||
|   import OpenIdEditor from "./OpenIdEditor.svelte" | ||||
|   import OpenJosm from "../Base/OpenJosm.svelte" | ||||
|   import MapillaryLink from "./MapillaryLink.svelte" | ||||
|   import Github from "../../assets/svg/Github.svelte" | ||||
|   import Bug from "../../assets/svg/Bug.svelte" | ||||
|   import Add from "../../assets/svg/Add.svelte" | ||||
|   import CopyrightPanel from "./CopyrightPanel.svelte" | ||||
|   import CopyrightAllIcons from "./CopyrightAllIcons.svelte" | ||||
|   import LanguagePicker from "../InputElement/LanguagePicker.svelte" | ||||
|   import LoginButton from "../Base/LoginButton.svelte" | ||||
|   import SelectedElementView from "./SelectedElementView.svelte" | ||||
|   import LayerConfig from "../../Models/ThemeConfig/LayerConfig" | ||||
|   import type { LayerConfigJson } from "../../Models/ThemeConfig/Json/LayerConfigJson" | ||||
|   import usersettings from "../../assets/generated/layers/usersettings.json" | ||||
|   import UserRelatedState from "../../Logic/State/UserRelatedState" | ||||
|   import ArrowDownTray from "@babeard/svelte-heroicons/mini/ArrowDownTray" | ||||
|   import DownloadPanel from "../DownloadFlow/DownloadPanel.svelte" | ||||
|   import Favourites from "../Favourites/Favourites.svelte" | ||||
|   import ReviewsOverview from "../Reviews/ReviewsOverview.svelte" | ||||
|   import Share from "@babeard/svelte-heroicons/solid/Share" | ||||
|   import ShareScreen from "./ShareScreen.svelte" | ||||
|   import FilterPage from "./FilterPage.svelte" | ||||
|   import RasterLayerOverview from "../Map/RasterLayerOverview.svelte" | ||||
|   import ThemeIntroPanel from "./ThemeIntroPanel.svelte" | ||||
|   import Marker from "../Map/Marker.svelte" | ||||
|   import LogoutButton from "../Base/LogoutButton.svelte" | ||||
|   import { BoltIcon } from "@babeard/svelte-heroicons/mini" | ||||
|   import Copyright from "../../assets/svg/Copyright.svelte" | ||||
| 
 | ||||
|   export let state: ThemeViewState | ||||
|   let userdetails = state.osmConnection.userDetails | ||||
| 
 | ||||
|   let usersettingslayer = new LayerConfig(<LayerConfigJson>usersettings, "usersettings", true) | ||||
| 
 | ||||
|   let layout = state.layout | ||||
|   let featureSwitches = state.featureSwitches | ||||
|   let showHome = featureSwitches.featureSwitchBackToThemeOverview | ||||
|   let pg = state.guistate.pageStates | ||||
|   export let onlyLink: boolean | ||||
|   const t = Translations.t.general.menu | ||||
| </script> | ||||
| 
 | ||||
| <div class="flex flex-col p-2 sm:p-3 low-interaction gap-y-2 sm:gap-y-3 h-screen overflow-y-auto"> | ||||
| 
 | ||||
|   <!-- User related: avatar, settings, favourits, logout --> | ||||
|   <div class="sidebar-unit"> | ||||
|     <LoginToggle {state}> | ||||
|       <LoginButton osmConnection={state.osmConnection} slot="not-logged-in"></LoginButton> | ||||
|       <div class="flex gap-x-4 items-center"> | ||||
|         {#if $userdetails.img} | ||||
|           <img src={$userdetails.img} class="rounded-full w-14 h-14" /> | ||||
|         {/if} | ||||
|         <b>{$userdetails.name}</b> | ||||
|       </div> | ||||
|     </LoginToggle> | ||||
| 
 | ||||
| 
 | ||||
|     <Page {onlyLink} shown={pg.usersettings}> | ||||
|       <div class="flex" slot="header"> | ||||
|         <CogIcon class="h-6 w-6" /> | ||||
|         <Tr t={UserRelatedState.usersettingsConfig.title.GetRenderValue({})} /> | ||||
|       </div> | ||||
| 
 | ||||
|       <!-- All shown components are set by 'usersettings.json', which happily uses some special visualisations created specifically for it --> | ||||
|       <LoginToggle {state}> | ||||
|         <div class="flex flex-col" slot="not-logged-in"> | ||||
|           <LanguagePicker availableLanguages={layout.language} /> | ||||
|           <Tr cls="alert" t={Translations.t.userinfo.notLoggedIn} /> | ||||
|           <LoginButton clss="primary" osmConnection={state.osmConnection} /> | ||||
|         </div> | ||||
|         <SelectedElementView | ||||
|           highlightedRendering={state.guistate.highlightedUserSetting} | ||||
|           layer={usersettingslayer} | ||||
|           selectedElement={{ | ||||
|                 type: "Feature", | ||||
|                 properties: { id: "settings" }, | ||||
|                 geometry: { type: "Point", coordinates: [0, 0] }, | ||||
|               }} | ||||
| 
 | ||||
|           {state} | ||||
|           tags={state.userRelatedState.preferencesAsTags} | ||||
|         /> | ||||
|       </LoginToggle> | ||||
| 
 | ||||
| 
 | ||||
|     </Page> | ||||
| 
 | ||||
|     <LoginToggle {state}> | ||||
|       <Page {onlyLink} shown={pg.favourites}> | ||||
| 
 | ||||
|         <div class="flex" slot="header"> | ||||
|           <HeartIcon class="h-6 w-6" /> | ||||
|           <Tr t={Translations.t.favouritePoi.tab} /> | ||||
|         </div> | ||||
|         <h3> | ||||
| 
 | ||||
|           <Tr t={Translations.t.favouritePoi.title} /> | ||||
|         </h3> | ||||
|         <div> | ||||
|           <Favourites {state} /> | ||||
|           <h3> | ||||
|             <Tr t={Translations.t.reviews.your_reviews} /> | ||||
|           </h3> | ||||
|           <ReviewsOverview {state} /> | ||||
|         </div> | ||||
|       </Page> | ||||
|       <div class="self-end"> | ||||
|         <LogoutButton osmConnection={state.osmConnection} /> | ||||
|       </div> | ||||
|     </LoginToggle> | ||||
| 
 | ||||
|     <LanguagePicker /> | ||||
| 
 | ||||
|   </div> | ||||
| 
 | ||||
| 
 | ||||
|   <!-- Theme related: documentation links, download, ... --> | ||||
|   <div class="sidebar-unit"> | ||||
|     <h3> | ||||
|       <Tr t={t.aboutCurrentThemeTitle} /> | ||||
|     </h3> | ||||
| 
 | ||||
|     <Page {onlyLink} shown={pg.about_theme}> | ||||
|       <div slot="link" class="flex"> | ||||
|         <Marker icons={layout.icon} size="h-6 w-6 mr-2" /> | ||||
|         <Tr t={t.showIntroduction} /> | ||||
|       </div> | ||||
|       <div class="flex" slot="header"> | ||||
|         <Marker icons={layout.icon} size="h-8 w-8 mr-4" /> | ||||
|         <Tr t={layout.title} /> | ||||
|       </div> | ||||
|       <ThemeIntroPanel {state} /> | ||||
|     </Page> | ||||
| 
 | ||||
|     <FilterPage {onlyLink} {state} /> | ||||
| 
 | ||||
|     <RasterLayerOverview {onlyLink} {state} /> | ||||
| 
 | ||||
|     <Page {onlyLink} shown={pg.share}> | ||||
|       <div class="flex" slot="header"> | ||||
|         <Share class="h-4 w-4" /> | ||||
|         <Tr t={Translations.t.general.sharescreen.title} /> | ||||
|       </div> | ||||
|       <ShareScreen {state} /> | ||||
|     </Page> | ||||
| 
 | ||||
| 
 | ||||
|     {#if state.featureSwitches.featureSwitchEnableExport} | ||||
|       <Page {onlyLink} shown={pg.download}> | ||||
|         <div slot="header" class="flex"> | ||||
|           <ArrowDownTray class="h-4 w-4" /> | ||||
|           <Tr t={Translations.t.general.download.title} /> | ||||
|         </div> | ||||
|         <DownloadPanel {state} /> | ||||
|       </Page> | ||||
|     {/if} | ||||
| 
 | ||||
|     {#if layout.official} | ||||
|       <a | ||||
|         class="flex" | ||||
|         href={"https://github.com/pietervdvn/MapComplete/blob/develop/Docs/Themes/" + | ||||
|         layout.id + | ||||
|         ".md"} | ||||
|         target="_blank" | ||||
|       > | ||||
|         <DocumentMagnifyingGlass class="h-6 w-6" /> | ||||
|         <Tr | ||||
|           t={Translations.t.general.attribution.openThemeDocumentation.Subs({ | ||||
|           name: layout.title, | ||||
|         })} | ||||
|         /> | ||||
|       </a> | ||||
| 
 | ||||
|       <a class="flex" href={Utils.OsmChaLinkFor(31, layout.id)} target="_blank"> | ||||
|         <DocumentChartBar class="h-6 w-6" /> | ||||
|         <Tr t={Translations.t.general.attribution.openOsmcha.Subs({ theme: layout.title })} /> | ||||
|       </a> | ||||
|     {/if} | ||||
|   </div> | ||||
| 
 | ||||
| 
 | ||||
|   <!-- Other links and tools for the given location: open iD/JOSM; community index, ... --> | ||||
|   <div class="sidebar-unit"> | ||||
| 
 | ||||
|     <h3> | ||||
|       <Tr t={t.moreUtilsTitle} /> | ||||
|     </h3> | ||||
|     {#if $showHome} | ||||
|       <a class="flex" href={Utils.HomepageLink()}> | ||||
|         <Add class="h-6 w-6" /> | ||||
|         {#if Utils.isIframe} | ||||
|           <Tr t={Translations.t.general.seeIndex} /> | ||||
|         {:else} | ||||
|           <Tr t={Translations.t.general.backToIndex} /> | ||||
|         {/if} | ||||
|       </a> | ||||
|     {/if} | ||||
| 
 | ||||
|     <Page {onlyLink} shown={pg.community_index}> | ||||
|       <div class="flex" slot="header"> | ||||
|         <Community class="h-6 w-6" /> | ||||
|         <Tr t={Translations.t.communityIndex.title} /> | ||||
|       </div> | ||||
|       <CommunityIndexView location={state.mapProperties.location} /> | ||||
|     </Page> | ||||
| 
 | ||||
| 
 | ||||
|     <If condition={featureSwitches.featureSwitchEnableLogin}> | ||||
|       <OpenIdEditor mapProperties={state.mapProperties} /> | ||||
|       <OpenJosm {state} /> | ||||
|       <MapillaryLink large={false} mapProperties={state.mapProperties} /> | ||||
|     </If> | ||||
| 
 | ||||
|   </div> | ||||
| 
 | ||||
| 
 | ||||
|   <!-- About MC: various outward links, legal info, ... --> | ||||
|   <div class="sidebar-unit"> | ||||
| 
 | ||||
|     <h3> | ||||
|       <Tr t={Translations.t.general.menu.aboutMapComplete} /> | ||||
|     </h3> | ||||
| 
 | ||||
|     <div class="hidden-on-mobile"> | ||||
| 
 | ||||
|       <Page {onlyLink} shown={pg.hotkeys}> | ||||
|         <div class="flex" slot="header"> | ||||
|           <BoltIcon class="w-6 h-6" /> | ||||
|           <Tr t={ Translations.t.hotkeyDocumentation.title} /> | ||||
|         </div> | ||||
|         <HotkeyTable /> | ||||
|       </Page> | ||||
|     </div> | ||||
| 
 | ||||
|     <a class="flex" href="https://github.com/pietervdvn/MapComplete/" target="_blank"> | ||||
|       <Github class="h-6 w-6" /> | ||||
|       <Tr t={Translations.t.general.attribution.gotoSourceCode} /> | ||||
|     </a> | ||||
| 
 | ||||
|     <a class="flex" href="https://github.com/pietervdvn/MapComplete/issues" target="_blank"> | ||||
|       <Bug class="h-6 w-6" /> | ||||
|       <Tr t={Translations.t.general.attribution.openIssueTracker} /> | ||||
|     </a> | ||||
| 
 | ||||
| 
 | ||||
|     <a class="flex" href="https://en.osm.town/@MapComplete" target="_blank"> | ||||
|       <Mastodon class="h-6 w-6" /> | ||||
|       <Tr t={Translations.t.general.attribution.followOnMastodon} /> | ||||
|     </a> | ||||
| 
 | ||||
|     <a class="flex" href="https://liberapay.com/pietervdvn/" target="_blank"> | ||||
|       <Liberapay class="h-6 w-6" /> | ||||
|       <Tr t={Translations.t.general.attribution.donate} /> | ||||
|     </a> | ||||
| 
 | ||||
| 
 | ||||
|     <Page {onlyLink} shown={pg.copyright}> | ||||
|       <div slot="header" class="flex"> | ||||
|         <Copyright class="w-8 h-8" /> | ||||
|         <Tr t={Translations.t.general.attribution.attributionTitle} /> | ||||
|       </div> | ||||
|       <CopyrightPanel {state} /> | ||||
|     </Page> | ||||
| 
 | ||||
| 
 | ||||
|     <Page {onlyLink} shown={pg.copyright_icons}> | ||||
|       <div slot="header" class="flex"> | ||||
|         <Copyright class="w-8 h-8" /> | ||||
|         <Tr t={ Translations.t.general.attribution.iconAttribution.title} /> | ||||
|       </div> | ||||
|       <CopyrightAllIcons {state} /> | ||||
| 
 | ||||
|     </Page> | ||||
| 
 | ||||
| 
 | ||||
|     <Page {onlyLink} shown={pg.privacy}> | ||||
|       <div class="flex" slot="header"> | ||||
|         <EyeIcon class="w-8 h-8" /> | ||||
|         <Tr t={Translations.t.privacy.title} /> | ||||
|       </div> | ||||
|       <PrivacyPolicy {state} /> | ||||
|     </Page> | ||||
| 
 | ||||
| 
 | ||||
|     <div class="subtle self-end"> | ||||
|       {Constants.vNumber} | ||||
|     </div> | ||||
|   </div> | ||||
| </div> | ||||
| 
 | ||||
| 
 | ||||
| <style> | ||||
|     :global(.sidebar-unit) { | ||||
|         display: flex; | ||||
|         flex-direction: column; | ||||
|         row-gap: 0.25rem; | ||||
|         background: var(--background-color); | ||||
|         padding: 0.5rem; | ||||
|         border-radius: 0.5rem; | ||||
|     } | ||||
| 
 | ||||
|     :global(.sidebar-unit > h3) { | ||||
|         margin-top: 0; | ||||
|         margin-bottom: 0.5rem; | ||||
|         padding: 0.25rem; | ||||
|     } | ||||
| 
 | ||||
|     :global(.sidebar-button svg, .sidebar-button img) { | ||||
|         width: 1.5rem; | ||||
|         height: 1.5rem; | ||||
|         margin-right: 0.5rem; | ||||
|         flex-shrink: 0; | ||||
|     } | ||||
| 
 | ||||
|     :global(.sidebar-button .weblate-link > svg) { | ||||
|         width: 0.75rem; | ||||
|         height: 0.75rem; | ||||
|         flex-shrink: 0; | ||||
|     } | ||||
| 
 | ||||
| 
 | ||||
|     :global(.sidebar-button, .sidebar-button, .sidebar-unit > a) { | ||||
|         display: flex; | ||||
|         align-items: center; | ||||
|         border-radius: 0.25rem !important; | ||||
|         padding: 0.4rem 0.75rem !important; | ||||
|         text-decoration: none !important; | ||||
|     } | ||||
| 
 | ||||
|     :global(.sidebar-button > svg , .sidebar-button > img, .sidebar-unit a > img, .sidebar-unit > a svg) { | ||||
|         margin-right: 0.5rem; | ||||
|         flex-shrink: 0; | ||||
|     } | ||||
| 
 | ||||
|     :global(.sidebar-button:hover, .sidebar-unit > a:hover) { | ||||
|         background: var(--low-interaction-background) !important; | ||||
|     } | ||||
| 
 | ||||
| 
 | ||||
| </style> | ||||
|  | @ -20,7 +20,7 @@ | |||
| 
 | ||||
| <MapControlButton | ||||
|   arialabel={Translations.t.general.labels.background} | ||||
|   on:click={() => state.guistate.backgroundLayerSelectionIsOpened.setData(true)} | ||||
|   on:click={() => state.guistate.pageStates.background.setData(true)} | ||||
|   {htmlElem} | ||||
| > | ||||
|   <StyleLoadingIndicator map={map ?? state.map} rasterLayer={state.mapProperties.rasterLayer}> | ||||
|  |  | |||
|  | @ -12,8 +12,6 @@ | |||
|   import { GeoLocationState } from "../../Logic/State/GeoLocationState" | ||||
|   import If from "../Base/If.svelte" | ||||
|   import { ExclamationTriangleIcon } from "@babeard/svelte-heroicons/mini" | ||||
|   import Location_refused from "../../assets/svg/Location_refused.svelte" | ||||
|   import Location from "../../assets/svg/Location.svelte" | ||||
|   import ChevronDoubleLeft from "@babeard/svelte-heroicons/solid/ChevronDoubleLeft" | ||||
|   import GeolocationIndicator from "./GeolocationIndicator.svelte" | ||||
| 
 | ||||
|  | @ -38,7 +36,7 @@ | |||
|     const glstate = state.geolocation.geolocationState | ||||
|     if (glstate.currentGPSLocation.data !== undefined) { | ||||
|       const c: GeolocationCoordinates = glstate.currentGPSLocation.data | ||||
|       state.guistate.themeIsOpened.setData(false) | ||||
|       state.guistate.pageStates.about_theme.setData(false) | ||||
|       const coor = { lon: c.longitude, lat: c.latitude } | ||||
|       state.mapProperties.location.setData(coor) | ||||
|     } | ||||
|  | @ -65,7 +63,7 @@ | |||
|     <Tr t={layout.descriptionTail} /> | ||||
| 
 | ||||
|     <!-- Buttons: open map, go to location, search --> | ||||
|     <NextButton clss="primary w-full" on:click={() => state.guistate.themeIsOpened.setData(false)}> | ||||
|     <NextButton clss="primary w-full" on:click={() => state.guistate.pageStates.about_theme.setData(false)}> | ||||
|       <div class="flex w-full flex-col items-center"> | ||||
|         <div class="flex w-full justify-center text-2xl"> | ||||
|           <Tr t={Translations.t.general.openTheMap} /> | ||||
|  | @ -96,7 +94,7 @@ | |||
|           <div style="min-width: 16rem; " class="grow"> | ||||
|             <Geosearch | ||||
|               bounds={state.mapProperties.bounds} | ||||
|               on:searchCompleted={() => state.guistate.themeIsOpened.setData(false)} | ||||
|               on:searchCompleted={() => state.guistate.pageStates.about_theme.setData(false)} | ||||
|               on:searchIsValid={(event) => { | ||||
|                 searchEnabled = event.detail | ||||
|               }} | ||||
|  | @ -140,20 +138,10 @@ | |||
|     {/if} | ||||
|   </div> | ||||
| 
 | ||||
|   {#if Utils.isIframe} | ||||
|     <div class="link-underline flex justify-end"> | ||||
|       <a href="https://mapcomplete.org" target="_blank"> | ||||
|         <Tr t={Translations.t.general.poweredByMapComplete} /> | ||||
|       </a> | ||||
|     </div> | ||||
|   {:else} | ||||
|     <If condition={state.featureSwitches.featureSwitchBackToThemeOverview}> | ||||
|       <div class="link-underline m-2 mx-4 flex w-full"> | ||||
|         <a class="flex w-fit items-center justify-end" href={Utils.HomepageLink()}> | ||||
|           <ChevronDoubleLeft class="h-4 w-4" /> | ||||
|           <Tr t={Translations.t.general.backToIndex} /> | ||||
|         </a> | ||||
|       </div> | ||||
|     </If> | ||||
|   {/if} | ||||
|   <div class="link-underline flex justify-end text-sm mt-8"> | ||||
|     <a href="https://mapcomplete.org" target="_blank"> | ||||
|       <Tr t={Translations.t.general.poweredByMapComplete} /> | ||||
|     </a> | ||||
|   </div> | ||||
| 
 | ||||
| </div> | ||||
|  |  | |||
|  | @ -65,10 +65,6 @@ | |||
|   <Tr cls="alert" t={Translations.t.general.download.toMuch} /> | ||||
| {:else} | ||||
|   <div class="flex w-full flex-col" /> | ||||
|   <h3> | ||||
|     <Tr t={t.title} /> | ||||
|   </h3> | ||||
| 
 | ||||
|   <DownloadButton | ||||
|     {state} | ||||
|     extension="geojson" | ||||
|  |  | |||
|  | @ -62,7 +62,6 @@ export class MapLibreAdaptor implements MapProperties, ExportableMap { | |||
|         if (!MapLibreAdaptor.pmtilesInited) { | ||||
|             maplibregl.addProtocol("pmtiles", new Protocol().tile) | ||||
|             MapLibreAdaptor.pmtilesInited = true | ||||
|             console.log("PM-tiles protocol added" + "") | ||||
|         } | ||||
|         this._maplibreMap = maplibreMap | ||||
| 
 | ||||
|  |  | |||
|  | @ -4,32 +4,30 @@ | |||
|    */ | ||||
|   import { Store, UIEventSource } from "../../Logic/UIEventSource" | ||||
|   import type { RasterLayerPolygon } from "../../Models/RasterLayers" | ||||
|   import type { MapProperties } from "../../Models/MapProperties" | ||||
|   import { Map as MlMap } from "maplibre-gl" | ||||
|   import RasterLayerPicker from "./RasterLayerPicker.svelte" | ||||
|   import type { EliCategory } from "../../Models/RasterLayerProperties" | ||||
|   import UserRelatedState from "../../Logic/State/UserRelatedState" | ||||
|   import Translations from "../i18n/Translations" | ||||
|   import Tr from "../Base/Tr.svelte" | ||||
|   import TitledPanel from "../Base/TitledPanel.svelte" | ||||
|   import Loading from "../Base/Loading.svelte" | ||||
|   import Page from "../Base/Page.svelte" | ||||
|   import ThemeViewState from "../../Models/ThemeViewState" | ||||
|   import { Square3Stack3dIcon } from "@babeard/svelte-heroicons/solid" | ||||
| 
 | ||||
|   export let availableLayers: { store: Store<RasterLayerPolygon[]> } | ||||
|   export let mapproperties: MapProperties | ||||
|   export let userstate: UserRelatedState | ||||
|   export let map: Store<MlMap> | ||||
|   export let state: ThemeViewState | ||||
| 
 | ||||
|   let map = state.map | ||||
|   let mapproperties = state.mapProperties | ||||
|   let userstate = state.userRelatedState | ||||
|   let shown = state.guistate.pageStates.background | ||||
|   let availableLayers: { store: Store<RasterLayerPolygon[]> } = state.availableLayers | ||||
|   let _availableLayers = availableLayers.store | ||||
|   /** | ||||
|    * Used to toggle the background layers on/off | ||||
|    */ | ||||
|   export let visible: UIEventSource<boolean> = undefined | ||||
| 
 | ||||
|   type CategoryType = "photo" | "map" | "other" | "osmbasedmap" | ||||
|   const categories: Record<CategoryType, EliCategory[]> = { | ||||
|     photo: ["photo", "historicphoto"], | ||||
|     map: ["map", "historicmap"], | ||||
|     other: ["other", "elevation"], | ||||
|     osmbasedmap: ["osmbasedmap"], | ||||
|     osmbasedmap: ["osmbasedmap"] | ||||
|   } | ||||
| 
 | ||||
|   function availableForCategory(type: CategoryType): Store<RasterLayerPolygon[]> { | ||||
|  | @ -45,27 +43,35 @@ | |||
|   const otherLayers = availableForCategory("other") | ||||
| 
 | ||||
|   function onApply() { | ||||
|     visible.setData(false) | ||||
|     shown.setData(false) | ||||
|   } | ||||
| 
 | ||||
|   function getPref(type: CategoryType): undefined | UIEventSource<string> { | ||||
|     return userstate?.osmConnection?.GetPreference("preferred-layer-" + type) | ||||
|   } | ||||
| 
 | ||||
|   export let onlyLink: boolean | ||||
| 
 | ||||
| </script> | ||||
| 
 | ||||
| <TitledPanel> | ||||
|   <Tr slot="title" t={Translations.t.general.backgroundMap} /> | ||||
| <Page {onlyLink} shown={shown} fullscreen={true}> | ||||
|   <div slot="header" class="flex" > | ||||
|     <Square3Stack3dIcon class="h-6 w-6" /> | ||||
| 
 | ||||
|   <Tr t={Translations.t.general.backgroundMap} /> | ||||
|   </div> | ||||
|   {#if $_availableLayers?.length < 1} | ||||
|     <Loading /> | ||||
|   {:else} | ||||
|     <div class="grid h-full w-full grid-cols-1 gap-2 md:grid-cols-2"> | ||||
| 
 | ||||
|     <div class="flex gap-x-2 flex-col sm:flex-row gap-y-2" style="height: calc( 100% - 5rem)"> | ||||
|       <RasterLayerPicker | ||||
|         availableLayers={$photoLayers} | ||||
|         favourite={getPref("photo")} | ||||
|         {map} | ||||
|         {mapproperties} | ||||
|         on:appliedLayer={onApply} | ||||
|         {visible} | ||||
|         {shown} | ||||
|       /> | ||||
|       <RasterLayerPicker | ||||
|         availableLayers={$mapLayers} | ||||
|  | @ -73,7 +79,7 @@ | |||
|         {map} | ||||
|         {mapproperties} | ||||
|         on:appliedLayer={onApply} | ||||
|         {visible} | ||||
|         {shown} | ||||
|       /> | ||||
|       <RasterLayerPicker | ||||
|         availableLayers={$osmbasedmapLayers} | ||||
|  | @ -81,7 +87,7 @@ | |||
|         {map} | ||||
|         {mapproperties} | ||||
|         on:appliedLayer={onApply} | ||||
|         {visible} | ||||
|         {shown} | ||||
|       /> | ||||
|       <RasterLayerPicker | ||||
|         availableLayers={$otherLayers} | ||||
|  | @ -89,8 +95,8 @@ | |||
|         {map} | ||||
|         {mapproperties} | ||||
|         on:appliedLayer={onApply} | ||||
|         {visible} | ||||
|         {shown} | ||||
|       /> | ||||
|     </div> | ||||
|   {/if} | ||||
| </TitledPanel> | ||||
| </Page> | ||||
|  |  | |||
|  | @ -13,7 +13,7 @@ | |||
|   export let mapproperties: MapProperties | ||||
|   export let map: Store<MlMap> | ||||
| 
 | ||||
|   export let visible: Store<boolean> = undefined | ||||
|   export let shown: Store<boolean> = undefined | ||||
| 
 | ||||
|   let dispatch = createEventDispatcher<{ appliedLayer }>() | ||||
| 
 | ||||
|  | @ -48,10 +48,10 @@ | |||
| 
 | ||||
|   let rasterLayerOnMap = UIEventSource.feedFrom(rasterLayer) | ||||
| 
 | ||||
|   if (visible) { | ||||
|   if (shown) { | ||||
|     onDestroy( | ||||
|       visible?.addCallbackAndRunD((visible) => { | ||||
|         if (visible) { | ||||
|       shown?.addCallbackAndRunD((shown) => { | ||||
|         if (shown) { | ||||
|           rasterLayerOnMap.setData(rasterLayer.data ?? availableLayers[0]) | ||||
|         } else { | ||||
|           rasterLayerOnMap.setData(undefined) | ||||
|  | @ -85,7 +85,7 @@ | |||
|           rasterLayer={rasterLayerOnMap} | ||||
|           placedOverMap={map} | ||||
|           placedOverMapProperties={mapproperties} | ||||
|           {visible} | ||||
|           visible={shown} | ||||
|         /> | ||||
|       </span> | ||||
|     </button> | ||||
|  |  | |||
|  | @ -401,6 +401,7 @@ | |||
|               > | ||||
|                 <input | ||||
|                   type="radio" | ||||
|                   class="self-center mr-1" | ||||
|                   bind:group={selectedMapping} | ||||
|                   name={"mappings-radio-" + config.id} | ||||
|                   value={i} | ||||
|  | @ -412,6 +413,7 @@ | |||
|               <label class="flex gap-x-1"> | ||||
|                 <input | ||||
|                   type="radio" | ||||
|                   class="self-center mr-1" | ||||
|                   bind:group={selectedMapping} | ||||
|                   name={"mappings-radio-" + config.id} | ||||
|                   value={config.mappings?.length} | ||||
|  | @ -448,6 +450,7 @@ | |||
|               > | ||||
|                 <input | ||||
|                   type="checkbox" | ||||
|                   class="self-center mr-1" | ||||
|                   name={"mappings-checkbox-" + config.id + "-" + i} | ||||
|                   bind:checked={checkedMappings[i]} | ||||
|                   on:keypress={(e) => onInputKeypress(e)} | ||||
|  | @ -458,6 +461,7 @@ | |||
|               <label class="flex gap-x-1"> | ||||
|                 <input | ||||
|                   type="checkbox" | ||||
|                   class="self-center mr-1" | ||||
|                   name={"mappings-checkbox-" + config.id + "-" + config.mappings?.length} | ||||
|                   bind:checked={checkedMappings[config.mappings.length]} | ||||
|                   on:keypress={(e) => onInputKeypress(e)} | ||||
|  |  | |||
|  | @ -13,64 +13,39 @@ | |||
|   import type { MapProperties } from "../Models/MapProperties" | ||||
|   import Geosearch from "./Search/Geosearch.svelte" | ||||
|   import Translations from "./i18n/Translations" | ||||
|   import usersettings from "../assets/generated/layers/usersettings.json" | ||||
|   import { | ||||
|     CogIcon, | ||||
|     EyeIcon, | ||||
|     HeartIcon, | ||||
|     MenuIcon, | ||||
|     XCircleIcon | ||||
|     MenuIcon | ||||
|   } from "@rgossiaux/svelte-heroicons/solid" | ||||
|   import Tr from "./Base/Tr.svelte" | ||||
|   import CommunityIndexView from "./BigComponents/CommunityIndexView.svelte" | ||||
|   import FloatOver from "./Base/FloatOver.svelte" | ||||
|   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.svelte" | ||||
|   import DownloadPanel from "./DownloadFlow/DownloadPanel.svelte" | ||||
|   import ModalRight from "./Base/ModalRight.svelte" | ||||
|   import LevelSelector from "./BigComponents/LevelSelector.svelte" | ||||
|   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 OpenBackgroundSelectorButton from "./BigComponents/OpenBackgroundSelectorButton.svelte" | ||||
|   import StateIndicator from "./BigComponents/StateIndicator.svelte" | ||||
|   import ShareScreen from "./BigComponents/ShareScreen.svelte" | ||||
|   import UploadingImageCounter from "./Image/UploadingImageCounter.svelte" | ||||
|   import PendingChangesIndicator from "./BigComponents/PendingChangesIndicator.svelte" | ||||
|   import Cross from "../assets/svg/Cross.svelte" | ||||
|   import LanguagePicker from "./InputElement/LanguagePicker.svelte" | ||||
|   import Min from "../assets/svg/Min.svelte" | ||||
|   import Plus from "../assets/svg/Plus.svelte" | ||||
|   import Filter from "../assets/svg/Filter.svelte" | ||||
|   import Community from "../assets/svg/Community.svelte" | ||||
|   import Favourites from "./Favourites/Favourites.svelte" | ||||
|   import ImageOperations from "./Image/ImageOperations.svelte" | ||||
|   import VisualFeedbackPanel from "./BigComponents/VisualFeedbackPanel.svelte" | ||||
|   import { Orientation } from "../Sensors/Orientation" | ||||
|   import GeolocationIndicator from "./BigComponents/GeolocationIndicator.svelte" | ||||
|   import Compass_arrow from "../assets/svg/Compass_arrow.svelte" | ||||
|   import ReverseGeocoding from "./BigComponents/ReverseGeocoding.svelte" | ||||
|   import FilterPanel from "./BigComponents/FilterPanel.svelte" | ||||
|   import PrivacyPolicy from "./BigComponents/PrivacyPolicy.svelte" | ||||
|   import { BBox } from "../Logic/BBox" | ||||
|   import ReviewsOverview from "./Reviews/ReviewsOverview.svelte" | ||||
|   import ExtraLinkButton from "./BigComponents/ExtraLinkButton.svelte" | ||||
|   import CloseAnimation from "./Base/CloseAnimation.svelte" | ||||
|   import { LastClickFeatureSource } from "../Logic/FeatureSource/Sources/LastClickFeatureSource" | ||||
|   import ArrowDownTray from "@babeard/svelte-heroicons/mini/ArrowDownTray" | ||||
|   import Share from "@babeard/svelte-heroicons/solid/Share" | ||||
|   import ChevronRight from "@babeard/svelte-heroicons/solid/ChevronRight" | ||||
|   import Marker from "./Map/Marker.svelte" | ||||
|   import AboutMapComplete from "./BigComponents/AboutMapComplete.svelte" | ||||
|   import HotkeyTable from "./BigComponents/HotkeyTable.svelte" | ||||
|   import SelectedElementPanel from "./Base/SelectedElementPanel.svelte" | ||||
|   import MenuDrawer from "./BigComponents/MenuDrawer.svelte" | ||||
|   import DrawerLeft from "./Base/DrawerLeft.svelte" | ||||
|   import type { LayerConfigJson } from "../Models/ThemeConfig/Json/LayerConfigJson" | ||||
|   import { GeocodingUtils } from "../Logic/Geocoding/GeocodingProvider" | ||||
| 
 | ||||
|  | @ -109,7 +84,6 @@ | |||
|   let visualFeedback = state.visualFeedback | ||||
|   let viewport: UIEventSource<HTMLDivElement> = new UIEventSource<HTMLDivElement>(undefined) | ||||
|   let mapproperties: MapProperties = state.mapProperties | ||||
|   let usersettingslayer = new LayerConfig(<LayerConfigJson>usersettings, "usersettings", true) | ||||
|   state.mapProperties.installCustomKeyboardHandler(viewport) | ||||
|   let canZoomIn = mapproperties.maxzoom.map( | ||||
|     (mz) => mapproperties.zoom.data < mz, | ||||
|  | @ -145,7 +119,6 @@ | |||
|     updateViewport() | ||||
|   }) | ||||
|   let featureSwitches: FeatureSwitchState = state.featureSwitches | ||||
|   let availableLayers = state.availableLayers | ||||
|   let currentViewLayer: LayerConfig = layout.layers.find((l) => l.id === "current_view") | ||||
|   let rasterLayer: Store<RasterLayerPolygon> = state.mapProperties.rasterLayer | ||||
|   let rasterLayerName = | ||||
|  | @ -157,8 +130,12 @@ | |||
|     }) | ||||
|   ) | ||||
|   let previewedImage = state.previewedImage | ||||
| 
 | ||||
|   let addNewFeatureMode = state.userRelatedState.addNewFeatureMode | ||||
|   let gpsAvailable = state.geolocation.geolocationState.gpsAvailable | ||||
|   let gpsButtonAriaLabel = state.geolocation.geolocationState.gpsStateExplanation | ||||
|   let debug = state.featureSwitches.featureSwitchIsDebugging | ||||
| 
 | ||||
| 
 | ||||
|   debug.addCallbackAndRun((dbg) => { | ||||
|     if (dbg) { | ||||
|       document.body.classList.add("debug") | ||||
|  | @ -179,26 +156,6 @@ | |||
|     animation?.cameraAnimation(mlmap) | ||||
|   } | ||||
| 
 | ||||
|   /** | ||||
|    * Needed for the animations | ||||
|    */ | ||||
|   let openMapButton: UIEventSource<HTMLElement> = new UIEventSource<HTMLElement>(undefined) | ||||
|   let openMenuButton: UIEventSource<HTMLElement> = new UIEventSource<HTMLElement>(undefined) | ||||
|   let openCurrentViewLayerButton: UIEventSource<HTMLElement> = new UIEventSource<HTMLElement>( | ||||
|     undefined | ||||
|   ) | ||||
|   let _openNewElementButton: HTMLButtonElement | ||||
|   let openNewElementButton: UIEventSource<HTMLElement> = new UIEventSource<HTMLElement>(undefined) | ||||
| 
 | ||||
|   $: { | ||||
|     openNewElementButton.setData(_openNewElementButton) | ||||
|   } | ||||
|   let openFilterButton: UIEventSource<HTMLElement> = new UIEventSource<HTMLElement>(undefined) | ||||
|   let openBackgroundButton: UIEventSource<HTMLElement> = new UIEventSource<HTMLElement>(undefined) | ||||
|   let addNewFeatureMode = state.userRelatedState.addNewFeatureMode | ||||
| 
 | ||||
|   let gpsAvailable = state.geolocation.geolocationState.gpsAvailable | ||||
|   let gpsButtonAriaLabel = state.geolocation.geolocationState.gpsStateExplanation | ||||
| </script> | ||||
| 
 | ||||
| <main> | ||||
|  | @ -221,6 +178,55 @@ | |||
|   <div class="pointer-events-none absolute top-0 left-0 w-full"> | ||||
|     <!-- Top components --> | ||||
| 
 | ||||
|     <div | ||||
|       class="flex bg-black-light-transparent pointer-events-auto items-center justify-between px-4 py-1 flex-wrap-reverse"> | ||||
|       <!-- Top bar with tools --> | ||||
|       <div class="flex items-center"> | ||||
| 
 | ||||
|         <MapControlButton | ||||
|           cls="m-0.5 p-0.5 sm:p-1" | ||||
|           arialabel={Translations.t.general.labels.menu} | ||||
|           on:click={() => {console.log("Opening...."); state.guistate.menuIsOpened.setData(true)}} | ||||
|           on:keydown={forwardEventToMap} | ||||
|         > | ||||
|           <MenuIcon class="h-6 w-6 cursor-pointer" /> | ||||
|         </MapControlButton> | ||||
| 
 | ||||
|         <MapControlButton | ||||
|           on:click={() => state.guistate.pageStates.about_theme.set(true)} | ||||
|           on:keydown={forwardEventToMap} | ||||
|         > | ||||
|           <div | ||||
|             class="m-0.5 mx-1 flex cursor-pointer items-center max-[480px]:w-full sm:mx-1 mr-2" | ||||
|           > | ||||
|             <Marker icons={layout.icon} size="h-6 w-6 shrink-0 mr-0.5 sm:mr-1 md:mr-2" /> | ||||
|             <b class="mr-1"> | ||||
|               <Tr t={layout.title} /> | ||||
|             </b> | ||||
|           </div> | ||||
|         </MapControlButton> | ||||
|       </div> | ||||
| 
 | ||||
| 
 | ||||
|       <If condition={state.featureSwitches.featureSwitchSearch}> | ||||
|         <div class="w-full sm:w-64 my-2 sm:mt-0"> | ||||
| 
 | ||||
|           <Geosearch | ||||
|             bounds={state.mapProperties.bounds} | ||||
|             on:searchCompleted={() => { | ||||
|             state.map?.data?.getCanvas()?.focus() | ||||
|           }} | ||||
|             perLayer={state.perLayer} | ||||
|             selectedElement={state.selectedElement} | ||||
|             geolocationState={state.geolocation.geolocationState} | ||||
|             searcher={state.geosearch} | ||||
|             {state} | ||||
|           /> | ||||
|         </div> | ||||
|       </If> | ||||
| 
 | ||||
|     </div> | ||||
| 
 | ||||
|     <div class="pointer-events-auto float-right mt-1 flex flex-col px-1 max-[480px]:w-full sm:m-2"> | ||||
|       <If condition={state.visualFeedback}> | ||||
|         {#if $selectedElement === undefined} | ||||
|  | @ -229,46 +235,12 @@ | |||
|           </div> | ||||
|         {/if} | ||||
|       </If> | ||||
|       <If condition={state.featureSwitches.featureSwitchSearch}> | ||||
|         <Geosearch | ||||
|           bounds={state.mapProperties.bounds} | ||||
|           on:searchCompleted={() => { | ||||
|             state.map?.data?.getCanvas()?.focus() | ||||
|           }} | ||||
|           perLayer={state.perLayer} | ||||
|           selectedElement={state.selectedElement} | ||||
|           geolocationState={state.geolocation.geolocationState} | ||||
|           searcher={state.geosearch} | ||||
|           {state} | ||||
|         /> | ||||
|       </If> | ||||
| 
 | ||||
|     </div> | ||||
|     <div class="float-left m-1 flex flex-col sm:mt-2"> | ||||
|       <If condition={state.featureSwitches.featureSwitchWelcomeMessage}> | ||||
|         <MapControlButton | ||||
|           on:click={() => state.guistate.themeIsOpened.setData(true)} | ||||
|           on:keydown={forwardEventToMap} | ||||
|           htmlElem={openMapButton} | ||||
|         > | ||||
|           <div | ||||
|             class="m-0.5 mx-1 flex cursor-pointer items-center max-[480px]:w-full sm:mx-1 md:mx-2" | ||||
|           > | ||||
|             <Marker icons={layout.icon} size="h-4 w-4 md:h-8 md:w-8 mr-0.5 sm:mr-1 md:mr-2" /> | ||||
|             <b class="mr-1"> | ||||
|               <Tr t={layout.title} /> | ||||
|             </b> | ||||
|             <ChevronRight class="h-4 w-4" /> | ||||
|           </div> | ||||
|         </MapControlButton> | ||||
| 
 | ||||
|         <MapControlButton | ||||
|           arialabel={Translations.t.general.labels.menu} | ||||
|           on:click={() => state.guistate.menuIsOpened.setData(true)} | ||||
|           on:keydown={forwardEventToMap} | ||||
|           htmlElem={openMenuButton} | ||||
|         > | ||||
|           <MenuIcon class="h-8 w-8 cursor-pointer" /> | ||||
|         </MapControlButton> | ||||
| 
 | ||||
|       </If> | ||||
|       {#if currentViewLayer?.tagRenderings && currentViewLayer.defaultIcon()} | ||||
|         <MapControlButton | ||||
|  | @ -276,7 +248,6 @@ | |||
|             state.selectCurrentView() | ||||
|           }} | ||||
|           on:keydown={forwardEventToMap} | ||||
|           htmlElem={openCurrentViewLayerButton} | ||||
|         > | ||||
|           <div class="h-8 w-8 cursor-pointer"> | ||||
|             <ToSvelte construct={() => currentViewLayer.defaultIcon()} /> | ||||
|  | @ -313,7 +284,6 @@ | |||
|             <button | ||||
|               class="low-interaction pointer-events-auto w-fit" | ||||
|               class:disabled={$currentZoom < Constants.minZoomLevelToAddNewPoint} | ||||
|               bind:this={_openNewElementButton} | ||||
|               on:click={() => { | ||||
|                 state.openNewDialog() | ||||
|               }} | ||||
|  | @ -337,7 +307,6 @@ | |||
|               arialabel={Translations.t.general.labels.filter} | ||||
|               on:click={() => state.guistate.openFilterView()} | ||||
|               on:keydown={forwardEventToMap} | ||||
|               htmlElem={openFilterButton} | ||||
|             > | ||||
|               <Filter class="h-6 w-6" /> | ||||
|             </MapControlButton> | ||||
|  | @ -346,25 +315,18 @@ | |||
|             <OpenBackgroundSelectorButton | ||||
|               hideTooltip={true} | ||||
|               {state} | ||||
|               htmlElem={openBackgroundButton} | ||||
|             /> | ||||
|           </If> | ||||
|           <a | ||||
|             class="bg-black-transparent pointer-events-auto ml-1 h-fit max-h-12 cursor-pointer self-end self-center overflow-hidden rounded-2xl px-1 text-white opacity-50 hover:opacity-100" | ||||
|             on:click={() => { | ||||
|               if (featureSwitches.featureSwitchWelcomeMessage.data) { | ||||
|                 state.guistate.themeViewTab.setData("copyright") | ||||
|                 state.guistate.themeIsOpened.setData(true) | ||||
|               } else { | ||||
|                 state.guistate.copyrightPanelIsOpened.setData(true) | ||||
|               } | ||||
|             }} | ||||
|           <button | ||||
|             class="unstyled bg-black-transparent pointer-events-auto ml-1 h-fit max-h-12 cursor-pointer overflow-hidden rounded-2xl px-1 text-white opacity-50 hover:opacity-100" | ||||
|             style="background: #00000088; padding: 0.25rem; border-radius: 2rem;" | ||||
|             on:click={() => {state.guistate.pageStates.copyright.set(true)}} | ||||
|           > | ||||
|             © <span class="hidden sm:inline sm:pr-2"> | ||||
|               OpenStreetMap | ||||
|               <span class="hidden w-24 md:inline md:pr-2">, {rasterLayerName}</span> | ||||
|             </span> | ||||
|           </a> | ||||
|           </button> | ||||
|         </div> | ||||
|       </div> | ||||
| 
 | ||||
|  | @ -434,6 +396,13 @@ | |||
|     <svelte:fragment slot="error" /> | ||||
|   </LoginToggle> | ||||
| 
 | ||||
|   <DrawerLeft shown={state.guistate.menuIsOpened}> | ||||
|     <div class="h-screen overflow-y-auto"> | ||||
|       <MenuDrawer onlyLink={true} {state} /> | ||||
|     </div> | ||||
|   </DrawerLeft> | ||||
|   <MenuDrawer onlyLink={false} {state} /> | ||||
| 
 | ||||
|   {#if $selectedElement !== undefined && $selectedLayer !== undefined && !$selectedLayer.popupInFloatover} | ||||
|     <!-- right modal with the selected element view --> | ||||
|     <ModalRight | ||||
|  | @ -476,225 +445,5 @@ | |||
|     </FloatOver> | ||||
|   </If> | ||||
| 
 | ||||
|   <!-- big theme menu --> | ||||
|   <If condition={state.guistate.themeIsOpened}> | ||||
|     <FloatOver on:close={() => state.guistate.themeIsOpened.setData(false)}> | ||||
|       <span slot="close-button"><!-- Disable the close button --></span> | ||||
|       <TabbedGroup | ||||
|         condition1={state.featureSwitches.featureSwitchEnableExport} | ||||
|         tab={state.guistate.themeViewTabIndex} | ||||
|       > | ||||
|         <div slot="post-tablist"> | ||||
|           <XCircleIcon | ||||
|             class="mr-2 h-8 w-8" | ||||
|             on:click={() => state.guistate.themeIsOpened.setData(false)} | ||||
|           /> | ||||
|         </div> | ||||
| 
 | ||||
|         <div class="flex" slot="title0"> | ||||
|           <Marker icons={layout.icon} size="h-4 w-4" /> | ||||
|           <Tr t={layout.title} /> | ||||
|         </div> | ||||
| 
 | ||||
|         <div class="m-4 h-full" slot="content0"> | ||||
|           <ThemeIntroPanel {state} /> | ||||
|         </div> | ||||
| 
 | ||||
|         <div class="flex" slot="title1"> | ||||
|           <If condition={state.featureSwitches.featureSwitchEnableExport}> | ||||
|             <ArrowDownTray class="h-4 w-4" /> | ||||
|             <Tr t={Translations.t.general.download.title} /> | ||||
|           </If> | ||||
|         </div> | ||||
|         <div class="m-4" slot="content1"> | ||||
|           <DownloadPanel {state} /> | ||||
|         </div> | ||||
| 
 | ||||
|         <div slot="title2"> | ||||
|           <Tr t={Translations.t.general.attribution.title} /> | ||||
|         </div> | ||||
| 
 | ||||
|         <div slot="content2" class="m-2 flex flex-col"> | ||||
|           <CopyrightPanel {state} /> | ||||
|         </div> | ||||
| 
 | ||||
|         <div class="flex" slot="title3"> | ||||
|           <Share class="h-4 w-4" /> | ||||
|           <Tr t={Translations.t.general.sharescreen.title} /> | ||||
|         </div> | ||||
|         <div class="m-2" slot="content3"> | ||||
|           <ShareScreen {state} /> | ||||
|         </div> | ||||
|       </TabbedGroup> | ||||
|     </FloatOver> | ||||
|   </If> | ||||
| 
 | ||||
|   <!-- Filterpane --> | ||||
|   <If condition={state.guistate.filtersPanelIsOpened}> | ||||
|     <FloatOver on:close={() => state.guistate.filtersPanelIsOpened.setData(false)}> | ||||
|       <FilterPanel {state} /> | ||||
|     </FloatOver> | ||||
|   </If> | ||||
| 
 | ||||
|   <!-- background layer selector --> | ||||
|   <IfHidden condition={state.guistate.backgroundLayerSelectionIsOpened}> | ||||
|     <FloatOver | ||||
|       on:close={() => { | ||||
|         state.guistate.backgroundLayerSelectionIsOpened.setData(false) | ||||
|       }} | ||||
|     > | ||||
|       <RasterLayerOverview | ||||
|         {availableLayers} | ||||
|         map={state.map} | ||||
|         mapproperties={state.mapProperties} | ||||
|         userstate={state.userRelatedState} | ||||
|         visible={state.guistate.backgroundLayerSelectionIsOpened} | ||||
|       /> | ||||
|     </FloatOver> | ||||
|   </IfHidden> | ||||
| 
 | ||||
|   <!-- Menu page --> | ||||
|   <If condition={state.guistate.menuIsOpened}> | ||||
|     <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} | ||||
|         tab={state.guistate.menuViewTabIndex} | ||||
|       > | ||||
|         <div slot="post-tablist"> | ||||
|           <XCircleIcon | ||||
|             class="mr-2 h-8 w-8" | ||||
|             on:click={() => state.guistate.menuIsOpened.setData(false)} | ||||
|           /> | ||||
|         </div> | ||||
|         <div class="flex" slot="title0"> | ||||
|           <Tr t={Translations.t.general.menu.aboutMapComplete} /> | ||||
|         </div> | ||||
| 
 | ||||
|         <div slot="content0" class="flex flex-col"> | ||||
|           <AboutMapComplete {state} /> | ||||
|           <div class="m-2 flex flex-col"> | ||||
|             <HotkeyTable /> | ||||
|           </div> | ||||
|         </div> | ||||
| 
 | ||||
|         <div class="flex" slot="title1"> | ||||
|           <CogIcon class="h-6 w-6" /> | ||||
|           <Tr t={UserRelatedState.usersettingsConfig.title.GetRenderValue({})} /> | ||||
|         </div> | ||||
| 
 | ||||
|         <div class="links-as-button py-8" slot="content1"> | ||||
|           <!-- All shown components are set by 'usersettings.json', which happily uses some special visualisations created specifically for it --> | ||||
|           <LoginToggle {state}> | ||||
|             <div class="flex flex-col" slot="not-logged-in"> | ||||
|               <LanguagePicker availableLanguages={layout.language} /> | ||||
|               <Tr cls="alert" t={Translations.t.userinfo.notLoggedIn} /> | ||||
|               <LoginButton clss="primary" osmConnection={state.osmConnection} /> | ||||
|             </div> | ||||
|             <SelectedElementView | ||||
|               highlightedRendering={state.guistate.highlightedUserSetting} | ||||
|               layer={usersettingslayer} | ||||
|               selectedElement={{ | ||||
|                 type: "Feature", | ||||
|                 properties: { id: "settings" }, | ||||
|                 geometry: { type: "Point", coordinates: [0, 0] }, | ||||
|               }} | ||||
|               {state} | ||||
|               tags={state.userRelatedState.preferencesAsTags} | ||||
|             /> | ||||
|           </LoginToggle> | ||||
|         </div> | ||||
| 
 | ||||
|         <div class="flex" slot="title2"> | ||||
|           <HeartIcon class="h-6 w-6" /> | ||||
|           <Tr t={Translations.t.favouritePoi.tab} /> | ||||
|         </div> | ||||
| 
 | ||||
|         <div class="m-2 flex flex-col" slot="content2"> | ||||
|           <h3> | ||||
|             <Tr t={Translations.t.favouritePoi.title} /> | ||||
|           </h3> | ||||
|           <Favourites {state} /> | ||||
|           <h3> | ||||
|             <Tr t={Translations.t.reviews.your_reviews} /> | ||||
|           </h3> | ||||
|           <ReviewsOverview {state} /> | ||||
|         </div> | ||||
|       </TabbedGroup> | ||||
|     </FloatOver> | ||||
|   </If> | ||||
| 
 | ||||
|   <!-- Privacy policy --> | ||||
|   <If condition={state.guistate.privacyPanelIsOpened}> | ||||
|     <FloatOver on:close={() => state.guistate.privacyPanelIsOpened.setData(false)}> | ||||
|       <div class="flex h-full flex-col overflow-hidden"> | ||||
|         <h2 class="low-interaction m-0 flex items-center p-4 drop-shadow-md"> | ||||
|           <EyeIcon class="w-6 pr-2" /> | ||||
|           <Tr t={Translations.t.privacy.title} /> | ||||
|         </h2> | ||||
|         <div class="overflow-auto p-4"> | ||||
|           <PrivacyPolicy {state} /> | ||||
|         </div> | ||||
|       </div> | ||||
|     </FloatOver> | ||||
|   </If> | ||||
| 
 | ||||
|   <!-- Attribution, copyright and about MapComplete (no menu case) --> | ||||
|   <If condition={state.guistate.copyrightPanelIsOpened}> | ||||
|     <FloatOver on:close={() => state.guistate.copyrightPanelIsOpened.setData(false)}> | ||||
|       <div class="flex h-full flex-col overflow-hidden"> | ||||
|         <h1 class="low-interaction m-0 flex items-center p-4 drop-shadow-md"> | ||||
|           <Tr t={Translations.t.general.attribution.title} /> | ||||
|         </h1> | ||||
|         <div class="overflow-auto p-4"> | ||||
|           <h2> | ||||
|             <Tr t={Translations.t.general.menu.aboutMapComplete} /> | ||||
|           </h2> | ||||
|           <AboutMapComplete {state} /> | ||||
|           <CopyrightPanel {state} /> | ||||
|         </div> | ||||
|       </div> | ||||
|     </FloatOver> | ||||
|   </If> | ||||
| 
 | ||||
|   <!-- Community index --> | ||||
|   <If condition={state.guistate.communityIndexPanelIsOpened}> | ||||
|     <FloatOver on:close={() => state.guistate.communityIndexPanelIsOpened.setData(false)}> | ||||
|       <div class="flex h-full flex-col overflow-hidden"> | ||||
|         <h2 class="low-interaction m-0 flex items-center p-4"> | ||||
|           <Community class="h-6 w-6" /> | ||||
|           <Tr t={Translations.t.communityIndex.title} /> | ||||
|         </h2> | ||||
|         <div class="overflow-auto p-4"> | ||||
|           <CommunityIndexView location={state.mapProperties.location} /> | ||||
|         </div> | ||||
|       </div> | ||||
|     </FloatOver> | ||||
|   </If> | ||||
| 
 | ||||
|   <CloseAnimation isOpened={state.guistate.themeIsOpened} moveTo={openMapButton} debug="theme" /> | ||||
|   <CloseAnimation isOpened={state.guistate.menuIsOpened} moveTo={openMenuButton} debug="menu" /> | ||||
|   <CloseAnimation | ||||
|     isOpened={selectedLayer.map((sl) => sl !== undefined && sl === currentViewLayer)} | ||||
|     moveTo={openCurrentViewLayerButton} | ||||
|     debug="currentViewLayer" | ||||
|   /> | ||||
|   <CloseAnimation | ||||
|     isOpened={selectedElement.map( | ||||
|       (sl) => sl !== undefined && sl?.properties?.id === LastClickFeatureSource.newPointElementId | ||||
|     )} | ||||
|     moveTo={openNewElementButton} | ||||
|     debug="newElement" | ||||
|   /> | ||||
|   <CloseAnimation | ||||
|     isOpened={state.guistate.filtersPanelIsOpened} | ||||
|     moveTo={openFilterButton} | ||||
|     debug="filter" | ||||
|   /> | ||||
|   <CloseAnimation | ||||
|     isOpened={state.guistate.backgroundLayerSelectionIsOpened} | ||||
|     moveTo={openBackgroundButton} | ||||
|     debug="bg" | ||||
|   /> | ||||
| </main> | ||||
|  |  | |||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue