Chore: housekeeping: lint
This commit is contained in:
parent
2cbd709d71
commit
663b194247
334 changed files with 4675 additions and 1730 deletions
|
@ -137,11 +137,12 @@ export default class DetermineLayout {
|
|||
if (json.layers === undefined && json.tagRenderings !== undefined) {
|
||||
// We got fed a layer instead of a theme
|
||||
const layerConfig = <LayerConfigJson>json
|
||||
const iconTr: string | TagRenderingConfigJson = <any>(
|
||||
layerConfig.pointRendering
|
||||
.map((mr) => mr?.marker?.find((icon) => icon.icon !== undefined)?.icon)
|
||||
.find((i) => i !== undefined)
|
||||
) ?? "bug"
|
||||
const iconTr: string | TagRenderingConfigJson =
|
||||
<any>(
|
||||
layerConfig.pointRendering
|
||||
.map((mr) => mr?.marker?.find((icon) => icon.icon !== undefined)?.icon)
|
||||
.find((i) => i !== undefined)
|
||||
) ?? "bug"
|
||||
const icon = new TagRenderingConfig(iconTr).render.txt
|
||||
json = {
|
||||
id: json.id,
|
||||
|
|
|
@ -31,7 +31,7 @@ export default class GenericImageProvider extends ImageProvider {
|
|||
key: key,
|
||||
url: value,
|
||||
provider: this,
|
||||
id: value
|
||||
id: value,
|
||||
}),
|
||||
]
|
||||
}
|
||||
|
|
|
@ -66,7 +66,7 @@ export class Imgur extends ImageProvider implements ImageUploader {
|
|||
url: value,
|
||||
key: key,
|
||||
provider: this,
|
||||
id: value
|
||||
id: value,
|
||||
}),
|
||||
]
|
||||
}
|
||||
|
@ -109,7 +109,7 @@ export class Imgur extends ImageProvider implements ImageUploader {
|
|||
licenseInfo.licenseShortName = data.license
|
||||
licenseInfo.artist = data.author
|
||||
licenseInfo.date = new Date(Number(imgurData.datetime) * 1000)
|
||||
licenseInfo.views = imgurData.views
|
||||
licenseInfo.views = imgurData.views
|
||||
|
||||
return licenseInfo
|
||||
}
|
||||
|
|
|
@ -163,6 +163,11 @@ export class WikimediaImageProvider extends ImageProvider {
|
|||
if (!image.startsWith("File:")) {
|
||||
image = "File:" + image
|
||||
}
|
||||
return { url: WikimediaImageProvider.PrepareUrl(image), key: undefined, provider: this , id: image}
|
||||
return {
|
||||
url: WikimediaImageProvider.PrepareUrl(image),
|
||||
key: undefined,
|
||||
provider: this,
|
||||
id: image,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,14 +1,42 @@
|
|||
import { Utils } from "../../Utils"
|
||||
/** This code is autogenerated - do not edit. Edit ./assets/layers/usersettings/usersettings.json instead */
|
||||
export class ThemeMetaTagging {
|
||||
public static readonly themeName = "usersettings"
|
||||
public static readonly themeName = "usersettings"
|
||||
|
||||
public metaTaggging_for_usersettings(feat: {properties: Record<string, string>}) {
|
||||
Utils.AddLazyProperty(feat.properties, '_mastodon_candidate_md', () => feat.properties._description.match(/\[[^\]]*\]\((.*(mastodon|en.osm.town).*)\).*/)?.at(1) )
|
||||
Utils.AddLazyProperty(feat.properties, '_d', () => feat.properties._description?.replace(/</g,'<')?.replace(/>/g,'>') ?? '' )
|
||||
Utils.AddLazyProperty(feat.properties, '_mastodon_candidate_a', () => (feat => {const e = document.createElement('div');e.innerHTML = feat.properties._d;return Array.from(e.getElementsByTagName("a")).filter(a => a.href.match(/mastodon|en.osm.town/) !== null)[0]?.href }) (feat) )
|
||||
Utils.AddLazyProperty(feat.properties, '_mastodon_link', () => (feat => {const e = document.createElement('div');e.innerHTML = feat.properties._d;return Array.from(e.getElementsByTagName("a")).filter(a => a.getAttribute("rel")?.indexOf('me') >= 0)[0]?.href})(feat) )
|
||||
Utils.AddLazyProperty(feat.properties, '_mastodon_candidate', () => feat.properties._mastodon_candidate_md ?? feat.properties._mastodon_candidate_a )
|
||||
feat.properties['__current_backgroun'] = 'initial_value'
|
||||
}
|
||||
}
|
||||
public metaTaggging_for_usersettings(feat: { properties: Record<string, string> }) {
|
||||
Utils.AddLazyProperty(feat.properties, "_mastodon_candidate_md", () =>
|
||||
feat.properties._description
|
||||
.match(/\[[^\]]*\]\((.*(mastodon|en.osm.town).*)\).*/)
|
||||
?.at(1)
|
||||
)
|
||||
Utils.AddLazyProperty(
|
||||
feat.properties,
|
||||
"_d",
|
||||
() => feat.properties._description?.replace(/</g, "<")?.replace(/>/g, ">") ?? ""
|
||||
)
|
||||
Utils.AddLazyProperty(feat.properties, "_mastodon_candidate_a", () =>
|
||||
((feat) => {
|
||||
const e = document.createElement("div")
|
||||
e.innerHTML = feat.properties._d
|
||||
return Array.from(e.getElementsByTagName("a")).filter(
|
||||
(a) => a.href.match(/mastodon|en.osm.town/) !== null
|
||||
)[0]?.href
|
||||
})(feat)
|
||||
)
|
||||
Utils.AddLazyProperty(feat.properties, "_mastodon_link", () =>
|
||||
((feat) => {
|
||||
const e = document.createElement("div")
|
||||
e.innerHTML = feat.properties._d
|
||||
return Array.from(e.getElementsByTagName("a")).filter(
|
||||
(a) => a.getAttribute("rel")?.indexOf("me") >= 0
|
||||
)[0]?.href
|
||||
})(feat)
|
||||
)
|
||||
Utils.AddLazyProperty(
|
||||
feat.properties,
|
||||
"_mastodon_candidate",
|
||||
() => feat.properties._mastodon_candidate_md ?? feat.properties._mastodon_candidate_a
|
||||
)
|
||||
feat.properties["__current_backgroun"] = "initial_value"
|
||||
}
|
||||
}
|
||||
|
|
|
@ -40,7 +40,9 @@ export interface P4CPicture {
|
|||
export default class NearbyImagesSearch {
|
||||
public static readonly services = ["mapillary", "flickr", "kartaview", "wikicommons"] as const
|
||||
public static readonly apiUrls = ["https://api.flickr.com"]
|
||||
private readonly individualStores: Store<{ images: P4CPicture[]; beforeFilter: number } | undefined>[]
|
||||
private readonly individualStores: Store<
|
||||
{ images: P4CPicture[]; beforeFilter: number } | undefined
|
||||
>[]
|
||||
private readonly _store: UIEventSource<P4CPicture[]> = new UIEventSource<P4CPicture[]>([])
|
||||
public readonly store: Store<P4CPicture[]> = this._store
|
||||
public readonly allDone: Store<boolean>
|
||||
|
@ -54,11 +56,11 @@ export default class NearbyImagesSearch {
|
|||
const allDone = new UIEventSource(false)
|
||||
this.allDone = allDone
|
||||
const self = this
|
||||
function updateAllDone(){
|
||||
const stillRunning = self.individualStores.some(store => store.data === undefined)
|
||||
function updateAllDone() {
|
||||
const stillRunning = self.individualStores.some((store) => store.data === undefined)
|
||||
allDone.setData(!stillRunning)
|
||||
}
|
||||
self.individualStores.forEach(s => s.addCallback(_ => updateAllDone()))
|
||||
self.individualStores.forEach((s) => s.addCallback((_) => updateAllDone()))
|
||||
|
||||
this._options = options
|
||||
if (features !== undefined) {
|
||||
|
@ -111,7 +113,7 @@ export default class NearbyImagesSearch {
|
|||
const searchRadius = options.searchRadius ?? 100
|
||||
return p4cStore.mapD(
|
||||
(imagesState) => {
|
||||
if(imagesState["error"]){
|
||||
if (imagesState["error"]) {
|
||||
return null
|
||||
}
|
||||
let images = imagesState["success"]
|
||||
|
|
|
@ -147,11 +147,12 @@ export class ExtractImages extends Conversion<
|
|||
.warn("Found an emtpy image")
|
||||
} else if (typeof img.leaf !== "string") {
|
||||
const c = context.enter(img.path)
|
||||
const msg = "found an image path that is not a string: " +
|
||||
const msg =
|
||||
"found an image path that is not a string: " +
|
||||
JSON.stringify(img.leaf)
|
||||
if(this._isOfficial) {
|
||||
if (this._isOfficial) {
|
||||
c.err(msg)
|
||||
}else {
|
||||
} else {
|
||||
c.warn(msg)
|
||||
}
|
||||
} else {
|
||||
|
|
|
@ -1454,10 +1454,10 @@ export class ValidateLayer extends Conversion<
|
|||
|
||||
for (let i = 0; i < layerConfig.titleIcons.length; i++) {
|
||||
const titleIcon = layerConfig.titleIcons[i]
|
||||
if (<any> titleIcon.render === "icons.defaults") {
|
||||
if (<any>titleIcon.render === "icons.defaults") {
|
||||
context.enters("titleIcons", i).err("Detected a literal 'icons.defaults'")
|
||||
}
|
||||
if (<any> titleIcon.render === "icons.rating") {
|
||||
if (<any>titleIcon.render === "icons.rating") {
|
||||
context.enters("titleIcons", i).err("Detected a literal 'icons.rating'")
|
||||
}
|
||||
}
|
||||
|
|
|
@ -29,8 +29,12 @@
|
|||
</script>
|
||||
|
||||
<div class="m-4 flex flex-col">
|
||||
<LanguagePicker clss="self-end" assignTo={state.language} availableLanguages={t.title.SupportedLanguages()}
|
||||
preferredLanguages={userLanguages} />
|
||||
<LanguagePicker
|
||||
clss="self-end"
|
||||
assignTo={state.language}
|
||||
availableLanguages={t.title.SupportedLanguages()}
|
||||
preferredLanguages={userLanguages}
|
||||
/>
|
||||
|
||||
<div class="mt-4 flex">
|
||||
<div class="m-3 flex-none">
|
||||
|
|
|
@ -23,9 +23,15 @@
|
|||
selectAppropriateValue()
|
||||
}
|
||||
}
|
||||
export let cls : string = undefined
|
||||
export let cls: string = undefined
|
||||
</script>
|
||||
|
||||
<select class={cls} bind:this={htmlElement} on:change={(e) => {value.setData(e.srcElement.value)}}>
|
||||
<select
|
||||
class={cls}
|
||||
bind:this={htmlElement}
|
||||
on:change={(e) => {
|
||||
value.setData(e.srcElement.value)
|
||||
}}
|
||||
>
|
||||
<slot />
|
||||
</select>
|
||||
|
|
|
@ -36,9 +36,15 @@
|
|||
dispatcher("submit", e.dataTransfer.files)
|
||||
}}
|
||||
>
|
||||
<label class={twMerge(cls, drawAttention ? "glowing-shadow" : "")}
|
||||
tabindex="0" for={"fileinput" + id}
|
||||
on:click={() => {console.log("Clicked", inputElement); inputElement.click()}}>
|
||||
<label
|
||||
class={twMerge(cls, drawAttention ? "glowing-shadow" : "")}
|
||||
tabindex="0"
|
||||
for={"fileinput" + id}
|
||||
on:click={() => {
|
||||
console.log("Clicked", inputElement)
|
||||
inputElement.click()
|
||||
}}
|
||||
>
|
||||
<slot />
|
||||
</label>
|
||||
<input
|
||||
|
|
|
@ -1,44 +1,50 @@
|
|||
<script lang="ts">
|
||||
import { createEventDispatcher, onMount } from "svelte";
|
||||
import { XCircleIcon } from "@rgossiaux/svelte-heroicons/solid";
|
||||
import { twMerge } from "tailwind-merge";
|
||||
import { Utils } from "../../Utils";
|
||||
import { trapFocus } from 'trap-focus-svelte'
|
||||
import { createEventDispatcher, onMount } from "svelte"
|
||||
import { XCircleIcon } from "@rgossiaux/svelte-heroicons/solid"
|
||||
import { twMerge } from "tailwind-merge"
|
||||
import { Utils } from "../../Utils"
|
||||
import { trapFocus } from "trap-focus-svelte"
|
||||
/**
|
||||
* The slotted element will be shown on top, with a lower-opacity border
|
||||
*/
|
||||
const dispatch = createEventDispatcher<{ close }>();
|
||||
const dispatch = createEventDispatcher<{ close }>()
|
||||
|
||||
export let extraClasses = "p-4 md:p-6";
|
||||
export let extraClasses = "p-4 md:p-6"
|
||||
|
||||
let mainContent: HTMLElement;
|
||||
let mainContent: HTMLElement
|
||||
onMount(() => {
|
||||
requestAnimationFrame(() => {
|
||||
Utils.focusOnFocusableChild(mainContent);
|
||||
});
|
||||
});
|
||||
|
||||
Utils.focusOnFocusableChild(mainContent)
|
||||
})
|
||||
})
|
||||
</script>
|
||||
|
||||
<!-- Draw the background over the total screen -->
|
||||
<div class="w-screen h-screen absolute top-0 left-0" style="background-color: #00000088; z-index: 20" on:click={() => {
|
||||
<!-- Draw the background over the total screen -->
|
||||
<div
|
||||
class="absolute top-0 left-0 h-screen w-screen"
|
||||
style="background-color: #00000088; z-index: 20"
|
||||
on:click={() => {
|
||||
dispatch("close")
|
||||
}}>
|
||||
</div>
|
||||
}}
|
||||
/>
|
||||
<!-- draw a _second_ absolute div, placed using 'bottom' which will be above the navigation bar on mobile browsers -->
|
||||
<div
|
||||
class={twMerge("absolute bottom-0 right-0 h-full w-screen", extraClasses)}
|
||||
use:trapFocus
|
||||
style="z-index: 21"
|
||||
>
|
||||
<div bind:this={mainContent} class="content normal-background" on:click|stopPropagation={() => {}}>
|
||||
<div
|
||||
bind:this={mainContent}
|
||||
class="content normal-background"
|
||||
on:click|stopPropagation={() => {}}
|
||||
>
|
||||
<div class="h-full rounded-xl">
|
||||
<slot />
|
||||
</div>
|
||||
<slot name="close-button">
|
||||
<!-- The close button is placed _after_ the default slot in order to always paint it on top -->
|
||||
<button
|
||||
class="absolute right-10 top-10 h-8 w-8 cursor-pointer p-0 border-none bg-white"
|
||||
class="absolute right-10 top-10 h-8 w-8 cursor-pointer border-none bg-white p-0"
|
||||
on:click={() => dispatch("close")}
|
||||
>
|
||||
<XCircleIcon />
|
||||
|
@ -47,13 +53,11 @@
|
|||
</div>
|
||||
</div>
|
||||
|
||||
|
||||
|
||||
<style>
|
||||
.content {
|
||||
height: 100%;
|
||||
border-radius: 0.5rem;
|
||||
overflow-x: hidden;
|
||||
box-shadow: 0 0 1rem #00000088;
|
||||
}
|
||||
.content {
|
||||
height: 100%;
|
||||
border-radius: 0.5rem;
|
||||
overflow-x: hidden;
|
||||
box-shadow: 0 0 1rem #00000088;
|
||||
}
|
||||
</style>
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
<script lang="ts">
|
||||
import { Store } from "../../Logic/UIEventSource";
|
||||
import { Store } from "../../Logic/UIEventSource"
|
||||
import { onDestroy } from "svelte"
|
||||
|
||||
/**
|
||||
|
|
|
@ -4,7 +4,7 @@
|
|||
import Translations from "../i18n/Translations"
|
||||
import Tr from "./Tr.svelte"
|
||||
|
||||
export let osmConnection: OsmConnection;
|
||||
export let osmConnection: OsmConnection
|
||||
</script>
|
||||
|
||||
<button
|
||||
|
|
|
@ -1,21 +1,18 @@
|
|||
<script lang="ts">
|
||||
import { createEventDispatcher, onMount } from "svelte";
|
||||
import { XCircleIcon } from "@rgossiaux/svelte-heroicons/solid";
|
||||
import { Utils } from "../../Utils";
|
||||
import { trapFocus } from 'trap-focus-svelte'
|
||||
import { createEventDispatcher, onMount } from "svelte"
|
||||
import { XCircleIcon } from "@rgossiaux/svelte-heroicons/solid"
|
||||
import { Utils } from "../../Utils"
|
||||
import { trapFocus } from "trap-focus-svelte"
|
||||
|
||||
/**
|
||||
* The slotted element will be shown on the right side
|
||||
*/
|
||||
const dispatch = createEventDispatcher<{ close }>();
|
||||
let mainContent: HTMLElement;
|
||||
|
||||
const dispatch = createEventDispatcher<{ close }>()
|
||||
let mainContent: HTMLElement
|
||||
|
||||
onMount(() => {
|
||||
window.setTimeout(
|
||||
() => Utils.focusOnFocusableChild(mainContent), 250
|
||||
);
|
||||
});
|
||||
window.setTimeout(() => Utils.focusOnFocusableChild(mainContent), 250)
|
||||
})
|
||||
</script>
|
||||
|
||||
<div
|
||||
|
|
|
@ -30,7 +30,7 @@
|
|||
}
|
||||
</script>
|
||||
|
||||
<div class="tabbedgroup flex h-full w-full focusable">
|
||||
<div class="tabbedgroup focusable flex h-full w-full">
|
||||
<TabGroup
|
||||
class="flex h-full w-full flex-col"
|
||||
defaultIndex={1}
|
||||
|
|
|
@ -34,8 +34,8 @@
|
|||
Hotkeys.RegisterHotkey({ ctrl: "F" }, Translations.t.hotkeyDocumentation.selectSearch, () => {
|
||||
feedback = undefined
|
||||
requestAnimationFrame(() => {
|
||||
inputElement?.focus()
|
||||
inputElement?.select()
|
||||
inputElement?.focus()
|
||||
inputElement?.select()
|
||||
})
|
||||
})
|
||||
|
||||
|
@ -75,11 +75,11 @@
|
|||
for (const layer of layers) {
|
||||
const found = layer.features.data.find((f) => f.properties.id === id)
|
||||
if (found === undefined) {
|
||||
continue;
|
||||
continue
|
||||
}
|
||||
selectedElement?.setData(found);
|
||||
console.log("Found an element that probably matches:", selectedElement?.data);
|
||||
break;
|
||||
selectedElement?.setData(found)
|
||||
console.log("Found an element that probably matches:", selectedElement?.data)
|
||||
break
|
||||
}
|
||||
}
|
||||
if (clearAfterView) {
|
||||
|
|
|
@ -1,11 +1,11 @@
|
|||
<script lang="ts">
|
||||
import Translations from "../i18n/Translations"
|
||||
import { Store } from "../../Logic/UIEventSource"
|
||||
import Tr from "../Base/Tr.svelte"
|
||||
import Mapillary_black from "../../assets/svg/Mapillary_black.svelte"
|
||||
import { Mapillary } from "../../Logic/ImageProviders/Mapillary"
|
||||
import Translations from "../i18n/Translations"
|
||||
import { Store } from "../../Logic/UIEventSource"
|
||||
import Tr from "../Base/Tr.svelte"
|
||||
import Mapillary_black from "../../assets/svg/Mapillary_black.svelte"
|
||||
import { Mapillary } from "../../Logic/ImageProviders/Mapillary"
|
||||
|
||||
/*
|
||||
/*
|
||||
A subtleButton which opens mapillary in a new tab at the current location
|
||||
*/
|
||||
|
||||
|
|
|
@ -1,22 +1,24 @@
|
|||
<script lang="ts">
|
||||
import type { Feature } from "geojson";
|
||||
import { Store, UIEventSource } from "../../Logic/UIEventSource";
|
||||
import LayerConfig from "../../Models/ThemeConfig/LayerConfig";
|
||||
import type { SpecialVisualizationState } from "../SpecialVisualization";
|
||||
import TagRenderingAnswer from "../Popup/TagRendering/TagRenderingAnswer.svelte";
|
||||
import Translations from "../i18n/Translations";
|
||||
import Tr from "../Base/Tr.svelte";
|
||||
import { XCircleIcon } from "@rgossiaux/svelte-heroicons/solid";
|
||||
import type { Feature } from "geojson"
|
||||
import { Store, UIEventSource } from "../../Logic/UIEventSource"
|
||||
import LayerConfig from "../../Models/ThemeConfig/LayerConfig"
|
||||
import type { SpecialVisualizationState } from "../SpecialVisualization"
|
||||
import TagRenderingAnswer from "../Popup/TagRendering/TagRenderingAnswer.svelte"
|
||||
import Translations from "../i18n/Translations"
|
||||
import Tr from "../Base/Tr.svelte"
|
||||
import { XCircleIcon } from "@rgossiaux/svelte-heroicons/solid"
|
||||
|
||||
export let state: SpecialVisualizationState;
|
||||
export let layer: LayerConfig;
|
||||
export let selectedElement: Feature;
|
||||
let tags: UIEventSource<Record<string, string>> = state.featureProperties.getStore(selectedElement.properties.id);
|
||||
export let state: SpecialVisualizationState
|
||||
export let layer: LayerConfig
|
||||
export let selectedElement: Feature
|
||||
let tags: UIEventSource<Record<string, string>> = state.featureProperties.getStore(
|
||||
selectedElement.properties.id
|
||||
)
|
||||
$: {
|
||||
tags = state.featureProperties.getStore(selectedElement.properties.id);
|
||||
tags = state.featureProperties.getStore(selectedElement.properties.id)
|
||||
}
|
||||
|
||||
let metatags: Store<Record<string, string>> = state.userRelatedState.preferencesAsTags;
|
||||
let metatags: Store<Record<string, string>> = state.userRelatedState.preferencesAsTags
|
||||
</script>
|
||||
|
||||
{#if $tags._deleted === "yes"}
|
||||
|
@ -34,7 +36,7 @@
|
|||
class="no-weblate title-icons links-as-button mr-2 flex flex-row flex-wrap items-center gap-x-0.5 p-1 pt-0.5 sm:pt-1"
|
||||
>
|
||||
{#each layer.titleIcons as titleIconConfig}
|
||||
{#if (titleIconConfig.condition?.matchesProperties($tags) ?? true) && (titleIconConfig.metacondition?.matchesProperties({ ...$metatags, ...$tags }) ?? true) && titleIconConfig.IsKnown($tags)}
|
||||
{#if (titleIconConfig.condition?.matchesProperties($tags) ?? true) && (titleIconConfig.metacondition?.matchesProperties( { ...$metatags, ...$tags } ) ?? true) && titleIconConfig.IsKnown($tags)}
|
||||
<div class={titleIconConfig.renderIconClass ?? "flex h-8 w-8 items-center"}>
|
||||
<TagRenderingAnswer
|
||||
config={titleIconConfig}
|
||||
|
@ -57,7 +59,7 @@
|
|||
{/if}
|
||||
|
||||
<style>
|
||||
:global(.title-icons a) {
|
||||
display: block !important;
|
||||
}
|
||||
:global(.title-icons a) {
|
||||
display: block !important;
|
||||
}
|
||||
</style>
|
||||
|
|
|
@ -1,20 +1,22 @@
|
|||
<script lang="ts">
|
||||
import type { Feature } from "geojson"
|
||||
import { Store, UIEventSource } from "../../Logic/UIEventSource";
|
||||
import { Store, UIEventSource } from "../../Logic/UIEventSource"
|
||||
import LayerConfig from "../../Models/ThemeConfig/LayerConfig"
|
||||
import type { SpecialVisualizationState } from "../SpecialVisualization"
|
||||
import TagRenderingEditable from "../Popup/TagRendering/TagRenderingEditable.svelte"
|
||||
import { onDestroy } from "svelte"
|
||||
import Translations from "../i18n/Translations"
|
||||
import Tr from "../Base/Tr.svelte"
|
||||
import TagRenderingConfig from "../../Models/ThemeConfig/TagRenderingConfig";
|
||||
import TagRenderingConfig from "../../Models/ThemeConfig/TagRenderingConfig"
|
||||
|
||||
export let state: SpecialVisualizationState
|
||||
export let layer: LayerConfig
|
||||
export let selectedElement: Feature
|
||||
export let highlightedRendering: UIEventSource<string> = undefined
|
||||
|
||||
export let tags: UIEventSource<Record<string, string>> = state.featureProperties.getStore(selectedElement.properties.id)
|
||||
export let tags: UIEventSource<Record<string, string>> = state.featureProperties.getStore(
|
||||
selectedElement.properties.id
|
||||
)
|
||||
|
||||
let _metatags: Record<string, string>
|
||||
onDestroy(
|
||||
|
@ -23,12 +25,14 @@
|
|||
})
|
||||
)
|
||||
|
||||
let knownTagRenderings: Store<TagRenderingConfig[]> = tags.mapD(tgs => layer.tagRenderings.filter(
|
||||
(config) =>
|
||||
(config.condition?.matchesProperties(tgs) ?? true) &&
|
||||
(config.metacondition?.matchesProperties({ ...tgs, ..._metatags }) ?? true) &&
|
||||
config.IsKnown(tgs)
|
||||
))
|
||||
let knownTagRenderings: Store<TagRenderingConfig[]> = tags.mapD((tgs) =>
|
||||
layer.tagRenderings.filter(
|
||||
(config) =>
|
||||
(config.condition?.matchesProperties(tgs) ?? true) &&
|
||||
(config.metacondition?.matchesProperties({ ...tgs, ..._metatags }) ?? true) &&
|
||||
config.IsKnown(tgs)
|
||||
)
|
||||
)
|
||||
</script>
|
||||
|
||||
{#if $tags._deleted === "yes"}
|
||||
|
@ -37,7 +41,7 @@
|
|||
<Tr t={Translations.t.general.returnToTheMap} />
|
||||
</button>
|
||||
{:else}
|
||||
<div class="flex h-full w-full flex-col gap-y-2 overflow-y-auto p-1 px-2 focusable" tabindex="-1">
|
||||
<div class="focusable flex h-full w-full flex-col gap-y-2 overflow-y-auto p-1 px-2" tabindex="-1">
|
||||
{#each $knownTagRenderings as config (config.id)}
|
||||
<TagRenderingEditable
|
||||
{tags}
|
||||
|
@ -46,7 +50,9 @@
|
|||
{selectedElement}
|
||||
{layer}
|
||||
{highlightedRendering}
|
||||
clss={$knownTagRenderings.length === 1 ? "h-full" : "tr-length-" + $knownTagRenderings.length}
|
||||
clss={$knownTagRenderings.length === 1
|
||||
? "h-full"
|
||||
: "tr-length-" + $knownTagRenderings.length}
|
||||
/>
|
||||
{/each}
|
||||
</div>
|
||||
|
|
|
@ -10,8 +10,8 @@
|
|||
let id = feature.properties.id
|
||||
let tags = state.featureProperties.getStore(id)
|
||||
let layer: LayerConfig = state.layout.getMatchingLayer(tags.data)
|
||||
|
||||
function select(){
|
||||
|
||||
function select() {
|
||||
state.selectedElement.setData(undefined)
|
||||
state.selectedLayer.setData(layer)
|
||||
state.selectedElement.setData(feature)
|
||||
|
@ -19,5 +19,5 @@
|
|||
</script>
|
||||
|
||||
<div on:click={() => select()} class="cursor-pointer">
|
||||
<TagRenderingAnswer config={layer.title} selectedElement={feature} {state} {tags} {layer} />
|
||||
<TagRenderingAnswer config={layer.title} selectedElement={feature} {state} {tags} {layer} />
|
||||
</div>
|
||||
|
|
|
@ -1,84 +1,90 @@
|
|||
<script lang="ts">
|
||||
import type { SpecialVisualizationState } from "../SpecialVisualization"
|
||||
import TagRenderingAnswer from "../Popup/TagRendering/TagRenderingAnswer.svelte"
|
||||
import type { Feature } from "geojson"
|
||||
import { ImmutableStore, UIEventSource } from "../../Logic/UIEventSource"
|
||||
import { GeoOperations } from "../../Logic/GeoOperations"
|
||||
import Center from "../../assets/svg/Center.svelte"
|
||||
|
||||
import type { SpecialVisualizationState } from "../SpecialVisualization";
|
||||
import TagRenderingAnswer from "../Popup/TagRendering/TagRenderingAnswer.svelte";
|
||||
import type { Feature } from "geojson";
|
||||
import { ImmutableStore, UIEventSource } from "../../Logic/UIEventSource";
|
||||
import { GeoOperations } from "../../Logic/GeoOperations";
|
||||
import Center from "../../assets/svg/Center.svelte";
|
||||
export let feature: Feature
|
||||
let properties: Record<string, string> = feature.properties
|
||||
export let state: SpecialVisualizationState
|
||||
let tags =
|
||||
state.featureProperties.getStore(properties.id) ??
|
||||
new UIEventSource<Record<string, string>>(properties)
|
||||
|
||||
export let feature: Feature;
|
||||
let properties: Record<string, string> = feature.properties;
|
||||
export let state: SpecialVisualizationState;
|
||||
let tags = state.featureProperties.getStore(properties.id) ?? new UIEventSource<Record<string, string>>(properties);
|
||||
|
||||
const favLayer = state.layerState.filteredLayers.get("favourite");
|
||||
const favConfig = favLayer?.layerDef;
|
||||
const titleConfig = favConfig?.title;
|
||||
const favLayer = state.layerState.filteredLayers.get("favourite")
|
||||
const favConfig = favLayer?.layerDef
|
||||
const titleConfig = favConfig?.title
|
||||
|
||||
function center() {
|
||||
const [lon, lat] = GeoOperations.centerpointCoordinates(feature);
|
||||
state.mapProperties.location.setData(
|
||||
{ lon, lat }
|
||||
);
|
||||
const [lon, lat] = GeoOperations.centerpointCoordinates(feature)
|
||||
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.mapProperties.zoom.setData(Math.min(17, Math.max(12, z)))
|
||||
state.guistate.menuIsOpened.setData(false)
|
||||
}
|
||||
|
||||
function select() {
|
||||
state.selectedElement.setData(feature);
|
||||
center();
|
||||
state.selectedElement.setData(feature)
|
||||
center()
|
||||
}
|
||||
|
||||
const coord = GeoOperations.centerpointCoordinates(feature);
|
||||
const coord = GeoOperations.centerpointCoordinates(feature)
|
||||
const distance = state.mapProperties.location.stabilized(500).mapD(({ lon, lat }) => {
|
||||
let meters = Math.round(GeoOperations.distanceBetween(coord, [lon, lat]));
|
||||
let meters = Math.round(GeoOperations.distanceBetween(coord, [lon, lat]))
|
||||
|
||||
if (meters < 1000) {
|
||||
return meters + "m";
|
||||
return meters + "m"
|
||||
}
|
||||
|
||||
meters = Math.round(meters / 100);
|
||||
const kmStr = "" + meters;
|
||||
|
||||
|
||||
return kmStr.substring(0, kmStr.length - 1) + "." + kmStr.substring(kmStr.length - 1) + "km";
|
||||
});
|
||||
const titleIconBlacklist = ["osmlink", "sharelink", "favourite_title_icon"];
|
||||
meters = Math.round(meters / 100)
|
||||
const kmStr = "" + meters
|
||||
|
||||
return kmStr.substring(0, kmStr.length - 1) + "." + kmStr.substring(kmStr.length - 1) + "km"
|
||||
})
|
||||
const titleIconBlacklist = ["osmlink", "sharelink", "favourite_title_icon"]
|
||||
</script>
|
||||
|
||||
{#if favLayer !== undefined}
|
||||
<div class="px-1 my-1 border-2 border-dashed border-gray-300 rounded flex justify-between flex-wrap grid-cols-2 items-center no-weblate">
|
||||
<button class="cursor-pointer ml-1 m-0 link justify-self-start" on:click={() => select()}>
|
||||
<TagRenderingAnswer {state} config={titleConfig} extraClasses="underline" layer={favConfig} selectedElement={feature}
|
||||
{tags} />
|
||||
</button>
|
||||
|
||||
|
||||
<div class="self-end flex items-center flex-wrap justify-self-end title-icons links-as-button gap-x-0.5 p-1 pt-0.5 sm:pt-1">
|
||||
{#each favConfig.titleIcons as titleIconConfig}
|
||||
{#if (titleIconBlacklist.indexOf(titleIconConfig.id) < 0) && (titleIconConfig.condition?.matchesProperties(properties) ?? true) && (titleIconConfig.metacondition?.matchesProperties({ ...properties, ...state.userRelatedState.preferencesAsTags.data }) ?? true) && titleIconConfig.IsKnown(properties)}
|
||||
<div class={titleIconConfig.renderIconClass ?? "flex h-8 w-8 items-center"}>
|
||||
<TagRenderingAnswer
|
||||
config={titleIconConfig}
|
||||
{tags}
|
||||
selectedElement={feature}
|
||||
{state}
|
||||
layer={favLayer.layerDef}
|
||||
extraClasses="h-full justify-center"
|
||||
/>
|
||||
</div>
|
||||
{/if}
|
||||
{/each}
|
||||
|
||||
<button class="p-1" on:click={() => center()}>
|
||||
<Center class="w-6 h-6" />
|
||||
<div
|
||||
class="no-weblate my-1 flex grid-cols-2 flex-wrap items-center justify-between rounded border-2 border-dashed border-gray-300 px-1"
|
||||
>
|
||||
<button class="link m-0 ml-1 cursor-pointer justify-self-start" on:click={() => select()}>
|
||||
<TagRenderingAnswer
|
||||
{state}
|
||||
config={titleConfig}
|
||||
extraClasses="underline"
|
||||
layer={favConfig}
|
||||
selectedElement={feature}
|
||||
{tags}
|
||||
/>
|
||||
</button>
|
||||
<div class="w-14">
|
||||
{$distance}
|
||||
|
||||
<div
|
||||
class="title-icons links-as-button flex flex-wrap items-center gap-x-0.5 self-end justify-self-end p-1 pt-0.5 sm:pt-1"
|
||||
>
|
||||
{#each favConfig.titleIcons as titleIconConfig}
|
||||
{#if titleIconBlacklist.indexOf(titleIconConfig.id) < 0 && (titleIconConfig.condition?.matchesProperties(properties) ?? true) && (titleIconConfig.metacondition?.matchesProperties( { ...properties, ...state.userRelatedState.preferencesAsTags.data } ) ?? true) && titleIconConfig.IsKnown(properties)}
|
||||
<div class={titleIconConfig.renderIconClass ?? "flex h-8 w-8 items-center"}>
|
||||
<TagRenderingAnswer
|
||||
config={titleIconConfig}
|
||||
{tags}
|
||||
selectedElement={feature}
|
||||
{state}
|
||||
layer={favLayer.layerDef}
|
||||
extraClasses="h-full justify-center"
|
||||
/>
|
||||
</div>
|
||||
{/if}
|
||||
{/each}
|
||||
|
||||
<button class="p-1" on:click={() => center()}>
|
||||
<Center class="h-6 w-6" />
|
||||
</button>
|
||||
<div class="w-14">
|
||||
{$distance}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
{/if}
|
||||
|
|
|
@ -1,68 +1,68 @@
|
|||
<script lang="ts">
|
||||
import type { SpecialVisualizationState } from "../SpecialVisualization";
|
||||
import FavouriteSummary from "./FavouriteSummary.svelte";
|
||||
import Translations from "../i18n/Translations";
|
||||
import Tr from "../Base/Tr.svelte";
|
||||
import { DownloadIcon } from "@rgossiaux/svelte-heroicons/solid";
|
||||
import { Utils } from "../../Utils";
|
||||
import { GeoOperations } from "../../Logic/GeoOperations";
|
||||
import type { Feature, LineString, Point } from "geojson";
|
||||
import LoginToggle from "../Base/LoginToggle.svelte";
|
||||
import LoginButton from "../Base/LoginButton.svelte";
|
||||
import type { SpecialVisualizationState } from "../SpecialVisualization"
|
||||
import FavouriteSummary from "./FavouriteSummary.svelte"
|
||||
import Translations from "../i18n/Translations"
|
||||
import Tr from "../Base/Tr.svelte"
|
||||
import { DownloadIcon } from "@rgossiaux/svelte-heroicons/solid"
|
||||
import { Utils } from "../../Utils"
|
||||
import { GeoOperations } from "../../Logic/GeoOperations"
|
||||
import type { Feature, LineString, Point } from "geojson"
|
||||
import LoginToggle from "../Base/LoginToggle.svelte"
|
||||
import LoginButton from "../Base/LoginButton.svelte"
|
||||
|
||||
/**
|
||||
* A panel showing all your favourites
|
||||
*/
|
||||
export let state: SpecialVisualizationState;
|
||||
let favourites = state.favourites.allFavourites;
|
||||
export let state: SpecialVisualizationState
|
||||
let favourites = state.favourites.allFavourites
|
||||
|
||||
function downloadGeojson() {
|
||||
const contents = { features: favourites.data, type: "FeatureCollection" };
|
||||
const contents = { features: favourites.data, type: "FeatureCollection" }
|
||||
Utils.offerContentsAsDownloadableFile(
|
||||
JSON.stringify(contents),
|
||||
"mapcomplete-favourites-" + (new Date().toISOString()) + ".geojson",
|
||||
"mapcomplete-favourites-" + new Date().toISOString() + ".geojson",
|
||||
{
|
||||
mimetype: "application/vnd.geo+json"
|
||||
mimetype: "application/vnd.geo+json",
|
||||
}
|
||||
);
|
||||
)
|
||||
}
|
||||
|
||||
function downloadGPX() {
|
||||
const gpx = GeoOperations.toGpxPoints(<Feature<Point>>favourites.data, "MapComplete favourites");
|
||||
Utils.offerContentsAsDownloadableFile(gpx,
|
||||
"mapcomplete-favourites-" + (new Date().toISOString()) + ".gpx",
|
||||
const gpx = GeoOperations.toGpxPoints(<Feature<Point>>favourites.data, "MapComplete favourites")
|
||||
Utils.offerContentsAsDownloadableFile(
|
||||
gpx,
|
||||
"mapcomplete-favourites-" + new Date().toISOString() + ".gpx",
|
||||
{
|
||||
mimetype: "{gpx=application/gpx+xml}"
|
||||
});
|
||||
|
||||
mimetype: "{gpx=application/gpx+xml}",
|
||||
}
|
||||
)
|
||||
}
|
||||
|
||||
</script>
|
||||
|
||||
<LoginToggle {state}>
|
||||
<div slot="not-logged-in">
|
||||
<LoginButton osmConnection={state.osmConnection}>
|
||||
<Tr t={Translations.t.favouritePoi.loginToSeeList}/>
|
||||
<Tr t={Translations.t.favouritePoi.loginToSeeList} />
|
||||
</LoginButton>
|
||||
</div>
|
||||
|
||||
<div class="flex flex-col" on:keypress={(e) => console.log("Got keypress", e)}>
|
||||
<Tr t={Translations.t.favouritePoi.intro.Subs({length: $favourites?.length ?? 0})} />
|
||||
<Tr t={Translations.t.favouritePoi.privacy} />
|
||||
|
||||
{#each $favourites as feature (feature.properties.id)}
|
||||
<FavouriteSummary {feature} {state} />
|
||||
{/each}
|
||||
<div class="flex flex-col" on:keypress={(e) => console.log("Got keypress", e)}>
|
||||
<Tr t={Translations.t.favouritePoi.intro.Subs({ length: $favourites?.length ?? 0 })} />
|
||||
<Tr t={Translations.t.favouritePoi.privacy} />
|
||||
|
||||
<div class="mt-8">
|
||||
<button class="flex p-2" on:click={() => downloadGeojson()}>
|
||||
<DownloadIcon class="h-6 w-6" />
|
||||
<Tr t={Translations.t.favouritePoi.downloadGeojson} />
|
||||
</button>
|
||||
<button class="flex p-2" on:click={() => downloadGPX()}>
|
||||
<DownloadIcon class="h-6 w-6" />
|
||||
<Tr t={Translations.t.favouritePoi.downloadGpx} />
|
||||
</button>
|
||||
{#each $favourites as feature (feature.properties.id)}
|
||||
<FavouriteSummary {feature} {state} />
|
||||
{/each}
|
||||
|
||||
<div class="mt-8">
|
||||
<button class="flex p-2" on:click={() => downloadGeojson()}>
|
||||
<DownloadIcon class="h-6 w-6" />
|
||||
<Tr t={Translations.t.favouritePoi.downloadGeojson} />
|
||||
</button>
|
||||
<button class="flex p-2" on:click={() => downloadGPX()}>
|
||||
<DownloadIcon class="h-6 w-6" />
|
||||
<Tr t={Translations.t.favouritePoi.downloadGpx} />
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</LoginToggle>
|
||||
|
|
|
@ -1,30 +1,34 @@
|
|||
<script lang="ts">
|
||||
/**
|
||||
* Shows an image with attribution
|
||||
*/
|
||||
import ImageAttribution from "./ImageAttribution.svelte"
|
||||
import type { ProvidedImage } from "../../Logic/ImageProviders/ImageProvider"
|
||||
import { Mapillary } from "../../Logic/ImageProviders/Mapillary"
|
||||
/**
|
||||
* Shows an image with attribution
|
||||
*/
|
||||
import ImageAttribution from "./ImageAttribution.svelte"
|
||||
import type { ProvidedImage } from "../../Logic/ImageProviders/ImageProvider"
|
||||
import { Mapillary } from "../../Logic/ImageProviders/Mapillary"
|
||||
|
||||
export let image: ProvidedImage
|
||||
let fallbackImage: string = undefined
|
||||
if (image.provider === Mapillary.singleton) {
|
||||
fallbackImage = "./assets/svg/blocked.svg"
|
||||
}
|
||||
export let image: ProvidedImage
|
||||
let fallbackImage: string = undefined
|
||||
if (image.provider === Mapillary.singleton) {
|
||||
fallbackImage = "./assets/svg/blocked.svg"
|
||||
}
|
||||
|
||||
let imgEl: HTMLImageElement
|
||||
export let imgClass: string = undefined
|
||||
let imgEl: HTMLImageElement
|
||||
export let imgClass: string = undefined
|
||||
</script>
|
||||
|
||||
|
||||
<div class="relative">
|
||||
<img bind:this={imgEl} src={image.url} class={imgClass ?? ""} on:error={(event) => {
|
||||
if(fallbackImage){
|
||||
imgEl.src = fallbackImage
|
||||
}
|
||||
}}>
|
||||
<img
|
||||
bind:this={imgEl}
|
||||
src={image.url}
|
||||
class={imgClass ?? ""}
|
||||
on:error={(event) => {
|
||||
if (fallbackImage) {
|
||||
imgEl.src = fallbackImage
|
||||
}
|
||||
}}
|
||||
/>
|
||||
|
||||
<div class="absolute bottom-0 left-0">
|
||||
<ImageAttribution {image}/>
|
||||
<ImageAttribution {image} />
|
||||
</div>
|
||||
</div>
|
||||
|
|
|
@ -1,32 +1,32 @@
|
|||
<script lang="ts">
|
||||
import { LicenseInfo } from "../../Logic/ImageProviders/LicenseInfo"
|
||||
import type { ProvidedImage } from "../../Logic/ImageProviders/ImageProvider"
|
||||
import { Store, UIEventSource } from "../../Logic/UIEventSource"
|
||||
import ToSvelte from "../Base/ToSvelte.svelte"
|
||||
import { EyeIcon } from "@rgossiaux/svelte-heroicons/solid"
|
||||
|
||||
/**
|
||||
* A small element showing the attribution of a single image
|
||||
*/
|
||||
export let image: ProvidedImage
|
||||
let license: Store<LicenseInfo> = UIEventSource.FromPromise(image.provider?.DownloadAttribution(image.url))
|
||||
let icon = image.provider?.SourceIcon(image.id)?.SetClass("block h-8 w-8 pr-2")
|
||||
import { LicenseInfo } from "../../Logic/ImageProviders/LicenseInfo"
|
||||
import type { ProvidedImage } from "../../Logic/ImageProviders/ImageProvider"
|
||||
import { Store, UIEventSource } from "../../Logic/UIEventSource"
|
||||
import ToSvelte from "../Base/ToSvelte.svelte"
|
||||
import { EyeIcon } from "@rgossiaux/svelte-heroicons/solid"
|
||||
|
||||
/**
|
||||
* A small element showing the attribution of a single image
|
||||
*/
|
||||
export let image: ProvidedImage
|
||||
let license: Store<LicenseInfo> = UIEventSource.FromPromise(
|
||||
image.provider?.DownloadAttribution(image.url)
|
||||
)
|
||||
let icon = image.provider?.SourceIcon(image.id)?.SetClass("block h-8 w-8 pr-2")
|
||||
</script>
|
||||
|
||||
|
||||
{#if $license !== undefined}
|
||||
<div class="flex bg-black text-white text-sm p-0.5 pl-5 pr-3 rounded-lg no-images">
|
||||
|
||||
<div class="no-images flex rounded-lg bg-black p-0.5 pl-5 pr-3 text-sm text-white">
|
||||
{#if icon !== undefined}
|
||||
<ToSvelte construct={icon} />
|
||||
{/if}
|
||||
|
||||
|
||||
<div class="flex flex-col">
|
||||
{#if $license.title}
|
||||
{#if $license.informationLocation}
|
||||
<a href={$license.informationLocation} target="_blank" rel="noopener nofollower">{$license.title}</a>
|
||||
<a href={$license.informationLocation} target="_blank" rel="noopener nofollower">
|
||||
{$license.title}
|
||||
</a>
|
||||
{:else}
|
||||
$license.title
|
||||
{/if}
|
||||
|
@ -39,7 +39,6 @@
|
|||
{/if}
|
||||
|
||||
<div class="flex justify-between">
|
||||
|
||||
{#if $license.license !== undefined || $license.licenseShortName !== undefined}
|
||||
<div>
|
||||
{$license?.license ?? $license?.licenseShortName}
|
||||
|
@ -55,12 +54,10 @@
|
|||
|
||||
{#if $license.views}
|
||||
<div class="flex justify-around self-center">
|
||||
<EyeIcon class="w-4 h-4 pr-1"/>
|
||||
<EyeIcon class="h-4 w-4 pr-1" />
|
||||
{$license.views}
|
||||
</div>
|
||||
{/if}
|
||||
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/if}
|
||||
|
|
|
@ -1,42 +1,45 @@
|
|||
<script lang="ts">/**
|
||||
* The 'imageOperations' previews an image and offers some extra tools (e.g. download)
|
||||
*/
|
||||
<script lang="ts">
|
||||
/**
|
||||
* The 'imageOperations' previews an image and offers some extra tools (e.g. download)
|
||||
*/
|
||||
|
||||
import type { ProvidedImage } from "../../Logic/ImageProviders/ImageProvider"
|
||||
import ImageAttribution from "./ImageAttribution.svelte"
|
||||
import ImagePreview from "./ImagePreview.svelte"
|
||||
import { DownloadIcon } from "@rgossiaux/svelte-heroicons/solid"
|
||||
import { Utils } from "../../Utils"
|
||||
import { twMerge } from "tailwind-merge";
|
||||
import type { ProvidedImage } from "../../Logic/ImageProviders/ImageProvider"
|
||||
import ImageAttribution from "./ImageAttribution.svelte"
|
||||
import ImagePreview from "./ImagePreview.svelte"
|
||||
import { DownloadIcon } from "@rgossiaux/svelte-heroicons/solid"
|
||||
import { Utils } from "../../Utils"
|
||||
import { twMerge } from "tailwind-merge"
|
||||
|
||||
export let image: ProvidedImage
|
||||
export let clss: string = undefined
|
||||
async function download() {
|
||||
const response = await fetch(image.url_hd ?? image.url )
|
||||
export let image: ProvidedImage
|
||||
export let clss: string = undefined
|
||||
async function download() {
|
||||
const response = await fetch(image.url_hd ?? image.url)
|
||||
const blob = await response.blob()
|
||||
Utils.offerContentsAsDownloadableFile(blob, new URL(image.url).pathname.split("/").at(-1), {
|
||||
mimetype: "image/jpg",
|
||||
mimetype: "image/jpg",
|
||||
})
|
||||
}
|
||||
|
||||
}
|
||||
</script>
|
||||
|
||||
<div class={twMerge("w-full h-full relative", clss)}>
|
||||
<div class="absolute top-0 left-0 w-full h-full overflow-hidden panzoom-container focusable">
|
||||
<ImagePreview image={image} />
|
||||
<div class={twMerge("relative h-full w-full", clss)}>
|
||||
<div class="panzoom-container focusable absolute top-0 left-0 h-full w-full overflow-hidden">
|
||||
<ImagePreview {image} />
|
||||
</div>
|
||||
<div class="absolute bottom-0 left-0 w-full pointer-events-none flex flex-wrap justify-between items-end">
|
||||
<div class="pointer-events-auto w-fit opacity-50 hover:opacity-100 transition-colors duration-200 m-1">
|
||||
<ImageAttribution image={image} />
|
||||
<div
|
||||
class="pointer-events-none absolute bottom-0 left-0 flex w-full flex-wrap items-end justify-between"
|
||||
>
|
||||
<div
|
||||
class="pointer-events-auto m-1 w-fit opacity-50 transition-colors duration-200 hover:opacity-100"
|
||||
>
|
||||
<ImageAttribution {image} />
|
||||
</div>
|
||||
|
||||
<button
|
||||
class="no-image-background flex items-center pointer-events-auto bg-black opacity-50 hover:opacity-100 text-white transition-colors duration-200"
|
||||
on:click={() => download()}>
|
||||
<DownloadIcon class="w-6 h-6 px-2 opacity-100" />
|
||||
class="no-image-background pointer-events-auto flex items-center bg-black text-white opacity-50 transition-colors duration-200 hover:opacity-100"
|
||||
on:click={() => download()}
|
||||
>
|
||||
<DownloadIcon class="h-6 w-6 px-2 opacity-100" />
|
||||
Download
|
||||
</button>
|
||||
</div>
|
||||
|
||||
|
||||
</div>
|
||||
|
|
|
@ -1,28 +1,27 @@
|
|||
<script lang="ts">
|
||||
/**
|
||||
* The image preview allows to drag and zoom in to the image
|
||||
*/
|
||||
import panzoom from "panzoom"
|
||||
import type { ProvidedImage } from "../../Logic/ImageProviders/ImageProvider"
|
||||
|
||||
/**
|
||||
* The image preview allows to drag and zoom in to the image
|
||||
*/
|
||||
import panzoom from "panzoom"
|
||||
import type { ProvidedImage } from "../../Logic/ImageProviders/ImageProvider"
|
||||
export let image: ProvidedImage
|
||||
let panzoomInstance = undefined
|
||||
let panzoomEl: HTMLElement
|
||||
|
||||
export let image : ProvidedImage
|
||||
let panzoomInstance = undefined
|
||||
let panzoomEl: HTMLElement
|
||||
|
||||
$: {
|
||||
if (panzoomEl) {
|
||||
panzoomInstance = panzoom(panzoomEl, { bounds: true,
|
||||
boundsPadding: 0.49,
|
||||
minZoom: 1,
|
||||
maxZoom: 25,
|
||||
initialZoom: 1.2
|
||||
})
|
||||
} else {
|
||||
panzoomInstance?.dispose()
|
||||
}
|
||||
$: {
|
||||
if (panzoomEl) {
|
||||
panzoomInstance = panzoom(panzoomEl, {
|
||||
bounds: true,
|
||||
boundsPadding: 0.49,
|
||||
minZoom: 1,
|
||||
maxZoom: 25,
|
||||
initialZoom: 1.2,
|
||||
})
|
||||
} else {
|
||||
panzoomInstance?.dispose()
|
||||
}
|
||||
|
||||
}
|
||||
</script>
|
||||
|
||||
<img bind:this={panzoomEl} src={image.url_hd ?? image.url} class="w-fit h-fit panzoom-image"/>
|
||||
<img bind:this={panzoomEl} src={image.url_hd ?? image.url} class="panzoom-image h-fit w-fit" />
|
||||
|
|
|
@ -1,21 +1,21 @@
|
|||
<script lang="ts">
|
||||
import { Store } from "../../Logic/UIEventSource"
|
||||
import type { OsmTags } from "../../Models/OsmFeature"
|
||||
import type { SpecialVisualizationState } from "../SpecialVisualization"
|
||||
import type { P4CPicture } from "../../Logic/Web/NearbyImagesSearch"
|
||||
import AllImageProviders from "../../Logic/ImageProviders/AllImageProviders"
|
||||
import LinkImageAction from "../../Logic/Osm/Actions/LinkImageAction"
|
||||
import ChangeTagAction from "../../Logic/Osm/Actions/ChangeTagAction"
|
||||
import { Tag } from "../../Logic/Tags/Tag"
|
||||
import { GeoOperations } from "../../Logic/GeoOperations"
|
||||
import type { Feature } from "geojson"
|
||||
import Translations from "../i18n/Translations"
|
||||
import LayerConfig from "../../Models/ThemeConfig/LayerConfig"
|
||||
import type { ProvidedImage } from "../../Logic/ImageProviders/ImageProvider"
|
||||
import AttributedImage from "./AttributedImage.svelte"
|
||||
import SpecialTranslation from "../Popup/TagRendering/SpecialTranslation.svelte"
|
||||
import { Store } from "../../Logic/UIEventSource"
|
||||
import type { OsmTags } from "../../Models/OsmFeature"
|
||||
import type { SpecialVisualizationState } from "../SpecialVisualization"
|
||||
import type { P4CPicture } from "../../Logic/Web/NearbyImagesSearch"
|
||||
import AllImageProviders from "../../Logic/ImageProviders/AllImageProviders"
|
||||
import LinkImageAction from "../../Logic/Osm/Actions/LinkImageAction"
|
||||
import ChangeTagAction from "../../Logic/Osm/Actions/ChangeTagAction"
|
||||
import { Tag } from "../../Logic/Tags/Tag"
|
||||
import { GeoOperations } from "../../Logic/GeoOperations"
|
||||
import type { Feature } from "geojson"
|
||||
import Translations from "../i18n/Translations"
|
||||
import LayerConfig from "../../Models/ThemeConfig/LayerConfig"
|
||||
import type { ProvidedImage } from "../../Logic/ImageProviders/ImageProvider"
|
||||
import AttributedImage from "./AttributedImage.svelte"
|
||||
import SpecialTranslation from "../Popup/TagRendering/SpecialTranslation.svelte"
|
||||
|
||||
export let tags: Store<OsmTags>
|
||||
export let tags: Store<OsmTags>
|
||||
export let lon: number
|
||||
export let lat: number
|
||||
export let state: SpecialVisualizationState
|
||||
|
@ -29,10 +29,10 @@
|
|||
const t = Translations.t.image.nearby
|
||||
const c = [lon, lat]
|
||||
const providedImage: ProvidedImage = {
|
||||
url: image.thumbUrl ?? image.pictureUrl,
|
||||
provider: AllImageProviders.byName(image.provider),
|
||||
date: new Date(image.date),
|
||||
id: Object.values(image.osmTags)[0]
|
||||
url: image.thumbUrl ?? image.pictureUrl,
|
||||
provider: AllImageProviders.byName(image.provider),
|
||||
date: new Date(image.date),
|
||||
id: Object.values(image.osmTags)[0],
|
||||
}
|
||||
let distance = Math.round(
|
||||
GeoOperations.distanceBetween([image.coordinates.lng, image.coordinates.lat], c)
|
||||
|
@ -44,7 +44,7 @@
|
|||
const url = image.osmTags[key]
|
||||
if (isLinked) {
|
||||
const action = new LinkImageAction(currentTags.id, key, url, tags, {
|
||||
theme: tags.data._orig_theme ?? state.layout.id,
|
||||
theme: tags.data._orig_theme ?? state.layout.id,
|
||||
changeType: "link-image",
|
||||
})
|
||||
state.changes.applyAction(action)
|
||||
|
@ -65,7 +65,7 @@
|
|||
|
||||
<div class="flex w-fit shrink-0 flex-col">
|
||||
<div on:click={() => state.previewedImage.setData(providedImage)} class="cursor-zoom-in">
|
||||
<AttributedImage image={providedImage} imgClass="max-h-64 w-auto"/>
|
||||
<AttributedImage image={providedImage} imgClass="max-h-64 w-auto" />
|
||||
</div>
|
||||
{#if linkable}
|
||||
<label>
|
||||
|
|
|
@ -1,43 +1,44 @@
|
|||
<script lang="ts">
|
||||
/**
|
||||
* Show nearby images which can be clicked
|
||||
*/
|
||||
import type { OsmTags } from "../../Models/OsmFeature"
|
||||
import { Store, UIEventSource } from "../../Logic/UIEventSource"
|
||||
import type { SpecialVisualizationState } from "../SpecialVisualization"
|
||||
import type { P4CPicture } from "../../Logic/Web/NearbyImagesSearch"
|
||||
import NearbyImagesSearch from "../../Logic/Web/NearbyImagesSearch"
|
||||
import LinkableImage from "./LinkableImage.svelte"
|
||||
import type { Feature } from "geojson"
|
||||
import LayerConfig from "../../Models/ThemeConfig/LayerConfig"
|
||||
import Loading from "../Base/Loading.svelte"
|
||||
import AllImageProviders from "../../Logic/ImageProviders/AllImageProviders"
|
||||
import Tr from "../Base/Tr.svelte"
|
||||
import Translations from "../i18n/Translations"
|
||||
import LoginToggle from "../Base/LoginToggle.svelte"
|
||||
/**
|
||||
* Show nearby images which can be clicked
|
||||
*/
|
||||
import type { OsmTags } from "../../Models/OsmFeature"
|
||||
import { Store, UIEventSource } from "../../Logic/UIEventSource"
|
||||
import type { SpecialVisualizationState } from "../SpecialVisualization"
|
||||
import type { P4CPicture } from "../../Logic/Web/NearbyImagesSearch"
|
||||
import NearbyImagesSearch from "../../Logic/Web/NearbyImagesSearch"
|
||||
import LinkableImage from "./LinkableImage.svelte"
|
||||
import type { Feature } from "geojson"
|
||||
import LayerConfig from "../../Models/ThemeConfig/LayerConfig"
|
||||
import Loading from "../Base/Loading.svelte"
|
||||
import AllImageProviders from "../../Logic/ImageProviders/AllImageProviders"
|
||||
import Tr from "../Base/Tr.svelte"
|
||||
import Translations from "../i18n/Translations"
|
||||
import LoginToggle from "../Base/LoginToggle.svelte"
|
||||
|
||||
export let tags: Store<OsmTags>
|
||||
export let state: SpecialVisualizationState
|
||||
export let lon: number
|
||||
export let lat: number
|
||||
export let feature: Feature
|
||||
export let tags: Store<OsmTags>
|
||||
export let state: SpecialVisualizationState
|
||||
export let lon: number
|
||||
export let lat: number
|
||||
export let feature: Feature
|
||||
|
||||
export let linkable: boolean = true
|
||||
export let layer: LayerConfig
|
||||
export let linkable: boolean = true
|
||||
export let layer: LayerConfig
|
||||
|
||||
let imagesProvider = new NearbyImagesSearch(
|
||||
{
|
||||
lon,
|
||||
lat,
|
||||
allowSpherical: new UIEventSource<boolean>(false),
|
||||
blacklist: AllImageProviders.LoadImagesFor(tags),
|
||||
},
|
||||
state.indexedFeatures,
|
||||
)
|
||||
let imagesProvider = new NearbyImagesSearch(
|
||||
{
|
||||
lon,
|
||||
lat,
|
||||
allowSpherical: new UIEventSource<boolean>(false),
|
||||
blacklist: AllImageProviders.LoadImagesFor(tags),
|
||||
},
|
||||
state.indexedFeatures
|
||||
)
|
||||
|
||||
let images: Store<P4CPicture[]> = imagesProvider.store.map((images) => images.slice(0, 20))
|
||||
let allDone = imagesProvider.allDone
|
||||
let images: Store<P4CPicture[]> = imagesProvider.store.map((images) => images.slice(0, 20))
|
||||
let allDone = imagesProvider.allDone
|
||||
</script>
|
||||
|
||||
<LoginToggle {state}>
|
||||
<div class="interactive border-interactive rounded-2xl p-2">
|
||||
<div class="flex justify-between">
|
||||
|
@ -53,9 +54,9 @@
|
|||
{:else}
|
||||
<div class="flex w-full space-x-1 overflow-x-auto" style="scroll-snap-type: x proximity">
|
||||
{#each $images as image (image.pictureUrl)}
|
||||
<span class="w-fit shrink-0" style="scroll-snap-align: start">
|
||||
<LinkableImage {tags} {image} {state} {lon} {lat} {feature} {layer} {linkable} />
|
||||
</span>
|
||||
<span class="w-fit shrink-0" style="scroll-snap-align: start">
|
||||
<LinkableImage {tags} {image} {state} {lon} {lat} {feature} {layer} {linkable} />
|
||||
</span>
|
||||
{/each}
|
||||
</div>
|
||||
{/if}
|
||||
|
|
|
@ -1,37 +1,39 @@
|
|||
<script lang="ts">
|
||||
import { Store } from "../../Logic/UIEventSource";
|
||||
import type { OsmTags } from "../../Models/OsmFeature";
|
||||
import type { SpecialVisualizationState } from "../SpecialVisualization";
|
||||
import type { Feature } from "geojson";
|
||||
import LayerConfig from "../../Models/ThemeConfig/LayerConfig";
|
||||
import Translations from "../i18n/Translations";
|
||||
import Tr from "../Base/Tr.svelte";
|
||||
import NearbyImages from "./NearbyImages.svelte";
|
||||
import { XCircleIcon } from "@babeard/svelte-heroicons/solid";
|
||||
import Camera_plus from "../../assets/svg/Camera_plus.svelte";
|
||||
import LoginToggle from "../Base/LoginToggle.svelte";
|
||||
import { Store } from "../../Logic/UIEventSource"
|
||||
import type { OsmTags } from "../../Models/OsmFeature"
|
||||
import type { SpecialVisualizationState } from "../SpecialVisualization"
|
||||
import type { Feature } from "geojson"
|
||||
import LayerConfig from "../../Models/ThemeConfig/LayerConfig"
|
||||
import Translations from "../i18n/Translations"
|
||||
import Tr from "../Base/Tr.svelte"
|
||||
import NearbyImages from "./NearbyImages.svelte"
|
||||
import { XCircleIcon } from "@babeard/svelte-heroicons/solid"
|
||||
import Camera_plus from "../../assets/svg/Camera_plus.svelte"
|
||||
import LoginToggle from "../Base/LoginToggle.svelte"
|
||||
|
||||
export let tags: Store<OsmTags>;
|
||||
export let state: SpecialVisualizationState;
|
||||
export let lon: number;
|
||||
export let lat: number;
|
||||
export let feature: Feature;
|
||||
export let tags: Store<OsmTags>
|
||||
export let state: SpecialVisualizationState
|
||||
export let lon: number
|
||||
export let lat: number
|
||||
export let feature: Feature
|
||||
|
||||
export let linkable: boolean = true;
|
||||
export let layer: LayerConfig;
|
||||
const t = Translations.t.image.nearby;
|
||||
export let linkable: boolean = true
|
||||
export let layer: LayerConfig
|
||||
const t = Translations.t.image.nearby
|
||||
|
||||
let expanded = false;
|
||||
let expanded = false
|
||||
</script>
|
||||
<LoginToggle {state}>
|
||||
|
||||
<LoginToggle {state}>
|
||||
{#if expanded}
|
||||
<NearbyImages {tags} {state} {lon} {lat} {feature} {linkable}>
|
||||
<button slot="corner"
|
||||
class="h-6 w-6 cursor-pointer no-image-background p-0 border-none"
|
||||
on:click={() => {
|
||||
expanded = false
|
||||
}}>
|
||||
<button
|
||||
slot="corner"
|
||||
class="no-image-background h-6 w-6 cursor-pointer border-none p-0"
|
||||
on:click={() => {
|
||||
expanded = false
|
||||
}}
|
||||
>
|
||||
<XCircleIcon />
|
||||
</button>
|
||||
</NearbyImages>
|
||||
|
@ -39,8 +41,8 @@
|
|||
<button
|
||||
class="flex w-full items-center"
|
||||
on:click={() => {
|
||||
expanded = true
|
||||
}}
|
||||
expanded = true
|
||||
}}
|
||||
>
|
||||
<Camera_plus class="mr-2 block h-8 w-8 p-1" />
|
||||
<Tr t={t.seeNearby} />
|
||||
|
|
|
@ -1,47 +1,47 @@
|
|||
<script lang="ts">
|
||||
/**
|
||||
* Shows an 'upload'-button which will start the upload for this feature
|
||||
*/
|
||||
/**
|
||||
* Shows an 'upload'-button which will start the upload for this feature
|
||||
*/
|
||||
|
||||
import type { SpecialVisualizationState } from "../SpecialVisualization"
|
||||
import { ImmutableStore, Store } from "../../Logic/UIEventSource"
|
||||
import type { OsmTags } from "../../Models/OsmFeature"
|
||||
import LoginToggle from "../Base/LoginToggle.svelte"
|
||||
import Translations from "../i18n/Translations"
|
||||
import Tr from "../Base/Tr.svelte"
|
||||
import UploadingImageCounter from "./UploadingImageCounter.svelte"
|
||||
import FileSelector from "../Base/FileSelector.svelte"
|
||||
import Camera_plus from "../../assets/svg/Camera_plus.svelte"
|
||||
import LoginButton from "../Base/LoginButton.svelte"
|
||||
import type { SpecialVisualizationState } from "../SpecialVisualization"
|
||||
import { ImmutableStore, Store } from "../../Logic/UIEventSource"
|
||||
import type { OsmTags } from "../../Models/OsmFeature"
|
||||
import LoginToggle from "../Base/LoginToggle.svelte"
|
||||
import Translations from "../i18n/Translations"
|
||||
import Tr from "../Base/Tr.svelte"
|
||||
import UploadingImageCounter from "./UploadingImageCounter.svelte"
|
||||
import FileSelector from "../Base/FileSelector.svelte"
|
||||
import Camera_plus from "../../assets/svg/Camera_plus.svelte"
|
||||
import LoginButton from "../Base/LoginButton.svelte"
|
||||
|
||||
export let state: SpecialVisualizationState
|
||||
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!
|
||||
*/
|
||||
export let image: string = undefined
|
||||
if (image === "") {
|
||||
image = undefined
|
||||
}
|
||||
export let labelText: string = undefined
|
||||
const t = Translations.t.image
|
||||
|
||||
let licenseStore = state?.userRelatedState?.imageLicense ?? new ImmutableStore("CC0")
|
||||
|
||||
function handleFiles(files: FileList) {
|
||||
for (let i = 0; i < files.length; i++) {
|
||||
const file = files.item(i)
|
||||
console.log("Got file", file.name)
|
||||
try {
|
||||
state?.imageUploadManager.uploadImageAndApply(file, tags, targetKey)
|
||||
} catch (e) {
|
||||
alert(e)
|
||||
}
|
||||
}
|
||||
export let tags: Store<OsmTags>
|
||||
export let targetKey: string = undefined
|
||||
/**
|
||||
* Image to show in the button
|
||||
* NOT the image to upload!
|
||||
*/
|
||||
export let image: string = undefined
|
||||
if (image === "") {
|
||||
image = undefined
|
||||
}
|
||||
export let labelText: string = undefined
|
||||
const t = Translations.t.image
|
||||
|
||||
let licenseStore = state?.userRelatedState?.imageLicense ?? new ImmutableStore("CC0")
|
||||
|
||||
function handleFiles(files: FileList) {
|
||||
for (let i = 0; i < files.length; i++) {
|
||||
const file = files.item(i)
|
||||
console.log("Got file", file.name)
|
||||
try {
|
||||
state?.imageUploadManager.uploadImageAndApply(file, tags, targetKey)
|
||||
} catch (e) {
|
||||
alert(e)
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<LoginToggle {state}>
|
||||
|
@ -56,7 +56,7 @@
|
|||
multiple={true}
|
||||
on:submit={(e) => handleFiles(e.detail)}
|
||||
>
|
||||
<div class="flex items-center" >
|
||||
<div class="flex items-center">
|
||||
{#if image !== undefined}
|
||||
<img src={image} aria-hidden="true" />
|
||||
{:else}
|
||||
|
@ -71,14 +71,14 @@
|
|||
</FileSelector>
|
||||
<div class="text-sm">
|
||||
<button
|
||||
class="link small "
|
||||
class="link small"
|
||||
on:click={() => {
|
||||
state.guistate.openUsersettings("picture-license")
|
||||
}}
|
||||
>
|
||||
<Tr t={t.currentLicense.Subs({ license: $licenseStore })} />
|
||||
</button>
|
||||
<Tr t={t.respectPrivacy} />
|
||||
<Tr t={t.respectPrivacy} />
|
||||
</div>
|
||||
</div>
|
||||
</LoginToggle>
|
||||
|
|
|
@ -15,7 +15,7 @@
|
|||
export let state: SpecialVisualizationState
|
||||
export let tags: Store<OsmTags> = undefined
|
||||
export let featureId = tags?.data?.id
|
||||
if(featureId === undefined){
|
||||
if (featureId === undefined) {
|
||||
throw "No tags or featureID given"
|
||||
}
|
||||
export let showThankYou: boolean = true
|
||||
|
|
|
@ -1,16 +1,16 @@
|
|||
<script lang="ts">
|
||||
// Languages in the language itself
|
||||
import native from "../../assets/language_native.json"
|
||||
// Translated languages
|
||||
import language_translations from "../../assets/language_translations.json"
|
||||
// Languages in the language itself
|
||||
import native from "../../assets/language_native.json"
|
||||
// Translated languages
|
||||
import language_translations from "../../assets/language_translations.json"
|
||||
|
||||
import { UIEventSource } from "../../Logic/UIEventSource"
|
||||
import Locale from "../i18n/Locale"
|
||||
import { LanguageIcon } from "@babeard/svelte-heroicons/solid"
|
||||
import Dropdown from "../Base/Dropdown.svelte"
|
||||
import { twMerge } from "tailwind-merge"
|
||||
import { UIEventSource } from "../../Logic/UIEventSource"
|
||||
import Locale from "../i18n/Locale"
|
||||
import { LanguageIcon } from "@babeard/svelte-heroicons/solid"
|
||||
import Dropdown from "../Base/Dropdown.svelte"
|
||||
import { twMerge } from "tailwind-merge"
|
||||
|
||||
/**
|
||||
/**
|
||||
* Languages one can choose from
|
||||
* Defaults to _all_ languages known by MapComplete
|
||||
*/
|
||||
|
@ -31,13 +31,13 @@
|
|||
}
|
||||
preferredFiltered = preferredLanguages?.filter((l) => availableLanguages.indexOf(l) >= 0)
|
||||
})
|
||||
export let clss : string = undefined
|
||||
export let clss: string = undefined
|
||||
let current = Locale.language
|
||||
</script>
|
||||
|
||||
{#if availableLanguages?.length > 1}
|
||||
<form class={twMerge("flex items-center max-w-full pr-4", clss)}>
|
||||
<LanguageIcon class="h-4 w-4 mr-1 shrink-0" />
|
||||
<form class={twMerge("flex max-w-full items-center pr-4", clss)}>
|
||||
<LanguageIcon class="mr-1 h-4 w-4 shrink-0" />
|
||||
<Dropdown cls="max-w-full" value={assignTo}>
|
||||
{#if preferredFiltered}
|
||||
{#each preferredFiltered as language}
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
<script lang="ts">
|
||||
import { IconConfig } from "../../Models/ThemeConfig/PointRenderingConfig";
|
||||
import { Store } from "../../Logic/UIEventSource";
|
||||
import Icon from "./Icon.svelte";
|
||||
import { IconConfig } from "../../Models/ThemeConfig/PointRenderingConfig"
|
||||
import { Store } from "../../Logic/UIEventSource"
|
||||
import Icon from "./Icon.svelte"
|
||||
|
||||
/**
|
||||
* Renders a single icon.
|
||||
|
|
|
@ -7,9 +7,9 @@
|
|||
/**
|
||||
* Renders a 'marker', which consists of multiple 'icons'
|
||||
*/
|
||||
export let marker: IconConfig[] = config?.marker;
|
||||
export let marker: IconConfig[] = config?.marker
|
||||
export let tags: Store<Record<string, string>>
|
||||
export let rotation: TagRenderingConfig = undefined;
|
||||
export let rotation: TagRenderingConfig = undefined
|
||||
let _rotation = rotation
|
||||
? tags.map((tags) => rotation.GetRenderValue(tags).Subs(tags).txt)
|
||||
: new ImmutableStore(0)
|
||||
|
|
|
@ -1,32 +1,32 @@
|
|||
<script lang="ts">
|
||||
import Pin from "../../assets/svg/Pin.svelte";
|
||||
import Square from "../../assets/svg/Square.svelte";
|
||||
import Circle from "../../assets/svg/Circle.svelte";
|
||||
import Checkmark from "../../assets/svg/Checkmark.svelte";
|
||||
import Clock from "../../assets/svg/Clock.svelte";
|
||||
import Close from "../../assets/svg/Close.svelte";
|
||||
import Crosshair from "../../assets/svg/Crosshair.svelte";
|
||||
import Help from "../../assets/svg/Help.svelte";
|
||||
import Home from "../../assets/svg/Home.svelte";
|
||||
import Invalid from "../../assets/svg/Invalid.svelte";
|
||||
import Location from "../../assets/svg/Location.svelte";
|
||||
import Location_empty from "../../assets/svg/Location_empty.svelte";
|
||||
import Location_locked from "../../assets/svg/Location_locked.svelte";
|
||||
import Note from "../../assets/svg/Note.svelte";
|
||||
import Resolved from "../../assets/svg/Resolved.svelte";
|
||||
import Ring from "../../assets/svg/Ring.svelte";
|
||||
import Scissors from "../../assets/svg/Scissors.svelte";
|
||||
import Teardrop from "../../assets/svg/Teardrop.svelte";
|
||||
import Teardrop_with_hole_green from "../../assets/svg/Teardrop_with_hole_green.svelte";
|
||||
import Triangle from "../../assets/svg/Triangle.svelte";
|
||||
import Brick_wall_square from "../../assets/svg/Brick_wall_square.svelte";
|
||||
import Brick_wall_round from "../../assets/svg/Brick_wall_round.svelte";
|
||||
import Gps_arrow from "../../assets/svg/Gps_arrow.svelte";
|
||||
import { HeartIcon } from "@babeard/svelte-heroicons/solid";
|
||||
import { HeartIcon as HeartOutlineIcon } from "@babeard/svelte-heroicons/outline";
|
||||
import Confirm from "../../assets/svg/Confirm.svelte";
|
||||
import Not_found from "../../assets/svg/Not_found.svelte";
|
||||
import { twMerge } from "tailwind-merge";
|
||||
import Pin from "../../assets/svg/Pin.svelte"
|
||||
import Square from "../../assets/svg/Square.svelte"
|
||||
import Circle from "../../assets/svg/Circle.svelte"
|
||||
import Checkmark from "../../assets/svg/Checkmark.svelte"
|
||||
import Clock from "../../assets/svg/Clock.svelte"
|
||||
import Close from "../../assets/svg/Close.svelte"
|
||||
import Crosshair from "../../assets/svg/Crosshair.svelte"
|
||||
import Help from "../../assets/svg/Help.svelte"
|
||||
import Home from "../../assets/svg/Home.svelte"
|
||||
import Invalid from "../../assets/svg/Invalid.svelte"
|
||||
import Location from "../../assets/svg/Location.svelte"
|
||||
import Location_empty from "../../assets/svg/Location_empty.svelte"
|
||||
import Location_locked from "../../assets/svg/Location_locked.svelte"
|
||||
import Note from "../../assets/svg/Note.svelte"
|
||||
import Resolved from "../../assets/svg/Resolved.svelte"
|
||||
import Ring from "../../assets/svg/Ring.svelte"
|
||||
import Scissors from "../../assets/svg/Scissors.svelte"
|
||||
import Teardrop from "../../assets/svg/Teardrop.svelte"
|
||||
import Teardrop_with_hole_green from "../../assets/svg/Teardrop_with_hole_green.svelte"
|
||||
import Triangle from "../../assets/svg/Triangle.svelte"
|
||||
import Brick_wall_square from "../../assets/svg/Brick_wall_square.svelte"
|
||||
import Brick_wall_round from "../../assets/svg/Brick_wall_round.svelte"
|
||||
import Gps_arrow from "../../assets/svg/Gps_arrow.svelte"
|
||||
import { HeartIcon } from "@babeard/svelte-heroicons/solid"
|
||||
import { HeartIcon as HeartOutlineIcon } from "@babeard/svelte-heroicons/outline"
|
||||
import Confirm from "../../assets/svg/Confirm.svelte"
|
||||
import Not_found from "../../assets/svg/Not_found.svelte"
|
||||
import { twMerge } from "tailwind-merge"
|
||||
|
||||
/**
|
||||
* Renders a single icon.
|
||||
|
@ -34,9 +34,9 @@
|
|||
* Icons -placed on top of each other- form a 'Marker' together
|
||||
*/
|
||||
|
||||
export let icon: string | undefined;
|
||||
export let color: string | undefined = undefined;
|
||||
export let clss: string | undefined = undefined;
|
||||
export let icon: string | undefined
|
||||
export let color: string | undefined = undefined
|
||||
export let clss: string | undefined = undefined
|
||||
</script>
|
||||
|
||||
{#if icon}
|
||||
|
@ -103,7 +103,6 @@
|
|||
{:else if icon === "not_found"}
|
||||
<Not_found class={twMerge(clss, "no-image-background")} {color} />
|
||||
{:else}
|
||||
<img class={clss ?? "h-full w-full"} src={icon} aria-hidden="true"
|
||||
alt="" />
|
||||
<img class={clss ?? "h-full w-full"} src={icon} aria-hidden="true" alt="" />
|
||||
{/if}
|
||||
{/if}
|
||||
|
|
|
@ -1,10 +1,10 @@
|
|||
<script lang="ts">
|
||||
import Icon from "./Icon.svelte";
|
||||
import Icon from "./Icon.svelte"
|
||||
|
||||
/**
|
||||
* Renders a 'marker', which consists of multiple 'icons'
|
||||
*/
|
||||
export let icons: { icon: string; color: string }[];
|
||||
export let icons: { icon: string; color: string }[]
|
||||
</script>
|
||||
|
||||
{#if icons !== undefined && icons.length > 0}
|
||||
|
|
|
@ -1,70 +1,60 @@
|
|||
<script lang="ts">
|
||||
import type { SpecialVisualizationState } from "../SpecialVisualization";
|
||||
import { Store, UIEventSource } from "../../Logic/UIEventSource";
|
||||
import Loading from "../../assets/svg/Loading.svelte";
|
||||
import Tr from "../Base/Tr.svelte";
|
||||
import Translations from "../i18n/Translations";
|
||||
import Icon from "../Map/Icon.svelte";
|
||||
import Maproulette from "../../Logic/Maproulette";
|
||||
import type { SpecialVisualizationState } from "../SpecialVisualization"
|
||||
import { Store, UIEventSource } from "../../Logic/UIEventSource"
|
||||
import Loading from "../../assets/svg/Loading.svelte"
|
||||
import Tr from "../Base/Tr.svelte"
|
||||
import Translations from "../i18n/Translations"
|
||||
import Icon from "../Map/Icon.svelte"
|
||||
import Maproulette from "../../Logic/Maproulette"
|
||||
|
||||
/**
|
||||
* A UI-element to change the status of a maproulette-task
|
||||
*/
|
||||
export let state: SpecialVisualizationState;
|
||||
export let tags: UIEventSource<Record<string, string>>;
|
||||
export let message: string;
|
||||
export let image: string;
|
||||
export let message_closed: string;
|
||||
export let statusToSet: string;
|
||||
export let maproulette_id_key: string;
|
||||
export let state: SpecialVisualizationState
|
||||
export let tags: UIEventSource<Record<string, string>>
|
||||
export let message: string
|
||||
export let image: string
|
||||
export let message_closed: string
|
||||
export let statusToSet: string
|
||||
export let maproulette_id_key: string
|
||||
|
||||
let applying = false;
|
||||
let failed = false;
|
||||
let applying = false
|
||||
let failed = false
|
||||
|
||||
/** Current status of the task*/
|
||||
let status: Store<number> = tags
|
||||
.map((tgs) => {
|
||||
if (tgs["status"]) {
|
||||
return tgs["status"];
|
||||
return tgs["status"]
|
||||
}
|
||||
return Maproulette.codeToIndex(tgs["mr_taskStatus"]);
|
||||
}).map(Number);
|
||||
return Maproulette.codeToIndex(tgs["mr_taskStatus"])
|
||||
})
|
||||
.map(Number)
|
||||
|
||||
async function apply() {
|
||||
const maproulette_id =
|
||||
tags.data[maproulette_id_key] ??
|
||||
tags.data.mr_taskId ??
|
||||
tags.data.id;
|
||||
const maproulette_id = tags.data[maproulette_id_key] ?? tags.data.mr_taskId ?? tags.data.id
|
||||
try {
|
||||
await Maproulette.singleton.closeTask(
|
||||
Number(maproulette_id),
|
||||
Number(statusToSet),
|
||||
{
|
||||
tags: `MapComplete MapComplete:${state.layout.id}`
|
||||
}
|
||||
);
|
||||
tags.data["mr_taskStatus"] =
|
||||
Maproulette.STATUS_MEANING[Number(statusToSet)];
|
||||
tags.data.status = statusToSet;
|
||||
tags.ping();
|
||||
await Maproulette.singleton.closeTask(Number(maproulette_id), Number(statusToSet), {
|
||||
tags: `MapComplete MapComplete:${state.layout.id}`,
|
||||
})
|
||||
tags.data["mr_taskStatus"] = Maproulette.STATUS_MEANING[Number(statusToSet)]
|
||||
tags.data.status = statusToSet
|
||||
tags.ping()
|
||||
} catch (e) {
|
||||
console.error(e);
|
||||
failed = true;
|
||||
console.error(e)
|
||||
failed = true
|
||||
}
|
||||
}
|
||||
|
||||
</script>
|
||||
|
||||
{#if failed}
|
||||
<div class="alert">
|
||||
ERROR - could not close the MapRoulette task
|
||||
</div>
|
||||
<div class="alert">ERROR - could not close the MapRoulette task</div>
|
||||
{:else if applying}
|
||||
<Loading>
|
||||
<Tr t={Translations.t.general.loading} />
|
||||
</Loading>
|
||||
{:else if $status === Maproulette.STATUS_OPEN}
|
||||
<button class="w-full p-4 no-image-background" on:click={() => apply()}>
|
||||
<button class="no-image-background w-full p-4" on:click={() => apply()}>
|
||||
<Icon clss="w-8 h-8 mr-2" icon={image} />
|
||||
{message}
|
||||
</button>
|
||||
|
|
|
@ -1,32 +1,42 @@
|
|||
<script lang="ts">/**
|
||||
* Simple visualisation which shows when the POI opens/closes next.
|
||||
*/
|
||||
import type { SpecialVisualizationState } from "../SpecialVisualization";
|
||||
import { Store, Stores } from "../../Logic/UIEventSource";
|
||||
import { OH } from "./OpeningHours";
|
||||
import opening_hours from "opening_hours";
|
||||
import Clock from "../../assets/svg/Clock.svelte";
|
||||
import { Utils } from "../../Utils";
|
||||
import Circle from "../../assets/svg/Circle.svelte";
|
||||
import Ring from "../../assets/svg/Ring.svelte";
|
||||
import { twMerge } from "tailwind-merge";
|
||||
<script lang="ts">
|
||||
/**
|
||||
* Simple visualisation which shows when the POI opens/closes next.
|
||||
*/
|
||||
import type { SpecialVisualizationState } from "../SpecialVisualization"
|
||||
import { Store, Stores } from "../../Logic/UIEventSource"
|
||||
import { OH } from "./OpeningHours"
|
||||
import opening_hours from "opening_hours"
|
||||
import Clock from "../../assets/svg/Clock.svelte"
|
||||
import { Utils } from "../../Utils"
|
||||
import Circle from "../../assets/svg/Circle.svelte"
|
||||
import Ring from "../../assets/svg/Ring.svelte"
|
||||
import { twMerge } from "tailwind-merge"
|
||||
|
||||
export let state: SpecialVisualizationState;
|
||||
export let tags: Store<Record<string, string>>;
|
||||
export let keyToUse: string = "opening_hours";
|
||||
export let prefix: string = undefined;
|
||||
export let postfix: string = undefined;
|
||||
let oh: Store<opening_hours | "error" | undefined> = OH.CreateOhObjectStore(tags, keyToUse, prefix, postfix);
|
||||
export let state: SpecialVisualizationState
|
||||
export let tags: Store<Record<string, string>>
|
||||
export let keyToUse: string = "opening_hours"
|
||||
export let prefix: string = undefined
|
||||
export let postfix: string = undefined
|
||||
let oh: Store<opening_hours | "error" | undefined> = OH.CreateOhObjectStore(
|
||||
tags,
|
||||
keyToUse,
|
||||
prefix,
|
||||
postfix
|
||||
)
|
||||
|
||||
let currentState = oh.mapD(oh => typeof oh === "string" ? undefined : oh.getState());
|
||||
let tomorrow = new Date();
|
||||
tomorrow.setTime(tomorrow.getTime() + 24 * 60 * 60 * 1000);
|
||||
let nextChange = oh
|
||||
.mapD(oh => typeof oh === "string" ? undefined : oh.getNextChange(new Date(), tomorrow), [Stores.Chronic(5 * 60 * 1000)])
|
||||
.mapD(date => Utils.TwoDigits(date.getHours()) + ":" + Utils.TwoDigits(date.getMinutes()));
|
||||
|
||||
let size = nextChange.map(change => change === undefined ? "absolute h-7 w-7" : "absolute h-5 w-5 top-0 left-1/4");
|
||||
let currentState = oh.mapD((oh) => (typeof oh === "string" ? undefined : oh.getState()))
|
||||
let tomorrow = new Date()
|
||||
tomorrow.setTime(tomorrow.getTime() + 24 * 60 * 60 * 1000)
|
||||
let nextChange = oh
|
||||
.mapD(
|
||||
(oh) => (typeof oh === "string" ? undefined : oh.getNextChange(new Date(), tomorrow)),
|
||||
[Stores.Chronic(5 * 60 * 1000)]
|
||||
)
|
||||
.mapD((date) => Utils.TwoDigits(date.getHours()) + ":" + Utils.TwoDigits(date.getMinutes()))
|
||||
|
||||
let size = nextChange.map((change) =>
|
||||
change === undefined ? "absolute h-7 w-7" : "absolute h-5 w-5 top-0 left-1/4"
|
||||
)
|
||||
</script>
|
||||
|
||||
{#if $currentState !== undefined}
|
||||
|
@ -40,11 +50,12 @@ let size = nextChange.map(change => change === undefined ? "absolute h-7 w-7" :
|
|||
{/if}
|
||||
|
||||
{#if $nextChange !== undefined}
|
||||
<span class="absolute bottom-0 font-bold text-sm" style="z-index: 1; background-color: #ffffff88; margin-top: 3px">
|
||||
<span
|
||||
class="absolute bottom-0 text-sm font-bold"
|
||||
style="z-index: 1; background-color: #ffffff88; margin-top: 3px"
|
||||
>
|
||||
{$nextChange}
|
||||
</span>
|
||||
{/if}
|
||||
|
||||
</div>
|
||||
|
||||
{/if}
|
||||
|
|
|
@ -1,162 +1,160 @@
|
|||
<script lang="ts">
|
||||
import LoginToggle from "../../Base/LoginToggle.svelte"
|
||||
import type { SpecialVisualizationState } from "../../SpecialVisualization"
|
||||
import Translations from "../../i18n/Translations"
|
||||
import Tr from "../../Base/Tr.svelte"
|
||||
import { TrashIcon } from "@babeard/svelte-heroicons/mini"
|
||||
import type { OsmId, OsmTags } from "../../../Models/OsmFeature"
|
||||
import DeleteConfig from "../../../Models/ThemeConfig/DeleteConfig"
|
||||
import TagRenderingQuestion from "../TagRendering/TagRenderingQuestion.svelte"
|
||||
import type { Feature } from "geojson"
|
||||
import { UIEventSource } from "../../../Logic/UIEventSource"
|
||||
import LayerConfig from "../../../Models/ThemeConfig/LayerConfig"
|
||||
import { TagsFilter } from "../../../Logic/Tags/TagsFilter"
|
||||
import { XCircleIcon } from "@rgossiaux/svelte-heroicons/solid"
|
||||
import { TagUtils } from "../../../Logic/Tags/TagUtils"
|
||||
import OsmChangeAction from "../../../Logic/Osm/Actions/OsmChangeAction"
|
||||
import DeleteAction from "../../../Logic/Osm/Actions/DeleteAction"
|
||||
import ChangeTagAction from "../../../Logic/Osm/Actions/ChangeTagAction"
|
||||
import Loading from "../../Base/Loading.svelte"
|
||||
import { DeleteFlowState } from "./DeleteFlowState"
|
||||
import { twJoin } from "tailwind-merge"
|
||||
import LoginToggle from "../../Base/LoginToggle.svelte"
|
||||
import type { SpecialVisualizationState } from "../../SpecialVisualization"
|
||||
import Translations from "../../i18n/Translations"
|
||||
import Tr from "../../Base/Tr.svelte"
|
||||
import { TrashIcon } from "@babeard/svelte-heroicons/mini"
|
||||
import type { OsmId, OsmTags } from "../../../Models/OsmFeature"
|
||||
import DeleteConfig from "../../../Models/ThemeConfig/DeleteConfig"
|
||||
import TagRenderingQuestion from "../TagRendering/TagRenderingQuestion.svelte"
|
||||
import type { Feature } from "geojson"
|
||||
import { UIEventSource } from "../../../Logic/UIEventSource"
|
||||
import LayerConfig from "../../../Models/ThemeConfig/LayerConfig"
|
||||
import { TagsFilter } from "../../../Logic/Tags/TagsFilter"
|
||||
import { XCircleIcon } from "@rgossiaux/svelte-heroicons/solid"
|
||||
import { TagUtils } from "../../../Logic/Tags/TagUtils"
|
||||
import OsmChangeAction from "../../../Logic/Osm/Actions/OsmChangeAction"
|
||||
import DeleteAction from "../../../Logic/Osm/Actions/DeleteAction"
|
||||
import ChangeTagAction from "../../../Logic/Osm/Actions/ChangeTagAction"
|
||||
import Loading from "../../Base/Loading.svelte"
|
||||
import { DeleteFlowState } from "./DeleteFlowState"
|
||||
import { twJoin } from "tailwind-merge"
|
||||
|
||||
export let state: SpecialVisualizationState
|
||||
export let deleteConfig: DeleteConfig
|
||||
export let state: SpecialVisualizationState
|
||||
export let deleteConfig: DeleteConfig
|
||||
|
||||
export let tags: UIEventSource<OsmTags>
|
||||
export let tags: UIEventSource<OsmTags>
|
||||
|
||||
let featureId: OsmId = <OsmId>tags.data.id
|
||||
let featureId: OsmId = <OsmId>tags.data.id
|
||||
|
||||
export let feature: Feature
|
||||
export let layer: LayerConfig
|
||||
export let feature: Feature
|
||||
export let layer: LayerConfig
|
||||
|
||||
const deleteAbility = new DeleteFlowState(featureId, state, deleteConfig.neededChangesets)
|
||||
const deleteAbility = new DeleteFlowState(featureId, state, deleteConfig.neededChangesets)
|
||||
|
||||
const canBeDeleted: UIEventSource<boolean | undefined> = deleteAbility.canBeDeleted
|
||||
const canBeDeletedReason = deleteAbility.canBeDeletedReason
|
||||
const canBeDeleted: UIEventSource<boolean | undefined> = deleteAbility.canBeDeleted
|
||||
const canBeDeletedReason = deleteAbility.canBeDeletedReason
|
||||
|
||||
const hasSoftDeletion = deleteConfig.softDeletionTags !== undefined
|
||||
let currentState: "start" | "confirm" | "applying" | "deleted" = "start"
|
||||
$: {
|
||||
deleteAbility.CheckDeleteability(true)
|
||||
const hasSoftDeletion = deleteConfig.softDeletionTags !== undefined
|
||||
let currentState: "start" | "confirm" | "applying" | "deleted" = "start"
|
||||
$: {
|
||||
deleteAbility.CheckDeleteability(true)
|
||||
}
|
||||
|
||||
const t = Translations.t.delete
|
||||
|
||||
let selectedTags: TagsFilter
|
||||
let changedProperties = undefined
|
||||
$: changedProperties = TagUtils.changeAsProperties(selectedTags?.asChange(tags?.data ?? {}) ?? [])
|
||||
let isHardDelete = undefined
|
||||
$: isHardDelete = changedProperties[DeleteConfig.deleteReasonKey] !== undefined
|
||||
|
||||
async function onDelete() {
|
||||
if (selectedTags === undefined) {
|
||||
return
|
||||
}
|
||||
currentState = "applying"
|
||||
let actionToTake: OsmChangeAction
|
||||
const changedProperties = TagUtils.changeAsProperties(selectedTags.asChange(tags?.data ?? {}))
|
||||
const deleteReason = changedProperties[DeleteConfig.deleteReasonKey]
|
||||
if (deleteReason) {
|
||||
// This is a proper, hard deletion
|
||||
actionToTake = new DeleteAction(
|
||||
featureId,
|
||||
deleteConfig.softDeletionTags,
|
||||
{
|
||||
theme: state?.layout?.id ?? "unknown",
|
||||
specialMotivation: deleteReason,
|
||||
},
|
||||
canBeDeleted.data
|
||||
)
|
||||
} else {
|
||||
// no _delete_reason is given, which implies that this is _not_ a deletion but merely a retagging via a nonDeleteMapping
|
||||
actionToTake = new ChangeTagAction(featureId, selectedTags, tags.data, {
|
||||
theme: state?.layout?.id ?? "unkown",
|
||||
changeType: "special-delete",
|
||||
})
|
||||
}
|
||||
|
||||
const t = Translations.t.delete
|
||||
|
||||
let selectedTags: TagsFilter
|
||||
let changedProperties = undefined
|
||||
$: changedProperties = TagUtils.changeAsProperties(selectedTags?.asChange(tags?.data ?? {}) ?? [])
|
||||
let isHardDelete = undefined
|
||||
$: isHardDelete = changedProperties[DeleteConfig.deleteReasonKey] !== undefined
|
||||
|
||||
async function onDelete() {
|
||||
if (selectedTags === undefined) {
|
||||
return
|
||||
}
|
||||
currentState = "applying"
|
||||
let actionToTake: OsmChangeAction
|
||||
const changedProperties = TagUtils.changeAsProperties(selectedTags.asChange(tags?.data ?? {}))
|
||||
const deleteReason = changedProperties[DeleteConfig.deleteReasonKey]
|
||||
if (deleteReason) {
|
||||
// This is a proper, hard deletion
|
||||
actionToTake = new DeleteAction(
|
||||
featureId,
|
||||
deleteConfig.softDeletionTags,
|
||||
{
|
||||
theme: state?.layout?.id ?? "unknown",
|
||||
specialMotivation: deleteReason,
|
||||
},
|
||||
canBeDeleted.data,
|
||||
)
|
||||
} else {
|
||||
// no _delete_reason is given, which implies that this is _not_ a deletion but merely a retagging via a nonDeleteMapping
|
||||
actionToTake = new ChangeTagAction(featureId, selectedTags, tags.data, {
|
||||
theme: state?.layout?.id ?? "unkown",
|
||||
changeType: "special-delete",
|
||||
})
|
||||
}
|
||||
|
||||
await state.changes?.applyAction(actionToTake)
|
||||
tags.data["_deleted"] = "yes"
|
||||
tags.ping()
|
||||
currentState = "deleted"
|
||||
}
|
||||
await state.changes?.applyAction(actionToTake)
|
||||
tags.data["_deleted"] = "yes"
|
||||
tags.ping()
|
||||
currentState = "deleted"
|
||||
}
|
||||
</script>
|
||||
<LoginToggle ignoreLoading={true} {state}>
|
||||
|
||||
<LoginToggle ignoreLoading={true} {state}>
|
||||
{#if $canBeDeleted === false && !hasSoftDeletion}
|
||||
<div class="low-interaction flex flex-col">
|
||||
<Tr t={$canBeDeletedReason} />
|
||||
<Tr cls="subtle" t={t.useSomethingElse} />
|
||||
</div>
|
||||
{:else}
|
||||
{#if currentState === "start"}
|
||||
{:else if currentState === "start"}
|
||||
<button
|
||||
class="flex items-center"
|
||||
on:click={() => {
|
||||
currentState = "confirm"
|
||||
}}
|
||||
>
|
||||
<TrashIcon class="h-6 w-6" />
|
||||
<Tr t={t.delete} />
|
||||
</button>
|
||||
{:else if currentState === "confirm"}
|
||||
<TagRenderingQuestion
|
||||
bind:selectedTags
|
||||
{tags}
|
||||
config={deleteConfig.constructTagRendering()}
|
||||
{state}
|
||||
selectedElement={feature}
|
||||
{layer}
|
||||
>
|
||||
<button
|
||||
class="flex items-center"
|
||||
on:click={() => {
|
||||
currentState = "confirm"
|
||||
}}
|
||||
slot="save-button"
|
||||
on:click={onDelete}
|
||||
class={twJoin(
|
||||
selectedTags === undefined && "disabled",
|
||||
"primary flex items-center bg-red-600"
|
||||
)}
|
||||
>
|
||||
<TrashIcon class="h-6 w-6" />
|
||||
<TrashIcon
|
||||
class={twJoin(
|
||||
"ml-1 mr-2 h-6 w-6 rounded-full p-1",
|
||||
selectedTags !== undefined && "bg-red-600"
|
||||
)}
|
||||
/>
|
||||
<Tr t={t.delete} />
|
||||
</button>
|
||||
{:else if currentState === "confirm"}
|
||||
<TagRenderingQuestion
|
||||
bind:selectedTags
|
||||
{tags}
|
||||
config={deleteConfig.constructTagRendering()}
|
||||
{state}
|
||||
selectedElement={feature}
|
||||
{layer}
|
||||
>
|
||||
<button
|
||||
slot="save-button"
|
||||
on:click={onDelete}
|
||||
class={twJoin(
|
||||
selectedTags === undefined && "disabled",
|
||||
"primary flex items-center bg-red-600"
|
||||
)}
|
||||
>
|
||||
<TrashIcon
|
||||
class={twJoin(
|
||||
"ml-1 mr-2 h-6 w-6 rounded-full p-1",
|
||||
selectedTags !== undefined && "bg-red-600"
|
||||
)}
|
||||
/>
|
||||
<Tr t={t.delete} />
|
||||
</button>
|
||||
<button slot="cancel" class="items-center" on:click={() => (currentState = "start")}>
|
||||
<Tr t={t.cancel} />
|
||||
</button>
|
||||
<XCircleIcon
|
||||
slot="upper-right"
|
||||
class="h-8 w-8 cursor-pointer"
|
||||
on:click={() => {
|
||||
currentState = "start"
|
||||
}}
|
||||
/>
|
||||
<button slot="cancel" class="items-center" on:click={() => (currentState = "start")}>
|
||||
<Tr t={t.cancel} />
|
||||
</button>
|
||||
<XCircleIcon
|
||||
slot="upper-right"
|
||||
class="h-8 w-8 cursor-pointer"
|
||||
on:click={() => {
|
||||
currentState = "start"
|
||||
}}
|
||||
/>
|
||||
|
||||
<div slot="under-buttons">
|
||||
{#if selectedTags !== undefined}
|
||||
{#if canBeDeleted && isHardDelete}
|
||||
<!-- This is a hard delete - explain that this is a hard delete...-->
|
||||
<Tr t={t.explanations.hardDelete} />
|
||||
{:else}
|
||||
<!-- This is a soft deletion: we explain _why_ the deletion is soft -->
|
||||
<Tr t={t.explanations.softDelete.Subs({ reason: $canBeDeletedReason })} />
|
||||
{/if}
|
||||
<div slot="under-buttons">
|
||||
{#if selectedTags !== undefined}
|
||||
{#if canBeDeleted && isHardDelete}
|
||||
<!-- This is a hard delete - explain that this is a hard delete...-->
|
||||
<Tr t={t.explanations.hardDelete} />
|
||||
{:else}
|
||||
<!-- This is a soft deletion: we explain _why_ the deletion is soft -->
|
||||
<Tr t={t.explanations.softDelete.Subs({ reason: $canBeDeletedReason })} />
|
||||
{/if}
|
||||
</div>
|
||||
</TagRenderingQuestion>
|
||||
{:else if currentState === "applying"}
|
||||
<Loading />
|
||||
{:else}
|
||||
<!-- currentState === 'deleted' -->
|
||||
|
||||
<div class="low-interaction flex">
|
||||
<TrashIcon class="h-6 w-6" />
|
||||
<Tr t={t.isDeleted} />
|
||||
{/if}
|
||||
</div>
|
||||
{/if}
|
||||
</TagRenderingQuestion>
|
||||
{:else if currentState === "applying"}
|
||||
<Loading />
|
||||
{:else}
|
||||
<!-- currentState === 'deleted' -->
|
||||
|
||||
<div class="low-interaction flex">
|
||||
<TrashIcon class="h-6 w-6" />
|
||||
<Tr t={t.isDeleted} />
|
||||
</div>
|
||||
{/if}
|
||||
</LoginToggle>
|
||||
|
|
|
@ -1,48 +1,47 @@
|
|||
<script lang="ts">
|
||||
import type { SpecialVisualizationState } from "../SpecialVisualization";
|
||||
import { HeartIcon as HeartSolidIcon } from "@babeard/svelte-heroicons/solid";
|
||||
import { HeartIcon as HeartOutlineIcon } from "@babeard/svelte-heroicons/outline";
|
||||
import Tr from "../Base/Tr.svelte";
|
||||
import Translations from "../i18n/Translations";
|
||||
import LoginToggle from "../Base/LoginToggle.svelte";
|
||||
import type { Feature } from "geojson";
|
||||
import LayerConfig from "../../Models/ThemeConfig/LayerConfig";
|
||||
import type { SpecialVisualizationState } from "../SpecialVisualization"
|
||||
import { HeartIcon as HeartSolidIcon } from "@babeard/svelte-heroicons/solid"
|
||||
import { HeartIcon as HeartOutlineIcon } from "@babeard/svelte-heroicons/outline"
|
||||
import Tr from "../Base/Tr.svelte"
|
||||
import Translations from "../i18n/Translations"
|
||||
import LoginToggle from "../Base/LoginToggle.svelte"
|
||||
import type { Feature } from "geojson"
|
||||
import LayerConfig from "../../Models/ThemeConfig/LayerConfig"
|
||||
|
||||
/**
|
||||
* A full-blown 'mark as favourite'-button
|
||||
*/
|
||||
export let state: SpecialVisualizationState;
|
||||
export let state: SpecialVisualizationState
|
||||
export let feature: Feature
|
||||
export let tags: Record<string, string>;
|
||||
export let tags: Record<string, string>
|
||||
export let layer: LayerConfig
|
||||
let isFavourite = tags?.map(tags => tags._favourite === "yes");
|
||||
const t = Translations.t.favouritePoi;
|
||||
let isFavourite = tags?.map((tags) => tags._favourite === "yes")
|
||||
const t = Translations.t.favouritePoi
|
||||
|
||||
function markFavourite(isFavourite: boolean) {
|
||||
state.favourites.markAsFavourite(feature, layer.id, state.layout.id, tags, isFavourite)
|
||||
}
|
||||
|
||||
</script>
|
||||
|
||||
<LoginToggle ignoreLoading={true} {state}>
|
||||
{#if $isFavourite}
|
||||
<div class="flex h-fit items-start">
|
||||
<HeartSolidIcon class="w-16 shrink-0 mr-2" on:click={() => markFavourite(false)} />
|
||||
<div class="flex flex-col w-full">
|
||||
<button class="flex flex-col items-start" on:click={() => markFavourite(false)}>
|
||||
<Tr t={t.button.unmark} />
|
||||
<Tr cls="normal-font subtle" t={t.button.unmarkNotDeleted}/>
|
||||
</button>
|
||||
{#if $isFavourite}
|
||||
<div class="flex h-fit items-start">
|
||||
<HeartSolidIcon class="mr-2 w-16 shrink-0" on:click={() => markFavourite(false)} />
|
||||
<div class="flex w-full flex-col">
|
||||
<button class="flex flex-col items-start" on:click={() => markFavourite(false)}>
|
||||
<Tr t={t.button.unmark} />
|
||||
<Tr cls="normal-font subtle" t={t.button.unmarkNotDeleted} />
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<Tr cls="font-bold thanks m-2 p-2 block" t={t.button.isFavourite} />
|
||||
{:else}
|
||||
<div class="flex items-start">
|
||||
<HeartOutlineIcon class="w-16 shrink-0 mr-2" on:click={() => markFavourite(true)} />
|
||||
{:else}
|
||||
<div class="flex items-start">
|
||||
<HeartOutlineIcon class="mr-2 w-16 shrink-0" on:click={() => markFavourite(true)} />
|
||||
<button class="flex w-full flex-col items-start" on:click={() => markFavourite(true)}>
|
||||
<Tr t={t.button.markAsFavouriteTitle} />
|
||||
<Tr cls="normal-font subtle" t={t.button.markDescription}/>
|
||||
<Tr cls="normal-font subtle" t={t.button.markDescription} />
|
||||
</button>
|
||||
</div>
|
||||
{/if}
|
||||
</div>
|
||||
{/if}
|
||||
</LoginToggle>
|
||||
|
|
|
@ -1,36 +1,35 @@
|
|||
<script lang="ts">
|
||||
import type { SpecialVisualizationState } from "../SpecialVisualization";
|
||||
import { HeartIcon as HeartSolidIcon } from "@babeard/svelte-heroicons/solid";
|
||||
import { HeartIcon as HeartOutlineIcon } from "@babeard/svelte-heroicons/outline";
|
||||
import Translations from "../i18n/Translations";
|
||||
import LoginToggle from "../Base/LoginToggle.svelte";
|
||||
import type { Feature } from "geojson";
|
||||
import LayerConfig from "../../Models/ThemeConfig/LayerConfig";
|
||||
import type { SpecialVisualizationState } from "../SpecialVisualization"
|
||||
import { HeartIcon as HeartSolidIcon } from "@babeard/svelte-heroicons/solid"
|
||||
import { HeartIcon as HeartOutlineIcon } from "@babeard/svelte-heroicons/outline"
|
||||
import Translations from "../i18n/Translations"
|
||||
import LoginToggle from "../Base/LoginToggle.svelte"
|
||||
import type { Feature } from "geojson"
|
||||
import LayerConfig from "../../Models/ThemeConfig/LayerConfig"
|
||||
|
||||
/**
|
||||
* A small 'mark as favourite'-button to serve as title-icon
|
||||
*/
|
||||
export let state: SpecialVisualizationState;
|
||||
export let feature: Feature;
|
||||
export let tags: Record<string, string>;
|
||||
export let layer: LayerConfig;
|
||||
let isFavourite = tags?.map(tags => tags._favourite === "yes");
|
||||
const t = Translations.t.favouritePoi;
|
||||
export let state: SpecialVisualizationState
|
||||
export let feature: Feature
|
||||
export let tags: Record<string, string>
|
||||
export let layer: LayerConfig
|
||||
let isFavourite = tags?.map((tags) => tags._favourite === "yes")
|
||||
const t = Translations.t.favouritePoi
|
||||
|
||||
function markFavourite(isFavourite: boolean) {
|
||||
state.favourites.markAsFavourite(feature, layer.id, state.layout.id, tags, isFavourite);
|
||||
state.favourites.markAsFavourite(feature, layer.id, state.layout.id, tags, isFavourite)
|
||||
}
|
||||
|
||||
</script>
|
||||
|
||||
<LoginToggle ignoreLoading={true} {state}>
|
||||
{#if $isFavourite}
|
||||
<button class="p-0 m-0 h-8 w-8" on:click={() => markFavourite(false)}>
|
||||
<HeartSolidIcon/>
|
||||
<button class="m-0 h-8 w-8 p-0" on:click={() => markFavourite(false)}>
|
||||
<HeartSolidIcon />
|
||||
</button>
|
||||
{:else}
|
||||
<button class="p-0 m-0 h-8 w-8 no-image-background" on:click={() => markFavourite(true)} >
|
||||
<HeartOutlineIcon/>
|
||||
<button class="no-image-background m-0 h-8 w-8 p-0" on:click={() => markFavourite(true)}>
|
||||
<HeartOutlineIcon />
|
||||
</button>
|
||||
{/if}
|
||||
</LoginToggle>
|
||||
|
|
|
@ -1,40 +1,38 @@
|
|||
<script lang="ts">
|
||||
import { UIEventSource } from "../../Logic/UIEventSource"
|
||||
import type { MoveReason } from "./MoveWizardState"
|
||||
import { MoveWizardState } from "./MoveWizardState"
|
||||
|
||||
import { UIEventSource } from "../../Logic/UIEventSource";
|
||||
import type { MoveReason } from "./MoveWizardState";
|
||||
import { MoveWizardState } from "./MoveWizardState";
|
||||
import LayerConfig from "../../Models/ThemeConfig/LayerConfig"
|
||||
import ToSvelte from "../Base/ToSvelte.svelte"
|
||||
import Tr from "../Base/Tr.svelte"
|
||||
import Translations from "../i18n/Translations"
|
||||
import Move from "../../assets/svg/Move.svelte"
|
||||
import Move_not_allowed from "../../assets/svg/Move_not_allowed.svelte"
|
||||
import type { SpecialVisualizationState } from "../SpecialVisualization"
|
||||
import { XCircleIcon } from "@babeard/svelte-heroicons/solid"
|
||||
import type { MapProperties } from "../../Models/MapProperties"
|
||||
import type { Feature, Point } from "geojson"
|
||||
import { GeoOperations } from "../../Logic/GeoOperations"
|
||||
import LocationInput from "../InputElement/Helpers/LocationInput.svelte"
|
||||
import OpenBackgroundSelectorButton from "../BigComponents/OpenBackgroundSelectorButton.svelte"
|
||||
import Geosearch from "../BigComponents/Geosearch.svelte"
|
||||
import Move_confirm from "../../assets/svg/Move_confirm.svelte"
|
||||
import If from "../Base/If.svelte"
|
||||
import Constants from "../../Models/Constants"
|
||||
|
||||
import LayerConfig from "../../Models/ThemeConfig/LayerConfig";
|
||||
import ToSvelte from "../Base/ToSvelte.svelte";
|
||||
import Tr from "../Base/Tr.svelte";
|
||||
import Translations from "../i18n/Translations";
|
||||
import Move from "../../assets/svg/Move.svelte";
|
||||
import Move_not_allowed from "../../assets/svg/Move_not_allowed.svelte";
|
||||
import type { SpecialVisualizationState } from "../SpecialVisualization";
|
||||
import { XCircleIcon } from "@babeard/svelte-heroicons/solid";
|
||||
import type { MapProperties } from "../../Models/MapProperties";
|
||||
import type { Feature, Point } from "geojson";
|
||||
import { GeoOperations } from "../../Logic/GeoOperations";
|
||||
import LocationInput from "../InputElement/Helpers/LocationInput.svelte";
|
||||
import OpenBackgroundSelectorButton from "../BigComponents/OpenBackgroundSelectorButton.svelte";
|
||||
import Geosearch from "../BigComponents/Geosearch.svelte";
|
||||
import Move_confirm from "../../assets/svg/Move_confirm.svelte";
|
||||
import If from "../Base/If.svelte";
|
||||
import Constants from "../../Models/Constants";
|
||||
export let state: SpecialVisualizationState
|
||||
|
||||
export let layer: LayerConfig
|
||||
export let featureToMove: Feature<Point>
|
||||
|
||||
export let state: SpecialVisualizationState;
|
||||
let id: string = featureToMove.properties.id
|
||||
let currentStep: "start" | "reason" | "pick_location" | "moved" = "start"
|
||||
const t = Translations.t.move
|
||||
const reason = new UIEventSource<MoveReason>(undefined)
|
||||
let [lon, lat] = GeoOperations.centerpointCoordinates(featureToMove)
|
||||
|
||||
export let layer: LayerConfig;
|
||||
export let featureToMove: Feature<Point>;
|
||||
|
||||
let id: string = featureToMove.properties.id;
|
||||
let currentStep: "start" | "reason" | "pick_location" | "moved" = "start";
|
||||
const t = Translations.t.move;
|
||||
const reason = new UIEventSource<MoveReason>(undefined);
|
||||
let [lon, lat] = GeoOperations.centerpointCoordinates(featureToMove);
|
||||
|
||||
let newLocation = new UIEventSource<{ lon: number; lat: number }>(undefined);
|
||||
let newLocation = new UIEventSource<{ lon: number; lat: number }>(undefined)
|
||||
|
||||
function initMapProperties() {
|
||||
return <any>{
|
||||
|
@ -45,20 +43,19 @@
|
|||
location: new UIEventSource({ lon, lat }),
|
||||
minzoom: new UIEventSource($reason.minZoom),
|
||||
rasterLayer: state.mapProperties.rasterLayer,
|
||||
zoom: new UIEventSource($reason?.startZoom ?? 16)
|
||||
};
|
||||
zoom: new UIEventSource($reason?.startZoom ?? 16),
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
let moveWizardState = new MoveWizardState(id, layer.allowMove, state);
|
||||
let notAllowed = moveWizardState.moveDisallowedReason;
|
||||
let currentMapProperties: MapProperties = undefined;
|
||||
let moveWizardState = new MoveWizardState(id, layer.allowMove, state)
|
||||
let notAllowed = moveWizardState.moveDisallowedReason
|
||||
let currentMapProperties: MapProperties = undefined
|
||||
</script>
|
||||
{#if moveWizardState.reasons.length > 0}
|
||||
|
||||
{#if moveWizardState.reasons.length > 0}
|
||||
{#if $notAllowed}
|
||||
<div class="flex m-2 p-2 rounded-lg bg-gray-200">
|
||||
<Move_not_allowed class="h-8 w-8 m-2" />
|
||||
<div class="m-2 flex rounded-lg bg-gray-200 p-2">
|
||||
<Move_not_allowed class="m-2 h-8 w-8" />
|
||||
<div class="flex flex-col">
|
||||
<Tr t={t.cannotBeMoved} />
|
||||
<Tr t={$notAllowed} />
|
||||
|
@ -66,79 +63,106 @@
|
|||
</div>
|
||||
{:else if currentStep === "start"}
|
||||
{#if moveWizardState.reasons.length === 1}
|
||||
<button class="flex" on:click={() => {reason.setData(moveWizardState.reasons[0]); currentStep = "pick_location"}}>
|
||||
<ToSvelte construct={moveWizardState.reasons[0].icon.SetStyle("height: 1.5rem; width: 1.5rem;")}></ToSvelte>
|
||||
<button
|
||||
class="flex"
|
||||
on:click={() => {
|
||||
reason.setData(moveWizardState.reasons[0])
|
||||
currentStep = "pick_location"
|
||||
}}
|
||||
>
|
||||
<ToSvelte
|
||||
construct={moveWizardState.reasons[0].icon.SetStyle("height: 1.5rem; width: 1.5rem;")}
|
||||
/>
|
||||
<Tr t={Translations.T(moveWizardState.reasons[0].invitingText)} />
|
||||
</button>
|
||||
{:else}
|
||||
<button class="flex" on:click={() => {currentStep = "reason"}}>
|
||||
<Move class="w-6 h-6" />
|
||||
<button
|
||||
class="flex"
|
||||
on:click={() => {
|
||||
currentStep = "reason"
|
||||
}}
|
||||
>
|
||||
<Move class="h-6 w-6" />
|
||||
<Tr t={t.inviteToMove.generic} />
|
||||
</button>
|
||||
{/if}
|
||||
{:else if currentStep === "reason"}
|
||||
<div class="flex flex-col interactive border-interactive p-2">
|
||||
|
||||
<div class="interactive border-interactive flex flex-col p-2">
|
||||
<Tr cls="text-lg font-bold" t={t.whyMove} />
|
||||
{#each moveWizardState.reasons as reasonSpec}
|
||||
<button on:click={() => {reason.setData(reasonSpec); currentStep = "pick_location"}}>
|
||||
<ToSvelte construct={reasonSpec.icon.SetClass("w-16 h-16 pr-2")} />
|
||||
<button
|
||||
on:click={() => {
|
||||
reason.setData(reasonSpec)
|
||||
currentStep = "pick_location"
|
||||
}}
|
||||
>
|
||||
<ToSvelte construct={reasonSpec.icon.SetClass("w-16 h-16 pr-2")} />
|
||||
<Tr t={reasonSpec.text} />
|
||||
</button>
|
||||
{/each}
|
||||
</div>
|
||||
|
||||
{:else if currentStep === "pick_location"}
|
||||
<div class="flex flex-col border-interactive interactive p-2">
|
||||
<div class="border-interactive interactive flex flex-col p-2">
|
||||
<Tr cls="text-lg font-bold" t={t.moveTitle} />
|
||||
|
||||
|
||||
<div class="relative w-full h-64">
|
||||
<LocationInput mapProperties={currentMapProperties = initMapProperties()} value={newLocation}
|
||||
initialCoordinate={{lon, lat}} />
|
||||
<div class="relative h-64 w-full">
|
||||
<LocationInput
|
||||
mapProperties={(currentMapProperties = initMapProperties())}
|
||||
value={newLocation}
|
||||
initialCoordinate={{ lon, lat }}
|
||||
/>
|
||||
<div class="absolute bottom-0 left-0">
|
||||
<OpenBackgroundSelectorButton {state} />
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{#if $reason.includeSearch}
|
||||
<Geosearch bounds={ currentMapProperties.bounds} clearAfterView={false} />
|
||||
<Geosearch bounds={currentMapProperties.bounds} clearAfterView={false} />
|
||||
{/if}
|
||||
|
||||
|
||||
<div class="flex flex-wrap">
|
||||
<If condition={currentMapProperties.zoom.mapD(zoom => zoom >= Constants.minZoomLevelToAddNewPoint)}>
|
||||
|
||||
<button class="flex flex primary w-full"
|
||||
on:click={() => {moveWizardState.moveFeature(newLocation.data, reason.data, featureToMove)}}>
|
||||
<Move class="w-6 h-6 mr-2" />
|
||||
<If
|
||||
condition={currentMapProperties.zoom.mapD(
|
||||
(zoom) => zoom >= Constants.minZoomLevelToAddNewPoint
|
||||
)}
|
||||
>
|
||||
<button
|
||||
class="primary flex flex w-full"
|
||||
on:click={() => {
|
||||
moveWizardState.moveFeature(newLocation.data, reason.data, featureToMove)
|
||||
}}
|
||||
>
|
||||
<Move class="mr-2 h-6 w-6" />
|
||||
<Tr t={t.confirmMove} />
|
||||
</button>
|
||||
|
||||
<div slot="else" class="alert">
|
||||
<Tr t={t.zoomInFurther} />
|
||||
</div>
|
||||
|
||||
</If>
|
||||
|
||||
<button class="w-full" on:click={() => {currentStep= "start"}}>
|
||||
<XCircleIcon class="w-6 h-6 mr-2" />
|
||||
<button
|
||||
class="w-full"
|
||||
on:click={() => {
|
||||
currentStep = "start"
|
||||
}}
|
||||
>
|
||||
<XCircleIcon class="mr-2 h-6 w-6" />
|
||||
<Tr t={t.cancel} />
|
||||
</button>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
||||
{:else if currentStep === "moved"}
|
||||
|
||||
<div class="flex flex-col">
|
||||
<Tr cls="thanks" t={t.pointIsMoved} />
|
||||
<button on:click={() => {currentStep = "reason"}}>
|
||||
<button
|
||||
on:click={() => {
|
||||
currentStep = "reason"
|
||||
}}
|
||||
>
|
||||
<Move />
|
||||
<Tr t={t.inviteToMoveAgain} />
|
||||
</button>
|
||||
</div>
|
||||
|
||||
{/if}
|
||||
{/if}
|
||||
|
|
|
@ -3,15 +3,15 @@
|
|||
* Shows all questions for which the answers are unknown.
|
||||
* The questions can either be shown all at once or one at a time (in which case they can be skipped)
|
||||
*/
|
||||
import TagRenderingConfig from "../../../Models/ThemeConfig/TagRenderingConfig";
|
||||
import { UIEventSource } from "../../../Logic/UIEventSource";
|
||||
import type { Feature } from "geojson";
|
||||
import type { SpecialVisualizationState } from "../../SpecialVisualization";
|
||||
import LayerConfig from "../../../Models/ThemeConfig/LayerConfig";
|
||||
import TagRenderingQuestion from "./TagRenderingQuestion.svelte";
|
||||
import Tr from "../../Base/Tr.svelte";
|
||||
import Translations from "../../i18n/Translations.js";
|
||||
import { Utils } from "../../../Utils";
|
||||
import TagRenderingConfig from "../../../Models/ThemeConfig/TagRenderingConfig"
|
||||
import { UIEventSource } from "../../../Logic/UIEventSource"
|
||||
import type { Feature } from "geojson"
|
||||
import type { SpecialVisualizationState } from "../../SpecialVisualization"
|
||||
import LayerConfig from "../../../Models/ThemeConfig/LayerConfig"
|
||||
import TagRenderingQuestion from "./TagRenderingQuestion.svelte"
|
||||
import Tr from "../../Base/Tr.svelte"
|
||||
import Translations from "../../i18n/Translations.js"
|
||||
import { Utils } from "../../../Utils"
|
||||
|
||||
export let layer: LayerConfig
|
||||
export let tags: UIEventSource<Record<string, string>>
|
||||
|
|
|
@ -5,7 +5,7 @@
|
|||
import TagRenderingMapping from "./TagRenderingMapping.svelte"
|
||||
import type { SpecialVisualizationState } from "../../SpecialVisualization"
|
||||
import type { Feature } from "geojson"
|
||||
import { Store, UIEventSource } from "../../../Logic/UIEventSource";
|
||||
import { Store, UIEventSource } from "../../../Logic/UIEventSource"
|
||||
import { onDestroy } from "svelte"
|
||||
import LayerConfig from "../../../Models/ThemeConfig/LayerConfig"
|
||||
import { twMerge } from "tailwind-merge"
|
||||
|
@ -21,8 +21,9 @@
|
|||
if (config === undefined) {
|
||||
throw "Config is undefined in tagRenderingAnswer"
|
||||
}
|
||||
let trs : Store<{then: Translation, icon?: string, iconClass?: string}[]> = tags.mapD(tags => Utils.NoNull(config?.GetRenderValues(tags)))
|
||||
|
||||
let trs: Store<{ then: Translation; icon?: string; iconClass?: string }[]> = tags.mapD((tags) =>
|
||||
Utils.NoNull(config?.GetRenderValues(tags))
|
||||
)
|
||||
</script>
|
||||
|
||||
{#if config !== undefined && (config?.condition === undefined || config.condition.matchesProperties($tags))}
|
||||
|
|
|
@ -1,41 +1,41 @@
|
|||
<script lang="ts">
|
||||
import TagRenderingConfig from "../../../Models/ThemeConfig/TagRenderingConfig";
|
||||
import { Store, UIEventSource } from "../../../Logic/UIEventSource";
|
||||
import type { Feature } from "geojson";
|
||||
import type { SpecialVisualizationState } from "../../SpecialVisualization";
|
||||
import TagRenderingAnswer from "./TagRenderingAnswer.svelte";
|
||||
import { PencilAltIcon, XCircleIcon } from "@rgossiaux/svelte-heroicons/solid";
|
||||
import TagRenderingQuestion from "./TagRenderingQuestion.svelte";
|
||||
import { onDestroy } from "svelte";
|
||||
import Tr from "../../Base/Tr.svelte";
|
||||
import Translations from "../../i18n/Translations.js";
|
||||
import LayerConfig from "../../../Models/ThemeConfig/LayerConfig";
|
||||
import { Utils } from "../../../Utils";
|
||||
import { twMerge } from "tailwind-merge";
|
||||
import TagRenderingConfig from "../../../Models/ThemeConfig/TagRenderingConfig"
|
||||
import { Store, UIEventSource } from "../../../Logic/UIEventSource"
|
||||
import type { Feature } from "geojson"
|
||||
import type { SpecialVisualizationState } from "../../SpecialVisualization"
|
||||
import TagRenderingAnswer from "./TagRenderingAnswer.svelte"
|
||||
import { PencilAltIcon, XCircleIcon } from "@rgossiaux/svelte-heroicons/solid"
|
||||
import TagRenderingQuestion from "./TagRenderingQuestion.svelte"
|
||||
import { onDestroy } from "svelte"
|
||||
import Tr from "../../Base/Tr.svelte"
|
||||
import Translations from "../../i18n/Translations.js"
|
||||
import LayerConfig from "../../../Models/ThemeConfig/LayerConfig"
|
||||
import { Utils } from "../../../Utils"
|
||||
import { twMerge } from "tailwind-merge"
|
||||
|
||||
export let config: TagRenderingConfig;
|
||||
export let tags: UIEventSource<Record<string, string>>;
|
||||
export let selectedElement: Feature | undefined;
|
||||
export let state: SpecialVisualizationState;
|
||||
export let layer: LayerConfig = undefined;
|
||||
export let config: TagRenderingConfig
|
||||
export let tags: UIEventSource<Record<string, string>>
|
||||
export let selectedElement: Feature | undefined
|
||||
export let state: SpecialVisualizationState
|
||||
export let layer: LayerConfig = undefined
|
||||
|
||||
export let editingEnabled: Store<boolean> | undefined = state?.featureSwitchUserbadge;
|
||||
export let editingEnabled: Store<boolean> | undefined = state?.featureSwitchUserbadge
|
||||
|
||||
export let highlightedRendering: UIEventSource<string> = undefined;
|
||||
export let clss;
|
||||
export let highlightedRendering: UIEventSource<string> = undefined
|
||||
export let clss
|
||||
/**
|
||||
* Indicates if this tagRendering currently shows the attribute or asks the question to _change_ the property
|
||||
*/
|
||||
export let editMode = !config.IsKnown(tags.data); // || showQuestionIfUnknown;
|
||||
export let editMode = !config.IsKnown(tags.data) // || showQuestionIfUnknown;
|
||||
if (tags) {
|
||||
onDestroy(
|
||||
tags.addCallbackD((tags) => {
|
||||
editMode = !config.IsKnown(tags);
|
||||
editMode = !config.IsKnown(tags)
|
||||
})
|
||||
);
|
||||
)
|
||||
}
|
||||
|
||||
let htmlElem: HTMLDivElement;
|
||||
let htmlElem: HTMLDivElement
|
||||
$: {
|
||||
if (editMode && htmlElem !== undefined && config.IsKnown(tags)) {
|
||||
// EditMode switched to true yet the answer is already known, so the person wants to make a change
|
||||
|
@ -43,40 +43,40 @@
|
|||
|
||||
// Some delay is applied to give Svelte the time to render the _question_
|
||||
window.setTimeout(() => {
|
||||
Utils.scrollIntoView(<any>htmlElem);
|
||||
}, 50);
|
||||
Utils.scrollIntoView(<any>htmlElem)
|
||||
}, 50)
|
||||
}
|
||||
}
|
||||
|
||||
const _htmlElement = new UIEventSource<HTMLElement>(undefined);
|
||||
$: _htmlElement.setData(htmlElem);
|
||||
const _htmlElement = new UIEventSource<HTMLElement>(undefined)
|
||||
$: _htmlElement.setData(htmlElem)
|
||||
|
||||
function setHighlighting() {
|
||||
if (highlightedRendering === undefined) {
|
||||
return;
|
||||
return
|
||||
}
|
||||
if (htmlElem === undefined) {
|
||||
return;
|
||||
return
|
||||
}
|
||||
const highlighted = highlightedRendering.data;
|
||||
const highlighted = highlightedRendering.data
|
||||
if (config.id === highlighted) {
|
||||
htmlElem.classList.add("glowing-shadow");
|
||||
htmlElem.tabIndex = "-1";
|
||||
console.log("Scrolling to", htmlElem);
|
||||
htmlElem.scrollIntoView({ behavior: "smooth" });
|
||||
Utils.focusOnFocusableChild(htmlElem);
|
||||
htmlElem.classList.add("glowing-shadow")
|
||||
htmlElem.tabIndex = "-1"
|
||||
console.log("Scrolling to", htmlElem)
|
||||
htmlElem.scrollIntoView({ behavior: "smooth" })
|
||||
Utils.focusOnFocusableChild(htmlElem)
|
||||
} else {
|
||||
htmlElem.classList.remove("glowing-shadow");
|
||||
htmlElem.classList.remove("glowing-shadow")
|
||||
}
|
||||
}
|
||||
|
||||
if (highlightedRendering) {
|
||||
onDestroy(highlightedRendering?.addCallbackAndRun(() => setHighlighting()));
|
||||
onDestroy(_htmlElement.addCallbackAndRun(() => setHighlighting()));
|
||||
onDestroy(highlightedRendering?.addCallbackAndRun(() => setHighlighting()))
|
||||
onDestroy(_htmlElement.addCallbackAndRun(() => setHighlighting()))
|
||||
}
|
||||
</script>
|
||||
|
||||
<div bind:this={htmlElem} class={twMerge(clss, "tr-"+config.id)}>
|
||||
<div bind:this={htmlElem} class={twMerge(clss, "tr-" + config.id)}>
|
||||
{#if config.question && (!editingEnabled || $editingEnabled)}
|
||||
{#if editMode}
|
||||
<TagRenderingQuestion {config} {tags} {selectedElement} {state} {layer}>
|
||||
|
@ -89,11 +89,13 @@
|
|||
>
|
||||
<Tr t={Translations.t.general.cancel} />
|
||||
</button>
|
||||
<button slot="upper-right"
|
||||
class="h-8 w-8 cursor-pointer border-none p-0"
|
||||
on:click={() => {
|
||||
<button
|
||||
slot="upper-right"
|
||||
class="h-8 w-8 cursor-pointer border-none p-0"
|
||||
on:click={() => {
|
||||
editMode = false
|
||||
}}>
|
||||
}}
|
||||
>
|
||||
<XCircleIcon />
|
||||
</button>
|
||||
</TagRenderingQuestion>
|
||||
|
|
|
@ -6,7 +6,7 @@
|
|||
import { UIEventSource } from "../../../Logic/UIEventSource"
|
||||
import LayerConfig from "../../../Models/ThemeConfig/LayerConfig"
|
||||
import { twJoin } from "tailwind-merge"
|
||||
import Icon from "../../Map/Icon.svelte";
|
||||
import Icon from "../../Map/Icon.svelte"
|
||||
|
||||
export let selectedElement: Feature
|
||||
export let tags: UIEventSource<Record<string, string>>
|
||||
|
@ -29,7 +29,7 @@
|
|||
|
||||
{#if mapping.icon !== undefined}
|
||||
<div class="inline-flex">
|
||||
<Icon icon={mapping.icon} clss={twJoin(`mapping-icon-${mapping.iconClass}`, "mr-1")}/>
|
||||
<Icon icon={mapping.icon} clss={twJoin(`mapping-icon-${mapping.iconClass}`, "mr-1")} />
|
||||
<SpecialTranslation t={mapping.then} {tags} {state} {layer} feature={selectedElement} />
|
||||
</div>
|
||||
{:else if mapping.then !== undefined}
|
||||
|
|
|
@ -1,56 +1,56 @@
|
|||
<script lang="ts">
|
||||
import { ImmutableStore, UIEventSource } from "../../../Logic/UIEventSource";
|
||||
import type { SpecialVisualizationState } from "../../SpecialVisualization";
|
||||
import Tr from "../../Base/Tr.svelte";
|
||||
import type { Feature } from "geojson";
|
||||
import type { Mapping } from "../../../Models/ThemeConfig/TagRenderingConfig";
|
||||
import TagRenderingConfig from "../../../Models/ThemeConfig/TagRenderingConfig";
|
||||
import { TagsFilter } from "../../../Logic/Tags/TagsFilter";
|
||||
import FreeformInput from "./FreeformInput.svelte";
|
||||
import Translations from "../../i18n/Translations.js";
|
||||
import ChangeTagAction from "../../../Logic/Osm/Actions/ChangeTagAction";
|
||||
import { createEventDispatcher, onDestroy } from "svelte";
|
||||
import LayerConfig from "../../../Models/ThemeConfig/LayerConfig";
|
||||
import SpecialTranslation from "./SpecialTranslation.svelte";
|
||||
import TagHint from "../TagHint.svelte";
|
||||
import LoginToggle from "../../Base/LoginToggle.svelte";
|
||||
import SubtleButton from "../../Base/SubtleButton.svelte";
|
||||
import Loading from "../../Base/Loading.svelte";
|
||||
import TagRenderingMappingInput from "./TagRenderingMappingInput.svelte";
|
||||
import { Translation } from "../../i18n/Translation";
|
||||
import Constants from "../../../Models/Constants";
|
||||
import { Unit } from "../../../Models/Unit";
|
||||
import UserRelatedState from "../../../Logic/State/UserRelatedState";
|
||||
import { twJoin } from "tailwind-merge";
|
||||
import { TagUtils } from "../../../Logic/Tags/TagUtils";
|
||||
import Search from "../../../assets/svg/Search.svelte";
|
||||
import Login from "../../../assets/svg/Login.svelte";
|
||||
import { ImmutableStore, UIEventSource } from "../../../Logic/UIEventSource"
|
||||
import type { SpecialVisualizationState } from "../../SpecialVisualization"
|
||||
import Tr from "../../Base/Tr.svelte"
|
||||
import type { Feature } from "geojson"
|
||||
import type { Mapping } from "../../../Models/ThemeConfig/TagRenderingConfig"
|
||||
import TagRenderingConfig from "../../../Models/ThemeConfig/TagRenderingConfig"
|
||||
import { TagsFilter } from "../../../Logic/Tags/TagsFilter"
|
||||
import FreeformInput from "./FreeformInput.svelte"
|
||||
import Translations from "../../i18n/Translations.js"
|
||||
import ChangeTagAction from "../../../Logic/Osm/Actions/ChangeTagAction"
|
||||
import { createEventDispatcher, onDestroy } from "svelte"
|
||||
import LayerConfig from "../../../Models/ThemeConfig/LayerConfig"
|
||||
import SpecialTranslation from "./SpecialTranslation.svelte"
|
||||
import TagHint from "../TagHint.svelte"
|
||||
import LoginToggle from "../../Base/LoginToggle.svelte"
|
||||
import SubtleButton from "../../Base/SubtleButton.svelte"
|
||||
import Loading from "../../Base/Loading.svelte"
|
||||
import TagRenderingMappingInput from "./TagRenderingMappingInput.svelte"
|
||||
import { Translation } from "../../i18n/Translation"
|
||||
import Constants from "../../../Models/Constants"
|
||||
import { Unit } from "../../../Models/Unit"
|
||||
import UserRelatedState from "../../../Logic/State/UserRelatedState"
|
||||
import { twJoin } from "tailwind-merge"
|
||||
import { TagUtils } from "../../../Logic/Tags/TagUtils"
|
||||
import Search from "../../../assets/svg/Search.svelte"
|
||||
import Login from "../../../assets/svg/Login.svelte"
|
||||
|
||||
export let config: TagRenderingConfig;
|
||||
export let tags: UIEventSource<Record<string, string>>;
|
||||
export let selectedElement: Feature;
|
||||
export let state: SpecialVisualizationState;
|
||||
export let layer: LayerConfig | undefined;
|
||||
export let selectedTags: TagsFilter = undefined;
|
||||
export let config: TagRenderingConfig
|
||||
export let tags: UIEventSource<Record<string, string>>
|
||||
export let selectedElement: Feature
|
||||
export let state: SpecialVisualizationState
|
||||
export let layer: LayerConfig | undefined
|
||||
export let selectedTags: TagsFilter = undefined
|
||||
|
||||
let feedback: UIEventSource<Translation> = new UIEventSource<Translation>(undefined);
|
||||
let feedback: UIEventSource<Translation> = new UIEventSource<Translation>(undefined)
|
||||
|
||||
let unit: Unit = layer?.units?.find((unit) => unit.appliesToKeys.has(config.freeform?.key));
|
||||
let unit: Unit = layer?.units?.find((unit) => unit.appliesToKeys.has(config.freeform?.key))
|
||||
|
||||
// Will be bound if a freeform is available
|
||||
let freeformInput = new UIEventSource<string>(tags?.[config.freeform?.key]);
|
||||
let selectedMapping: number = undefined;
|
||||
let checkedMappings: boolean[];
|
||||
let freeformInput = new UIEventSource<string>(tags?.[config.freeform?.key])
|
||||
let selectedMapping: number = undefined
|
||||
let checkedMappings: boolean[]
|
||||
|
||||
let mappings: Mapping[] = config?.mappings;
|
||||
let searchTerm: UIEventSource<string> = new UIEventSource("");
|
||||
let mappings: Mapping[] = config?.mappings
|
||||
let searchTerm: UIEventSource<string> = new UIEventSource("")
|
||||
|
||||
let dispatch = createEventDispatcher<{
|
||||
saved: {
|
||||
config: TagRenderingConfig
|
||||
applied: TagsFilter
|
||||
}
|
||||
}>();
|
||||
}>()
|
||||
|
||||
/**
|
||||
* Prepares and fills the checkedMappings
|
||||
|
@ -58,12 +58,12 @@
|
|||
function initialize(tgs: Record<string, string>, confg: TagRenderingConfig) {
|
||||
mappings = confg.mappings?.filter((m) => {
|
||||
if (typeof m.hideInAnswer === "boolean") {
|
||||
return !m.hideInAnswer;
|
||||
return !m.hideInAnswer
|
||||
}
|
||||
return !m.hideInAnswer.matchesProperties(tgs);
|
||||
});
|
||||
return !m.hideInAnswer.matchesProperties(tgs)
|
||||
})
|
||||
// We received a new config -> reinit
|
||||
unit = layer?.units?.find((unit) => unit.appliesToKeys.has(config.freeform?.key));
|
||||
unit = layer?.units?.find((unit) => unit.appliesToKeys.has(config.freeform?.key))
|
||||
|
||||
if (
|
||||
confg.mappings?.length > 0 &&
|
||||
|
@ -71,52 +71,51 @@
|
|||
(checkedMappings === undefined ||
|
||||
checkedMappings?.length < confg.mappings.length + (confg.freeform ? 1 : 0))
|
||||
) {
|
||||
const seenFreeforms = [];
|
||||
TagUtils.FlattenMultiAnswer();
|
||||
const seenFreeforms = []
|
||||
TagUtils.FlattenMultiAnswer()
|
||||
checkedMappings = [
|
||||
...confg.mappings.map((mapping) => {
|
||||
const matches = TagUtils.MatchesMultiAnswer(mapping.if, tgs);
|
||||
const matches = TagUtils.MatchesMultiAnswer(mapping.if, tgs)
|
||||
if (matches && confg.freeform) {
|
||||
const newProps = TagUtils.changeAsProperties(mapping.if.asChange());
|
||||
seenFreeforms.push(newProps[confg.freeform.key]);
|
||||
const newProps = TagUtils.changeAsProperties(mapping.if.asChange())
|
||||
seenFreeforms.push(newProps[confg.freeform.key])
|
||||
}
|
||||
return matches;
|
||||
})
|
||||
];
|
||||
return matches
|
||||
}),
|
||||
]
|
||||
|
||||
if (tgs !== undefined && confg.freeform) {
|
||||
const unseenFreeformValues = tgs[confg.freeform.key]?.split(";") ?? [];
|
||||
const unseenFreeformValues = tgs[confg.freeform.key]?.split(";") ?? []
|
||||
for (const seenFreeform of seenFreeforms) {
|
||||
if (!seenFreeform) {
|
||||
continue;
|
||||
continue
|
||||
}
|
||||
const index = unseenFreeformValues.indexOf(seenFreeform);
|
||||
const index = unseenFreeformValues.indexOf(seenFreeform)
|
||||
if (index < 0) {
|
||||
continue;
|
||||
continue
|
||||
}
|
||||
unseenFreeformValues.splice(index, 1);
|
||||
unseenFreeformValues.splice(index, 1)
|
||||
}
|
||||
// TODO this has _to much_ values
|
||||
freeformInput.setData(unseenFreeformValues.join(";"));
|
||||
checkedMappings.push(unseenFreeformValues.length > 0);
|
||||
freeformInput.setData(unseenFreeformValues.join(";"))
|
||||
checkedMappings.push(unseenFreeformValues.length > 0)
|
||||
}
|
||||
}
|
||||
if (confg.freeform?.key) {
|
||||
if (!confg.multiAnswer) {
|
||||
// Somehow, setting multi-answer freeform values is broken if this is not set
|
||||
freeformInput.setData(tgs[confg.freeform.key]);
|
||||
freeformInput.setData(tgs[confg.freeform.key])
|
||||
}
|
||||
|
||||
} else {
|
||||
freeformInput.setData(undefined);
|
||||
freeformInput.setData(undefined)
|
||||
}
|
||||
feedback.setData(undefined);
|
||||
feedback.setData(undefined)
|
||||
}
|
||||
|
||||
$: {
|
||||
// Even though 'config' is not declared as a store, Svelte uses it as one to update the component
|
||||
// We want to (re)-initialize whenever the 'tags' or 'config' change - but not when 'checkedConfig' changes
|
||||
initialize($tags, config);
|
||||
initialize($tags, config)
|
||||
}
|
||||
|
||||
$: {
|
||||
|
@ -126,53 +125,52 @@
|
|||
selectedMapping,
|
||||
checkedMappings,
|
||||
tags.data
|
||||
);
|
||||
)
|
||||
} catch (e) {
|
||||
console.error("Could not calculate changeSpecification:", e);
|
||||
selectedTags = undefined;
|
||||
console.error("Could not calculate changeSpecification:", e)
|
||||
selectedTags = undefined
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
function onSave() {
|
||||
if (selectedTags === undefined) {
|
||||
return;
|
||||
return
|
||||
}
|
||||
if (layer === undefined || (layer?.source === null && layer.id !== "favourite")) {
|
||||
/**
|
||||
* This is a special, priviliged layer.
|
||||
* We simply apply the tags onto the records
|
||||
*/
|
||||
const kv = selectedTags.asChange(tags.data);
|
||||
const kv = selectedTags.asChange(tags.data)
|
||||
for (const { k, v } of kv) {
|
||||
if (v === undefined || v === "") {
|
||||
delete tags.data[k];
|
||||
delete tags.data[k]
|
||||
} else {
|
||||
tags.data[k] = v
|
||||
}
|
||||
feedback.setData(undefined);
|
||||
feedback.setData(undefined)
|
||||
}
|
||||
tags.ping()
|
||||
return;
|
||||
return
|
||||
}
|
||||
dispatch("saved", { config, applied: selectedTags });
|
||||
dispatch("saved", { config, applied: selectedTags })
|
||||
const change = new ChangeTagAction(tags.data.id, selectedTags, tags.data, {
|
||||
theme: tags.data["_orig_theme"] ?? state.layout.id,
|
||||
changeType: "answer"
|
||||
});
|
||||
freeformInput.setData(undefined);
|
||||
selectedMapping = undefined;
|
||||
selectedTags = undefined;
|
||||
changeType: "answer",
|
||||
})
|
||||
freeformInput.setData(undefined)
|
||||
selectedMapping = undefined
|
||||
selectedTags = undefined
|
||||
|
||||
change
|
||||
.CreateChangeDescriptions()
|
||||
.then((changes) => state.changes.applyChanges(changes))
|
||||
.catch(console.error);
|
||||
.catch(console.error)
|
||||
}
|
||||
|
||||
function onInputKeypress(e: Event) {
|
||||
if (e.key === "Enter") {
|
||||
onSave();
|
||||
onSave()
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -183,27 +181,26 @@
|
|||
selectedMapping,
|
||||
checkedMappings,
|
||||
tags.data
|
||||
);
|
||||
)
|
||||
} catch (e) {
|
||||
console.error("Could not calculate changeSpecification:", e);
|
||||
selectedTags = undefined;
|
||||
console.error("Could not calculate changeSpecification:", e)
|
||||
selectedTags = undefined
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
let featureSwitchIsTesting = state?.featureSwitchIsTesting ?? new ImmutableStore(false);
|
||||
let featureSwitchIsTesting = state?.featureSwitchIsTesting ?? new ImmutableStore(false)
|
||||
let featureSwitchIsDebugging =
|
||||
state?.featureSwitches?.featureSwitchIsDebugging ?? new ImmutableStore(false);
|
||||
let showTags = state?.userRelatedState?.showTags ?? new ImmutableStore(undefined);
|
||||
let numberOfCs = state?.osmConnection?.userDetails?.data?.csCount ?? 0;
|
||||
let question = config.question;
|
||||
$: question = config.question;
|
||||
state?.featureSwitches?.featureSwitchIsDebugging ?? new ImmutableStore(false)
|
||||
let showTags = state?.userRelatedState?.showTags ?? new ImmutableStore(undefined)
|
||||
let numberOfCs = state?.osmConnection?.userDetails?.data?.csCount ?? 0
|
||||
let question = config.question
|
||||
$: question = config.question
|
||||
if (state?.osmConnection) {
|
||||
onDestroy(
|
||||
state.osmConnection?.userDetails?.addCallbackAndRun((ud) => {
|
||||
numberOfCs = ud.csCount;
|
||||
numberOfCs = ud.csCount
|
||||
})
|
||||
);
|
||||
)
|
||||
}
|
||||
</script>
|
||||
|
||||
|
@ -212,10 +209,10 @@
|
|||
class="interactive border-interactive relative flex flex-col overflow-y-auto px-2"
|
||||
style="max-height: 75vh"
|
||||
>
|
||||
<div class="sticky top-0 interactive pt-1 flex justify-between" style="z-index: 11">
|
||||
<span class="font-bold">
|
||||
<SpecialTranslation t={question} {tags} {state} {layer} feature={selectedElement} />
|
||||
</span>
|
||||
<div class="interactive sticky top-0 flex justify-between pt-1" style="z-index: 11">
|
||||
<span class="font-bold">
|
||||
<SpecialTranslation t={question} {tags} {state} {layer} feature={selectedElement} />
|
||||
</span>
|
||||
<slot name="upper-right" />
|
||||
</div>
|
||||
|
||||
|
@ -268,8 +265,7 @@
|
|||
bind:group={selectedMapping}
|
||||
name={"mappings-radio-" + config.id}
|
||||
value={i}
|
||||
on:keypress={e => onInputKeypress(e)}
|
||||
|
||||
on:keypress={(e) => onInputKeypress(e)}
|
||||
/>
|
||||
</TagRenderingMappingInput>
|
||||
{/each}
|
||||
|
@ -280,7 +276,7 @@
|
|||
bind:group={selectedMapping}
|
||||
name={"mappings-radio-" + config.id}
|
||||
value={config.mappings?.length}
|
||||
on:keypress={e => onInputKeypress(e)}
|
||||
on:keypress={(e) => onInputKeypress(e)}
|
||||
/>
|
||||
<FreeformInput
|
||||
{config}
|
||||
|
@ -312,7 +308,7 @@
|
|||
type="checkbox"
|
||||
name={"mappings-checkbox-" + config.id + "-" + i}
|
||||
bind:checked={checkedMappings[i]}
|
||||
on:keypress={e => onInputKeypress(e)}
|
||||
on:keypress={(e) => onInputKeypress(e)}
|
||||
/>
|
||||
</TagRenderingMappingInput>
|
||||
{/each}
|
||||
|
@ -322,7 +318,7 @@
|
|||
type="checkbox"
|
||||
name={"mappings-checkbox-" + config.id + "-" + config.mappings?.length}
|
||||
bind:checked={checkedMappings[config.mappings.length]}
|
||||
on:keypress={e => onInputKeypress(e)}
|
||||
on:keypress={(e) => onInputKeypress(e)}
|
||||
/>
|
||||
<FreeformInput
|
||||
{config}
|
||||
|
|
|
@ -1,30 +1,28 @@
|
|||
<script lang="ts">
|
||||
import { UIEventSource } from "../../Logic/UIEventSource";
|
||||
import { OsmConnection } from "../../Logic/Osm/OsmConnection";
|
||||
import Marker from "../Map/Marker.svelte";
|
||||
import NextButton from "../Base/NextButton.svelte";
|
||||
import { AllKnownLayouts } from "../../Customizations/AllKnownLayouts";
|
||||
import { AllSharedLayers } from "../../Customizations/AllSharedLayers";
|
||||
import { createEventDispatcher } from "svelte";
|
||||
import { UIEventSource } from "../../Logic/UIEventSource"
|
||||
import { OsmConnection } from "../../Logic/Osm/OsmConnection"
|
||||
import Marker from "../Map/Marker.svelte"
|
||||
import NextButton from "../Base/NextButton.svelte"
|
||||
import { AllKnownLayouts } from "../../Customizations/AllKnownLayouts"
|
||||
import { AllSharedLayers } from "../../Customizations/AllSharedLayers"
|
||||
import { createEventDispatcher } from "svelte"
|
||||
|
||||
export let info: { id: string; owner: number };
|
||||
export let category: "layers" | "themes";
|
||||
export let osmConnection: OsmConnection;
|
||||
const dispatch = createEventDispatcher<{ layerSelected: string }>();
|
||||
export let info: { id: string; owner: number }
|
||||
export let category: "layers" | "themes"
|
||||
export let osmConnection: OsmConnection
|
||||
const dispatch = createEventDispatcher<{ layerSelected: string }>()
|
||||
|
||||
let displayName = UIEventSource.FromPromise(
|
||||
osmConnection.getInformationAboutUser(info.owner)
|
||||
).mapD((response) => response.display_name);
|
||||
let selfId = osmConnection.userDetails.mapD((ud) => ud.uid);
|
||||
|
||||
).mapD((response) => response.display_name)
|
||||
let selfId = osmConnection.userDetails.mapD((ud) => ud.uid)
|
||||
|
||||
function fetchIconDescription(layerId): any {
|
||||
if (category === "themes") {
|
||||
return AllKnownLayouts.allKnownLayouts.get(layerId).icon;
|
||||
return AllKnownLayouts.allKnownLayouts.get(layerId).icon
|
||||
}
|
||||
return AllSharedLayers.getSharedLayersConfigs().get(layerId)?._layerIcon;
|
||||
return AllSharedLayers.getSharedLayersConfigs().get(layerId)?._layerIcon
|
||||
}
|
||||
|
||||
</script>
|
||||
|
||||
<NextButton clss="small" on:click={() => dispatch("layerSelected", info)}>
|
||||
|
@ -39,7 +37,7 @@
|
|||
- {info.owner}
|
||||
{/if}
|
||||
)
|
||||
{:else }
|
||||
{:else}
|
||||
({info.owner})
|
||||
{/if}
|
||||
{/if}
|
||||
|
|
|
@ -74,8 +74,8 @@
|
|||
|
||||
let highlightedItem: UIEventSource<HighlightedTagRendering> = state.highlightedItem
|
||||
function deleteLayer() {
|
||||
state.delete()
|
||||
backToStudio()
|
||||
state.delete()
|
||||
backToStudio()
|
||||
}
|
||||
</script>
|
||||
|
||||
|
@ -119,10 +119,9 @@
|
|||
<div class="flex flex-col" slot="content0">
|
||||
<Region {state} configs={perRegion["Basic"]} />
|
||||
<div class="mt-12">
|
||||
|
||||
<button on:click={() => deleteLayer()} class="small" >
|
||||
<TrashIcon class="h-6 w-6"/> Delete this layer
|
||||
</button>
|
||||
<button on:click={() => deleteLayer()} class="small">
|
||||
<TrashIcon class="h-6 w-6" /> Delete this layer
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
|
|
@ -107,7 +107,7 @@ export abstract class EditJsonState<T> {
|
|||
return entry
|
||||
}
|
||||
|
||||
public async delete(){
|
||||
public async delete() {
|
||||
await this.server.delete(this.getId().data, this.category)
|
||||
}
|
||||
public getStoreFor<T>(path: ReadonlyArray<string | number>): UIEventSource<T | undefined> {
|
||||
|
@ -297,9 +297,8 @@ export default class EditLayerState extends EditJsonState<LayerConfigJson> {
|
|||
|
||||
this.addMissingTagRenderingIds()
|
||||
|
||||
|
||||
function cleanArray(data: object, key: string): boolean{
|
||||
if(!data){
|
||||
function cleanArray(data: object, key: string): boolean {
|
||||
if (!data) {
|
||||
return false
|
||||
}
|
||||
if (data[key]) {
|
||||
|
@ -317,17 +316,17 @@ export default class EditLayerState extends EditJsonState<LayerConfigJson> {
|
|||
this.configuration.addCallbackAndRunD((layer) => {
|
||||
let changed = cleanArray(layer, "tagRenderings") || cleanArray(layer, "pointRenderings")
|
||||
for (const tr of layer.tagRenderings ?? []) {
|
||||
if(typeof tr === "string"){
|
||||
if (typeof tr === "string") {
|
||||
continue
|
||||
}
|
||||
|
||||
const qtr = (<QuestionableTagRenderingConfigJson> tr)
|
||||
if(qtr.freeform && Object.keys(qtr.freeform ).length === 0){
|
||||
const qtr = <QuestionableTagRenderingConfigJson>tr
|
||||
if (qtr.freeform && Object.keys(qtr.freeform).length === 0) {
|
||||
delete qtr.freeform
|
||||
changed = true
|
||||
}
|
||||
}
|
||||
if(changed){
|
||||
if (changed) {
|
||||
this.configuration.ping()
|
||||
}
|
||||
})
|
||||
|
|
|
@ -63,7 +63,7 @@ export default class StudioServer {
|
|||
return
|
||||
}
|
||||
await fetch(this.urlFor(id, category), {
|
||||
method: "DELETE"
|
||||
method: "DELETE",
|
||||
})
|
||||
}
|
||||
async update(id: string, config: string, category: "layers" | "themes") {
|
||||
|
|
|
@ -260,7 +260,12 @@
|
|||
<Loading />
|
||||
</div>
|
||||
{:else if state === "editing_layer"}
|
||||
<EditLayer state={editLayerState} backToStudio={() => {state = undefined}}>
|
||||
<EditLayer
|
||||
state={editLayerState}
|
||||
backToStudio={() => {
|
||||
state = undefined
|
||||
}}
|
||||
>
|
||||
<BackButton
|
||||
clss="small p-1"
|
||||
imageClass="w-8 h-8"
|
||||
|
@ -288,19 +293,18 @@
|
|||
{#if { intro, tagrenderings: intro_tagrenderings }[$showIntro]?.sections}
|
||||
<FloatOver
|
||||
on:close={() => {
|
||||
showIntro.setData("no")
|
||||
}}
|
||||
showIntro.setData("no")
|
||||
}}
|
||||
>
|
||||
<div class="flex h-full p-4 pr-12">
|
||||
<Walkthrough
|
||||
pages={{ intro, tagrenderings: intro_tagrenderings }[$showIntro]?.sections}
|
||||
on:done={() => {
|
||||
showIntro.setData("no")
|
||||
}}
|
||||
showIntro.setData("no")
|
||||
}}
|
||||
/>
|
||||
</div>
|
||||
</FloatOver>
|
||||
{/if}
|
||||
|
||||
</LoginToggle>
|
||||
</If>
|
||||
|
|
|
@ -1,116 +1,122 @@
|
|||
<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, HeartIcon, 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 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 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 MapillaryLink from "./BigComponents/MapillaryLink.svelte";
|
||||
import OpenIdEditor from "./BigComponents/OpenIdEditor.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 Summary from "./BigComponents/Summary.svelte";
|
||||
import LanguagePicker from "./InputElement/LanguagePicker.svelte";
|
||||
import Mastodon from "../assets/svg/Mastodon.svelte";
|
||||
import Bug from "../assets/svg/Bug.svelte";
|
||||
import Liberapay from "../assets/svg/Liberapay.svelte";
|
||||
import OpenJosm from "./Base/OpenJosm.svelte";
|
||||
import Min from "../assets/svg/Min.svelte";
|
||||
import Plus from "../assets/svg/Plus.svelte";
|
||||
import Filter from "../assets/svg/Filter.svelte";
|
||||
import Add from "../assets/svg/Add.svelte";
|
||||
import Statistics from "../assets/svg/Statistics.svelte";
|
||||
import Community from "../assets/svg/Community.svelte";
|
||||
import Download from "../assets/svg/Download.svelte";
|
||||
import Share from "../assets/svg/Share.svelte";
|
||||
import Favourites from "./Favourites/Favourites.svelte";
|
||||
import ImageOperations from "./Image/ImageOperations.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,
|
||||
HeartIcon,
|
||||
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 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 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 MapillaryLink from "./BigComponents/MapillaryLink.svelte"
|
||||
import OpenIdEditor from "./BigComponents/OpenIdEditor.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 Summary from "./BigComponents/Summary.svelte"
|
||||
import LanguagePicker from "./InputElement/LanguagePicker.svelte"
|
||||
import Mastodon from "../assets/svg/Mastodon.svelte"
|
||||
import Bug from "../assets/svg/Bug.svelte"
|
||||
import Liberapay from "../assets/svg/Liberapay.svelte"
|
||||
import OpenJosm from "./Base/OpenJosm.svelte"
|
||||
import Min from "../assets/svg/Min.svelte"
|
||||
import Plus from "../assets/svg/Plus.svelte"
|
||||
import Filter from "../assets/svg/Filter.svelte"
|
||||
import Add from "../assets/svg/Add.svelte"
|
||||
import Statistics from "../assets/svg/Statistics.svelte"
|
||||
import Community from "../assets/svg/Community.svelte"
|
||||
import Download from "../assets/svg/Download.svelte"
|
||||
import Share from "../assets/svg/Share.svelte"
|
||||
import Favourites from "./Favourites/Favourites.svelte"
|
||||
import ImageOperations from "./Image/ImageOperations.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> = new UIEventSource<Feature>(undefined);
|
||||
|
||||
state.selectedElement.addCallback(selected => {
|
||||
if(!selected){
|
||||
let maplibremap: UIEventSource<MlMap> = state.map
|
||||
let selectedElement: UIEventSource<Feature> = new UIEventSource<Feature>(undefined)
|
||||
|
||||
state.selectedElement.addCallback((selected) => {
|
||||
if (!selected) {
|
||||
selectedElement.setData(selected)
|
||||
return
|
||||
}
|
||||
if(selected !== selectedElement.data){
|
||||
if (selected !== selectedElement.data) {
|
||||
// We first set the selected element to 'undefined' to force the popup to close...
|
||||
selectedElement.setData(undefined)
|
||||
}
|
||||
// ... we give svelte some time to update with requestAnimationFrame ...
|
||||
window.requestAnimationFrame(() => {
|
||||
// ... and we force a fresh popup window
|
||||
selectedElement.setData(selected)
|
||||
// ... and we force a fresh popup window
|
||||
selectedElement.setData(selected)
|
||||
})
|
||||
|
||||
})
|
||||
|
||||
let selectedLayer: Store<LayerConfig> = state.selectedElement.mapD(element => state.layout.getMatchingLayer(element.properties));
|
||||
|
||||
let currentZoom = state.mapProperties.zoom;
|
||||
let showCrosshair = state.userRelatedState.showCrosshair;
|
||||
let arrowKeysWereUsed = state.mapProperties.lastKeyNavigation;
|
||||
let centerFeatures = state.closestFeatures.features;
|
||||
|
||||
let selectedLayer: Store<LayerConfig> = state.selectedElement.mapD((element) =>
|
||||
state.layout.getMatchingLayer(element.properties)
|
||||
)
|
||||
|
||||
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 currentZoom = state.mapProperties.zoom
|
||||
let showCrosshair = state.userRelatedState.showCrosshair
|
||||
let arrowKeysWereUsed = state.mapProperties.lastKeyNavigation
|
||||
let centerFeatures = state.closestFeatures.features
|
||||
|
||||
let mapproperties: MapProperties = state.mapProperties
|
||||
let featureSwitches: FeatureSwitchState = state.featureSwitches
|
||||
let availableLayers = state.availableLayers
|
||||
let userdetails = state.osmConnection.userDetails
|
||||
let currentViewLayer = layout.layers.find((l) => l.id === "current_view")
|
||||
let rasterLayer: Store<RasterLayerPolygon> = state.mapProperties.rasterLayer
|
||||
let rasterLayerName =
|
||||
rasterLayer.data?.properties?.name ?? AvailableRasterLayers.maptilerDefaultLayer.properties.name;
|
||||
rasterLayer.data?.properties?.name ?? AvailableRasterLayers.maptilerDefaultLayer.properties.name
|
||||
onDestroy(
|
||||
rasterLayer.addCallbackAndRunD((l) => {
|
||||
rasterLayerName = l.properties.name;
|
||||
rasterLayerName = l.properties.name
|
||||
})
|
||||
);
|
||||
let previewedImage = state.previewedImage;
|
||||
)
|
||||
let previewedImage = state.previewedImage
|
||||
</script>
|
||||
|
||||
<div class="absolute top-0 left-0 h-screen w-screen overflow-hidden">
|
||||
|
@ -211,7 +217,7 @@
|
|||
</div>
|
||||
|
||||
{#if $arrowKeysWereUsed !== undefined && $centerFeatures?.length > 0}
|
||||
<div class="pointer-events-auto interactive p-1">
|
||||
<div class="interactive pointer-events-auto p-1">
|
||||
{#each $centerFeatures as feat, i (feat.properties.id)}
|
||||
<div class="flex">
|
||||
<b>{i + 1}.</b>
|
||||
|
@ -240,7 +246,9 @@
|
|||
<If condition={featureSwitches.featureSwitchGeolocation}>
|
||||
<MapControlButton>
|
||||
<ToSvelte
|
||||
construct={new GeolocationControl(state.geolocation, mapproperties).SetClass("block w-8 h-8")}
|
||||
construct={new GeolocationControl(state.geolocation, mapproperties).SetClass(
|
||||
"block w-8 h-8"
|
||||
)}
|
||||
/>
|
||||
</MapControlButton>
|
||||
</If>
|
||||
|
@ -258,10 +266,10 @@
|
|||
{/if}
|
||||
</LoginToggle>
|
||||
|
||||
<If condition={state.previewedImage.map(i => i!==undefined)}>
|
||||
<If condition={state.previewedImage.map((i) => i !== undefined)}>
|
||||
<FloatOver extraClasses="p-1" on:close={() => state.previewedImage.setData(undefined)}>
|
||||
<div
|
||||
class="absolute right-4 top-4 h-8 w-8 cursor-pointer rounded-full hover:bg-white bg-white/50 transition-colors duration-200"
|
||||
class="absolute right-4 top-4 h-8 w-8 cursor-pointer rounded-full bg-white/50 transition-colors duration-200 hover:bg-white"
|
||||
on:click={() => previewedImage.setData(undefined)}
|
||||
slot="close-button"
|
||||
>
|
||||
|
@ -271,14 +279,14 @@
|
|||
</FloatOver>
|
||||
</If>
|
||||
|
||||
{#if $selectedElement !== undefined && $selectedLayer !== undefined && !($selectedLayer.popupInFloatover)}
|
||||
{#if $selectedElement !== undefined && $selectedLayer !== undefined && !$selectedLayer.popupInFloatover}
|
||||
<!-- right modal with the selected element view -->
|
||||
<ModalRight
|
||||
on:close={() => {
|
||||
selectedElement.setData(undefined)
|
||||
}}
|
||||
>
|
||||
<div slot="close-button"/>
|
||||
<div slot="close-button" />
|
||||
<div class="normal-background absolute flex h-full w-full flex-col">
|
||||
<SelectedElementTitle {state} layer={$selectedLayer} selectedElement={$selectedElement} />
|
||||
<SelectedElementView {state} layer={$selectedLayer} selectedElement={$selectedElement} />
|
||||
|
@ -293,7 +301,7 @@
|
|||
selectedElement.setData(undefined)
|
||||
}}
|
||||
>
|
||||
<div class="h-full w-full flex focusable">
|
||||
<div class="focusable flex h-full w-full">
|
||||
<SelectedElementView {state} layer={$selectedLayer} selectedElement={$selectedElement} />
|
||||
</div>
|
||||
</FloatOver>
|
||||
|
@ -304,7 +312,6 @@
|
|||
<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}
|
||||
>
|
||||
|
@ -381,7 +388,7 @@
|
|||
state.guistate.backgroundLayerSelectionIsOpened.setData(false)
|
||||
}}
|
||||
>
|
||||
<div class="h-full p-2 focusable">
|
||||
<div class="focusable h-full p-2">
|
||||
<RasterLayerOverview
|
||||
{availableLayers}
|
||||
map={state.map}
|
||||
|
@ -474,7 +481,7 @@
|
|||
<Tr t={Translations.t.favouritePoi.tab} />
|
||||
</div>
|
||||
|
||||
<div class="flex flex-col m-2" slot="content2">
|
||||
<div class="m-2 flex flex-col" slot="content2">
|
||||
<h3>
|
||||
<Tr t={Translations.t.favouritePoi.title} />
|
||||
</h3>
|
||||
|
|
|
@ -46,7 +46,7 @@
|
|||
</span>
|
||||
</DisclosureButton>
|
||||
<DisclosurePanel>
|
||||
<FromHtml clss="wikipedia-article" src={$wikipediaDetails.restOfArticle} />
|
||||
<FromHtml clss="wikipedia-article" src={$wikipediaDetails.restOfArticle} />
|
||||
</DisclosurePanel>
|
||||
</Disclosure>
|
||||
{/if}
|
||||
|
|
|
@ -1,11 +1,11 @@
|
|||
{
|
||||
"contributors": [
|
||||
{
|
||||
"commits": 6418,
|
||||
"commits": 6533,
|
||||
"contributor": "Pieter Vander Vennet"
|
||||
},
|
||||
{
|
||||
"commits": 421,
|
||||
"commits": 424,
|
||||
"contributor": "Robin van der Linde"
|
||||
},
|
||||
{
|
||||
|
@ -49,7 +49,7 @@
|
|||
"contributor": "karelleketers"
|
||||
},
|
||||
{
|
||||
"commits": 24,
|
||||
"commits": 25,
|
||||
"contributor": "dependabot[bot]"
|
||||
},
|
||||
{
|
||||
|
@ -116,6 +116,10 @@
|
|||
"commits": 10,
|
||||
"contributor": "LiamSimons"
|
||||
},
|
||||
{
|
||||
"commits": 9,
|
||||
"contributor": "Codain"
|
||||
},
|
||||
{
|
||||
"commits": 9,
|
||||
"contributor": "Midgard"
|
||||
|
@ -124,10 +128,6 @@
|
|||
"commits": 8,
|
||||
"contributor": "pelderson"
|
||||
},
|
||||
{
|
||||
"commits": 8,
|
||||
"contributor": "Codain"
|
||||
},
|
||||
{
|
||||
"commits": 8,
|
||||
"contributor": "Mateusz Konieczny"
|
||||
|
@ -172,6 +172,10 @@
|
|||
"commits": 3,
|
||||
"contributor": "Léo Villeveygoux"
|
||||
},
|
||||
{
|
||||
"commits": 2,
|
||||
"contributor": "eMerzh"
|
||||
},
|
||||
{
|
||||
"commits": 2,
|
||||
"contributor": "bxl-forever"
|
||||
|
@ -276,10 +280,6 @@
|
|||
"commits": 1,
|
||||
"contributor": "Wouter van der Wal"
|
||||
},
|
||||
{
|
||||
"commits": 1,
|
||||
"contributor": "eMerzh"
|
||||
},
|
||||
{
|
||||
"commits": 1,
|
||||
"contributor": "Reiner Herrmann"
|
||||
|
|
|
@ -3,6 +3,7 @@
|
|||
"ca"
|
||||
],
|
||||
"AE": [
|
||||
"en",
|
||||
"ar"
|
||||
],
|
||||
"AF": [
|
||||
|
@ -396,8 +397,8 @@
|
|||
"ro"
|
||||
],
|
||||
"MG": [
|
||||
"fr",
|
||||
"mg"
|
||||
"mg",
|
||||
"fr"
|
||||
],
|
||||
"MH": [
|
||||
"en",
|
||||
|
@ -408,7 +409,8 @@
|
|||
"mk"
|
||||
],
|
||||
"ML": [
|
||||
"fr"
|
||||
"bm",
|
||||
"ff"
|
||||
],
|
||||
"MM": [
|
||||
"my"
|
||||
|
|
|
@ -1449,6 +1449,9 @@
|
|||
"ru": "бамана",
|
||||
"sv": "bambara",
|
||||
"_meta": {
|
||||
"countries": [
|
||||
"ML"
|
||||
],
|
||||
"dir": [
|
||||
"left-to-right",
|
||||
"right-to-left"
|
||||
|
@ -3008,6 +3011,7 @@
|
|||
"zh_Hant": "英語",
|
||||
"_meta": {
|
||||
"countries": [
|
||||
"AE",
|
||||
"AG",
|
||||
"BB",
|
||||
"BI",
|
||||
|
@ -3373,6 +3377,9 @@
|
|||
"zh_Hans": "富拉语",
|
||||
"zh_Hant": "富拉語",
|
||||
"_meta": {
|
||||
"countries": [
|
||||
"ML"
|
||||
],
|
||||
"dir": [
|
||||
"left-to-right",
|
||||
"right-to-left"
|
||||
|
@ -3678,7 +3685,6 @@
|
|||
"LU",
|
||||
"MC",
|
||||
"MG",
|
||||
"ML",
|
||||
"MU",
|
||||
"NE",
|
||||
"RW",
|
||||
|
|
|
@ -1,11 +1,11 @@
|
|||
{
|
||||
"contributors": [
|
||||
{
|
||||
"commits": 319,
|
||||
"commits": 320,
|
||||
"contributor": "kjon"
|
||||
},
|
||||
{
|
||||
"commits": 296,
|
||||
"commits": 298,
|
||||
"contributor": "Pieter Vander Vennet"
|
||||
},
|
||||
{
|
||||
|
@ -296,6 +296,10 @@
|
|||
"commits": 4,
|
||||
"contributor": "Jan Zabel"
|
||||
},
|
||||
{
|
||||
"commits": 3,
|
||||
"contributor": "ssantos"
|
||||
},
|
||||
{
|
||||
"commits": 3,
|
||||
"contributor": "Stéphane De Greef"
|
||||
|
@ -540,10 +544,6 @@
|
|||
"commits": 1,
|
||||
"contributor": "Janina Ellinghaus"
|
||||
},
|
||||
{
|
||||
"commits": 1,
|
||||
"contributor": "ssantos"
|
||||
},
|
||||
{
|
||||
"commits": 1,
|
||||
"contributor": "Andre Fajar N"
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue