Merge develop

This commit is contained in:
Pieter Vander Vennet 2023-10-30 16:32:43 +01:00
commit 29ff09024f
287 changed files with 14955 additions and 4036 deletions

View file

@ -12,28 +12,30 @@
let id = Math.random() * 1000000000 + ""
</script>
<form on:change|preventDefault={() => {
drawAttention = false
dispatcher("submit", inputElement.files)
}}
on:dragend={() => {
console.log("Drag end")
drawAttention = false
}}
on:dragenter|preventDefault|stopPropagation={(e) => {
console.log("Dragging enter")
drawAttention = true
e.dataTransfer.drop = "copy"
}}
on:dragstart={() => {
console.log("DragStart")
drawAttention = false
}}
on:drop|preventDefault|stopPropagation={(e) => {
console.log("Got a 'drop'")
drawAttention = false
dispatcher("submit", e.dataTransfer.files)
}}>
<form
on:change|preventDefault={() => {
drawAttention = false
dispatcher("submit", inputElement.files)
}}
on:dragend={() => {
console.log("Drag end")
drawAttention = false
}}
on:dragenter|preventDefault|stopPropagation={(e) => {
console.log("Dragging enter")
drawAttention = true
e.dataTransfer.drop = "copy"
}}
on:dragstart={() => {
console.log("DragStart")
drawAttention = false
}}
on:drop|preventDefault|stopPropagation={(e) => {
console.log("Got a 'drop'")
drawAttention = false
dispatcher("submit", e.dataTransfer.files)
}}
>
<label class={twMerge(cls, drawAttention ? "glowing-shadow" : "")} for={"fileinput" + id}>
<slot />
</label>
@ -44,7 +46,6 @@
id={"fileinput" + id}
{multiple}
name="file-input"
type="file"
/>
</form>

View file

@ -11,7 +11,9 @@
<div
class="absolute top-0 right-0 h-screen w-screen p-4 md:p-6"
style="background-color: #00000088; z-index: 20"
on:click={() => {dispatch("close")}}
on:click={() => {
dispatch("close")
}}
>
<div class="content normal-background" on:click|stopPropagation={() => {}}>
<div class="h-full rounded-xl">

View file

