Merge develop

This commit is contained in:
Pieter Vander Vennet 2024-09-02 12:08:22 +02:00
commit bcd53405c8
197 changed files with 5675 additions and 5188 deletions

View file

@ -27,6 +27,7 @@
import Github from "../assets/svg/Github.svelte"
import { Utils } from "../Utils"
import { ArrowTrendingUp } from "@babeard/svelte-heroicons/solid/ArrowTrendingUp"
import Searchbar from "./Base/Searchbar.svelte"
const featureSwitches = new OsmConnectionFeatureSwitches()
const osmConnection = new OsmConnection({
@ -42,7 +43,7 @@
const tr = Translations.t.general.morescreen
let userLanguages = osmConnection.userDetails.map((ud) => ud.languages)
let themeSearchText: UIEventSource<string | undefined> = new UIEventSource<string>(undefined)
let themeSearchText: UIEventSource<string | undefined> = new UIEventSource<string>("")
document.addEventListener("keydown", function(event) {
if (event.ctrlKey && event.code === "KeyF") {
@ -91,24 +92,7 @@
</div>
</div>
<form
class="flex justify-center"
on:submit|preventDefault={() => MoreScreen.applySearch(themeSearchText.data)}
>
<label
class="neutral-label my-2 flex w-full items-center rounded-full border-2 border-black sm:w-1/2"
>
<SearchIcon aria-hidden="true" class="h-8 w-8" />
<input
autofocus
bind:value={$themeSearchText}
class="mr-4 w-full outline-none"
id="theme-search"
type="search"
use:placeholder={tr.searchForATheme}
/>
</label>
</form>
<Searchbar value={themeSearchText} placeholder={tr.searchForATheme} on:search={() => MoreScreen.applySearch(themeSearchText.data)}/>
<ThemesList search={themeSearchText} {state} themes={MoreScreen.officialThemes} />

View file

@ -3,6 +3,7 @@
import { ariaLabel } from "../../Utils/ariaLabel"
import Translations from "../i18n/Translations"
import { XCircleIcon } from "@babeard/svelte-heroicons/solid"
import { CloseButton } from "flowbite-svelte"
/**
* The slotted element will be shown on top, with a lower-opacity border
@ -29,7 +30,7 @@
}}
>
<div
class="content normal-background pointer-events-auto h-full"
class="content relative normal-background pointer-events-auto h-full"
on:click|stopPropagation={() => {}}
>
<div class="h-full rounded-xl">
@ -37,22 +38,21 @@
</div>
<slot name="close-button">
<!-- The close button is placed _after_ the default slot in order to always paint it on top -->
<div
class="absolute right-10 top-10 m-0 cursor-pointer rounded-full border-0 border-none bg-white p-0"
style="margin: -0.25rem"
on:click={() => dispatch("close")}
use:ariaLabel={Translations.t.general.backToMap}
>
<XCircleIcon class="h-8 w-8" />
<div class="absolute top-0 right-0">
<CloseButton class="normal-background mt-2 mr-2"
on:click={() => dispatch("close")}
/>
</div>
</slot>
</div>
</div>
<style>
.content {
border-radius: 0.5rem;
overflow-x: hidden;
box-shadow: 0 0 1rem #00000088;
}
.content {
border-radius: 0.5rem;
overflow-x: hidden;
box-shadow: 0 0 1rem #00000088;
}
</style>

View file

@ -29,7 +29,7 @@
size="xl"
{defaultClass} {bodyClass} {dialogClass} {headerClass}
color="none">
<h1 slot="header" class="w-full">
<h1 slot="header" class="page-header w-full">
<slot name="header" />
</h1>
<slot />
@ -44,3 +44,16 @@
</slot>
</button>
{/if}
<style>
:global(.page-header) {
display: flex;
align-items: center;
}
:global(.page-header svg) {
width: 2rem;
height: 2rem;
margin-right: 0.75rem;
}
</style>

View file

@ -0,0 +1,43 @@
<script lang="ts">
import { UIEventSource } from "../../Logic/UIEventSource"
import Translations from "../i18n/Translations"
import { createEventDispatcher } from "svelte"
import { placeholder as set_placeholder } from "../../Utils/placeholder"
import { SearchIcon } from "@rgossiaux/svelte-heroicons/solid"
import { ariaLabel } from "../../Utils/ariaLabel"
import { Translation } from "../i18n/Translation"
export let value: UIEventSource<string>
let _value = value.data ?? ""
value.addCallbackD(v => {
_value = v
})
$: value.set(_value)
const dispatch = createEventDispatcher<{ search }>()
export let placeholder: Translation = Translations.t.general.search.search
</script>
<form
class="flex justify-center"
on:submit|preventDefault={() => dispatch("search")}
>
<label
class="neutral-label my-2 flex w-full items-center rounded-full border-2 border-black sm:w-1/2 box-shadow"
>
<input
type="search"
style=" --tw-ring-color: rgb(0 0 0 / 0) !important;"
class="ml-4 pl-1 w-full outline-none border-none"
on:keypress={(keypr) => {
return keypr.key === "Enter" ? dispatch("search") : undefined
}}
bind:value={_value}
use:set_placeholder={placeholder}
use:ariaLabel={Translations.t.general.search.search}
/>
<SearchIcon aria-hidden="true" class="h-8 w-8 mx-2" />
</label>
</form>

View file

@ -11,7 +11,7 @@
import CommunityIndexView from "./CommunityIndexView.svelte"
import Community from "../../assets/svg/Community.svelte"
import LoginToggle from "../Base/LoginToggle.svelte"
import { Sidebar } from "flowbite-svelte"
import { CloseButton, Sidebar } from "flowbite-svelte"
import HotkeyTable from "./HotkeyTable.svelte"
import { Utils } from "../../Utils"
import Constants from "../../Models/Constants"
@ -47,6 +47,8 @@
import LogoutButton from "../Base/LogoutButton.svelte"
import { BoltIcon } from "@babeard/svelte-heroicons/mini"
import Copyright from "../../assets/svg/Copyright.svelte"
import Pencil from "../../assets/svg/Pencil.svelte"
import Squares2x2 from "@babeard/svelte-heroicons/mini/Squares2x2"
export let state: ThemeViewState
let userdetails = state.osmConnection.userDetails
@ -62,6 +64,23 @@
</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">
<div class="flex justify-between">
<h2>
<Tr t={t.title}/>
</h2>
<CloseButton on:click={() => {pg.menu.set(false)}} />
</div>
{#if $showHome}
<a class="flex button primary" href={Utils.HomepageLink()}>
<Squares2x2 class="h-10 w-10" />
{#if Utils.isIframe}
<Tr t={Translations.t.general.seeIndex} />
{:else}
<Tr t={Translations.t.general.backToIndex} />
{/if}
</a>
{/if}
<!-- User related: avatar, settings, favourits, logout -->
<div class="sidebar-unit">
@ -77,10 +96,10 @@
<Page {onlyLink} shown={pg.usersettings}>
<div class="flex" slot="header">
<CogIcon class="h-6 w-6" />
<svelte:fragment slot="header">
<CogIcon/>
<Tr t={UserRelatedState.usersettingsConfig.title.GetRenderValue({})} />
</div>
</svelte:fragment>
<!-- All shown components are set by 'usersettings.json', which happily uses some special visualisations created specifically for it -->
<LoginToggle {state}>
@ -109,10 +128,12 @@
<LoginToggle {state}>
<Page {onlyLink} shown={pg.favourites}>
<div class="flex" slot="header">
<HeartIcon class="h-6 w-6" />
<svelte:fragment slot="header">
<HeartIcon />
<Tr t={Translations.t.favouritePoi.tab} />
</div>
</svelte:fragment>
<h3>
<Tr t={Translations.t.favouritePoi.title} />
@ -146,10 +167,10 @@
<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" />
<svelte:fragment slot="header">
<Marker icons={layout.icon} />
<Tr t={layout.title} />
</div>
</svelte:fragment>
<ThemeIntroPanel {state} />
</Page>
@ -158,20 +179,20 @@
<RasterLayerOverview {onlyLink} {state} />
<Page {onlyLink} shown={pg.share}>
<div class="flex" slot="header">
<Share class="h-4 w-4" />
<svelte:fragment slot="header">
<Share/>
<Tr t={Translations.t.general.sharescreen.title} />
</div>
</svelte:fragment>
<ShareScreen {state} />
</Page>
{#if state.featureSwitches.featureSwitchEnableExport}
<Page {onlyLink} shown={pg.download}>
<div slot="header" class="flex">
<ArrowDownTray class="h-4 w-4" />
<svelte:fragment slot="header">
<ArrowDownTray />
<Tr t={Translations.t.general.download.title} />
</div>
</svelte:fragment>
<DownloadPanel {state} />
</Page>
{/if}
@ -206,22 +227,13 @@
<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" />
<svelte:fragment slot="header">
<Community/>
<Tr t={Translations.t.communityIndex.title} />
</div>
</svelte:fragment>
<CommunityIndexView location={state.mapProperties.location} />
</Page>
@ -242,13 +254,20 @@
<Tr t={Translations.t.general.menu.aboutMapComplete} />
</h3>
<div class="hidden-on-mobile">
<a
class="flex"
href={window.location.protocol + "//" + window.location.host + "/studio.html"}
>
<Pencil class="mr-2 h-6 w-6" />
<Tr t={Translations.t.general.morescreen.createYourOwnTheme} />
</a>
<div class="hidden-on-mobile w-full">
<Page {onlyLink} shown={pg.hotkeys}>
<div class="flex" slot="header">
<BoltIcon class="w-6 h-6" />
<svelte:fragment slot="header">
<BoltIcon />
<Tr t={ Translations.t.hotkeyDocumentation.title} />
</div>
</svelte:fragment>
<HotkeyTable />
</Page>
</div>
@ -276,29 +295,29 @@
<Page {onlyLink} shown={pg.copyright}>
<div slot="header" class="flex">
<Copyright class="w-8 h-8" />
<svelte:fragment slot="header">
<Copyright />
<Tr t={Translations.t.general.attribution.attributionTitle} />
</div>
</svelte:fragment>
<CopyrightPanel {state} />
</Page>
<Page {onlyLink} shown={pg.copyright_icons}>
<div slot="header" class="flex">
<Copyright class="w-8 h-8" />
<svelte:fragment slot="header" >
<Copyright/>
<Tr t={ Translations.t.general.attribution.iconAttribution.title} />
</div>
</svelte:fragment>
<CopyrightAllIcons {state} />
</Page>
<Page {onlyLink} shown={pg.privacy}>
<div class="flex" slot="header">
<EyeIcon class="w-8 h-8" />
<svelte:fragment slot="header">
<EyeIcon />
<Tr t={Translations.t.privacy.title} />
</div>
</svelte:fragment>
<PrivacyPolicy {state} />
</Page>
@ -340,12 +359,14 @@
}
:global(.sidebar-button, .sidebar-button, .sidebar-unit > a) {
:global(.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;
width: 100%;
text-align: start;
}
:global(.sidebar-button > svg , .sidebar-button > img, .sidebar-unit a > img, .sidebar-unit > a svg) {

View file

@ -8,6 +8,7 @@
import Tr from "../Base/Tr.svelte"
import { XCircleIcon } from "@rgossiaux/svelte-heroicons/solid"
import { ariaLabel } from "../../Utils/ariaLabel"
import { CloseButton } from "flowbite-svelte"
export let state: SpecialVisualizationState
export let layer: LayerConfig
@ -72,14 +73,10 @@
{/if}
</div>
<slot name="close-button">
<button
class="mt-2 h-fit shrink-0 cursor-pointer self-center rounded-full border-none p-0"
on:click={() => state.selectedElement.setData(undefined)}
style="border: 0 !important; padding: 0 !important;"
use:ariaLabel={Translations.t.general.backToMap}
>
<XCircleIcon aria-hidden={true} class="h-8 w-8" />
</button>
<div class="mt-4">
<CloseButton on:click={() => state.selectedElement.setData(undefined)}/>
</div>
</slot>
</div>

View file

@ -35,6 +35,8 @@
let enableBackground = true
let enableGeolocation = true
let location = state.mapProperties.location
function calculateLinkToShare(
showWelcomeMessage: boolean,
enableLogin: boolean,
@ -116,10 +118,12 @@
)
</script>
<div class="flex flex-col">
<div class="flex flex-col link-underline">
<a href="geo:{$location.lat},{$location.lon}">Open the current location in other applications</a>
<div class="flex flex-col">
<Tr t={tr.intro} />
<Copyable {state} text={linkToShare} />
</div>
<div class="flex justify-center">

View file

@ -21,7 +21,7 @@
state.mapProperties.location.setData({ lon, lat })
const z = state.mapProperties.zoom.data
state.mapProperties.zoom.setData(Math.min(17, Math.max(12, z)))
state.guistate.menuIsOpened.setData(false)
state.guistate.pageStates.menu.setData(false)
}
function select() {

View file

@ -41,7 +41,7 @@
/>
{#if canZoom && loaded}
<div class="absolute right-0 top-0 bg-black-transparent rounded-bl-full">
<div class="absolute right-0 top-0 bg-black-transparent rounded-bl-full" on:click={() => previewedImage.set(image)}>
<MagnifyingGlassPlusIcon class="w-8 h-8 pl-3 pb-3 cursor-zoom-in" color="white" />
</div>
{/if}

View file

@ -11,6 +11,8 @@
import { twMerge } from "tailwind-merge"
import { UIEventSource } from "../../Logic/UIEventSource"
import Loading from "../Base/Loading.svelte"
import Tr from "../Base/Tr.svelte"
import Translations from "../i18n/Translations"
export let image: ProvidedImage
export let clss: string = undefined
@ -48,7 +50,7 @@
on:click={() => download()}
>
<DownloadIcon class="h-6 w-6 px-2 opacity-100" />
Download
<Tr t={Translations.t.general.download.downloadImage}/>
</button>
</div>
</div>

View file

@ -1996,6 +1996,9 @@ export default class SpecialVisualizations {
): BaseUIElement {
const translation = tagSource.map((tags) => {
const presets = state.layout.getMatchingLayer(tags)?.presets
if(!presets){
return undefined
}
const matchingPresets = presets
.filter((pr) => pr.description !== undefined)
.filter((pr) => new And(pr.tags).matchesProperties(tags))

View file

@ -46,10 +46,12 @@
import DrawerRight from "./Base/DrawerRight.svelte"
import SearchResults from "./Search/SearchResults.svelte"
import { CloseButton } from "flowbite-svelte"
import Hash from "../Logic/Web/Hash"
export let state: ThemeViewState
let layout = state.layout
let maplibremap: UIEventSource<MlMap> = state.map
let state_selectedElement = state.selectedElement
let selectedElement: UIEventSource<Feature> = new UIEventSource<Feature>(undefined)
let compass = Orientation.singleton.alpha
let compassLoaded = Orientation.singleton.gotMeasurement
@ -71,6 +73,7 @@
})
})
let selectedLayer: Store<LayerConfig> = state.selectedElement.mapD((element) => {
if (element.properties.id.startsWith("current_view")) {
return currentViewLayer
@ -85,11 +88,11 @@
state.mapProperties.installCustomKeyboardHandler(viewport)
let canZoomIn = mapproperties.maxzoom.map(
(mz) => mapproperties.zoom.data < mz,
[mapproperties.zoom]
[mapproperties.zoom],
)
let canZoomOut = mapproperties.minzoom.map(
(mz) => mapproperties.zoom.data > mz,
[mapproperties.zoom]
[mapproperties.zoom],
)
function updateViewport() {
@ -105,7 +108,7 @@
const bottomRight = mlmap.unproject([rect.right, rect.bottom])
const bbox = new BBox([
[topLeft.lng, topLeft.lat],
[bottomRight.lng, bottomRight.lat]
[bottomRight.lng, bottomRight.lat],
])
state.visualFeedbackViewportBounds.setData(bbox)
}
@ -125,7 +128,7 @@
onDestroy(
rasterLayer.addCallbackAndRunD((l) => {
rasterLayerName = l.properties.name
})
}),
)
let previewedImage = state.previewedImage
let addNewFeatureMode = state.userRelatedState.addNewFeatureMode
@ -154,6 +157,7 @@
animation?.cameraAnimation(mlmap)
}
let hash = Hash.hash
</script>
<main>
@ -172,6 +176,110 @@
/>
</div>
{/if}
<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.pageStates.menu.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 $debug && $hash}
<div class="alert">
{$hash}
</div>
{/if}
<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}
/>
</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}
<div class="w-fit">
<VisualFeedbackPanel {state} />
</div>
{/if}
</If>
</div>
<div class="float-left m-1 flex flex-col sm:mt-2">
<If condition={state.featureSwitches.featureSwitchWelcomeMessage}>
</If>
{#if currentViewLayer?.tagRenderings && currentViewLayer.defaultIcon()}
<MapControlButton
on:click={() => {
state.selectCurrentView()
}}
on:keydown={forwardEventToMap}
>
<div class="h-8 w-8 cursor-pointer">
<ToSvelte construct={() => currentViewLayer.defaultIcon()} />
</div>
</MapControlButton>
{/if}
<ExtraLinkButton {state} />
<UploadingImageCounter featureId="*" showThankYou={false} {state} />
<PendingChangesIndicator {state} />
<If condition={state.featureSwitchIsTesting}>
<div class="alert w-fit">Testmode</div>
</If>
{#if state.osmConnection.Backend().startsWith("https://master.apis.dev.openstreetmap.org")}
<div class="thanks">Testserver</div>
{/if}
<If condition={state.featureSwitches.featureSwitchFakeUser}>
<div class="alert w-fit">Faking a user (Testmode)</div>
</If>
</div>
<div class="flex w-full flex-col items-center justify-center">
<!-- Flex and w-full are needed for the positioning -->
<!-- Centermessage -->
<StateIndicator {state} />
<ReverseGeocoding {state} />
</div>
</div>
<div class="pointer-events-none absolute bottom-0 left-0 mb-4 w-screen">
<!-- bottom controls -->
<div class="flex w-full items-end justify-between px-4">
@ -395,7 +503,7 @@
<svelte:fragment slot="error" />
</LoginToggle>
<DrawerLeft shown={state.guistate.menuIsOpened}>
<DrawerLeft shown={state.guistate.pageStates.menu}>
<div class="h-screen overflow-y-auto">
<MenuDrawer onlyLink={true} {state} />
</div>
@ -406,11 +514,11 @@
<!-- right modal with the selected element view -->
<ModalRight
on:close={() => {
selectedElement.setData(undefined)
state.selectedElement.setData(undefined)
}}
>
<div slot="close-button" />
<SelectedElementPanel {state} selected={$selectedElement} />
<SelectedElementPanel {state} selected={$state_selectedElement} />
</ModalRight>
{/if}
@ -424,7 +532,7 @@
}}
>
<span slot="close-button" />
<SelectedElementPanel absolute={false} {state} selected={$selectedElement} />
<SelectedElementPanel absolute={false} {state} selected={$state_selectedElement} />
</FloatOver>
{:else}
<FloatOver
@ -432,7 +540,7 @@
state.selectedElement.setData(undefined)
}}
>
<SelectedElementView {state} layer={$selectedLayer} selectedElement={$selectedElement} />
<SelectedElementView {state} layer={$selectedLayer} selectedElement={$state_selectedElement} />
</FloatOver>
{/if}
{/if}