@ -23,11 +23,11 @@ export default class Hotkeys {
>([])
private static textElementSelected(event: KeyboardEvent): boolean {
if(event.ctrlKey || event.altKey){
if (event.ctrlKey || event.altKey) {
// This is an event with a modifier-key, lets not ignore it
return false
}
if(event.key === "Escape"){
if (event.key === "Escape") {
return false // Another not-printable character that should not be ignored
}
return ["input", "textarea"].includes(document?.activeElement?.tagName?.toLowerCase())

View file

@ -12,21 +12,20 @@
* E.g.
* condition3 = new ImmutableStore(false) will always hide tab3 (the fourth tab)
*/
let tr = new ImmutableStore(true);
export let condition0: Store<boolean> = tr;
export let condition1: Store<boolean> = tr;
export let condition2: Store<boolean> = tr;
export let condition3: Store<boolean> = tr;
export let condition4: Store<boolean> = tr;
export let condition5: Store<boolean> = tr;
export let condition6: Store<boolean> = tr;
export let tab: UIEventSource<number> = new UIEventSource<number>(0);
let tabElements: HTMLElement[] = [];
$: tabElements[$tab]?.click();
const tr = new ImmutableStore(true)
export let condition0: Store<boolean> = tr
export let condition1: Store<boolean> = tr
export let condition2: Store<boolean> = tr
export let condition3: Store<boolean> = tr
export let condition4: Store<boolean> = tr
export let condition5: Store<boolean> = tr
export let condition6: Store<boolean> = tr
export let tab: UIEventSource<number> = new UIEventSource<number>(0)
let tabElements: HTMLElement[] = []
$: tabElements[$tab]?.click()
$: {
if (tabElements[tab.data]) {
window.setTimeout(() => tabElements[tab.data].click(), 50);
window.setTimeout(() => tabElements[tab.data].click(), 50)
}
}
</script>
@ -138,44 +137,44 @@
</div>
<style>
.tabbedgroup {
max-height: 100vh;
height: 100%;
}
.tabbedgroup {
max-height: 100vh;
height: 100%;
}
:global(.tabpanel) {
height: 100%;
}
:global(.tabpanel) {
height: 100%;
}
:global(.tabpanels) {
height: calc(100% - 2rem);
}
:global(.tabpanels) {
height: calc(100% - 2rem);
}
:global(.tab) {
margin: 0.25rem;
padding: 0.25rem;
padding-left: 0.75rem;
padding-right: 0.75rem;
border-radius: 1rem;
}
:global(.tab) {
margin: 0.25rem;
padding: 0.25rem;
padding-left: 0.75rem;
padding-right: 0.75rem;
border-radius: 1rem;
}
:global(.tab .flex) {
align-items: center;
gap: 0.25rem;
}
:global(.tab .flex) {
align-items: center;
gap: 0.25rem;
}
:global(.tab span|div) {
align-items: center;
gap: 0.25rem;
display: flex;
}
:global(.tab span|div) {
align-items: center;
gap: 0.25rem;
display: flex;
}
:global(.tab-selected svg) {
fill: var(--catch-detail-color-contrast);
}
:global(.tab-selected svg) {
fill: var(--catch-detail-color-contrast);
}
:global(.tab-unselected) {
background-color: var(--background-color) !important;
color: var(--foreground-color) !important;
}
:global(.tab-unselected) {
background-color: var(--background-color) !important;
color: var(--foreground-color) !important;
}
</style>

View file

@ -38,7 +38,7 @@
if (value.data === undefined) {
value.setData(coordinate)
}
if(coordinate === undefined){
if (coordinate === undefined) {
coordinate = value.data
}
export let snapToLayers: string[] | undefined
@ -47,8 +47,6 @@
export let snappedTo: UIEventSource<string | undefined>
let preciseLocation: UIEventSource<{ lon: number; lat: number }> = new UIEventSource<{
lon: number
lat: number
@ -75,7 +73,7 @@
rasterLayer: UIEventSource.feedFrom(state.mapProperties.rasterLayer),
}
if(targetLayer){
if (targetLayer) {
const featuresForLayer = state.perLayer.get(targetLayer.id)
if (featuresForLayer) {
new ShowDataLayer(map, {

View file

@ -1,21 +1,23 @@
<script lang="ts">
import type { SpecialVisualizationState } from "../SpecialVisualization"
import { Store } from "../../Logic/UIEventSource"
import { Changes } from "../../Logic/Osm/Changes"
import Loading from "../Base/Loading.svelte"
import Translations from "../i18n/Translations"
import Tr from "../Base/Tr.svelte"
import type { SpecialVisualizationState } from "../SpecialVisualization"
import { Store } from "../../Logic/UIEventSource"
import { Changes } from "../../Logic/Osm/Changes"
import Loading from "../Base/Loading.svelte"
import Translations from "../i18n/Translations"
import Tr from "../Base/Tr.svelte"
export let state: SpecialVisualizationState
export let state: SpecialVisualizationState
const changes: Changes = state.changes
const isUploading: Store<boolean> = changes.isUploading
const pendingChangesCount: Store<number> = changes.pendingChanges.map(ls => ls.length)
const errors = changes.errors
const changes: Changes = state.changes
const isUploading: Store<boolean> = changes.isUploading
const pendingChangesCount: Store<number> = changes.pendingChanges.map((ls) => ls.length)
const errors = changes.errors
</script>
<div class="flex flex-col pointer-events-auto" on:click={() => changes.flushChanges("Pending changes indicator clicked")}>
<div
class="pointer-events-auto flex flex-col"
on:click={() => changes.flushChanges("Pending changes indicator clicked")}
>
{#if $isUploading}
<Loading>
<Tr cls="thx" t={Translations.t.general.uploadingChanges} />
@ -23,10 +25,13 @@
{:else if $pendingChangesCount === 1}
<Tr cls="alert" t={Translations.t.general.uploadPendingSingle} />
{:else if $pendingChangesCount > 1}
<Tr cls="alert" t={Translations.t.general.uploadPending.Subs({count: $pendingChangesCount})} />
<Tr
cls="alert"
t={Translations.t.general.uploadPending.Subs({ count: $pendingChangesCount })}
/>
{/if}
{#each $errors as error}
<Tr cls="alert" t={Translations.t.general.uploadError.Subs({error})} />
<Tr cls="alert" t={Translations.t.general.uploadError.Subs({ error })} />
{/each}
</div>

View file

@ -15,19 +15,19 @@
export let userDetails: UIEventSource<UserDetails>
export let state: { layoutToUse?: { id: string }; osmConnection: OsmConnection }
export let selected: boolean = false
let unlockedPersonal = LocalStorageSource.GetParsed("unlocked_personal_theme", false)
userDetails.addCallbackAndRunD(userDetails => {
if(!userDetails.loggedIn){
return
}
if(userDetails.csCount > Constants.userJourney.personalLayoutUnlock){
unlockedPersonal.setData(true)
}
return true
userDetails.addCallbackAndRunD((userDetails) => {
if (!userDetails.loggedIn) {
return
}
if (userDetails.csCount > Constants.userJourney.personalLayoutUnlock) {
unlockedPersonal.setData(true)
}
return true
})
$: title = new Translation(
theme.title,
!isCustom && !theme.mustHaveLanguage ? "themes:" + theme.id + ".title" : undefined

View file

@ -1,48 +1,51 @@
<script lang="ts">
import Translations from "../i18n/Translations";
import Svg from "../../Svg";
import Tr from "../Base/Tr.svelte";
import NextButton from "../Base/NextButton.svelte";
import Geosearch from "./Geosearch.svelte";
import ToSvelte from "../Base/ToSvelte.svelte";
import ThemeViewState from "../../Models/ThemeViewState";
import { Store, UIEventSource } from "../../Logic/UIEventSource";
import { SearchIcon } from "@rgossiaux/svelte-heroicons/solid";
import { twJoin } from "tailwind-merge";
import { Utils } from "../../Utils";
import type { GeolocationPermissionState } from "../../Logic/State/GeoLocationState";
import If from "../Base/If.svelte";
import Translations from "../i18n/Translations"
import Svg from "../../Svg"
import Tr from "../Base/Tr.svelte"
import NextButton from "../Base/NextButton.svelte"
import Geosearch from "./Geosearch.svelte"
import ToSvelte from "../Base/ToSvelte.svelte"
import ThemeViewState from "../../Models/ThemeViewState"
import { UIEventSource } from "../../Logic/UIEventSource"
import { SearchIcon } from "@rgossiaux/svelte-heroicons/solid"
import { twJoin } from "tailwind-merge"
import { Utils } from "../../Utils"
import type { GeolocationPermissionState } from "../../Logic/State/GeoLocationState"
import { GeoLocationState } from "../../Logic/State/GeoLocationState"
import If from "../Base/If.svelte"
import { ExclamationTriangleIcon } from "@babeard/svelte-heroicons/mini"
import type { Readable } from "svelte/store"
/**
* The theme introduction panel
*/
export let state: ThemeViewState;
let layout = state.layout;
let selectedElement = state.selectedElement;
let selectedLayer = state.selectedLayer;
/**
* The theme introduction panel
*/
export let state: ThemeViewState
let layout = state.layout
let selectedElement = state.selectedElement
let selectedLayer = state.selectedLayer
let triggerSearch: UIEventSource<any> = new UIEventSource<any>(undefined);
let searchEnabled = false;
let triggerSearch: UIEventSource<any> = new UIEventSource<any>(undefined)
let searchEnabled = false
let geopermission: Store<GeolocationPermissionState> =
state.geolocation.geolocationState.permission;
let currentGPSLocation = state.geolocation.geolocationState.currentGPSLocation;
let geopermission: Readable<GeolocationPermissionState> =
state.geolocation.geolocationState.permission
let currentGPSLocation = state.geolocation.geolocationState.currentGPSLocation
geopermission.addCallback((perm) => console.log(">>>> Permission", perm));
geopermission.addCallback((perm) => console.log(">>>> Permission", perm))
function jumpToCurrentLocation() {
const glstate = state.geolocation.geolocationState;
if (glstate.currentGPSLocation.data !== undefined) {
const c: GeolocationCoordinates = glstate.currentGPSLocation.data;
state.guistate.themeIsOpened.setData(false);
const coor = { lon: c.longitude, lat: c.latitude };
state.mapProperties.location.setData(coor);
}
if (glstate.permission.data !== "granted") {
glstate.requestPermission();
return;
}
function jumpToCurrentLocation() {
const glstate = state.geolocation.geolocationState
if (glstate.currentGPSLocation.data !== undefined) {
const c: GeolocationCoordinates = glstate.currentGPSLocation.data
state.guistate.themeIsOpened.setData(false)
const coor = { lon: c.longitude, lat: c.latitude }
state.mapProperties.location.setData(coor)
}
if (glstate.permission.data !== "granted") {
glstate.requestPermission()
return
}
}
</script>
<div class="flex h-full flex-col justify-between">
@ -63,7 +66,6 @@
</div>
</NextButton>
<div class="flex w-full flex-wrap sm:flex-nowrap">
<If condition={state.featureSwitches.featureSwitchGeolocation}>
{#if $currentGPSLocation !== undefined || $geopermission === "prompt"}
@ -73,12 +75,15 @@
</button>
<!-- No geolocation granted - we don't show the button -->
{:else if $geopermission === "requested"}
<button class="disabled flex w-full items-center gap-x-2" on:click={jumpToCurrentLocation}>
<button
class="disabled flex w-full items-center gap-x-2"
on:click={jumpToCurrentLocation}
>
<!-- Even though disabled, when clicking we request the location again in case the contributor dismissed the location popup -->
<ToSvelte
construct={Svg.crosshair_svg()
.SetClass("w-8 h-8")
.SetStyle("animation: 3s linear 0s infinite normal none running spin;")}
.SetClass("w-8 h-8")
.SetStyle("animation: 3s linear 0s infinite normal none running spin;")}
/>
<Tr t={Translations.t.general.waitingForGeopermission} />
</button>
@ -91,24 +96,25 @@
<button class="disabled flex w-full items-center gap-x-2">
<ToSvelte
construct={Svg.crosshair_svg()
.SetClass("w-8 h-8")
.SetStyle("animation: 3s linear 0s infinite normal none running spin;")}
.SetClass("w-8 h-8")
.SetStyle("animation: 3s linear 0s infinite normal none running spin;")}
/>
<Tr t={Translations.t.general.waitingForLocation} />
</button>
{/if}
</If>
<If condition={state.featureSwitches.featureSwitchSearch}>
<div class=".button low-interaction m-1 flex w-full items-center gap-x-2 rounded border p-2">
<div
class=".button low-interaction m-1 flex h-fit w-full items-center gap-x-2 rounded border p-2"
>
<div class="w-full">
<Geosearch
bounds={state.mapProperties.bounds}
on:searchCompleted={() => state.guistate.themeIsOpened.setData(false)}
on:searchIsValid={(isValid) => {
searchEnabled = isValid
}}
searchEnabled = isValid
}}
perLayer={state.perLayer}
{selectedElement}
{selectedLayer}
@ -116,7 +122,10 @@
/>
</div>
<button
class={twJoin("flex items-center justify-between gap-x-2", !searchEnabled && "disabled")}
class={twJoin(
"flex items-center justify-between gap-x-2",
!searchEnabled && "disabled"
)}
on:click={() => triggerSearch.ping()}
>
<Tr t={Translations.t.general.search.searchShort} />
@ -125,6 +134,23 @@
</div>
</If>
</div>
{#if $currentGPSLocation === undefined && $geopermission === "requested" && GeoLocationState.isSafari()}
<a
href="https://support.apple.com/en-us/HT207092"
class="button w-full"
target="_blank"
rel="noopener"
>
<div class="link-underline m-1 flex w-full">
<ExclamationTriangleIcon class="w-12 pr-2" />
<div class="flex w-full flex-col">
<Tr cls="font-normal" t={Translations.t.general.enableGeolocationForSafari} />
<Tr t={Translations.t.general.enableGeolocationForSafariLink} />
</div>
</div>
</a>
{/if}
</div>
<div class="links-as-button links-w-full m-2 flex flex-col gap-y-1">

View file

@ -81,7 +81,7 @@
mimetype="image/png"
mainText={t.downloadAsPng}
helperText={t.downloadAsPngHelper}
construct={() => state.mapProperties.exportAsPng(4)}
construct={() => state.mapProperties.exportAsPng(1)}
/>
<div class="flex flex-col">

View file

@ -17,6 +17,7 @@
export let state: SpecialVisualizationState
export let tags: Store<OsmTags>
export let targetKey: string = undefined
/**
* Image to show in the button
* NOT the image to upload!
@ -35,7 +36,7 @@
const file = files.item(i)
console.log("Got file", file.name)
try {
state.imageUploadManager?.uploadImageAndApply(file, tags)
state?.imageUploadManager.uploadImageAndApply(file, tags, targetKey)
} catch (e) {
alert(e)
}

View file

@ -1,24 +1,24 @@
<script lang="ts">
/**
* Shows information about how much images are uploaded for the given feature
*
* Either pass in a store with tags or a featureId.
*/
/**
* Shows information about how much images are uploaded for the given feature
*
* Either pass in a store with tags or a featureId.
*/
import type { SpecialVisualizationState } from "../SpecialVisualization"
import { Store } from "../../Logic/UIEventSource"
import type { OsmTags } from "../../Models/OsmFeature"
import Translations from "../i18n/Translations"
import Tr from "../Base/Tr.svelte"
import Loading from "../Base/Loading.svelte"
import type { SpecialVisualizationState } from "../SpecialVisualization"
import { Store } from "../../Logic/UIEventSource"
import type { OsmTags } from "../../Models/OsmFeature"
import Translations from "../i18n/Translations"
import Tr from "../Base/Tr.svelte"
import Loading from "../Base/Loading.svelte"
export let state: SpecialVisualizationState
export let tags: Store<OsmTags>
export let featureId = tags.data.id
export let showThankYou: boolean = true
const { uploadStarted, uploadFinished, retried, failed } =
state.imageUploadManager.getCountsFor(featureId)
const t = Translations.t.image
export let state: SpecialVisualizationState
export let tags: Store<OsmTags>
export let featureId = tags.data.id
export let showThankYou: boolean = true
const { uploadStarted, uploadFinished, retried, failed } =
state.imageUploadManager.getCountsFor(featureId)
const t = Translations.t.image
</script>
{#if $uploadStarted === 1}

View file

@ -1,11 +1,12 @@
<script lang="ts">/**
* Opens the 'Opening hours input' in another top level window
*/
import { UIEventSource } from "../../../Logic/UIEventSource"
import ToSvelte from "../../Base/ToSvelte.svelte"
import OpeningHoursInput from "../../OpeningHours/OpeningHoursInput"
<script lang="ts">
/**
* Opens the 'Opening hours input' in another top level window
*/
import { UIEventSource } from "../../../Logic/UIEventSource"
import ToSvelte from "../../Base/ToSvelte.svelte"
import OpeningHoursInput from "../../OpeningHours/OpeningHoursInput"
export let value: UIEventSource<string>
export let value: UIEventSource<string>
</script>
<ToSvelte construct={new OpeningHoursInput(value)}></ToSvelte>
<ToSvelte construct={new OpeningHoursInput(value)} />

View file

@ -1,30 +1,34 @@
<script lang="ts">
import { Utils } from "../Utils"
import { Store, UIEventSource } from "../Logic/UIEventSource"
import Loading from "./Base/Loading.svelte"
import { OsmConnection } from "../Logic/Osm/OsmConnection"
import { Utils } from "../Utils"
import { Store, UIEventSource } from "../Logic/UIEventSource"
import Loading from "./Base/Loading.svelte"
import { OsmConnection } from "../Logic/Osm/OsmConnection"
const osmConnection = new OsmConnection({
attemptLogin: true
})
let loggedInContributor: Store<string> = osmConnection.userDetails.map(ud => ud.name)
export let source = "https://raw.githubusercontent.com/pietervdvn/MapComplete-data/main/picture-leaderboard.json"
let data: Store<undefined | {
const osmConnection = new OsmConnection({
attemptLogin: true,
})
let loggedInContributor: Store<string> = osmConnection.userDetails.map((ud) => ud.name)
export let source =
"https://raw.githubusercontent.com/pietervdvn/MapComplete-data/main/picture-leaderboard.json"
let data: Store<
| undefined
| {
leaderboard: {
rank: number,
name: string,
account: string,
nrOfImages: number
}[],
median: number,
totalAuthors: number,
rank: number
name: string
account: string
nrOfImages: number
}[]
median: number
totalAuthors: number
byLicense: {
license: string, total: number, authors: string[]
},
license: string
total: number
authors: string[]
}
date: string
}> = UIEventSource.FromPromise(Utils.downloadJsonCached(source))
}
> = UIEventSource.FromPromise(Utils.downloadJsonCached(source))
</script>
<h1>Contributed images with MapComplete: leaderboard</h1>
@ -43,13 +47,14 @@
</td>
<td>
{#if $loggedInContributor === contributor.name}
<a class="thanks" href="{contributor.account}">{contributor.name}</a>
<a class="thanks" href={contributor.account}>{contributor.name}</a>
{:else}
<a href="{contributor.account}">{contributor.name}</a>
<a href={contributor.account}>{contributor.name}</a>
{/if}
</td>
<td>
<b>{contributor.nrOfImages}</b> total images
<b>{contributor.nrOfImages}</b>
total images
</td>
</tr>
{/each}

View file

@ -1,14 +1,14 @@
import { Store, UIEventSource } from "../../Logic/UIEventSource";
import type { Map as MLMap } from "maplibre-gl";
import { Map as MlMap, SourceSpecification } from "maplibre-gl";
import { AvailableRasterLayers, RasterLayerPolygon } from "../../Models/RasterLayers";
import { Utils } from "../../Utils";
import { BBox } from "../../Logic/BBox";
import { ExportableMap, MapProperties } from "../../Models/MapProperties";
import SvelteUIElement from "../Base/SvelteUIElement";
import MaplibreMap from "./MaplibreMap.svelte";
import { RasterLayerProperties } from "../../Models/RasterLayerProperties";
import * as htmltoimage from "html-to-image";
import { Store, UIEventSource } from "../../Logic/UIEventSource"
import type { Map as MLMap } from "maplibre-gl"
import { Map as MlMap, SourceSpecification } from "maplibre-gl"
import { AvailableRasterLayers, RasterLayerPolygon } from "../../Models/RasterLayers"
import { Utils } from "../../Utils"
import { BBox } from "../../Logic/BBox"
import { ExportableMap, MapProperties } from "../../Models/MapProperties"
import SvelteUIElement from "../Base/SvelteUIElement"
import MaplibreMap from "./MaplibreMap.svelte"
import { RasterLayerProperties } from "../../Models/RasterLayerProperties"
import * as htmltoimage from "html-to-image"
/**
* The 'MapLibreAdaptor' bridges 'MapLibre' with the various properties of the `MapProperties`
@ -224,7 +224,7 @@ export class MapLibreAdaptor implements MapProperties, ExportableMap {
return url
}
public async exportAsPng(dpiFactor: number): Promise<Blob> {
public async exportAsPng(markerScale: number = 1): Promise<Blob> {
const map = this._maplibreMap.data
if (!map) {
return undefined
@ -235,14 +235,14 @@ export class MapLibreAdaptor implements MapProperties, ExportableMap {
const ctx = drawOn.getContext("2d")
// Set up CSS size.
MapLibreAdaptor.setDpi(drawOn, ctx, dpiFactor / map.getPixelRatio())
MapLibreAdaptor.setDpi(drawOn, ctx, markerScale / map.getPixelRatio())
await this.exportBackgroundOnCanvas(ctx)
// MapLibreAdaptor.setDpi(drawOn, ctx, 1)
const markers = await this.drawMarkers(dpiFactor)
const markers = await this.drawMarkers(markerScale)
ctx.drawImage(markers, 0, 0, drawOn.width, drawOn.height)
ctx.scale(dpiFactor, dpiFactor)
ctx.scale(markerScale, markerScale)
this._maplibreMap.data?.resize()
return await new Promise<Blob>((resolve) => drawOn.toBlob((blob) => resolve(blob)))
}

View file

@ -16,7 +16,7 @@ import { LayerConfigJson } from "../../Models/ThemeConfig/Json/LayerConfigJson"
import PerLayerFeatureSourceSplitter from "../../Logic/FeatureSource/PerLayerFeatureSourceSplitter"
import FilteredLayer from "../../Models/FilteredLayer"
import SimpleFeatureSource from "../../Logic/FeatureSource/Sources/SimpleFeatureSource"
import { CLIENT_RENEG_LIMIT } from "tls";
import { CLIENT_RENEG_LIMIT } from "tls"
class PointRenderingLayer {
private readonly _config: PointRenderingConfig
@ -409,7 +409,7 @@ class LineRenderingLayer {
this._listenerInstalledOn.add(id)
tags.addCallbackAndRunD((properties) => {
// Make sure to use 'getSource' here, the layer names are different!
if(map.getSource(this._layername) === undefined){
if (map.getSource(this._layername) === undefined) {
return true
}
map.setFeatureState(

View file

@ -289,6 +289,14 @@ export class OH {
* rules[0].startHour // => 11
* rules[3].endHour // => 19
*
* const rules = OH.ParseRule("Mo 20:00-02:00");
* rules.length // => 2
* rules[0].weekday // => 0
* rules[0].startHour // => 20
* rules[0].endHour // => 0
* rules[1].weekday // => 1
* rules[1].startHour // => 0
* rules[1].endHour // => 2
*/
public static ParseRule(rule: string): OpeningHour[] {
try {
@ -414,14 +422,14 @@ export class OH {
}
/*
This function converts a number of ranges (generated by OpeningHours.js) into all the hours of day that a change occurs.
E.g.
Monday, some business is opended from 9:00 till 17:00
Tuesday from 9:30 till 18:00
Wednesday from 9:30 till 12:30
This function will extract all those moments of change and will return 9:00, 9:30, 12:30, 17:00 and 18:00
This list will be sorted
*/
This function converts a number of ranges (generated by OpeningHours.js) into all the hours of day that a change occurs.
E.g.
Monday, some business is opended from 9:00 till 17:00
Tuesday from 9:30 till 18:00
Wednesday from 9:30 till 12:30
This function will extract all those moments of change and will return 9:00, 9:30, 12:30, 17:00 and 18:00
This list will be sorted
*/
public static allChangeMoments(
ranges: {
isOpen: boolean
@ -507,9 +515,9 @@ export class OH {
}
/*
Calculates when the business is opened (or on holiday) between two dates.
Returns a matrix of ranges, where [0] is a list of ranges when it is opened on monday, [1] is a list of ranges for tuesday, ...
*/
Calculates when the business is opened (or on holiday) between two dates.
Returns a matrix of ranges, where [0] is a list of ranges when it is opened on monday, [1] is a list of ranges for tuesday, ...
*/
public static GetRanges(
oh: any,
from: Date,
@ -560,6 +568,9 @@ export class OH {
return values
}
/**
* OH.parseHHMM("12:30") // => {hours: 12, minutes: 30}
*/
private static parseHHMM(hhmm: string): { hours: number; minutes: number } {
if (hhmm === undefined || hhmm == null) {
return null
@ -575,6 +586,10 @@ export class OH {
return hm
}
/**
* OH.ParseHhmmRanges("20:00-22:15") // => [{startHour: 20, startMinutes: 0, endHour: 22, endMinutes: 15}]
* OH.ParseHhmmRanges("20:00-02:15") // => [{startHour: 20, startMinutes: 0, endHour: 2, endMinutes: 15}]
*/
private static ParseHhmmRanges(hhmms: string): {
startHour: number
startMinutes: number
@ -641,24 +656,53 @@ export class OH {
endHour: number
endMinutes: number
}[]
) {
): {
weekday: number
startHour: number
startMinutes: number
endHour: number
endMinutes: number
}[] {
if ((weekdays ?? null) == null || (timeranges ?? null) == null) {
return null
}
const ohs: OpeningHour[] = []
for (const timerange of timeranges) {
const overMidnight =
!(timerange.endHour === 0 && timerange.endMinutes === 0) &&
(timerange.endHour < timerange.startHour ||
(timerange.endHour == timerange.startHour &&
timerange.endMinutes < timerange.startMinutes))
for (const weekday of weekdays) {
ohs.push({
weekday: weekday,
startHour: timerange.startHour,
startMinutes: timerange.startMinutes,
endHour: timerange.endHour,
endMinutes: timerange.endMinutes,
})
if (!overMidnight) {
ohs.push({
weekday: weekday,
startHour: timerange.startHour,
startMinutes: timerange.startMinutes,
endHour: timerange.endHour,
endMinutes: timerange.endMinutes,
})
} else {
ohs.push({
weekday: weekday,
startHour: timerange.startHour,
startMinutes: timerange.startMinutes,
endHour: 0,
endMinutes: 0,
})
ohs.push({
weekday: (weekday + 1) % 7,
startHour: 0,
startMinutes: 0,
endHour: timerange.endHour,
endMinutes: timerange.endMinutes,
})
}
}
}
return ohs
}
public static getMondayBefore(d) {
d = new Date(d)
const day = d.getDay()

View file

@ -82,6 +82,7 @@ export default class OpeningHoursInput extends InputElement<string> {
const rules = valueWithoutPrefix.data?.split(";") ?? []
for (const rule of rules) {
if (OH.ParsePHRule(rule) !== null) {
// We found the rule containing the public holiday information
ph = rule
break
}

View file

@ -162,16 +162,16 @@
<LoginButton osmConnection={state.osmConnection} slot="not-logged-in">
<Tr slot="message" t={Translations.t.general.add.pleaseLogin} />
</LoginButton>
{#if $zoom < Constants.minZoomLevelToAddNewPoint}
{#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 $isLoading}
<div class="alert">
<Loading>
<Tr t={Translations.t.general.add.stillLoading} />
</Loading>
</div>
{:else if selectedPreset === undefined}
<!-- First, select the correct preset -->
<PresetList

View file

@ -30,7 +30,12 @@
if (flayer.isDisplayed.data === false) {
// The layer is not displayed...
if (!state.featureSwitches.featureSwitchFilter.data) {
console.log("Not showing presets for layer", flayer.layerDef.id, "as not displayed and featureSwitchFilter.data is set",state.featureSwitches.featureSwitchFilter.data)
console.log(
"Not showing presets for layer",
flayer.layerDef.id,
"as not displayed and featureSwitchFilter.data is set",
state.featureSwitches.featureSwitchFilter.data
)
// ...and we cannot enable the layer control -> we skip, as these presets can never be shown anyway
continue
}

View file

@ -2,50 +2,50 @@
/**
* UIcomponent to create a new note at the given location
*/
import type { SpecialVisualizationState } from "../SpecialVisualization";
import { UIEventSource } from "../../Logic/UIEventSource";
import { LocalStorageSource } from "../../Logic/Web/LocalStorageSource";
import ValidatedInput from "../InputElement/ValidatedInput.svelte";
import SubtleButton from "../Base/SubtleButton.svelte";
import Tr from "../Base/Tr.svelte";
import Translations from "../i18n/Translations.js";
import type { Feature, Point } from "geojson";
import LoginToggle from "../Base/LoginToggle.svelte";
import FilteredLayer from "../../Models/FilteredLayer";
import NewPointLocationInput from "../BigComponents/NewPointLocationInput.svelte";
import ToSvelte from "../Base/ToSvelte.svelte";
import Svg from "../../Svg";
import type { SpecialVisualizationState } from "../SpecialVisualization"
import { UIEventSource } from "../../Logic/UIEventSource"
import { LocalStorageSource } from "../../Logic/Web/LocalStorageSource"
import ValidatedInput from "../InputElement/ValidatedInput.svelte"
import SubtleButton from "../Base/SubtleButton.svelte"
import Tr from "../Base/Tr.svelte"
import Translations from "../i18n/Translations.js"
import type { Feature, Point } from "geojson"
import LoginToggle from "../Base/LoginToggle.svelte"
import FilteredLayer from "../../Models/FilteredLayer"
import NewPointLocationInput from "../BigComponents/NewPointLocationInput.svelte"
import ToSvelte from "../Base/ToSvelte.svelte"
import Svg from "../../Svg"
export let coordinate: UIEventSource<{ lon: number; lat: number }>;
export let state: SpecialVisualizationState;
export let coordinate: UIEventSource<{ lon: number; lat: number }>
export let state: SpecialVisualizationState
let comment: UIEventSource<string> = LocalStorageSource.Get("note-text");
let created = false;
let comment: UIEventSource<string> = LocalStorageSource.Get("note-text")
let created = false
let notelayer: FilteredLayer = state.layerState.filteredLayers.get("note");
let notelayer: FilteredLayer = state.layerState.filteredLayers.get("note")
let hasFilter = notelayer?.hasFilter;
let isDisplayed = notelayer?.isDisplayed;
let hasFilter = notelayer?.hasFilter
let isDisplayed = notelayer?.isDisplayed
function enableNoteLayer() {
state.guistate.closeAll();
isDisplayed.setData(true);
state.guistate.closeAll()
isDisplayed.setData(true)
}
async function uploadNote() {
let txt = comment.data;
let txt = comment.data
if (txt === undefined || txt === "") {
return;
return
}
const loc = coordinate.data;
txt += "\n\n #MapComplete #" + state?.layout?.id;
const id = await state?.osmConnection?.openNote(loc.lat, loc.lon, txt);
console.log("Created a note, got id", id);
const loc = coordinate.data
txt += "\n\n #MapComplete #" + state?.layout?.id
const id = await state?.osmConnection?.openNote(loc.lat, loc.lon, txt)
console.log("Created a note, got id", id)
const feature = <Feature<Point>>{
type: "Feature",
geometry: {
type: "Point",
coordinates: [loc.lon, loc.lat]
coordinates: [loc.lon, loc.lat],
},
properties: {
id: "" + id.id,
@ -56,22 +56,22 @@
text: txt,
html: txt,
user: state.osmConnection?.userDetails?.data?.name,
uid: state.osmConnection?.userDetails?.data?.uid
}
])
}
};
// Normally, the 'Changes' will generate the new element. The 'notes' are an exception to this
state.newFeatures.features.data.push(feature);
state.newFeatures.features.ping();
state.selectedElement?.setData(feature);
if (state.featureProperties.trackFeature) {
state.featureProperties.trackFeature(feature);
uid: state.osmConnection?.userDetails?.data?.uid,
},
]),
},
}
comment.setData("");
created = true;
state.selectedElement.setData(feature);
state.selectedLayer.setData(state.layerState.filteredLayers.get("note"));
// Normally, the 'Changes' will generate the new element. The 'notes' are an exception to this
state.newFeatures.features.data.push(feature)
state.newFeatures.features.ping()
state.selectedElement?.setData(feature)
if (state.featureProperties.trackFeature) {
state.featureProperties.trackFeature(feature)
}
comment.setData("")
created = true
state.selectedElement.setData(feature)
state.selectedLayer.setData(state.layerState.filteredLayers.get("note"))
}
</script>
@ -109,15 +109,14 @@
<ValidatedInput type="text" value={comment} />
</div>
<div class="w-full h-56">
<NewPointLocationInput value={coordinate} {state} >
<div class="h-56 w-full">
<NewPointLocationInput value={coordinate} {state}>
<div class="h-20 w-full pb-10" slot="image">
<ToSvelte construct={Svg.note_svg().SetClass("h-10 w-full")}/>
<ToSvelte construct={Svg.note_svg().SetClass("h-10 w-full")} />
</div>
</NewPointLocationInput>
</div>
<LoginToggle {state}>
<span slot="loading"><!--empty: don't show a loading message--></span>
<div slot="not-logged-in" class="alert">

View file

@ -112,7 +112,10 @@
<button
slot="save-button"
on:click={onDelete}
class={twJoin(selectedTags === undefined && "disabled", "primary flex bg-red-600 items-center")}
class={twJoin(
selectedTags === undefined && "disabled",
"primary flex items-center bg-red-600"
)}
>
<TrashIcon
class={twJoin(

View file

@ -26,7 +26,7 @@
{#if !userDetails || $userDetails.loggedIn}
<div>
{#if tags === undefined}
<slot name="no-tags"><Tr cls="subtle" t={Translations.t.general.noTagsSelected}></Tr></slot>
<slot name="no-tags"><Tr cls="subtle" t={Translations.t.general.noTagsSelected} /></slot>
{:else if embedIn === undefined}
<FromHtml src={tagsExplanation} />
{:else}

View file

@ -180,13 +180,21 @@
</script>
{#if config.question !== undefined}
<div class="interactive border-interactive flex flex-col p-1 px-2 relative overflow-y-auto" style="max-height: 85vh">
<div
class="interactive border-interactive relative flex flex-col overflow-y-auto p-1 px-2"
style="max-height: 85vh"
>
<div class="sticky top-0" style="z-index: 11">
<div class="flex justify-between sticky top-0 interactive">
<span class="font-bold">
<SpecialTranslation t={config.question} {tags} {state} {layer} feature={selectedElement} />
</span>
<div class="interactive sticky top-0 flex justify-between">
<span class="font-bold">
<SpecialTranslation
t={config.question}
{tags}
{state}
{layer}
feature={selectedElement}
/>
</span>
<slot name="upper-right" />
</div>
@ -204,7 +212,7 @@
</div>
{#if config.mappings?.length >= 8}
<div class="flex w-full sticky">
<div class="sticky flex w-full">
<img src="./assets/svg/search.svg" class="h-6 w-6" />
<input type="text" bind:value={$searchTerm} class="w-full" />
</div>
@ -318,8 +326,10 @@
<Tr t={$feedback} />
</div>
{/if}
<div class="flex flex-wrap-reverse items-stretch justify-end sm:flex-nowrap sticky bottom-0 interactive"
style="z-index: 11">
<div
class="interactive sticky bottom-0 flex flex-wrap-reverse items-stretch justify-end sm:flex-nowrap"
style="z-index: 11"
>
<!-- TagRenderingQuestion-buttons -->
<slot name="cancel" />
<slot name="save-button" {selectedTags}>

View file

@ -680,11 +680,13 @@ export default class SpecialVisualizations {
},
],
constr: (state, tags, args) => {
const targetKey = args[0] === "" ? undefined : args[0]
return new SvelteUIElement(UploadImage, {
state,
tags,
targetKey,
labelText: args[1],
image: args[0],
image: args[2],
})
},
},

View file

@ -1,115 +1,115 @@
<script lang="ts">
import { Store, UIEventSource } from "../Logic/UIEventSource"
import { Map as MlMap } from "maplibre-gl"
import MaplibreMap from "./Map/MaplibreMap.svelte"
import FeatureSwitchState from "../Logic/State/FeatureSwitchState"
import MapControlButton from "./Base/MapControlButton.svelte"
import ToSvelte from "./Base/ToSvelte.svelte"
import If from "./Base/If.svelte"
import { GeolocationControl } from "./BigComponents/GeolocationControl"
import type { Feature } from "geojson"
import SelectedElementView from "./BigComponents/SelectedElementView.svelte"
import LayerConfig from "../Models/ThemeConfig/LayerConfig"
import Filterview from "./BigComponents/Filterview.svelte"
import ThemeViewState from "../Models/ThemeViewState"
import type { MapProperties } from "../Models/MapProperties"
import Geosearch from "./BigComponents/Geosearch.svelte"
import Translations from "./i18n/Translations"
import { CogIcon, EyeIcon, MenuIcon, XCircleIcon } from "@rgossiaux/svelte-heroicons/solid"
import Tr from "./Base/Tr.svelte"
import CommunityIndexView from "./BigComponents/CommunityIndexView.svelte"
import FloatOver from "./Base/FloatOver.svelte"
import PrivacyPolicy from "./BigComponents/PrivacyPolicy"
import Constants from "../Models/Constants"
import TabbedGroup from "./Base/TabbedGroup.svelte"
import UserRelatedState from "../Logic/State/UserRelatedState"
import LoginToggle from "./Base/LoginToggle.svelte"
import LoginButton from "./Base/LoginButton.svelte"
import CopyrightPanel from "./BigComponents/CopyrightPanel"
import DownloadPanel from "./DownloadFlow/DownloadPanel.svelte"
import ModalRight from "./Base/ModalRight.svelte"
import { Utils } from "../Utils"
import Hotkeys from "./Base/Hotkeys"
import { VariableUiElement } from "./Base/VariableUIElement"
import SvelteUIElement from "./Base/SvelteUIElement"
import OverlayToggle from "./BigComponents/OverlayToggle.svelte"
import LevelSelector from "./BigComponents/LevelSelector.svelte"
import ExtraLinkButton from "./BigComponents/ExtraLinkButton"
import SelectedElementTitle from "./BigComponents/SelectedElementTitle.svelte"
import Svg from "../Svg"
import ThemeIntroPanel from "./BigComponents/ThemeIntroPanel.svelte"
import type { RasterLayerPolygon } from "../Models/RasterLayers"
import { AvailableRasterLayers } from "../Models/RasterLayers"
import RasterLayerOverview from "./Map/RasterLayerOverview.svelte"
import IfHidden from "./Base/IfHidden.svelte"
import { onDestroy } from "svelte"
import { OpenJosm } from "./BigComponents/OpenJosm"
import MapillaryLink from "./BigComponents/MapillaryLink.svelte"
import OpenIdEditor from "./BigComponents/OpenIdEditor.svelte"
import OpenBackgroundSelectorButton from "./BigComponents/OpenBackgroundSelectorButton.svelte"
import StateIndicator from "./BigComponents/StateIndicator.svelte"
import LanguagePicker from "./LanguagePicker"
import Locale from "./i18n/Locale"
import ShareScreen from "./BigComponents/ShareScreen.svelte"
import UploadingImageCounter from "./Image/UploadingImageCounter.svelte"
import PendingChangesIndicator from "./BigComponents/PendingChangesIndicator.svelte"
import { Store, UIEventSource } from "../Logic/UIEventSource"
import { Map as MlMap } from "maplibre-gl"
import MaplibreMap from "./Map/MaplibreMap.svelte"
import FeatureSwitchState from "../Logic/State/FeatureSwitchState"
import MapControlButton from "./Base/MapControlButton.svelte"
import ToSvelte from "./Base/ToSvelte.svelte"
import If from "./Base/If.svelte"
import { GeolocationControl } from "./BigComponents/GeolocationControl"
import type { Feature } from "geojson"
import SelectedElementView from "./BigComponents/SelectedElementView.svelte"
import LayerConfig from "../Models/ThemeConfig/LayerConfig"
import Filterview from "./BigComponents/Filterview.svelte"
import ThemeViewState from "../Models/ThemeViewState"
import type { MapProperties } from "../Models/MapProperties"
import Geosearch from "./BigComponents/Geosearch.svelte"
import Translations from "./i18n/Translations"
import { CogIcon, EyeIcon, MenuIcon, XCircleIcon } from "@rgossiaux/svelte-heroicons/solid"
import Tr from "./Base/Tr.svelte"
import CommunityIndexView from "./BigComponents/CommunityIndexView.svelte"
import FloatOver from "./Base/FloatOver.svelte"
import PrivacyPolicy from "./BigComponents/PrivacyPolicy"
import Constants from "../Models/Constants"
import TabbedGroup from "./Base/TabbedGroup.svelte"
import UserRelatedState from "../Logic/State/UserRelatedState"
import LoginToggle from "./Base/LoginToggle.svelte"
import LoginButton from "./Base/LoginButton.svelte"
import CopyrightPanel from "./BigComponents/CopyrightPanel"
import DownloadPanel from "./DownloadFlow/DownloadPanel.svelte"
import ModalRight from "./Base/ModalRight.svelte"
import { Utils } from "../Utils"
import Hotkeys from "./Base/Hotkeys"
import { VariableUiElement } from "./Base/VariableUIElement"
import SvelteUIElement from "./Base/SvelteUIElement"
import OverlayToggle from "./BigComponents/OverlayToggle.svelte"
import LevelSelector from "./BigComponents/LevelSelector.svelte"
import ExtraLinkButton from "./BigComponents/ExtraLinkButton"
import SelectedElementTitle from "./BigComponents/SelectedElementTitle.svelte"
import Svg from "../Svg"
import ThemeIntroPanel from "./BigComponents/ThemeIntroPanel.svelte"
import type { RasterLayerPolygon } from "../Models/RasterLayers"
import { AvailableRasterLayers } from "../Models/RasterLayers"
import RasterLayerOverview from "./Map/RasterLayerOverview.svelte"
import IfHidden from "./Base/IfHidden.svelte"
import { onDestroy } from "svelte"
import { OpenJosm } from "./BigComponents/OpenJosm"
import MapillaryLink from "./BigComponents/MapillaryLink.svelte"
import OpenIdEditor from "./BigComponents/OpenIdEditor.svelte"
import OpenBackgroundSelectorButton from "./BigComponents/OpenBackgroundSelectorButton.svelte"
import StateIndicator from "./BigComponents/StateIndicator.svelte"
import LanguagePicker from "./LanguagePicker"
import Locale from "./i18n/Locale"
import ShareScreen from "./BigComponents/ShareScreen.svelte"
import UploadingImageCounter from "./Image/UploadingImageCounter.svelte"
import PendingChangesIndicator from "./BigComponents/PendingChangesIndicator.svelte"
export let state: ThemeViewState
let layout = state.layout
export let state: ThemeViewState
let layout = state.layout
let maplibremap: UIEventSource<MlMap> = state.map
let selectedElement: UIEventSource<Feature> = state.selectedElement
let selectedLayer: UIEventSource<LayerConfig> = state.selectedLayer
let maplibremap: UIEventSource<MlMap> = state.map
let selectedElement: UIEventSource<Feature> = state.selectedElement
let selectedLayer: UIEventSource<LayerConfig> = state.selectedLayer
const selectedElementView = selectedElement.map(
(selectedElement) => {
// Svelte doesn't properly reload some of the legacy UI-elements
// As such, we _reconstruct_ the selectedElementView every time a new feature is selected
// This is a bit wasteful, but until everything is a svelte-component, this should do the trick
const layer = selectedLayer.data
if (selectedElement === undefined || layer === undefined) {
return undefined
}
const selectedElementView = selectedElement.map(
(selectedElement) => {
// Svelte doesn't properly reload some of the legacy UI-elements
// As such, we _reconstruct_ the selectedElementView every time a new feature is selected
// This is a bit wasteful, but until everything is a svelte-component, this should do the trick
const layer = selectedLayer.data
if (selectedElement === undefined || layer === undefined) {
return undefined
}
if (!(layer.tagRenderings?.length > 0) || layer.title === undefined) {
return undefined
}
if (!(layer.tagRenderings?.length > 0) || layer.title === undefined) {
return undefined
}
const tags = state.featureProperties.getStore(selectedElement.properties.id)
return new SvelteUIElement(SelectedElementView, { state, layer, selectedElement, tags })
},
[selectedLayer],
)
const tags = state.featureProperties.getStore(selectedElement.properties.id)
return new SvelteUIElement(SelectedElementView, { state, layer, selectedElement, tags })
},
[selectedLayer]
)
const selectedElementTitle = selectedElement.map(
(selectedElement) => {
// Svelte doesn't properly reload some of the legacy UI-elements
// As such, we _reconstruct_ the selectedElementView every time a new feature is selected
// This is a bit wasteful, but until everything is a svelte-component, this should do the trick
const layer = selectedLayer.data
if (selectedElement === undefined || layer === undefined) {
return undefined
}
const selectedElementTitle = selectedElement.map(
(selectedElement) => {
// Svelte doesn't properly reload some of the legacy UI-elements
// As such, we _reconstruct_ the selectedElementView every time a new feature is selected
// This is a bit wasteful, but until everything is a svelte-component, this should do the trick
const layer = selectedLayer.data
if (selectedElement === undefined || layer === undefined) {
return undefined
}
const tags = state.featureProperties.getStore(selectedElement.properties.id)
return new SvelteUIElement(SelectedElementTitle, { state, layer, selectedElement, tags })
},
[selectedLayer],
)
const tags = state.featureProperties.getStore(selectedElement.properties.id)
return new SvelteUIElement(SelectedElementTitle, { state, layer, selectedElement, tags })
},
[selectedLayer]
)
let mapproperties: MapProperties = state.mapProperties
let featureSwitches: FeatureSwitchState = state.featureSwitches
let availableLayers = state.availableLayers
let userdetails = state.osmConnection.userDetails
let currentViewLayer = layout.layers.find((l) => l.id === "current_view")
let rasterLayer: Store<RasterLayerPolygon> = state.mapProperties.rasterLayer
let rasterLayerName =
rasterLayer.data?.properties?.name ?? AvailableRasterLayers.maptilerDefaultLayer.properties.name
onDestroy(
rasterLayer.addCallbackAndRunD((l) => {
rasterLayerName = l.properties.name
}),
)
let mapproperties: MapProperties = state.mapProperties
let featureSwitches: FeatureSwitchState = state.featureSwitches
let availableLayers = state.availableLayers
let userdetails = state.osmConnection.userDetails
let currentViewLayer = layout.layers.find((l) => l.id === "current_view")
let rasterLayer: Store<RasterLayerPolygon> = state.mapProperties.rasterLayer
let rasterLayerName =
rasterLayer.data?.properties?.name ?? AvailableRasterLayers.maptilerDefaultLayer.properties.name
onDestroy(
rasterLayer.addCallbackAndRunD((l) => {
rasterLayerName = l.properties.name
})
)
</script>
<div class="absolute top-0 left-0 h-screen w-screen overflow-hidden">
@ -155,8 +155,8 @@
<ToSvelte
construct={() => new ExtraLinkButton(state, layout.extraLink).SetClass("pointer-events-auto")}
/>
<UploadingImageCounter {state} featureId="*" showThankYou={false}/>
<PendingChangesIndicator {state}/>
<UploadingImageCounter featureId="*" showThankYou={false} {state} />
<PendingChangesIndicator {state} />
<If condition={state.featureSwitchIsTesting}>
<div class="alert w-fit">Testmode</div>
</If>
@ -174,7 +174,12 @@
<div class="flex flex-col">
<If condition={featureSwitches.featureSwitchEnableLogin}>
{#if state.lastClickObject.hasPresets || state.lastClickObject.hasNoteLayer}
<button class="w-fit pointer-events-auto" on:click={() => {state.openNewDialog()}}>
<button
class="pointer-events-auto w-fit"
on:click={() => {
state.openNewDialog()
}}
>
{#if state.lastClickObject.hasPresets}
<Tr t={Translations.t.general.add.title} />
{:else}
@ -197,9 +202,9 @@
<a
class="bg-black-transparent pointer-events-auto h-fit max-h-12 cursor-pointer self-end overflow-hidden rounded-2xl pl-1 pr-2 text-white opacity-50 hover:opacity-100"
on:click={() => {
state.guistate.themeViewTab.setData("copyright")
state.guistate.themeIsOpened.setData(true)
}}
state.guistate.themeViewTab.setData("copyright")
state.guistate.themeIsOpened.setData(true)
}}
>
© OpenStreetMap, <span class="w-24">{rasterLayerName}</span>
</a>
@ -236,17 +241,18 @@
</div>
</div>
<LoginToggle ignoreLoading={true} {state }>
<If condition={state.userRelatedState.showCrosshair.map(s => s === "yes")}>
<If condition={state.mapProperties.zoom.map(z => z >= 17)}>
<div class="absolute top-0 left-0 flex items-center justify-center pointer-events-none w-full h-full">
<LoginToggle ignoreLoading={true} {state}>
<If condition={state.userRelatedState.showCrosshair.map((s) => s === "yes")}>
<If condition={state.mapProperties.zoom.map((z) => z >= 17)}>
<div
class="pointer-events-none absolute top-0 left-0 flex h-full w-full items-center justify-center"
>
<ToSvelte construct={Svg.cross_svg()} />
</div>
</If>
</If>
</LoginToggle>
<If
condition={selectedElementView.map(
(v) =>
@ -271,7 +277,6 @@
</ModalRight>
</If>
<If
condition={selectedElementView.map(
(v) =>
@ -293,7 +298,10 @@
<!-- Theme menu -->
<FloatOver on:close={() => state.guistate.themeIsOpened.setData(false)}>
<span slot="close-button"><!-- Disable the close button --></span>
<TabbedGroup condition1={state.featureSwitches.featureSwitchFilter} tab={state.guistate.themeViewTabIndex}>
<TabbedGroup
condition1={state.featureSwitches.featureSwitchFilter}
tab={state.guistate.themeViewTabIndex}
>
<div slot="post-tablist">
<XCircleIcon
class="mr-2 h-8 w-8"
@ -362,7 +370,11 @@
<IfHidden condition={state.guistate.backgroundLayerSelectionIsOpened}>
<!-- background layer selector -->
<FloatOver on:close={() => {state.guistate.backgroundLayerSelectionIsOpened.setData(false)}}>
<FloatOver
on:close={() => {
state.guistate.backgroundLayerSelectionIsOpened.setData(false)
}}
>
<div class="h-full p-2">
<RasterLayerOverview
{availableLayers}
@ -377,11 +389,13 @@
<If condition={state.guistate.menuIsOpened}>
<!-- Menu page -->
<FloatOver on:close={() => state.guistate.menuIsOpened.setData(false) }>
<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}>
<TabbedGroup
condition1={featureSwitches.featureSwitchEnableLogin}
condition2={state.featureSwitches.featureSwitchCommunityIndex}
tab={state.guistate.menuViewTabIndex}
>
<div slot="post-tablist">
<XCircleIcon
class="mr-2 h-8 w-8"
@ -470,7 +484,7 @@
<OpenIdEditor mapProperties={state.mapProperties} />
<ToSvelte
construct={() =>
new OpenJosm(state.osmConnection, state.mapProperties.bounds).SetClass("w-full")}
new OpenJosm(state.osmConnection, state.mapProperties.bounds).SetClass("w-full")}
/>
<MapillaryLink mapProperties={state.mapProperties} />
</If>
@ -480,5 +494,3 @@
</TabbedGroup>
</FloatOver>
</If>