forked from MapComplete/MapComplete
Merge develop
This commit is contained in:
commit
d959b6b40b
290 changed files with 37178 additions and 2200 deletions
|
|
@ -1,74 +1,73 @@
|
|||
<script lang="ts">
|
||||
import { OsmConnectionFeatureSwitches } from "../Logic/State/FeatureSwitchState";
|
||||
import { OsmConnection } from "../Logic/Osm/OsmConnection";
|
||||
import { QueryParameters } from "../Logic/Web/QueryParameters";
|
||||
import UserRelatedState from "../Logic/State/UserRelatedState";
|
||||
import LanguagePicker from "./InputElement/LanguagePicker.svelte";
|
||||
import Translations from "./i18n/Translations";
|
||||
import Logo from "../assets/svg/Logo.svelte";
|
||||
import Tr from "./Base/Tr.svelte";
|
||||
import ToSvelte from "./Base/ToSvelte.svelte";
|
||||
import MoreScreen from "./BigComponents/MoreScreen";
|
||||
import LoginToggle from "./Base/LoginToggle.svelte";
|
||||
import Pencil from "../assets/svg/Pencil.svelte";
|
||||
import Login from "../assets/svg/Login.svelte";
|
||||
import Constants from "../Models/Constants";
|
||||
import { OsmConnectionFeatureSwitches } from "../Logic/State/FeatureSwitchState"
|
||||
import { OsmConnection } from "../Logic/Osm/OsmConnection"
|
||||
import { QueryParameters } from "../Logic/Web/QueryParameters"
|
||||
import UserRelatedState from "../Logic/State/UserRelatedState"
|
||||
import LanguagePicker from "./InputElement/LanguagePicker.svelte"
|
||||
import Translations from "./i18n/Translations"
|
||||
import Logo from "../assets/svg/Logo.svelte"
|
||||
import Tr from "./Base/Tr.svelte"
|
||||
import ToSvelte from "./Base/ToSvelte.svelte"
|
||||
import MoreScreen from "./BigComponents/MoreScreen"
|
||||
import LoginToggle from "./Base/LoginToggle.svelte"
|
||||
import Pencil from "../assets/svg/Pencil.svelte"
|
||||
import Login from "../assets/svg/Login.svelte"
|
||||
import Constants from "../Models/Constants"
|
||||
|
||||
const featureSwitches = new OsmConnectionFeatureSwitches();
|
||||
const featureSwitches = new OsmConnectionFeatureSwitches()
|
||||
const osmConnection = new OsmConnection({
|
||||
fakeUser: featureSwitches.featureSwitchFakeUser.data,
|
||||
oauth_token: QueryParameters.GetQueryParameter(
|
||||
"oauth_token",
|
||||
undefined,
|
||||
"Used to complete the login"
|
||||
)
|
||||
});
|
||||
const state = new UserRelatedState(osmConnection);
|
||||
const t = Translations.t.index;
|
||||
let userLanguages = osmConnection.userDetails.map(ud => ud.languages);
|
||||
),
|
||||
})
|
||||
const state = new UserRelatedState(osmConnection)
|
||||
const t = Translations.t.index
|
||||
let userLanguages = osmConnection.userDetails.map((ud) => ud.languages)
|
||||
</script>
|
||||
|
||||
<div class="flex flex-col m-4">
|
||||
<div class="self-end">
|
||||
<LanguagePicker assignTo={state.language} availableLanguages={t.title.SupportedLanguages()}
|
||||
<div class="m-4 flex flex-col">
|
||||
<LanguagePicker clss="self-end" assignTo={state.language} availableLanguages={t.title.SupportedLanguages()}
|
||||
preferredLanguages={userLanguages} />
|
||||
</div>
|
||||
|
||||
<div class="flex mt-4">
|
||||
|
||||
<div class="flex-none m-3">
|
||||
<Logo alt="MapComplete Logo" class="w-12 h-12 sm:h-24 sm:w-24" />
|
||||
<div class="mt-4 flex">
|
||||
<div class="m-3 flex-none">
|
||||
<Logo alt="MapComplete Logo" class="h-12 w-12 sm:h-24 sm:w-24" />
|
||||
</div>
|
||||
|
||||
<div class="flex flex-col">
|
||||
<h1 class="tracking-tight font-extrabold md:text-6xl m-0">
|
||||
<h1 class="m-0 font-extrabold tracking-tight md:text-6xl">
|
||||
<Tr t={t.title} />
|
||||
</h1>
|
||||
<Tr cls="my-4 mr-4 text-base font-semibold sm:text-lg md:mt-5 md:text-xl lg:mx-0"
|
||||
t={Translations.t.index.intro} />
|
||||
|
||||
<Tr
|
||||
cls="my-4 mr-4 text-base font-semibold sm:text-lg md:mt-5 md:text-xl lg:mx-0"
|
||||
t={Translations.t.index.intro}
|
||||
/>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
|
||||
<ToSvelte construct={new MoreScreen(state, true)} />
|
||||
|
||||
<LoginToggle state={state}>
|
||||
<LoginToggle {state}>
|
||||
<div slot="not-logged-in">
|
||||
<button class="w-full" on:click={() => osmConnection.AttemptLogin()}>
|
||||
<Login class="w-6 h-6 mr-2 "/>
|
||||
<Login class="mr-2 h-6 w-6 " />
|
||||
<Tr t={Translations.t.index.logIn} />
|
||||
</button>
|
||||
</div>
|
||||
<a class="w-full h-fit button" href={window.location.protocol + "//" + window.location.host + "/studio.html"}>
|
||||
<Pencil class="w-6 h-6 mr-2" />
|
||||
<Tr t={ Translations.t.general.morescreen.createYourOwnTheme} />
|
||||
<a
|
||||
class="button h-fit w-full"
|
||||
href={window.location.protocol + "//" + window.location.host + "/studio.html"}
|
||||
>
|
||||
<Pencil class="mr-2 h-6 w-6" />
|
||||
<Tr t={Translations.t.general.morescreen.createYourOwnTheme} />
|
||||
</a>
|
||||
</LoginToggle>
|
||||
|
||||
<Tr cls="link-underline" t={Translations.t.general.aboutMapComplete.intro}/>
|
||||
<div class="subtle self-end mb-16">
|
||||
|
||||
<Tr cls="link-underline" t={Translations.t.general.aboutMapComplete.intro} />
|
||||
<div class="subtle mb-16 self-end">
|
||||
v{Constants.vNumber}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
|
|
|||
|
|
@ -5,7 +5,7 @@
|
|||
*/
|
||||
import { Store } from "../../Logic/UIEventSource"
|
||||
import { onDestroy } from "svelte"
|
||||
import Hand from "../../assets/svg/Hand.svelte";
|
||||
import Hand from "../../assets/svg/Hand.svelte"
|
||||
|
||||
let mainElem: HTMLElement
|
||||
export let hideSignal: Store<any>
|
||||
|
|
|
|||
|
|
@ -1,31 +1,31 @@
|
|||
<script lang="ts">
|
||||
import { UIEventSource } from "../../Logic/UIEventSource.js";
|
||||
import { UIEventSource } from "../../Logic/UIEventSource.js"
|
||||
|
||||
export let value: UIEventSource<any>
|
||||
let i: any = value.data
|
||||
let htmlElement : HTMLSelectElement
|
||||
function selectAppropriateValue(){
|
||||
if(!htmlElement){
|
||||
return;
|
||||
let htmlElement: HTMLSelectElement
|
||||
function selectAppropriateValue() {
|
||||
if (!htmlElement) {
|
||||
return
|
||||
}
|
||||
const v = value.data
|
||||
for (let option of htmlElement.getElementsByTagName("option")) {
|
||||
if(option.value === v){
|
||||
if (option.value === v) {
|
||||
option.selected = true
|
||||
return
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
value.addCallbackD(() => selectAppropriateValue())
|
||||
$: {
|
||||
if(htmlElement){
|
||||
if (htmlElement) {
|
||||
selectAppropriateValue()
|
||||
}
|
||||
}
|
||||
export let cls : string = undefined
|
||||
</script>
|
||||
|
||||
<select 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>
|
||||
|
|
|
|||
|
|
@ -6,7 +6,7 @@
|
|||
import Tr from "./Tr.svelte"
|
||||
import { OsmConnection } from "../../Logic/Osm/OsmConnection"
|
||||
import { ImmutableStore, UIEventSource } from "../../Logic/UIEventSource"
|
||||
import Invalid from "../../assets/svg/Invalid.svelte";
|
||||
import Invalid from "../../assets/svg/Invalid.svelte"
|
||||
|
||||
export let state: {
|
||||
osmConnection: OsmConnection
|
||||
|
|
|
|||
|
|
@ -1,13 +1,17 @@
|
|||
<script lang="ts">
|
||||
import { OsmConnection } from "../../Logic/Osm/OsmConnection";
|
||||
import Logout from "../../assets/svg/Logout.svelte";
|
||||
import Translations from "../i18n/Translations";
|
||||
import Tr from "./Tr.svelte";
|
||||
import { OsmConnection } from "../../Logic/Osm/OsmConnection"
|
||||
import Logout from "../../assets/svg/Logout.svelte"
|
||||
import Translations from "../i18n/Translations"
|
||||
import Tr from "./Tr.svelte"
|
||||
|
||||
export let osmConnection: OsmConnection;
|
||||
</script>
|
||||
|
||||
<button on:click={() => {osmConnection.LogOut()}}>
|
||||
<Logout class="w-6 h-6" />
|
||||
<button
|
||||
on:click={() => {
|
||||
state.osmConnection.LogOut()
|
||||
}}
|
||||
>
|
||||
<Logout class="h-6 w-6" />
|
||||
<Tr t={Translations.t.general.logout} />
|
||||
</button>
|
||||
|
|
|
|||
|
|
@ -1,37 +1,37 @@
|
|||
<script lang="ts">
|
||||
import { UIEventSource } from "../../Logic/UIEventSource";
|
||||
import Translations from "../i18n/Translations";
|
||||
import Tr from "./Tr.svelte";
|
||||
import Josm_logo from "../../assets/svg/Josm_logo.svelte";
|
||||
import Constants from "../../Models/Constants";
|
||||
import type { SpecialVisualizationState } from "../SpecialVisualization";
|
||||
import { UIEventSource } from "../../Logic/UIEventSource"
|
||||
import Translations from "../i18n/Translations"
|
||||
import Tr from "./Tr.svelte"
|
||||
import Josm_logo from "../../assets/svg/Josm_logo.svelte"
|
||||
import Constants from "../../Models/Constants"
|
||||
import type { SpecialVisualizationState } from "../SpecialVisualization"
|
||||
|
||||
export let state : SpecialVisualizationState
|
||||
const t = Translations.t.general.attribution;
|
||||
const josmState = new UIEventSource<"OK" | string>(undefined);
|
||||
export let state: SpecialVisualizationState
|
||||
const t = Translations.t.general.attribution
|
||||
const josmState = new UIEventSource<"OK" | string>(undefined)
|
||||
// Reset after 15s
|
||||
josmState.stabilized(15000).addCallbackD(() => josmState.setData(undefined));
|
||||
josmState.stabilized(15000).addCallbackD(() => josmState.setData(undefined))
|
||||
|
||||
const showButton = state.osmConnection.userDetails.map(
|
||||
(ud) => ud.loggedIn && ud.csCount >= Constants.userJourney.historyLinkVisible
|
||||
);
|
||||
)
|
||||
|
||||
function openJosm() {
|
||||
const bbox = state.mapProperties. bounds.data;
|
||||
const bbox = state.mapProperties.bounds.data
|
||||
if (bbox === undefined) {
|
||||
return;
|
||||
return
|
||||
}
|
||||
const top = bbox.getNorth();
|
||||
const bottom = bbox.getSouth();
|
||||
const right = bbox.getEast();
|
||||
const left = bbox.getWest();
|
||||
const josmLink = `http://127.0.0.1:8111/load_and_zoom?left=${left}&right=${right}&top=${top}&bottom=${bottom}`;
|
||||
const top = bbox.getNorth()
|
||||
const bottom = bbox.getSouth()
|
||||
const right = bbox.getEast()
|
||||
const left = bbox.getWest()
|
||||
const josmLink = `http://127.0.0.1:8111/load_and_zoom?left=${left}&right=${right}&top=${top}&bottom=${bottom}`
|
||||
Utils.download(josmLink)
|
||||
.then((answer) => josmState.setData(answer.replace(/\n/g, "").trim()))
|
||||
.catch(() => josmState.setData("ERROR"));
|
||||
.catch(() => josmState.setData("ERROR"))
|
||||
}
|
||||
|
||||
</script>
|
||||
|
||||
{#if $showButton}
|
||||
{#if $josmState === undefined}
|
||||
<!-- empty -->
|
||||
|
|
|
|||
|
|
@ -1,7 +1,7 @@
|
|||
<script lang="ts">
|
||||
import ToSvelte from "./ToSvelte.svelte"
|
||||
import Svg from "../../Svg"
|
||||
import Share from "../../assets/svg/Share.svelte";
|
||||
import Share from "../../assets/svg/Share.svelte"
|
||||
|
||||
export let generateShareData: () => {
|
||||
text: string
|
||||
|
|
@ -26,6 +26,6 @@
|
|||
|
||||
<button on:click={share} class="secondary m-0 h-8 w-8 p-0">
|
||||
<slot name="content">
|
||||
<Share class="w-7 h-7 p-1"/>
|
||||
<Share class="h-7 w-7 p-1" />
|
||||
</slot>
|
||||
</button>
|
||||
|
|
|
|||
|
|
@ -1,7 +1,7 @@
|
|||
<script lang="ts">
|
||||
import Locale from "../i18n/Locale"
|
||||
import LinkToWeblate from "./LinkToWeblate"
|
||||
import Translate from "../../assets/svg/Translate.svelte";
|
||||
import Translate from "../../assets/svg/Translate.svelte"
|
||||
|
||||
/**
|
||||
* Shows a small icon which will open up weblate; a contributor can translate the item for 'context' there
|
||||
|
|
|
|||
|
|
@ -1,84 +1,99 @@
|
|||
<script lang="ts">
|
||||
import { Store, UIEventSource } from "../../Logic/UIEventSource"
|
||||
import type { SpecialVisualizationState } from "../SpecialVisualization"
|
||||
import { Utils } from "../../Utils"
|
||||
import Loading from "../../assets/svg/Loading.svelte"
|
||||
|
||||
import { Store, UIEventSource } from "../../Logic/UIEventSource";
|
||||
import type { SpecialVisualizationState } from "../SpecialVisualization";
|
||||
import { Utils } from "../../Utils";
|
||||
import Loading from "../../assets/svg/Loading.svelte";
|
||||
export let tags: Store<Record<string, string>>
|
||||
export let giggityUrl: string
|
||||
export let state: SpecialVisualizationState
|
||||
|
||||
export let tags: Store<Record<string, string>>;
|
||||
export let giggityUrl: string;
|
||||
export let state: SpecialVisualizationState;
|
||||
|
||||
let name = $tags["name"];
|
||||
let events: UIEventSource<{
|
||||
date: Date,
|
||||
start: string,
|
||||
duration: string,
|
||||
room: string,
|
||||
slug: string,
|
||||
url: string,
|
||||
title: string,
|
||||
track: string,
|
||||
type: string,
|
||||
language: string,
|
||||
abstract: string,
|
||||
description: string,
|
||||
persons: string,
|
||||
} []> = new UIEventSource(undefined);
|
||||
let name = $tags["name"]
|
||||
let events: UIEventSource<
|
||||
{
|
||||
date: Date
|
||||
start: string
|
||||
duration: string
|
||||
room: string
|
||||
slug: string
|
||||
url: string
|
||||
title: string
|
||||
track: string
|
||||
type: string
|
||||
language: string
|
||||
abstract: string
|
||||
description: string
|
||||
persons: string
|
||||
}[]
|
||||
> = new UIEventSource(undefined)
|
||||
|
||||
async function loadXml() {
|
||||
if (!name) {
|
||||
console.log("Not fetching giggity events as name is", name, tags);
|
||||
return;
|
||||
console.log("Not fetching giggity events as name is", name, tags)
|
||||
return
|
||||
}
|
||||
const xmlStr = await Utils.downloadAdvanced(giggityUrl);
|
||||
console.log("Raw xml", xmlStr);
|
||||
const parser = new DOMParser();
|
||||
let doc = parser.parseFromString(xmlStr.content, "application/xml");
|
||||
let days = Array.from(doc.documentElement.getElementsByTagName("day"));
|
||||
let today = new Date().toISOString().split("T")[0];
|
||||
const eventsToday = days.find(day => day.getAttribute("date") === today);
|
||||
console.log("Events today", eventsToday);
|
||||
const childs = ["date", "start", "duration", "room", "slug", "url", "title", "track", "type", "language", "abstract", "description", "persons"];
|
||||
const xmlStr = await Utils.downloadAdvanced(giggityUrl)
|
||||
console.log("Raw xml", xmlStr)
|
||||
const parser = new DOMParser()
|
||||
let doc = parser.parseFromString(xmlStr.content, "application/xml")
|
||||
let days = Array.from(doc.documentElement.getElementsByTagName("day"))
|
||||
let today = new Date().toISOString().split("T")[0]
|
||||
const eventsToday = days.find((day) => day.getAttribute("date") === today)
|
||||
console.log("Events today", eventsToday)
|
||||
const childs = [
|
||||
"date",
|
||||
"start",
|
||||
"duration",
|
||||
"room",
|
||||
"slug",
|
||||
"url",
|
||||
"title",
|
||||
"track",
|
||||
"type",
|
||||
"language",
|
||||
"abstract",
|
||||
"description",
|
||||
"persons",
|
||||
]
|
||||
|
||||
const now = new Date().toISOString().split("T")[1].substring(0, 5)
|
||||
let eventsList = [];
|
||||
let eventsList = []
|
||||
for (const eventXml of Array.from(eventsToday.getElementsByTagName("event"))) {
|
||||
const event: Record<string, string> = {};
|
||||
const event: Record<string, string> = {}
|
||||
for (const child of childs) {
|
||||
const v = Array.from(eventXml.getElementsByTagName(child)).map(xml => xml.textContent).join("; ");
|
||||
event[child] = v;
|
||||
const v = Array.from(eventXml.getElementsByTagName(child))
|
||||
.map((xml) => xml.textContent)
|
||||
.join("; ")
|
||||
event[child] = v
|
||||
}
|
||||
if(!name.startsWith(event.room)){
|
||||
if (!name.startsWith(event.room)) {
|
||||
continue
|
||||
}
|
||||
if(now > event.start){
|
||||
if (now > event.start) {
|
||||
continue
|
||||
}
|
||||
eventsList.push(event);
|
||||
eventsList.push(event)
|
||||
}
|
||||
events.setData(eventsList);
|
||||
events.setData(eventsList)
|
||||
}
|
||||
|
||||
loadXml();
|
||||
|
||||
loadXml()
|
||||
</script>
|
||||
|
||||
{#if $events === undefined}
|
||||
<Loading class="h-4">Loading giggity events from {giggityUrl}</Loading>
|
||||
{:else if $events.length === 0}
|
||||
{:else if $events.length === 0}
|
||||
<i>No upcoming events in this room</i>
|
||||
{:else}
|
||||
<div>
|
||||
<h2>Upcoming events</h2>
|
||||
{#each $events as event}
|
||||
<div class="flex flex-col m-2 border border-gray-200 border-dotted">
|
||||
<div class="m-2 flex flex-col border border-dotted border-gray-200">
|
||||
{#if event.url}
|
||||
<h3><a href={event.url} target="_blank">{event.title}</a></h3>
|
||||
|
||||
{:else }
|
||||
<h3>{event.title}</h3>
|
||||
{/if}
|
||||
{:else}
|
||||
<h3>{event.title}</h3>
|
||||
{/if}
|
||||
<div><b>{event.start}</b></div>
|
||||
<i>By {event.persons}</i>
|
||||
<div>
|
||||
|
|
|
|||
|
|
@ -1,14 +1,13 @@
|
|||
<script lang="ts">
|
||||
import Translations from "../i18n/Translations"
|
||||
import Svg from "../../Svg"
|
||||
import { Store } from "../../Logic/UIEventSource"
|
||||
import Tr from "../Base/Tr.svelte"
|
||||
import ToSvelte from "../Base/ToSvelte.svelte"
|
||||
import Mapillary_black from "../../assets/svg/Mapillary_black.svelte";
|
||||
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
|
||||
*/
|
||||
/*
|
||||
A subtleButton which opens mapillary in a new tab at the current location
|
||||
*/
|
||||
|
||||
export let mapProperties: {
|
||||
readonly zoom: Store<number>
|
||||
|
|
@ -16,13 +15,11 @@
|
|||
}
|
||||
let location = mapProperties.location
|
||||
let zoom = mapProperties.zoom
|
||||
let mapillaryLink = `https://www.mapillary.com/app/?focus=map&lat=${$location?.lat ?? 0}&lng=${
|
||||
$location?.lon ?? 0
|
||||
}&z=${Math.max(($zoom ?? 2) - 1, 1)}`
|
||||
let mapillaryLink = Mapillary.createLink($location, $zoom)
|
||||
</script>
|
||||
|
||||
<a class="button flex items-center" href={mapillaryLink} target="_blank">
|
||||
<Mapillary_black class="w-12 h-12 m-2 mr-4 shrink-0"/>
|
||||
<Mapillary_black class="m-2 mr-4 h-12 w-12 shrink-0" />
|
||||
<div class="flex flex-col">
|
||||
<Tr t={Translations.t.general.attribution.openMapillary} />
|
||||
<Tr cls="subtle" t={Translations.t.general.attribution.mapillaryHelp} />
|
||||
|
|
|
|||
|
|
@ -118,7 +118,7 @@ export default class MoreScreen extends Combine {
|
|||
if (search === undefined) {
|
||||
return true
|
||||
}
|
||||
search = search.toLocaleLowerCase()
|
||||
search = Utils.RemoveDiacritics(search.toLocaleLowerCase())
|
||||
if (search.length > 3 && layout.id.toLowerCase().indexOf(search) >= 0) {
|
||||
return true
|
||||
}
|
||||
|
|
@ -131,7 +131,7 @@ export default class MoreScreen extends Combine {
|
|||
continue
|
||||
}
|
||||
const term = entity["*"] ?? entity[Locale.language.data]
|
||||
if (term?.toLowerCase()?.indexOf(search) >= 0) {
|
||||
if (Utils.RemoveDiacritics(term?.toLowerCase())?.indexOf(search) >= 0) {
|
||||
return true
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -16,7 +16,7 @@
|
|||
import LayerConfig from "../../Models/ThemeConfig/LayerConfig"
|
||||
import { Utils } from "../../Utils"
|
||||
import { createEventDispatcher } from "svelte"
|
||||
import Move_arrows from "../../assets/svg/Move_arrows.svelte";
|
||||
import Move_arrows from "../../assets/svg/Move_arrows.svelte"
|
||||
|
||||
/**
|
||||
* An advanced location input, which has support to:
|
||||
|
|
@ -126,6 +126,6 @@
|
|||
maxDistanceInMeters="50"
|
||||
>
|
||||
<slot name="image" slot="image">
|
||||
<Move_arrows class="h-full max-h-24" />
|
||||
<Move_arrows class="h-full max-h-24" />
|
||||
</slot>
|
||||
</LocationInput>
|
||||
|
|
|
|||
|
|
@ -38,7 +38,6 @@
|
|||
<div class="flex flex-col">
|
||||
<!-- Title element-->
|
||||
<h3>
|
||||
|
||||
<TagRenderingAnswer config={layer.title} {selectedElement} {state} {tags} {layer} />
|
||||
</h3>
|
||||
<div
|
||||
|
|
|
|||
|
|
@ -20,15 +20,20 @@
|
|||
_metatags = tags
|
||||
})
|
||||
)
|
||||
|
||||
|
||||
let knownTagRenderings = layer.tagRenderings
|
||||
.filter(config => (config.condition?.matchesProperties($tags) ?? true) && (config.metacondition?.matchesProperties({ ...$tags, ..._metatags } ?? true)
|
||||
&& config.IsKnown($tags)))
|
||||
|
||||
let knownTagRenderings = layer.tagRenderings.filter(
|
||||
(config) =>
|
||||
(config.condition?.matchesProperties($tags) ?? true) &&
|
||||
config.metacondition?.matchesProperties({ ...$tags, ..._metatags } ?? true) &&
|
||||
config.IsKnown($tags)
|
||||
)
|
||||
$: {
|
||||
knownTagRenderings = layer.tagRenderings
|
||||
.filter(config => (config.condition?.matchesProperties($tags) ?? true) && (config.metacondition?.matchesProperties({ ...$tags, ..._metatags } ?? true)
|
||||
&& config.IsKnown($tags)))
|
||||
knownTagRenderings = layer.tagRenderings.filter(
|
||||
(config) =>
|
||||
(config.condition?.matchesProperties($tags) ?? true) &&
|
||||
config.metacondition?.matchesProperties({ ...$tags, ..._metatags } ?? true) &&
|
||||
config.IsKnown($tags)
|
||||
)
|
||||
}
|
||||
</script>
|
||||
|
||||
|
|
@ -40,15 +45,15 @@
|
|||
{:else}
|
||||
<div class="flex h-full flex-col gap-y-2 overflow-y-auto p-1 px-2">
|
||||
{#each knownTagRenderings as config (config.id)}
|
||||
<TagRenderingEditable
|
||||
{tags}
|
||||
{config}
|
||||
{state}
|
||||
{selectedElement}
|
||||
{layer}
|
||||
{highlightedRendering}
|
||||
clss={knownTagRenderings.length === 1 ? "h-full" : "tr-length-"+knownTagRenderings.length}
|
||||
/>
|
||||
<TagRenderingEditable
|
||||
{tags}
|
||||
{config}
|
||||
{state}
|
||||
{selectedElement}
|
||||
{layer}
|
||||
{highlightedRendering}
|
||||
clss={knownTagRenderings.length === 1 ? "h-full" : "tr-length-" + knownTagRenderings.length}
|
||||
/>
|
||||
{/each}
|
||||
</div>
|
||||
{/if}
|
||||
|
|
|
|||
|
|
@ -1,16 +1,15 @@
|
|||
<script lang="ts">
|
||||
import type { Feature } from "geojson";
|
||||
import type { SpecialVisualizationState } from "../SpecialVisualization";
|
||||
import SelectedElementTitle from "./SelectedElementTitle.svelte";
|
||||
import LayerConfig from "../../Models/ThemeConfig/LayerConfig";
|
||||
import TagRenderingAnswer from "../Popup/TagRendering/TagRenderingAnswer.svelte";
|
||||
import type { Feature } from "geojson"
|
||||
import type { SpecialVisualizationState } from "../SpecialVisualization"
|
||||
import SelectedElementTitle from "./SelectedElementTitle.svelte"
|
||||
import LayerConfig from "../../Models/ThemeConfig/LayerConfig"
|
||||
import TagRenderingAnswer from "../Popup/TagRendering/TagRenderingAnswer.svelte"
|
||||
|
||||
export let state: SpecialVisualizationState;
|
||||
export let state: SpecialVisualizationState
|
||||
export let feature: Feature
|
||||
let id = feature.properties.id
|
||||
let tags = state.featureProperties.getStore(id);
|
||||
let tags = state.featureProperties.getStore(id)
|
||||
let layer: LayerConfig = state.layout.getMatchingLayer(tags.data)
|
||||
|
||||
</script>
|
||||
<TagRenderingAnswer config={layer.title} selectedElement={feature} {state} {tags} {layer} />
|
||||
|
||||
<TagRenderingAnswer config={layer.title} selectedElement={feature} {state} {tags} {layer} />
|
||||
|
|
|
|||
|
|
@ -87,10 +87,10 @@
|
|||
|
||||
{#if theme.id !== personal.id || $unlockedPersonal}
|
||||
<SubtleLink href={$href} options={{ extraClasses: "w-full" }}>
|
||||
<img slot="image" src={theme.icon} class="mr-2 m-1 sm:mr-4 sm:m-2 block h-11 w-11" alt="" />
|
||||
<img slot="image" src={theme.icon} class="m-1 mr-2 block h-11 w-11 sm:m-2 sm:mr-4" alt="" />
|
||||
<span class="flex flex-col overflow-hidden text-ellipsis">
|
||||
<Tr t={title} />
|
||||
|
||||
|
||||
{#if selected}
|
||||
<span class="alert">
|
||||
<Tr t={Translations.t.general.morescreen.enterToOpen} />
|
||||
|
|
|
|||
|
|
@ -1,21 +1,21 @@
|
|||
<script lang="ts">
|
||||
import Translations from "../i18n/Translations";
|
||||
import Tr from "../Base/Tr.svelte";
|
||||
import NextButton from "../Base/NextButton.svelte";
|
||||
import Geosearch from "./Geosearch.svelte";
|
||||
import ThemeViewState from "../../Models/ThemeViewState";
|
||||
import { UIEventSource } from "../../Logic/UIEventSource";
|
||||
import { SearchIcon } from "@rgossiaux/svelte-heroicons/solid";
|
||||
import { twJoin } from "tailwind-merge";
|
||||
import { Utils } from "../../Utils";
|
||||
import type { GeolocationPermissionState } from "../../Logic/State/GeoLocationState";
|
||||
import { GeoLocationState } from "../../Logic/State/GeoLocationState";
|
||||
import If from "../Base/If.svelte";
|
||||
import { ExclamationTriangleIcon } from "@babeard/svelte-heroicons/mini";
|
||||
import type { Readable } from "svelte/store";
|
||||
import Add from "../../assets/svg/Add.svelte";
|
||||
import Location_refused from "../../assets/svg/Location_refused.svelte";
|
||||
import Crosshair from "../../assets/svg/Crosshair.svelte";
|
||||
import Translations from "../i18n/Translations"
|
||||
import Tr from "../Base/Tr.svelte"
|
||||
import NextButton from "../Base/NextButton.svelte"
|
||||
import Geosearch from "./Geosearch.svelte"
|
||||
import ThemeViewState from "../../Models/ThemeViewState"
|
||||
import { UIEventSource } from "../../Logic/UIEventSource"
|
||||
import { SearchIcon } from "@rgossiaux/svelte-heroicons/solid"
|
||||
import { twJoin } from "tailwind-merge"
|
||||
import { Utils } from "../../Utils"
|
||||
import type { GeolocationPermissionState } from "../../Logic/State/GeoLocationState"
|
||||
import { GeoLocationState } from "../../Logic/State/GeoLocationState"
|
||||
import If from "../Base/If.svelte"
|
||||
import { ExclamationTriangleIcon } from "@babeard/svelte-heroicons/mini"
|
||||
import type { Readable } from "svelte/store"
|
||||
import Add from "../../assets/svg/Add.svelte"
|
||||
import Location_refused from "../../assets/svg/Location_refused.svelte"
|
||||
import Crosshair from "../../assets/svg/Crosshair.svelte"
|
||||
|
||||
/**
|
||||
* The theme introduction panel
|
||||
|
|
@ -71,7 +71,7 @@
|
|||
<If condition={state.featureSwitches.featureSwitchGeolocation}>
|
||||
{#if $currentGPSLocation !== undefined || $geopermission === "prompt"}
|
||||
<button class="flex w-full items-center gap-x-2" on:click={jumpToCurrentLocation}>
|
||||
<Crosshair class="w-8 h-8"/>
|
||||
<Crosshair class="h-8 w-8" />
|
||||
<Tr t={Translations.t.general.openTheMapAtGeolocation} />
|
||||
</button>
|
||||
<!-- No geolocation granted - we don't show the button -->
|
||||
|
|
@ -81,17 +81,23 @@
|
|||
on:click={jumpToCurrentLocation}
|
||||
>
|
||||
<!-- Even though disabled, when clicking we request the location again in case the contributor dismissed the location popup -->
|
||||
<Crosshair class="w-8 h-8" style="animation: 3s linear 0s infinite normal none running spin;" />
|
||||
<Crosshair
|
||||
class="h-8 w-8"
|
||||
style="animation: 3s linear 0s infinite normal none running spin;"
|
||||
/>
|
||||
<Tr t={Translations.t.general.waitingForGeopermission} />
|
||||
</button>
|
||||
{:else if $geopermission === "denied"}
|
||||
<button class="disabled flex w-full items-center gap-x-2">
|
||||
<Location_refused class="w-8 h-8"/>
|
||||
<Location_refused class="h-8 w-8" />
|
||||
<Tr t={Translations.t.general.geopermissionDenied} />
|
||||
</button>
|
||||
{:else}
|
||||
<button class="disabled flex w-full items-center gap-x-2">
|
||||
<Crosshair class="w-8 h-8" style="animation: 3s linear 0s infinite normal none running spin;" />
|
||||
<Crosshair
|
||||
class="h-8 w-8"
|
||||
style="animation: 3s linear 0s infinite normal none running spin;"
|
||||
/>
|
||||
<Tr t={Translations.t.general.waitingForLocation} />
|
||||
</button>
|
||||
{/if}
|
||||
|
|
@ -149,7 +155,7 @@
|
|||
<div class="links-as-button links-w-full m-2 flex flex-col gap-y-1">
|
||||
<!-- bottom buttons, a bit hidden away: switch layout -->
|
||||
<a class="flex" href={Utils.HomepageLink()}>
|
||||
<Add class="h-6 w-6"/>
|
||||
<Add class="h-6 w-6" />
|
||||
<Tr t={Translations.t.general.backToIndex} />
|
||||
</a>
|
||||
</div>
|
||||
|
|
|
|||
|
|
@ -71,7 +71,6 @@
|
|||
gpsLayer.isDisplayed.setData(gpsIsDisplayed)
|
||||
state.userRelatedState.preferencesAsTags.data["__showTimeSensitiveIcons"] = "yes"
|
||||
state.userRelatedState.preferencesAsTags.ping()
|
||||
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
|
|
|||
|
|
@ -11,7 +11,7 @@
|
|||
import Locale from "../i18n/Locale"
|
||||
import { UIEventSource } from "../../Logic/UIEventSource"
|
||||
import DownloadHelper from "./DownloadHelper"
|
||||
import Qr from "../../Utils/Qr";
|
||||
import Qr from "../../Utils/Qr"
|
||||
|
||||
export let templateName: string
|
||||
export let state: ThemeViewState
|
||||
|
|
@ -31,11 +31,11 @@
|
|||
freeComponentId: "belowmap",
|
||||
createImage: (key: string, width: string, height: string) => {
|
||||
console.log("Creating an image for key", key)
|
||||
if(key === "qr"){
|
||||
if (key === "qr") {
|
||||
const toShare = window.location.href.split("#")[0]
|
||||
return new Qr(toShare).toImageElement(parseFloat(width), parseFloat(height))
|
||||
}
|
||||
return downloadHelper.createImage(key, width, height);
|
||||
return downloadHelper.createImage(key, width, height)
|
||||
},
|
||||
textSubstitutions: <Record<string, string>>{
|
||||
"layout.title": state.layout.title,
|
||||
|
|
@ -62,7 +62,10 @@
|
|||
extension="pdf"
|
||||
helperText={t.downloadAsPdfHelper}
|
||||
metaIsIncluded={false}
|
||||
mainText={t.pdf.current_view_generic.Subs({orientation: template.orientation, paper_size: template.format.toUpperCase()})}
|
||||
mainText={t.pdf.current_view_generic.Subs({
|
||||
orientation: template.orientation,
|
||||
paper_size: template.format.toUpperCase(),
|
||||
})}
|
||||
mimetype="application/pdf"
|
||||
{state}
|
||||
/>
|
||||
|
|
|
|||
|
|
@ -5,21 +5,37 @@ import ImageProvider from "../../Logic/ImageProviders/ImageProvider"
|
|||
import BaseUIElement from "../BaseUIElement"
|
||||
import { Mapillary } from "../../Logic/ImageProviders/Mapillary"
|
||||
import { UIEventSource } from "../../Logic/UIEventSource"
|
||||
import { Feature } from "geojson"
|
||||
import { GeoOperations } from "../../Logic/GeoOperations"
|
||||
|
||||
export class AttributedImage extends Combine {
|
||||
constructor(imageInfo: { url: string; provider?: ImageProvider; date?: Date }) {
|
||||
constructor(imageInfo: {
|
||||
id: string,
|
||||
url: string;
|
||||
provider?: ImageProvider;
|
||||
date?: Date
|
||||
}, feature?: Feature) {
|
||||
let img: BaseUIElement
|
||||
img = new Img(imageInfo.url, false, {
|
||||
fallbackImage:
|
||||
imageInfo.provider === Mapillary.singleton ? "./assets/svg/blocked.svg" : undefined,
|
||||
})
|
||||
|
||||
let location: {
|
||||
lon: number,
|
||||
lat: number
|
||||
} = undefined
|
||||
if (feature) {
|
||||
|
||||
const [lon, lat] = GeoOperations.centerpointCoordinates(feature)
|
||||
location = { lon, lat }
|
||||
}
|
||||
let attr: BaseUIElement = undefined
|
||||
if (imageInfo.provider !== undefined) {
|
||||
attr = new Attribution(
|
||||
UIEventSource.FromPromise(imageInfo.provider?.DownloadAttribution(imageInfo.url)),
|
||||
imageInfo.provider?.SourceIcon(),
|
||||
imageInfo.date
|
||||
imageInfo.provider?.SourceIcon(imageInfo.id, location),
|
||||
imageInfo.date,
|
||||
)
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -28,6 +28,7 @@ export default class Attribution extends VariableUiElement {
|
|||
title = new Link(title, license.informationLocation.href, true)
|
||||
}
|
||||
}
|
||||
|
||||
return new Combine([
|
||||
icon
|
||||
?.SetClass("block left")
|
||||
|
|
|
|||
|
|
@ -9,19 +9,21 @@ import ImageProvider from "../../Logic/ImageProviders/ImageProvider"
|
|||
import { OsmConnection } from "../../Logic/Osm/OsmConnection"
|
||||
import { Changes } from "../../Logic/Osm/Changes"
|
||||
import LayoutConfig from "../../Models/ThemeConfig/LayoutConfig"
|
||||
import { Feature } from "geojson"
|
||||
|
||||
export class ImageCarousel extends Toggle {
|
||||
constructor(
|
||||
images: Store<{ key: string; url: string; provider: ImageProvider }[]>,
|
||||
images: Store<{ id:string, key: string; url: string; provider: ImageProvider }[]>,
|
||||
tags: Store<any>,
|
||||
state: { osmConnection?: OsmConnection; changes?: Changes; layout: LayoutConfig }
|
||||
state: { osmConnection?: OsmConnection; changes?: Changes; layout: LayoutConfig },
|
||||
feature: Feature
|
||||
) {
|
||||
const uiElements = images.map(
|
||||
(imageURLS: { key: string; url: string; provider: ImageProvider }[]) => {
|
||||
(imageURLS: { key: string; url: string; provider: ImageProvider, id: string }[]) => {
|
||||
const uiElements: BaseUIElement[] = []
|
||||
for (const url of imageURLS) {
|
||||
try {
|
||||
let image = new AttributedImage(url)
|
||||
let image = new AttributedImage(url, feature)
|
||||
|
||||
if (url.key !== undefined) {
|
||||
image = new Combine([
|
||||
|
|
|
|||
|
|
@ -3,15 +3,15 @@
|
|||
* 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 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"
|
||||
|
||||
export let state: SpecialVisualizationState
|
||||
|
||||
|
|
@ -57,7 +57,7 @@
|
|||
{#if image !== undefined}
|
||||
<img src={image} />
|
||||
{:else}
|
||||
<Camera_plus class="block w-12 h-12 p-1 text-4xl"/>
|
||||
<Camera_plus class="block h-12 w-12 p-1 text-4xl" />
|
||||
{/if}
|
||||
{#if labelText}
|
||||
{labelText}
|
||||
|
|
|
|||
|
|
@ -6,7 +6,7 @@
|
|||
import MaplibreMap from "../../Map/MaplibreMap.svelte"
|
||||
import ToSvelte from "../../Base/ToSvelte.svelte"
|
||||
import Svg from "../../../Svg.js"
|
||||
import Direction_stroke from "../../../assets/svg/Direction_stroke.svelte";
|
||||
import Direction_stroke from "../../../assets/svg/Direction_stroke.svelte"
|
||||
|
||||
/**
|
||||
* A visualisation to pick a direction on a map background.
|
||||
|
|
@ -68,6 +68,6 @@
|
|||
</div>
|
||||
|
||||
<div bind:this={directionElem} class="absolute top-0 left-0 h-full w-full">
|
||||
<Direction_stroke/>
|
||||
<Direction_stroke />
|
||||
</div>
|
||||
</div>
|
||||
|
|
|
|||
|
|
@ -12,7 +12,7 @@
|
|||
import * as turf from "@turf/turf"
|
||||
import LayerConfig from "../../../Models/ThemeConfig/LayerConfig"
|
||||
import { createEventDispatcher, onDestroy } from "svelte"
|
||||
import Move_arrows from "../../../assets/svg/Move_arrows.svelte";
|
||||
import Move_arrows from "../../../assets/svg/Move_arrows.svelte"
|
||||
|
||||
/**
|
||||
* A visualisation to pick a location on a map background
|
||||
|
|
@ -91,7 +91,7 @@
|
|||
class="pointer-events-none absolute top-0 left-0 flex h-full w-full items-center p-8 opacity-50"
|
||||
>
|
||||
<slot name="image">
|
||||
<Move_arrows class="h-full max-h-24"/>
|
||||
<Move_arrows class="h-full max-h-24" />
|
||||
</slot>
|
||||
</div>
|
||||
|
||||
|
|
|
|||
|
|
@ -1,45 +1,44 @@
|
|||
<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 { 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
|
||||
*/
|
||||
export let availableLanguages: string[] = Object.keys(native);
|
||||
export let availableLanguages: string[] = Object.keys(native)
|
||||
/**
|
||||
* EventStore to assign to, defaults to 'Locale.langauge'
|
||||
*/
|
||||
export let assignTo: UIEventSource<string> = Locale.language;
|
||||
export let preferredLanguages: UIEventSource<string[]> = undefined;
|
||||
let preferredFiltered: string[] = undefined;
|
||||
preferredLanguages?.addCallbackAndRunD(preferredLanguages => {
|
||||
let lng = navigator.language;
|
||||
export let assignTo: UIEventSource<string> = Locale.language
|
||||
export let preferredLanguages: UIEventSource<string[]> = undefined
|
||||
let preferredFiltered: string[] = undefined
|
||||
preferredLanguages?.addCallbackAndRunD((preferredLanguages) => {
|
||||
let lng = navigator.language
|
||||
if (lng === "en-US") {
|
||||
lng = "en";
|
||||
lng = "en"
|
||||
}
|
||||
if (preferredLanguages?.indexOf(lng) < 0) {
|
||||
preferredLanguages?.push(lng);
|
||||
preferredLanguages?.push(lng)
|
||||
}
|
||||
preferredFiltered = preferredLanguages?.filter(l => availableLanguages.indexOf(l) >= 0);
|
||||
});
|
||||
|
||||
let current = Locale.language;
|
||||
|
||||
preferredFiltered = preferredLanguages?.filter((l) => availableLanguages.indexOf(l) >= 0)
|
||||
})
|
||||
export let clss : string = undefined
|
||||
let current = Locale.language
|
||||
</script>
|
||||
|
||||
{#if availableLanguages?.length > 1}
|
||||
<form class="flex items-center">
|
||||
|
||||
<LanguageIcon class="h-4 w-4 mr-1" />
|
||||
<Dropdown value={assignTo}>
|
||||
<form class={twMerge("flex items-center max-w-full pr-4", clss)}>
|
||||
<LanguageIcon class="h-4 w-4 mr-1 shrink-0" />
|
||||
<Dropdown cls="max-w-full" value={assignTo}>
|
||||
{#if preferredFiltered}
|
||||
{#each preferredFiltered as language}
|
||||
<option value={language} class="font-bold">
|
||||
|
|
@ -49,18 +48,17 @@
|
|||
{/if}
|
||||
</option>
|
||||
{/each}
|
||||
<option disabled></option>
|
||||
<option disabled />
|
||||
{/if}
|
||||
|
||||
{#each availableLanguages as language}
|
||||
<option value={language} class="font-bold">
|
||||
{native[language] ?? ""}
|
||||
{#if language !== $current}
|
||||
({(language_translations[language]?.[$current] + " - " + language) ?? language})
|
||||
({language_translations[language]?.[$current] + " - " + language ?? language})
|
||||
{/if}
|
||||
</option>
|
||||
{/each}
|
||||
</Dropdown>
|
||||
</form>
|
||||
|
||||
{/if}
|
||||
|
|
|
|||
|
|
@ -1,16 +1,18 @@
|
|||
<script lang="ts">
|
||||
import { IconConfig } from "../../Models/ThemeConfig/PointRenderingConfig";
|
||||
import { ImmutableStore, Store } from "../../Logic/UIEventSource";
|
||||
import DynamicIcon from "./DynamicIcon.svelte";
|
||||
import TagRenderingConfig from "../../Models/ThemeConfig/TagRenderingConfig";
|
||||
import { IconConfig } from "../../Models/ThemeConfig/PointRenderingConfig"
|
||||
import { ImmutableStore, Store } from "../../Logic/UIEventSource"
|
||||
import DynamicIcon from "./DynamicIcon.svelte"
|
||||
import TagRenderingConfig from "../../Models/ThemeConfig/TagRenderingConfig"
|
||||
|
||||
/**
|
||||
* Renders a 'marker', which consists of multiple 'icons'
|
||||
*/
|
||||
export let marker: IconConfig[] = config?.marker;
|
||||
export let tags: Store<Record<string, string>>
|
||||
export let rotation: TagRenderingConfig = undefined;
|
||||
export let tags: Store<Record<string, string>>;
|
||||
let _rotation = rotation ? tags.map(tags => rotation.GetRenderValue(tags).Subs(tags).txt) : new ImmutableStore(0);
|
||||
let _rotation = rotation
|
||||
? tags.map((tags) => rotation.GetRenderValue(tags).Subs(tags).txt)
|
||||
: new ImmutableStore(0)
|
||||
</script>
|
||||
|
||||
{#if marker && marker}
|
||||
|
|
|
|||
|
|
@ -12,7 +12,7 @@
|
|||
import { createEventDispatcher } from "svelte"
|
||||
import ToSvelte from "../Base/ToSvelte.svelte"
|
||||
import Svg from "../../Svg"
|
||||
import Plantnet_logo from "../../assets/svg/Plantnet_logo.svelte";
|
||||
import Plantnet_logo from "../../assets/svg/Plantnet_logo.svelte"
|
||||
|
||||
/**
|
||||
* The main entry point for the plantnet wizard
|
||||
|
|
@ -143,7 +143,7 @@
|
|||
</BackButton>
|
||||
{/if}
|
||||
<div class="low-interaction flex self-end rounded-xl p-2">
|
||||
<Plantnet_logo class="w-8 h-8 p-1 mr-1 bg-white rounded-full"/>
|
||||
<Plantnet_logo class="mr-1 h-8 w-8 rounded-full bg-white p-1" />
|
||||
<Tr t={t.poweredByPlantnet} />
|
||||
</div>
|
||||
</div>
|
||||
|
|
|
|||
|
|
@ -3,110 +3,110 @@
|
|||
* This component ties together all the steps that are needed to create a new point.
|
||||
* There are many subcomponents which help with that
|
||||
*/
|
||||
import type { SpecialVisualizationState } from "../../SpecialVisualization";
|
||||
import PresetList from "./PresetList.svelte";
|
||||
import type PresetConfig from "../../../Models/ThemeConfig/PresetConfig";
|
||||
import LayerConfig from "../../../Models/ThemeConfig/LayerConfig";
|
||||
import Tr from "../../Base/Tr.svelte";
|
||||
import SubtleButton from "../../Base/SubtleButton.svelte";
|
||||
import Translations from "../../i18n/Translations.js";
|
||||
import TagHint from "../TagHint.svelte";
|
||||
import { And } from "../../../Logic/Tags/And.js";
|
||||
import LoginToggle from "../../Base/LoginToggle.svelte";
|
||||
import Constants from "../../../Models/Constants.js";
|
||||
import FilteredLayer from "../../../Models/FilteredLayer";
|
||||
import { Store, UIEventSource } from "../../../Logic/UIEventSource";
|
||||
import { EyeIcon, EyeOffIcon } from "@rgossiaux/svelte-heroicons/solid";
|
||||
import LoginButton from "../../Base/LoginButton.svelte";
|
||||
import NewPointLocationInput from "../../BigComponents/NewPointLocationInput.svelte";
|
||||
import CreateNewNodeAction from "../../../Logic/Osm/Actions/CreateNewNodeAction";
|
||||
import { OsmWay } from "../../../Logic/Osm/OsmObject";
|
||||
import { Tag } from "../../../Logic/Tags/Tag";
|
||||
import type { WayId } from "../../../Models/OsmFeature";
|
||||
import Loading from "../../Base/Loading.svelte";
|
||||
import type { GlobalFilter } from "../../../Models/GlobalFilter";
|
||||
import { onDestroy } from "svelte";
|
||||
import NextButton from "../../Base/NextButton.svelte";
|
||||
import BackButton from "../../Base/BackButton.svelte";
|
||||
import ToSvelte from "../../Base/ToSvelte.svelte";
|
||||
import Svg from "../../../Svg";
|
||||
import OpenBackgroundSelectorButton from "../../BigComponents/OpenBackgroundSelectorButton.svelte";
|
||||
import { twJoin } from "tailwind-merge";
|
||||
import Confirm from "../../../assets/svg/Confirm.svelte";
|
||||
import Close from "../../../assets/svg/Close.svelte";
|
||||
import type { SpecialVisualizationState } from "../../SpecialVisualization"
|
||||
import PresetList from "./PresetList.svelte"
|
||||
import type PresetConfig from "../../../Models/ThemeConfig/PresetConfig"
|
||||
import LayerConfig from "../../../Models/ThemeConfig/LayerConfig"
|
||||
import Tr from "../../Base/Tr.svelte"
|
||||
import SubtleButton from "../../Base/SubtleButton.svelte"
|
||||
import Translations from "../../i18n/Translations.js"
|
||||
import TagHint from "../TagHint.svelte"
|
||||
import { And } from "../../../Logic/Tags/And.js"
|
||||
import LoginToggle from "../../Base/LoginToggle.svelte"
|
||||
import Constants from "../../../Models/Constants.js"
|
||||
import FilteredLayer from "../../../Models/FilteredLayer"
|
||||
import { Store, UIEventSource } from "../../../Logic/UIEventSource"
|
||||
import { EyeIcon, EyeOffIcon } from "@rgossiaux/svelte-heroicons/solid"
|
||||
import LoginButton from "../../Base/LoginButton.svelte"
|
||||
import NewPointLocationInput from "../../BigComponents/NewPointLocationInput.svelte"
|
||||
import CreateNewNodeAction from "../../../Logic/Osm/Actions/CreateNewNodeAction"
|
||||
import { OsmWay } from "../../../Logic/Osm/OsmObject"
|
||||
import { Tag } from "../../../Logic/Tags/Tag"
|
||||
import type { WayId } from "../../../Models/OsmFeature"
|
||||
import Loading from "../../Base/Loading.svelte"
|
||||
import type { GlobalFilter } from "../../../Models/GlobalFilter"
|
||||
import { onDestroy } from "svelte"
|
||||
import NextButton from "../../Base/NextButton.svelte"
|
||||
import BackButton from "../../Base/BackButton.svelte"
|
||||
import ToSvelte from "../../Base/ToSvelte.svelte"
|
||||
import Svg from "../../../Svg"
|
||||
import OpenBackgroundSelectorButton from "../../BigComponents/OpenBackgroundSelectorButton.svelte"
|
||||
import { twJoin } from "tailwind-merge"
|
||||
import Confirm from "../../../assets/svg/Confirm.svelte"
|
||||
import Close from "../../../assets/svg/Close.svelte"
|
||||
|
||||
export let coordinate: { lon: number; lat: number };
|
||||
export let state: SpecialVisualizationState;
|
||||
export let coordinate: { lon: number; lat: number }
|
||||
export let state: SpecialVisualizationState
|
||||
|
||||
let selectedPreset: {
|
||||
preset: PresetConfig
|
||||
layer: LayerConfig
|
||||
icon: string
|
||||
tags: Record<string, string>
|
||||
} = undefined;
|
||||
let checkedOfGlobalFilters: number = 0;
|
||||
let confirmedCategory = false;
|
||||
} = undefined
|
||||
let checkedOfGlobalFilters: number = 0
|
||||
let confirmedCategory = false
|
||||
$: if (selectedPreset === undefined) {
|
||||
confirmedCategory = false;
|
||||
creating = false;
|
||||
checkedOfGlobalFilters = 0;
|
||||
confirmedCategory = false
|
||||
creating = false
|
||||
checkedOfGlobalFilters = 0
|
||||
}
|
||||
|
||||
let flayer: FilteredLayer = undefined;
|
||||
let layerIsDisplayed: UIEventSource<boolean> | undefined = undefined;
|
||||
let layerHasFilters: Store<boolean> | undefined = undefined;
|
||||
let globalFilter: UIEventSource<GlobalFilter[]> = state.layerState.globalFilters;
|
||||
let _globalFilter: GlobalFilter[] = [];
|
||||
let flayer: FilteredLayer = undefined
|
||||
let layerIsDisplayed: UIEventSource<boolean> | undefined = undefined
|
||||
let layerHasFilters: Store<boolean> | undefined = undefined
|
||||
let globalFilter: UIEventSource<GlobalFilter[]> = state.layerState.globalFilters
|
||||
let _globalFilter: GlobalFilter[] = []
|
||||
onDestroy(
|
||||
globalFilter.addCallbackAndRun((globalFilter) => {
|
||||
console.log("Global filters are", globalFilter);
|
||||
_globalFilter = globalFilter ?? [];
|
||||
console.log("Global filters are", globalFilter)
|
||||
_globalFilter = globalFilter ?? []
|
||||
})
|
||||
);
|
||||
)
|
||||
$: {
|
||||
flayer = state.layerState.filteredLayers.get(selectedPreset?.layer?.id);
|
||||
layerIsDisplayed = flayer?.isDisplayed;
|
||||
layerHasFilters = flayer?.hasFilter;
|
||||
flayer = state.layerState.filteredLayers.get(selectedPreset?.layer?.id)
|
||||
layerIsDisplayed = flayer?.isDisplayed
|
||||
layerHasFilters = flayer?.hasFilter
|
||||
}
|
||||
const t = Translations.t.general.add;
|
||||
const t = Translations.t.general.add
|
||||
|
||||
const zoom = state.mapProperties.zoom;
|
||||
const zoom = state.mapProperties.zoom
|
||||
|
||||
const isLoading = state.dataIsLoading;
|
||||
let preciseCoordinate: UIEventSource<{ lon: number; lat: number }> = new UIEventSource(undefined);
|
||||
let snappedToObject: UIEventSource<string> = new UIEventSource<string>(undefined);
|
||||
const isLoading = state.dataIsLoading
|
||||
let preciseCoordinate: UIEventSource<{ lon: number; lat: number }> = new UIEventSource(undefined)
|
||||
let snappedToObject: UIEventSource<string> = new UIEventSource<string>(undefined)
|
||||
|
||||
// Small helper variable: if the map is tapped, we should let the 'Next'-button grab some attention as users have to click _that_ to continue, not the map
|
||||
let preciseInputIsTapped = false;
|
||||
let preciseInputIsTapped = false
|
||||
|
||||
let creating = false;
|
||||
let creating = false
|
||||
|
||||
/**
|
||||
* Call when the user should restart the flow by clicking on the map, e.g. because they disabled filters.
|
||||
* Will delete the lastclick-location
|
||||
*/
|
||||
function abort() {
|
||||
state.selectedElement.setData(undefined);
|
||||
state.selectedElement.setData(undefined)
|
||||
// When aborted, we force the contributors to place the pin _again_
|
||||
// This is because there might be a nearby object that was disabled; this forces them to re-evaluate the map
|
||||
state.lastClickObject.features.setData([]);
|
||||
preciseInputIsTapped = false;
|
||||
state.lastClickObject.features.setData([])
|
||||
preciseInputIsTapped = false
|
||||
}
|
||||
|
||||
async function confirm() {
|
||||
creating = true;
|
||||
const location: { lon: number; lat: number } = preciseCoordinate.data;
|
||||
const snapTo: WayId | undefined = <WayId>snappedToObject.data;
|
||||
creating = true
|
||||
const location: { lon: number; lat: number } = preciseCoordinate.data
|
||||
const snapTo: WayId | undefined = <WayId>snappedToObject.data
|
||||
const tags: Tag[] = selectedPreset.preset.tags.concat(
|
||||
..._globalFilter.map((f) => f?.onNewPoint?.tags ?? [])
|
||||
);
|
||||
console.log("Creating new point at", location, "snapped to", snapTo, "with tags", tags);
|
||||
)
|
||||
console.log("Creating new point at", location, "snapped to", snapTo, "with tags", tags)
|
||||
|
||||
let snapToWay: undefined | OsmWay = undefined;
|
||||
let snapToWay: undefined | OsmWay = undefined
|
||||
if (snapTo !== undefined && snapTo !== null) {
|
||||
const downloaded = await state.osmObjectDownloader.DownloadObjectAsync(snapTo, 0);
|
||||
const downloaded = await state.osmObjectDownloader.DownloadObjectAsync(snapTo, 0)
|
||||
if (downloaded !== "deleted") {
|
||||
snapToWay = downloaded;
|
||||
snapToWay = downloaded
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -114,44 +114,44 @@
|
|||
theme: state.layout?.id ?? "unkown",
|
||||
changeType: "create",
|
||||
snapOnto: snapToWay,
|
||||
reusePointWithinMeters: 1
|
||||
});
|
||||
await state.changes.applyAction(newElementAction);
|
||||
state.newFeatures.features.ping();
|
||||
reusePointWithinMeters: 1,
|
||||
})
|
||||
await state.changes.applyAction(newElementAction)
|
||||
state.newFeatures.features.ping()
|
||||
// The 'changes' should have created a new point, which added this into the 'featureProperties'
|
||||
const newId = newElementAction.newElementId;
|
||||
console.log("Applied pending changes, fetching store for", newId);
|
||||
const tagsStore = state.featureProperties.getStore(newId);
|
||||
const newId = newElementAction.newElementId
|
||||
console.log("Applied pending changes, fetching store for", newId)
|
||||
const tagsStore = state.featureProperties.getStore(newId)
|
||||
if (!tagsStore) {
|
||||
console.error("Bug: no tagsStore found for", newId);
|
||||
console.error("Bug: no tagsStore found for", newId)
|
||||
}
|
||||
{
|
||||
// Set some metainfo
|
||||
const properties = tagsStore.data;
|
||||
const properties = tagsStore.data
|
||||
if (snapTo) {
|
||||
// metatags (starting with underscore) are not uploaded, so we can safely mark this
|
||||
delete properties["_referencing_ways"];
|
||||
properties["_referencing_ways"] = `["${snapTo}"]`;
|
||||
delete properties["_referencing_ways"]
|
||||
properties["_referencing_ways"] = `["${snapTo}"]`
|
||||
}
|
||||
properties["_backend"] = state.osmConnection.Backend();
|
||||
properties["_last_edit:timestamp"] = new Date().toISOString();
|
||||
const userdetails = state.osmConnection.userDetails.data;
|
||||
properties["_last_edit:contributor"] = userdetails.name;
|
||||
properties["_last_edit:uid"] = "" + userdetails.uid;
|
||||
tagsStore.ping();
|
||||
properties["_backend"] = state.osmConnection.Backend()
|
||||
properties["_last_edit:timestamp"] = new Date().toISOString()
|
||||
const userdetails = state.osmConnection.userDetails.data
|
||||
properties["_last_edit:contributor"] = userdetails.name
|
||||
properties["_last_edit:uid"] = "" + userdetails.uid
|
||||
tagsStore.ping()
|
||||
}
|
||||
const feature = state.indexedFeatures.featuresById.data.get(newId);
|
||||
console.log("Selecting feature", feature, "and opening their popup");
|
||||
abort();
|
||||
state.selectedLayer.setData(selectedPreset.layer);
|
||||
state.selectedElement.setData(feature);
|
||||
tagsStore.ping();
|
||||
const feature = state.indexedFeatures.featuresById.data.get(newId)
|
||||
console.log("Selecting feature", feature, "and opening their popup")
|
||||
abort()
|
||||
state.selectedLayer.setData(selectedPreset.layer)
|
||||
state.selectedElement.setData(feature)
|
||||
tagsStore.ping()
|
||||
}
|
||||
|
||||
function confirmSync() {
|
||||
confirm()
|
||||
.then((_) => console.debug("New point successfully handled"))
|
||||
.catch((e) => console.error("Handling the new point went wrong due to", e));
|
||||
.catch((e) => console.error("Handling the new point went wrong due to", e))
|
||||
}
|
||||
</script>
|
||||
|
||||
|
|
|
|||
|
|
@ -9,6 +9,7 @@
|
|||
import Lazy from "../Base/Lazy"
|
||||
import BaseUIElement from "../BaseUIElement"
|
||||
import LayoutConfig from "../../Models/ThemeConfig/LayoutConfig"
|
||||
import { VariableUiElement } from "../Base/VariableUIElement"
|
||||
|
||||
//Svelte props
|
||||
export let tags: UIEventSource<any>
|
||||
|
|
@ -54,13 +55,11 @@
|
|||
return parts
|
||||
})
|
||||
|
||||
let _allTags = []
|
||||
onDestroy(
|
||||
allTags.addCallbackAndRunD((allTags) => {
|
||||
_allTags = allTags
|
||||
})
|
||||
const tagsTable = new VariableUiElement(
|
||||
allTags.mapD((_allTags) =>
|
||||
new Table(["Key", "Value"], _allTags).SetClass("zebra-table break-all")
|
||||
)
|
||||
)
|
||||
const tagsTable = new Table(["Key", "Value"], _allTags).SetClass("zebra-table break-all")
|
||||
</script>
|
||||
|
||||
<section>
|
||||
|
|
|
|||
|
|
@ -15,8 +15,8 @@
|
|||
import NewPointLocationInput from "../BigComponents/NewPointLocationInput.svelte"
|
||||
import ToSvelte from "../Base/ToSvelte.svelte"
|
||||
import Svg from "../../Svg"
|
||||
import Layers from "../../assets/svg/Layers.svelte";
|
||||
import AddSmall from "../../assets/svg/AddSmall.svelte";
|
||||
import Layers from "../../assets/svg/Layers.svelte"
|
||||
import AddSmall from "../../assets/svg/AddSmall.svelte"
|
||||
|
||||
export let coordinate: UIEventSource<{ lon: number; lat: number }>
|
||||
export let state: SpecialVisualizationState
|
||||
|
|
@ -99,7 +99,7 @@
|
|||
<Tr t={Translations.t.notes.noteLayerHasFilters} />
|
||||
</div>
|
||||
<SubtleButton on:click={() => notelayer.disableAllFilters()}>
|
||||
<Layers class="mr-4 h-8 w-8"/>
|
||||
<Layers class="mr-4 h-8 w-8" />
|
||||
<Tr slot="message" t={Translations.t.notes.disableAllNoteFilters} />
|
||||
</SubtleButton>
|
||||
</div>
|
||||
|
|
|
|||
|
|
@ -28,11 +28,13 @@
|
|||
|
||||
const t = Translations.t.image.nearby
|
||||
const c = [lon, lat]
|
||||
console.log(">>>", image)
|
||||
let attributedImage = new AttributedImage({
|
||||
url: image.thumbUrl ?? image.pictureUrl,
|
||||
provider: AllImageProviders.byName(image.provider),
|
||||
date: new Date(image.date),
|
||||
})
|
||||
id: Object.values(image.osmTags)[0]
|
||||
}, feature)
|
||||
let distance = Math.round(
|
||||
GeoOperations.distanceBetween([image.coordinates.lng, image.coordinates.lat], c)
|
||||
)
|
||||
|
|
@ -42,7 +44,7 @@
|
|||
const key = Object.keys(image.osmTags)[0]
|
||||
const url = image.osmTags[key]
|
||||
if (isLinked) {
|
||||
const action = new LinkImageAction(currentTags.id, key, url, currentTags, {
|
||||
const action = new LinkImageAction(currentTags.id, key, url, tags, {
|
||||
theme: state.layout.id,
|
||||
changeType: "link-image",
|
||||
})
|
||||
|
|
|
|||
|
|
@ -35,6 +35,7 @@
|
|||
)
|
||||
|
||||
let images: Store<P4CPicture[]> = imagesProvider.store.map((images) => images.slice(0, 20))
|
||||
let allDone = imagesProvider.allDone
|
||||
</script>
|
||||
|
||||
<div class="interactive border-interactive rounded-2xl p-2">
|
||||
|
|
@ -44,8 +45,10 @@
|
|||
</h4>
|
||||
<slot name="corner" />
|
||||
</div>
|
||||
{#if $images.length === 0}
|
||||
{#if !$allDone}
|
||||
<Loading />
|
||||
{:else if $images.length === 0}
|
||||
<Tr t={Translations.t.image.nearby.noNearbyImages} cls="alert"/>
|
||||
{:else}
|
||||
<div class="flex w-full space-x-1 overflow-x-auto" style="scroll-snap-type: x proximity">
|
||||
{#each $images as image (image.pictureUrl)}
|
||||
|
|
|
|||
|
|
@ -11,7 +11,7 @@
|
|||
import ToSvelte from "../Base/ToSvelte.svelte"
|
||||
import { XCircleIcon } from "@babeard/svelte-heroicons/solid"
|
||||
import exp from "constants"
|
||||
import Camera_plus from "../../assets/svg/Camera_plus.svelte";
|
||||
import Camera_plus from "../../assets/svg/Camera_plus.svelte"
|
||||
|
||||
export let tags: Store<OsmTags>
|
||||
export let state: SpecialVisualizationState
|
||||
|
|
@ -43,7 +43,7 @@
|
|||
expanded = true
|
||||
}}
|
||||
>
|
||||
<Camera_plus class="block w-8 h-8 p-1 mr-2"/>
|
||||
<Camera_plus class="mr-2 block h-8 w-8 p-1" />
|
||||
<Tr t={t.seeNearby} />
|
||||
</button>
|
||||
{/if}
|
||||
|
|
|
|||
|
|
@ -67,12 +67,12 @@
|
|||
},
|
||||
[skippedQuestions]
|
||||
)
|
||||
let firstQuestion = questionsToAsk.map(qta => qta[0])
|
||||
let firstQuestion = questionsToAsk.map((qta) => qta[0])
|
||||
|
||||
let answered: number = 0
|
||||
let skipped: number = 0
|
||||
|
||||
function skip(question: {id: string}, didAnswer: boolean = false) {
|
||||
function skip(question: { id: string }, didAnswer: boolean = false) {
|
||||
skippedQuestions.data.add(question.id)
|
||||
skippedQuestions.ping()
|
||||
if (didAnswer) {
|
||||
|
|
@ -145,25 +145,25 @@
|
|||
</div>
|
||||
{:else}
|
||||
<TagRenderingQuestion
|
||||
config={$firstQuestion}
|
||||
{layer}
|
||||
{selectedElement}
|
||||
{state}
|
||||
{tags}
|
||||
on:saved={() => {
|
||||
skip($firstQuestion, true)
|
||||
config={$firstQuestion}
|
||||
{layer}
|
||||
{selectedElement}
|
||||
{state}
|
||||
{tags}
|
||||
on:saved={() => {
|
||||
skip($firstQuestion, true)
|
||||
}}
|
||||
>
|
||||
<button
|
||||
class="secondary"
|
||||
on:click={() => {
|
||||
skip($firstQuestion)
|
||||
}}
|
||||
slot="cancel"
|
||||
>
|
||||
<button
|
||||
class="secondary"
|
||||
on:click={() => {
|
||||
skip($firstQuestion)
|
||||
}}
|
||||
slot="cancel"
|
||||
>
|
||||
<Tr t={Translations.t.general.skip} />
|
||||
</button>
|
||||
</TagRenderingQuestion>
|
||||
<Tr t={Translations.t.general.skip} />
|
||||
</button>
|
||||
</TagRenderingQuestion>
|
||||
{/if}
|
||||
</div>
|
||||
{/if}
|
||||
|
|
|
|||
|
|
@ -106,7 +106,7 @@
|
|||
</div>
|
||||
{/if}
|
||||
{:else}
|
||||
<div class="w-full h-full overflow-hidden p-2">
|
||||
<div class="h-full w-full overflow-hidden p-2">
|
||||
<TagRenderingAnswer {config} {tags} {selectedElement} {state} {layer} />
|
||||
</div>
|
||||
{/if}
|
||||
|
|
|
|||
|
|
@ -1,45 +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 { 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 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 dispatch = createEventDispatcher<{
|
||||
saved: {
|
||||
config: TagRenderingConfig
|
||||
applied: TagsFilter
|
||||
}
|
||||
}>();
|
||||
|
||||
/**
|
||||
* Prepares and fills the checkedMappings
|
||||
|
|
@ -47,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 &&
|
||||
|
|
@ -60,59 +71,53 @@
|
|||
(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.addCallbackAndRun(freeformValue => {
|
||||
checkedMappings[checkedMappings.length - 1] = !!freeformValue
|
||||
})
|
||||
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);
|
||||
}
|
||||
export let selectedTags: TagsFilter = undefined
|
||||
|
||||
let mappings: Mapping[] = config?.mappings
|
||||
let searchTerm: UIEventSource<string> = new UIEventSource("")
|
||||
|
||||
$: {
|
||||
try {
|
||||
|
|
@ -121,10 +126,67 @@
|
|||
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) {
|
||||
console.log("SelectedTags is undefined, ignoring 'onSave'-event");
|
||||
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);
|
||||
for (const { k, v } of kv) {
|
||||
if (v === undefined || v === "") {
|
||||
delete tags.data[k];
|
||||
} else {
|
||||
freeformInput.setData(undefined);
|
||||
}
|
||||
feedback.setData(undefined);
|
||||
}
|
||||
}
|
||||
|
||||
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;
|
||||
|
||||
change
|
||||
.CreateChangeDescriptions()
|
||||
.then((changes) => state.changes.applyChanges(changes))
|
||||
.catch(console.error);
|
||||
}
|
||||
|
||||
function onInputKeypress(e: Event) {
|
||||
if (e.key === "Enter") {
|
||||
onSave();
|
||||
}
|
||||
}
|
||||
|
||||
$: {
|
||||
try {
|
||||
selectedTags = config?.constructChangeSpecification(
|
||||
$freeformInput,
|
||||
selectedMapping,
|
||||
checkedMappings,
|
||||
tags.data
|
||||
);
|
||||
} catch (e) {
|
||||
console.error("Could not calculate changeSpecification:", e);
|
||||
selectedTags = undefined;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -133,85 +195,35 @@
|
|||
config: TagRenderingConfig
|
||||
applied: TagsFilter
|
||||
}
|
||||
}>()
|
||||
}>();
|
||||
|
||||
function onSave() {
|
||||
if (selectedTags === undefined) {
|
||||
console.log("SelectedTags is undefined, ignoring 'onSave'-event")
|
||||
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)
|
||||
for (const { k, v } of kv) {
|
||||
if (v === undefined || v === "") {
|
||||
delete tags.data[k]
|
||||
} else {
|
||||
tags.data[k] = v
|
||||
}
|
||||
}
|
||||
tags.ping()
|
||||
return
|
||||
}
|
||||
|
||||
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
|
||||
|
||||
change
|
||||
.CreateChangeDescriptions()
|
||||
.then((changes) => state.changes.applyChanges(changes))
|
||||
.catch(console.error)
|
||||
}
|
||||
|
||||
function onInputKeypress(e: Event){
|
||||
if (e.key === "Enter") {
|
||||
onSave();
|
||||
}
|
||||
}
|
||||
|
||||
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>
|
||||
|
||||
{#if question !== undefined}
|
||||
<div
|
||||
class="interactive border-interactive relative flex flex-col overflow-y-auto p-1 px-2"
|
||||
style="max-height: 85vh"
|
||||
class="interactive border-interactive relative flex flex-col overflow-y-auto px-2"
|
||||
style="max-height: 75vh"
|
||||
>
|
||||
<div class="sticky top-0" style="z-index: 11">
|
||||
<div class="interactive sticky top-0 flex justify-between">
|
||||
<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}
|
||||
/>
|
||||
<SpecialTranslation t={question} {tags} {state} {layer} feature={selectedElement} />
|
||||
</span>
|
||||
<slot name="upper-right" />
|
||||
</div>
|
||||
<slot name="upper-right" />
|
||||
</div>
|
||||
|
||||
{#if config.questionhint}
|
||||
|
|
@ -228,7 +240,7 @@
|
|||
|
||||
{#if config.mappings?.length >= 8}
|
||||
<div class="sticky flex w-full">
|
||||
<Search class="h-6 w-6"/>
|
||||
<Search class="h-6 w-6" />
|
||||
<input type="text" bind:value={$searchTerm} class="w-full" />
|
||||
</div>
|
||||
{/if}
|
||||
|
|
@ -263,8 +275,8 @@
|
|||
bind:group={selectedMapping}
|
||||
name={"mappings-radio-" + config.id}
|
||||
value={i}
|
||||
on:keypress={onInputKeypress}
|
||||
|
||||
on:keypress={e => onInputKeypress(e)}
|
||||
|
||||
/>
|
||||
</TagRenderingMappingInput>
|
||||
{/each}
|
||||
|
|
@ -275,7 +287,7 @@
|
|||
bind:group={selectedMapping}
|
||||
name={"mappings-radio-" + config.id}
|
||||
value={config.mappings?.length}
|
||||
on:keypress={onInputKeypress}
|
||||
on:keypress={e => onInputKeypress(e)}
|
||||
/>
|
||||
<FreeformInput
|
||||
{config}
|
||||
|
|
@ -307,7 +319,7 @@
|
|||
type="checkbox"
|
||||
name={"mappings-checkbox-" + config.id + "-" + i}
|
||||
bind:checked={checkedMappings[i]}
|
||||
on:keypress={onInputKeypress}
|
||||
on:keypress={e => onInputKeypress(e)}
|
||||
/>
|
||||
</TagRenderingMappingInput>
|
||||
{/each}
|
||||
|
|
@ -317,7 +329,7 @@
|
|||
type="checkbox"
|
||||
name={"mappings-checkbox-" + config.id + "-" + config.mappings?.length}
|
||||
bind:checked={checkedMappings[config.mappings.length]}
|
||||
on:keypress={onInputKeypress}
|
||||
on:keypress={e => onInputKeypress(e)}
|
||||
/>
|
||||
<FreeformInput
|
||||
{config}
|
||||
|
|
|
|||
|
|
@ -12,7 +12,7 @@
|
|||
import LayerConfig from "../../Models/ThemeConfig/LayerConfig"
|
||||
import ToSvelte from "../Base/ToSvelte.svelte"
|
||||
import Svg from "../../Svg"
|
||||
import Mangrove_logo from "../../assets/svg/Mangrove_logo.svelte";
|
||||
import Mangrove_logo from "../../assets/svg/Mangrove_logo.svelte"
|
||||
|
||||
/**
|
||||
* An element showing all reviews
|
||||
|
|
@ -41,7 +41,7 @@
|
|||
<Tr t={Translations.t.reviews.no_reviews_yet} />
|
||||
{/if}
|
||||
<div class="flex justify-end">
|
||||
<Mangrove_logo class="w-12 h-12 shrink-0 p-1"/>
|
||||
<Mangrove_logo class="h-12 w-12 shrink-0 p-1" />
|
||||
<Tr cls="text-sm subtle" t={Translations.t.reviews.attribution} />
|
||||
</div>
|
||||
</div>
|
||||
|
|
|
|||
|
|
@ -2,9 +2,9 @@
|
|||
import ToSvelte from "../Base/ToSvelte.svelte"
|
||||
import Svg from "../../Svg"
|
||||
import { createEventDispatcher } from "svelte"
|
||||
import Star from "../../assets/svg/Star.svelte";
|
||||
import Star_half from "../../assets/svg/Star_half.svelte";
|
||||
import Star_outline from "../../assets/svg/Star_outline.svelte";
|
||||
import Star from "../../assets/svg/Star.svelte"
|
||||
import Star_half from "../../assets/svg/Star_half.svelte"
|
||||
import Star_outline from "../../assets/svg/Star_outline.svelte"
|
||||
|
||||
export let score: number
|
||||
export let cutoff: number
|
||||
|
|
@ -26,10 +26,10 @@
|
|||
on:mousemove={(e) => dispatch("hover", { score: getScore(e) })}
|
||||
>
|
||||
{#if score >= cutoff}
|
||||
<Star class={starSize}/>
|
||||
<Star class={starSize} />
|
||||
{:else if score + 10 >= cutoff}
|
||||
<Star_half class={starSize}/>
|
||||
<Star_half class={starSize} />
|
||||
{:else}
|
||||
<Star_outline class={starSize}/>
|
||||
<Star_outline class={starSize} />
|
||||
{/if}
|
||||
</div>
|
||||
|
|
|
|||
|
|
@ -90,7 +90,7 @@ export interface SpecialVisualization {
|
|||
readonly funcName: string
|
||||
readonly docs: string | BaseUIElement
|
||||
readonly example?: string
|
||||
readonly needsUrls: string[]
|
||||
readonly needsUrls: string[] | ((args: string[]) => string)
|
||||
|
||||
/**
|
||||
* Indicates that this special visualisation will make requests to the 'alLNodesDatabase' and that it thus should be included
|
||||
|
|
|
|||
|
|
@ -659,7 +659,7 @@ export default class SpecialVisualizations {
|
|||
},
|
||||
],
|
||||
needsUrls: AllImageProviders.apiUrls,
|
||||
constr: (state, tags, args) => {
|
||||
constr: (state, tags, args, feature) => {
|
||||
let imagePrefixes: string[] = undefined
|
||||
if (args.length > 0) {
|
||||
imagePrefixes = [].concat(...args.map((a) => a.split(",")))
|
||||
|
|
@ -667,7 +667,8 @@ export default class SpecialVisualizations {
|
|||
return new ImageCarousel(
|
||||
AllImageProviders.LoadImagesFor(tags, imagePrefixes),
|
||||
tags,
|
||||
state
|
||||
state,
|
||||
feature
|
||||
)
|
||||
},
|
||||
},
|
||||
|
|
@ -1461,7 +1462,7 @@ export default class SpecialVisualizations {
|
|||
},
|
||||
],
|
||||
docs: "Shows events that are happening based on a Giggity URL",
|
||||
needsUrls: ["*"],
|
||||
needsUrls: (args) => args[0],
|
||||
constr(
|
||||
state: SpecialVisualizationState,
|
||||
tagSource: UIEventSource<Record<string, string>>,
|
||||
|
|
|
|||
|
|
@ -10,13 +10,14 @@
|
|||
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);
|
||||
|
||||
|
||||
function fetchIconDescription(layerId): any {
|
||||
if (category === "themes") {
|
||||
return AllKnownLayouts.allKnownLayouts.get(layerId).icon;
|
||||
|
|
@ -24,7 +25,6 @@
|
|||
return AllSharedLayers.getSharedLayersConfigs().get(layerId)?._layerIcon;
|
||||
}
|
||||
|
||||
const dispatch = createEventDispatcher<{ layerSelected: string }>();
|
||||
</script>
|
||||
|
||||
<NextButton clss="small" on:click={() => dispatch("layerSelected", info)}>
|
||||
|
|
@ -33,12 +33,14 @@
|
|||
</div>
|
||||
<b class="px-1">{info.id}</b>
|
||||
{#if info.owner && info.owner !== $selfId}
|
||||
{#if displayName}
|
||||
{#if $displayName}
|
||||
(made by {$displayName}
|
||||
{#if window.location.host.startsWith("127.0.0.1")}
|
||||
- {info.owner}
|
||||
{/if}
|
||||
)
|
||||
{:else }
|
||||
({info.owner})
|
||||
{/if}
|
||||
{/if}
|
||||
</NextButton>
|
||||
|
|
|
|||
|
|
@ -9,7 +9,7 @@
|
|||
import { Utils } from "../../Utils"
|
||||
import type { ConversionMessage } from "../../Models/ThemeConfig/Conversion/Conversion"
|
||||
import ErrorIndicatorForRegion from "./ErrorIndicatorForRegion.svelte"
|
||||
import { ChevronRightIcon } from "@rgossiaux/svelte-heroicons/solid"
|
||||
import { ChevronRightIcon, TrashIcon } from "@rgossiaux/svelte-heroicons/solid"
|
||||
import SchemaBasedInput from "./SchemaBasedInput.svelte"
|
||||
import FloatOver from "../Base/FloatOver.svelte"
|
||||
import TagRenderingInput from "./TagRenderingInput.svelte"
|
||||
|
|
@ -21,6 +21,7 @@
|
|||
const layerSchema: ConfigMeta[] = <any>layerSchemaRaw
|
||||
|
||||
export let state: EditLayerState
|
||||
export let backToStudio: () => void
|
||||
let messages = state.messages
|
||||
let hasErrors = messages.mapD(
|
||||
(m: ConversionMessage[]) => m.filter((m) => m.level === "error").length
|
||||
|
|
@ -72,6 +73,10 @@
|
|||
})
|
||||
|
||||
let highlightedItem: UIEventSource<HighlightedTagRendering> = state.highlightedItem
|
||||
function deleteLayer() {
|
||||
state.delete()
|
||||
backToStudio()
|
||||
}
|
||||
</script>
|
||||
|
||||
<div class="flex h-screen flex-col">
|
||||
|
|
@ -113,6 +118,12 @@
|
|||
</div>
|
||||
<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>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div slot="title1" class="flex">
|
||||
|
|
|
|||
|
|
@ -107,6 +107,9 @@ export abstract class EditJsonState<T> {
|
|||
return entry
|
||||
}
|
||||
|
||||
public async delete(){
|
||||
await this.server.delete(this.getId().data, this.category)
|
||||
}
|
||||
public getStoreFor<T>(path: ReadonlyArray<string | number>): UIEventSource<T | undefined> {
|
||||
const key = path.join(".")
|
||||
|
||||
|
|
@ -294,16 +297,39 @@ export default class EditLayerState extends EditJsonState<LayerConfigJson> {
|
|||
|
||||
this.addMissingTagRenderingIds()
|
||||
|
||||
this.configuration.addCallbackAndRunD((layer) => {
|
||||
if (layer.tagRenderings) {
|
||||
|
||||
function cleanArray(data: object, key: string): boolean{
|
||||
if(!data){
|
||||
return false
|
||||
}
|
||||
if (data[key]) {
|
||||
// A bit of cleanup
|
||||
const lBefore = layer.tagRenderings.length
|
||||
const cleaned = Utils.NoNull(layer.tagRenderings)
|
||||
const lBefore = data[key].length
|
||||
const cleaned = Utils.NoNull(data[key])
|
||||
if (cleaned.length != lBefore) {
|
||||
layer.tagRenderings = cleaned
|
||||
this.configuration.ping()
|
||||
data[key] = cleaned
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
this.configuration.addCallbackAndRunD((layer) => {
|
||||
let changed = cleanArray(layer, "tagRenderings") || cleanArray(layer, "pointRenderings")
|
||||
for (const tr of layer.tagRenderings ?? []) {
|
||||
if(typeof tr === "string"){
|
||||
continue
|
||||
}
|
||||
|
||||
const qtr = (<QuestionableTagRenderingConfigJson> tr)
|
||||
if(qtr.freeform && Object.keys(qtr.freeform ).length === 0){
|
||||
delete qtr.freeform
|
||||
changed = true
|
||||
}
|
||||
}
|
||||
if(changed){
|
||||
this.configuration.ping()
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -64,7 +64,16 @@
|
|||
}
|
||||
}
|
||||
newPath.push(...toAdd)
|
||||
console.log("Fused path ", path.join("."), "+", i,"+", subpartPath.join("."),"into",newPath.join("."))
|
||||
console.log(
|
||||
"Fused path ",
|
||||
path.join("."),
|
||||
"+",
|
||||
i,
|
||||
"+",
|
||||
subpartPath.join("."),
|
||||
"into",
|
||||
newPath.join(".")
|
||||
)
|
||||
return newPath
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -58,7 +58,14 @@ export default class StudioServer {
|
|||
return undefined
|
||||
}
|
||||
}
|
||||
|
||||
async delete(id: string, category: "layers" | "themes") {
|
||||
if (id === undefined || id === "") {
|
||||
return
|
||||
}
|
||||
await fetch(this.urlFor(id, category), {
|
||||
method: "DELETE"
|
||||
})
|
||||
}
|
||||
async update(id: string, config: string, category: "layers" | "themes") {
|
||||
if (id === undefined || id === "") {
|
||||
return
|
||||
|
|
|
|||
|
|
@ -3,104 +3,104 @@
|
|||
* Little helper class to deal with choosing a builtin tagRendering or defining one yourself.
|
||||
* Breaks the ideology that everything should be schema based
|
||||
*/
|
||||
import EditLayerState from "./EditLayerState";
|
||||
import type { ConfigMeta } from "./configMeta";
|
||||
import EditLayerState from "./EditLayerState"
|
||||
import type { ConfigMeta } from "./configMeta"
|
||||
import type {
|
||||
MappingConfigJson,
|
||||
QuestionableTagRenderingConfigJson
|
||||
} from "../../Models/ThemeConfig/Json/QuestionableTagRenderingConfigJson";
|
||||
import TagRenderingConfig from "../../Models/ThemeConfig/TagRenderingConfig";
|
||||
import TagRenderingEditable from "../Popup/TagRendering/TagRenderingEditable.svelte";
|
||||
import { Store, UIEventSource } from "../../Logic/UIEventSource";
|
||||
import * as questions from "../../assets/generated/layers/questions.json";
|
||||
import MappingInput from "./MappingInput.svelte";
|
||||
import { TrashIcon } from "@rgossiaux/svelte-heroicons/outline";
|
||||
import questionableTagRenderingSchemaRaw from "../../assets/schemas/questionabletagrenderingconfigmeta.json";
|
||||
import SchemaBasedField from "./SchemaBasedField.svelte";
|
||||
import Region from "./Region.svelte";
|
||||
import NextButton from "../Base/NextButton.svelte";
|
||||
import { QuestionMarkCircleIcon } from "@rgossiaux/svelte-heroicons/solid";
|
||||
import { LocalStorageSource } from "../../Logic/Web/LocalStorageSource";
|
||||
import { onMount } from "svelte";
|
||||
QuestionableTagRenderingConfigJson,
|
||||
} from "../../Models/ThemeConfig/Json/QuestionableTagRenderingConfigJson"
|
||||
import TagRenderingConfig from "../../Models/ThemeConfig/TagRenderingConfig"
|
||||
import TagRenderingEditable from "../Popup/TagRendering/TagRenderingEditable.svelte"
|
||||
import { Store, UIEventSource } from "../../Logic/UIEventSource"
|
||||
import * as questions from "../../assets/generated/layers/questions.json"
|
||||
import MappingInput from "./MappingInput.svelte"
|
||||
import { TrashIcon } from "@rgossiaux/svelte-heroicons/outline"
|
||||
import questionableTagRenderingSchemaRaw from "../../assets/schemas/questionabletagrenderingconfigmeta.json"
|
||||
import SchemaBasedField from "./SchemaBasedField.svelte"
|
||||
import Region from "./Region.svelte"
|
||||
import NextButton from "../Base/NextButton.svelte"
|
||||
import { QuestionMarkCircleIcon } from "@rgossiaux/svelte-heroicons/solid"
|
||||
import { LocalStorageSource } from "../../Logic/Web/LocalStorageSource"
|
||||
import { onMount } from "svelte"
|
||||
|
||||
export let state: EditLayerState;
|
||||
export let schema: ConfigMeta;
|
||||
export let path: (string | number)[];
|
||||
let expertMode = state.expertMode;
|
||||
const store = state.getStoreFor(path);
|
||||
let value = store.data;
|
||||
export let state: EditLayerState
|
||||
export let schema: ConfigMeta
|
||||
export let path: (string | number)[]
|
||||
let expertMode = state.expertMode
|
||||
const store = state.getStoreFor(path)
|
||||
let value = store.data
|
||||
let hasSeenIntro = UIEventSource.asBoolean(
|
||||
LocalStorageSource.Get("studio-seen-tagrendering-tutorial", "false")
|
||||
);
|
||||
)
|
||||
onMount(() => {
|
||||
if (!hasSeenIntro.data) {
|
||||
state.showIntro.setData("tagrenderings");
|
||||
hasSeenIntro.setData(true);
|
||||
state.showIntro.setData("tagrenderings")
|
||||
hasSeenIntro.setData(true)
|
||||
}
|
||||
});
|
||||
})
|
||||
/**
|
||||
* Allows the theme builder to create 'writable' themes.
|
||||
* Should only be enabled for 'tagrenderings' in the theme, if the source is OSM
|
||||
*/
|
||||
let allowQuestions: Store<boolean> = state.configuration.mapD(
|
||||
(config) => path.at(0) === "tagRenderings" && config.source?.geoJson === undefined
|
||||
);
|
||||
)
|
||||
|
||||
let mappingsBuiltin: MappingConfigJson[] = [];
|
||||
let perLabel: Record<string, MappingConfigJson> = {};
|
||||
let mappingsBuiltin: MappingConfigJson[] = []
|
||||
let perLabel: Record<string, MappingConfigJson> = {}
|
||||
for (const tr of questions.tagRenderings) {
|
||||
let description = tr["description"] ?? tr["question"] ?? "No description available";
|
||||
description = description["en"] ?? description;
|
||||
let description = tr["description"] ?? tr["question"] ?? "No description available"
|
||||
description = description["en"] ?? description
|
||||
if (tr["labels"]) {
|
||||
const labels: string[] = tr["labels"];
|
||||
const labels: string[] = tr["labels"]
|
||||
for (const label of labels) {
|
||||
let labelMapping: MappingConfigJson = perLabel[label];
|
||||
let labelMapping: MappingConfigJson = perLabel[label]
|
||||
|
||||
if (!labelMapping) {
|
||||
labelMapping = {
|
||||
if: "value=" + label,
|
||||
then: {
|
||||
en: "Builtin collection <b>" + label + "</b>:"
|
||||
}
|
||||
};
|
||||
perLabel[label] = labelMapping;
|
||||
mappingsBuiltin.push(labelMapping);
|
||||
en: "Builtin collection <b>" + label + "</b>:",
|
||||
},
|
||||
}
|
||||
perLabel[label] = labelMapping
|
||||
mappingsBuiltin.push(labelMapping)
|
||||
}
|
||||
labelMapping.then.en = labelMapping.then.en + "<div>" + description + "</div>";
|
||||
labelMapping.then.en = labelMapping.then.en + "<div>" + description + "</div>"
|
||||
}
|
||||
}
|
||||
|
||||
mappingsBuiltin.push({
|
||||
if: "value=" + tr["id"],
|
||||
then: {
|
||||
en: "Builtin <b>" + tr["id"] + "</b> <div class='subtle'>" + description + "</div>"
|
||||
}
|
||||
});
|
||||
en: "Builtin <b>" + tr["id"] + "</b> <div class='subtle'>" + description + "</div>",
|
||||
},
|
||||
})
|
||||
}
|
||||
|
||||
const configBuiltin = new TagRenderingConfig(<QuestionableTagRenderingConfigJson>{
|
||||
question: "Which builtin element should be shown?",
|
||||
mappings: mappingsBuiltin
|
||||
});
|
||||
mappings: mappingsBuiltin,
|
||||
})
|
||||
|
||||
const tags = new UIEventSource({ value });
|
||||
const tags = new UIEventSource({ value })
|
||||
|
||||
tags.addCallbackAndRunD((tgs) => {
|
||||
store.setData(tgs["value"]);
|
||||
});
|
||||
store.setData(tgs["value"])
|
||||
})
|
||||
|
||||
let mappings: UIEventSource<MappingConfigJson[]> = state.getStoreFor([...path, "mappings"]);
|
||||
let mappings: UIEventSource<MappingConfigJson[]> = state.getStoreFor([...path, "mappings"])
|
||||
|
||||
const topLevelItems: Record<string, ConfigMeta> = {};
|
||||
const topLevelItems: Record<string, ConfigMeta> = {}
|
||||
for (const item of questionableTagRenderingSchemaRaw) {
|
||||
if (item.path.length === 1) {
|
||||
topLevelItems[item.path[0]] = <ConfigMeta>item;
|
||||
topLevelItems[item.path[0]] = <ConfigMeta>item
|
||||
}
|
||||
}
|
||||
|
||||
function initMappings() {
|
||||
if (mappings.data === undefined) {
|
||||
mappings.setData([]);
|
||||
mappings.setData([])
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -113,28 +113,25 @@
|
|||
"condition",
|
||||
"metacondition",
|
||||
"mappings",
|
||||
"icon"
|
||||
]);
|
||||
const ignored = new Set(["labels", "description", "classes"]);
|
||||
"icon",
|
||||
])
|
||||
const ignored = new Set(["labels", "description", "classes"])
|
||||
|
||||
const freeformSchemaAll = <ConfigMeta[]>(
|
||||
questionableTagRenderingSchemaRaw.filter(
|
||||
(schema) =>
|
||||
schema.path.length == 2 &&
|
||||
schema.path[0] === "freeform" &&
|
||||
($allowQuestions)
|
||||
(schema) => schema.path.length == 2 && schema.path[0] === "freeform" && $allowQuestions
|
||||
)
|
||||
);
|
||||
)
|
||||
let freeformSchema = $expertMode
|
||||
? freeformSchemaAll
|
||||
: freeformSchemaAll.filter((schema) => schema.hints?.group !== "expert");
|
||||
: freeformSchemaAll.filter((schema) => schema.hints?.group !== "expert")
|
||||
const missing: string[] = questionableTagRenderingSchemaRaw
|
||||
.filter(
|
||||
(schema) =>
|
||||
schema.path.length >= 1 && !items.has(schema.path[0]) && !ignored.has(schema.path[0])
|
||||
)
|
||||
.map((schema) => schema.path.join("."));
|
||||
console.log({ state });
|
||||
.map((schema) => schema.path.join("."))
|
||||
console.log({ state })
|
||||
</script>
|
||||
|
||||
{#if typeof $store === "string"}
|
||||
|
|
|
|||
|
|
@ -15,7 +15,6 @@
|
|||
import If from "./Base/If.svelte"
|
||||
import BackButton from "./Base/BackButton.svelte"
|
||||
import ChooseLayerToEdit from "./Studio/ChooseLayerToEdit.svelte"
|
||||
import { LocalStorageSource } from "../Logic/Web/LocalStorageSource"
|
||||
import FloatOver from "./Base/FloatOver.svelte"
|
||||
import Walkthrough from "./Walkthrough/Walkthrough.svelte"
|
||||
import * as intro from "../assets/studio_introduction.json"
|
||||
|
|
@ -26,10 +25,10 @@
|
|||
import EditTheme from "./Studio/EditTheme.svelte"
|
||||
import * as meta from "../../package.json"
|
||||
import Checkbox from "./Base/Checkbox.svelte"
|
||||
import { Utils } from "../Utils";
|
||||
import Translations from "./i18n/Translations";
|
||||
import Tr from "./Base/Tr.svelte";
|
||||
import Add from "../assets/svg/Add.svelte";
|
||||
import { Utils } from "../Utils"
|
||||
import Translations from "./i18n/Translations"
|
||||
import Tr from "./Base/Tr.svelte"
|
||||
import Add from "../assets/svg/Add.svelte"
|
||||
|
||||
export let studioUrl =
|
||||
window.location.hostname === "127.0.0.2"
|
||||
|
|
@ -155,7 +154,7 @@
|
|||
Contact <a href="https://app.element.io/#/room/#MapComplete:matrix.org">
|
||||
the MapComplete community via the chat.
|
||||
</a>
|
||||
Someone might be able to help you
|
||||
Someone might be able to help you
|
||||
</li>
|
||||
<li>
|
||||
File <a href="https://github.com/pietervdvn/MapComplete/issues">an issue</a>
|
||||
|
|
@ -197,7 +196,7 @@
|
|||
<QuestionMarkCircleIcon class="h-6 w-6" />
|
||||
Show the introduction again
|
||||
</button>
|
||||
<a class="flex button" href={Utils.HomepageLink()}>
|
||||
<a class="button flex" href={Utils.HomepageLink()}>
|
||||
<Add class="h-6 w-6" />
|
||||
<Tr t={Translations.t.general.backToIndex} />
|
||||
</a>
|
||||
|
|
@ -261,7 +260,7 @@
|
|||
<Loading />
|
||||
</div>
|
||||
{:else if state === "editing_layer"}
|
||||
<EditLayer state={editLayerState}>
|
||||
<EditLayer state={editLayerState} backToStudio={() => {state = undefined}}>
|
||||
<BackButton
|
||||
clss="small p-1"
|
||||
imageClass="w-8 h-8"
|
||||
|
|
@ -285,22 +284,23 @@
|
|||
</BackButton>
|
||||
</EditTheme>
|
||||
{/if}
|
||||
</LoginToggle>
|
||||
</If>
|
||||
|
||||
{#if { intro, tagrenderings: intro_tagrenderings }[$showIntro]?.sections}
|
||||
<FloatOver
|
||||
on:close={() => {
|
||||
{#if { intro, tagrenderings: intro_tagrenderings }[$showIntro]?.sections}
|
||||
<FloatOver
|
||||
on:close={() => {
|
||||
showIntro.setData("no")
|
||||
}}
|
||||
>
|
||||
<div class="flex h-full p-4 pr-12">
|
||||
<Walkthrough
|
||||
pages={{ intro, tagrenderings: intro_tagrenderings }[$showIntro]?.sections}
|
||||
on:done={() => {
|
||||
>
|
||||
<div class="flex h-full p-4 pr-12">
|
||||
<Walkthrough
|
||||
pages={{ intro, tagrenderings: intro_tagrenderings }[$showIntro]?.sections}
|
||||
on:done={() => {
|
||||
showIntro.setData("no")
|
||||
}}
|
||||
/>
|
||||
</div>
|
||||
</FloatOver>
|
||||
{/if}
|
||||
/>
|
||||
</div>
|
||||
</FloatOver>
|
||||
{/if}
|
||||
|
||||
</LoginToggle>
|
||||
</If>
|
||||
|
|
|
|||
|
|
@ -1,8 +1,8 @@
|
|||
<script lang="ts">
|
||||
import Svg from "../Svg";
|
||||
import Loading from "./Base/Loading.svelte";
|
||||
import ToSvelte from "./Base/ToSvelte.svelte";
|
||||
import Community from "../assets/svg/Community.svelte";
|
||||
import Svg from "../Svg"
|
||||
import Loading from "./Base/Loading.svelte"
|
||||
import ToSvelte from "./Base/ToSvelte.svelte"
|
||||
import Community from "../assets/svg/Community.svelte"
|
||||
</script>
|
||||
|
||||
<div>
|
||||
|
|
@ -41,30 +41,29 @@
|
|||
|
||||
<div class="flex">
|
||||
<button class="primary">
|
||||
<Community class="w-6 h-6" />
|
||||
<Community class="h-6 w-6" />
|
||||
Main action
|
||||
</button>
|
||||
<button class="primary disabled">
|
||||
<Community class="w-6 h-6" />
|
||||
<Community class="h-6 w-6" />
|
||||
Main action (disabled)
|
||||
</button>
|
||||
|
||||
<button class="small">
|
||||
<Community class="w-6 h-6" />
|
||||
<Community class="h-6 w-6" />
|
||||
Small button
|
||||
</button>
|
||||
|
||||
<button class="small primary">Small button</button>
|
||||
<button class="small primary disabled">Small, disabled button</button>
|
||||
|
||||
</div>
|
||||
<div class="flex">
|
||||
<button>
|
||||
<Community class="w-6 h-6" />
|
||||
<Community class="h-6 w-6" />
|
||||
Secondary action
|
||||
</button>
|
||||
<button class="disabled">
|
||||
<Community class="w-6 h-6" />
|
||||
<Community class="h-6 w-6" />
|
||||
Secondary action (disabled)
|
||||
</button>
|
||||
</div>
|
||||
|
|
@ -83,7 +82,7 @@
|
|||
</label>
|
||||
<label for="javascript">
|
||||
<input id="javascript" name="fav_language" type="radio" value="JavaScript" />
|
||||
<Community class="w-8 h-8"/>
|
||||
<Community class="h-8 w-8" />
|
||||
JavaScript
|
||||
</label>
|
||||
</div>
|
||||
|
|
@ -107,26 +106,26 @@
|
|||
|
||||
<div class="flex">
|
||||
<button class="primary">
|
||||
<Community class="w-6 h-6" />
|
||||
<Community class="h-6 w-6" />
|
||||
Main action
|
||||
</button>
|
||||
<button class="primary disabled">
|
||||
<Community class="w-6 h-6" />
|
||||
<Community class="h-6 w-6" />
|
||||
Main action (disabled)
|
||||
</button>
|
||||
<button class="small">
|
||||
<Community class="w-6 h-6" />
|
||||
<Community class="h-6 w-6" />
|
||||
Small button
|
||||
</button>
|
||||
</div>
|
||||
|
||||
<div class="flex">
|
||||
<button>
|
||||
<Community class="w-6 h-6" />
|
||||
<Community class="h-6 w-6" />
|
||||
Secondary action
|
||||
</button>
|
||||
<button class="disabled">
|
||||
<Community class="w-6 h-6" />
|
||||
<Community class="h-6 w-6" />
|
||||
Secondary action (disabled)
|
||||
</button>
|
||||
</div>
|
||||
|
|
|
|||
|
|
@ -1,16 +1,5 @@
|
|||
<script lang="ts">
|
||||
// Testing grounds
|
||||
import MarkAsFavourite from "./Popup/MarkAsFavourite.svelte";
|
||||
import { UIEventSource } from "../Logic/UIEventSource";
|
||||
import { OsmConnection } from "../Logic/Osm/OsmConnection";
|
||||
|
||||
const osmConnection = new OsmConnection({attemptLogin: true})
|
||||
function resetFavs(){
|
||||
osmConnection.preferencesHandler.removeAllWithPrefix("mapcomplete-favourite-")
|
||||
console.log("CLEARED!")
|
||||
}
|
||||
|
||||
</script>
|
||||
|
||||
|
||||
<button on:click={() => resetFavs()} >Clear</button>
|
||||
<div class="w-full">No tests</div>
|
||||
|
|
|
|||
|
|
@ -65,13 +65,78 @@
|
|||
import Download from "../assets/svg/Download.svelte";
|
||||
import Share from "../assets/svg/Share.svelte";
|
||||
import Favourites from "./Favourites/Favourites.svelte";
|
||||
import { Store, UIEventSource } from "../Logic/UIEventSource"
|
||||
import { Map as MlMap } from "maplibre-gl"
|
||||
import MaplibreMap from "./Map/MaplibreMap.svelte"
|
||||
import FeatureSwitchState from "../Logic/State/FeatureSwitchState"
|
||||
import MapControlButton from "./Base/MapControlButton.svelte"
|
||||
import ToSvelte from "./Base/ToSvelte.svelte"
|
||||
import If from "./Base/If.svelte"
|
||||
import { GeolocationControl } from "./BigComponents/GeolocationControl"
|
||||
import type { Feature } from "geojson"
|
||||
import SelectedElementView from "./BigComponents/SelectedElementView.svelte"
|
||||
import LayerConfig from "../Models/ThemeConfig/LayerConfig"
|
||||
import Filterview from "./BigComponents/Filterview.svelte"
|
||||
import ThemeViewState from "../Models/ThemeViewState"
|
||||
import type { MapProperties } from "../Models/MapProperties"
|
||||
import Geosearch from "./BigComponents/Geosearch.svelte"
|
||||
import Translations from "./i18n/Translations"
|
||||
import { CogIcon, EyeIcon, MenuIcon, XCircleIcon } from "@rgossiaux/svelte-heroicons/solid"
|
||||
import Tr from "./Base/Tr.svelte"
|
||||
import CommunityIndexView from "./BigComponents/CommunityIndexView.svelte"
|
||||
import FloatOver from "./Base/FloatOver.svelte"
|
||||
import PrivacyPolicy from "./BigComponents/PrivacyPolicy"
|
||||
import Constants from "../Models/Constants"
|
||||
import TabbedGroup from "./Base/TabbedGroup.svelte"
|
||||
import UserRelatedState from "../Logic/State/UserRelatedState"
|
||||
import LoginToggle from "./Base/LoginToggle.svelte"
|
||||
import LoginButton from "./Base/LoginButton.svelte"
|
||||
import CopyrightPanel from "./BigComponents/CopyrightPanel"
|
||||
import DownloadPanel from "./DownloadFlow/DownloadPanel.svelte"
|
||||
import ModalRight from "./Base/ModalRight.svelte"
|
||||
import { Utils } from "../Utils"
|
||||
import Hotkeys from "./Base/Hotkeys"
|
||||
import { VariableUiElement } from "./Base/VariableUIElement"
|
||||
import SvelteUIElement from "./Base/SvelteUIElement"
|
||||
import OverlayToggle from "./BigComponents/OverlayToggle.svelte"
|
||||
import LevelSelector from "./BigComponents/LevelSelector.svelte"
|
||||
import ExtraLinkButton from "./BigComponents/ExtraLinkButton"
|
||||
import SelectedElementTitle from "./BigComponents/SelectedElementTitle.svelte"
|
||||
import 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 Mastodon from "../assets/svg/Mastodon.svelte"
|
||||
import Bug from "../assets/svg/Bug.svelte"
|
||||
import Liberapay from "../assets/svg/Liberapay.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 LanguagePicker from "./InputElement/LanguagePicker.svelte"
|
||||
import OpenJosm from "./Base/OpenJosm.svelte"
|
||||
|
||||
export let state: ThemeViewState;
|
||||
let layout = state.layout;
|
||||
export let state: ThemeViewState
|
||||
let layout = state.layout
|
||||
|
||||
let maplibremap: UIEventSource<MlMap> = state.map;
|
||||
let selectedElement: UIEventSource<Feature> = state.selectedElement;
|
||||
let selectedLayer: UIEventSource<LayerConfig> = state.selectedLayer;
|
||||
let maplibremap: UIEventSource<MlMap> = state.map
|
||||
let selectedElement: UIEventSource<Feature> = state.selectedElement
|
||||
let selectedLayer: UIEventSource<LayerConfig> = state.selectedLayer
|
||||
|
||||
let currentZoom = state.mapProperties.zoom;
|
||||
let showCrosshair = state.userRelatedState.showCrosshair;
|
||||
|
|
@ -87,50 +152,50 @@
|
|||
return undefined;
|
||||
}
|
||||
|
||||
if (!(layer.tagRenderings?.length > 0) || layer.title === undefined) {
|
||||
return undefined;
|
||||
}
|
||||
if (!(layer.tagRenderings?.length > 0) || layer.title === undefined) {
|
||||
return undefined
|
||||
}
|
||||
|
||||
const tags = state.featureProperties.getStore(selectedElement.properties.id);
|
||||
return new SvelteUIElement(SelectedElementView, {
|
||||
state,
|
||||
layer,
|
||||
selectedElement,
|
||||
tags
|
||||
}).SetClass("h-full w-full");
|
||||
},
|
||||
[selectedLayer]
|
||||
);
|
||||
const tags = state.featureProperties.getStore(selectedElement.properties.id)
|
||||
return new SvelteUIElement(SelectedElementView, {
|
||||
state,
|
||||
layer,
|
||||
selectedElement,
|
||||
tags,
|
||||
}).SetClass("h-full w-full")
|
||||
},
|
||||
[selectedLayer],
|
||||
)
|
||||
|
||||
const selectedElementTitle = selectedElement.map(
|
||||
(selectedElement) => {
|
||||
// Svelte doesn't properly reload some of the legacy UI-elements
|
||||
// As such, we _reconstruct_ the selectedElementView every time a new feature is selected
|
||||
// This is a bit wasteful, but until everything is a svelte-component, this should do the trick
|
||||
const layer = selectedLayer.data;
|
||||
if (selectedElement === undefined || layer === undefined) {
|
||||
return undefined;
|
||||
}
|
||||
const selectedElementTitle = selectedElement.map(
|
||||
(selectedElement) => {
|
||||
// Svelte doesn't properly reload some of the legacy UI-elements
|
||||
// As such, we _reconstruct_ the selectedElementView every time a new feature is selected
|
||||
// This is a bit wasteful, but until everything is a svelte-component, this should do the trick
|
||||
const layer = selectedLayer.data
|
||||
if (selectedElement === undefined || layer === undefined) {
|
||||
return undefined
|
||||
}
|
||||
|
||||
const tags = state.featureProperties.getStore(selectedElement.properties.id);
|
||||
return new SvelteUIElement(SelectedElementTitle, { state, layer, selectedElement, tags });
|
||||
},
|
||||
[selectedLayer]
|
||||
);
|
||||
const tags = state.featureProperties.getStore(selectedElement.properties.id)
|
||||
return new SvelteUIElement(SelectedElementTitle, { state, layer, selectedElement, tags })
|
||||
},
|
||||
[selectedLayer],
|
||||
)
|
||||
|
||||
let mapproperties: MapProperties = state.mapProperties;
|
||||
let featureSwitches: FeatureSwitchState = state.featureSwitches;
|
||||
let availableLayers = state.availableLayers;
|
||||
let userdetails = state.osmConnection.userDetails;
|
||||
let currentViewLayer = layout.layers.find((l) => l.id === "current_view");
|
||||
let rasterLayer: Store<RasterLayerPolygon> = state.mapProperties.rasterLayer;
|
||||
let rasterLayerName =
|
||||
rasterLayer.data?.properties?.name ?? AvailableRasterLayers.maptilerDefaultLayer.properties.name;
|
||||
onDestroy(
|
||||
rasterLayer.addCallbackAndRunD((l) => {
|
||||
rasterLayerName = l.properties.name;
|
||||
})
|
||||
);
|
||||
let mapproperties: MapProperties = state.mapProperties
|
||||
let featureSwitches: FeatureSwitchState = state.featureSwitches
|
||||
let availableLayers = state.availableLayers
|
||||
let userdetails = state.osmConnection.userDetails
|
||||
let currentViewLayer = layout.layers.find((l) => l.id === "current_view")
|
||||
let rasterLayer: Store<RasterLayerPolygon> = state.mapProperties.rasterLayer
|
||||
let rasterLayerName =
|
||||
rasterLayer.data?.properties?.name ?? AvailableRasterLayers.maptilerDefaultLayer.properties.name
|
||||
onDestroy(
|
||||
rasterLayer.addCallbackAndRunD((l) => {
|
||||
rasterLayerName = l.properties.name
|
||||
}),
|
||||
)
|
||||
</script>
|
||||
|
||||
<div class="absolute top-0 left-0 h-screen w-screen overflow-hidden">
|
||||
|
|
@ -214,7 +279,7 @@
|
|||
<!-- bottom left elements -->
|
||||
<If condition={state.featureSwitches.featureSwitchFilter}>
|
||||
<MapControlButton on:click={() => state.guistate.openFilterView()}>
|
||||
<Filter class="h-6 w-6"/>
|
||||
<Filter class="h-6 w-6" />
|
||||
</MapControlButton>
|
||||
</If>
|
||||
<If condition={state.featureSwitches.featureSwitchBackgroundSelection}>
|
||||
|
|
@ -253,10 +318,10 @@
|
|||
</div>
|
||||
</If>
|
||||
<MapControlButton on:click={() => mapproperties.zoom.update((z) => z + 1)}>
|
||||
<Plus class="w-8 h-8" />
|
||||
<Plus class="h-8 w-8" />
|
||||
</MapControlButton>
|
||||
<MapControlButton on:click={() => mapproperties.zoom.update((z) => z - 1)}>
|
||||
<Min class="w-8 h-8"/>
|
||||
<Min class="h-8 w-8" />
|
||||
</MapControlButton>
|
||||
<If condition={featureSwitches.featureSwitchGeolocation}>
|
||||
<MapControlButton>
|
||||
|
|
@ -272,7 +337,7 @@
|
|||
</div>
|
||||
|
||||
<LoginToggle ignoreLoading={true} {state}>
|
||||
{#if ($showCrosshair === "yes" && $currentZoom >= 17) || $showCrosshair === "always" || $arrowKeysWereUsed !== undefined }
|
||||
{#if ($showCrosshair === "yes" && $currentZoom >= 17) || $showCrosshair === "always" || $arrowKeysWereUsed !== undefined}
|
||||
<div
|
||||
class="pointer-events-none absolute top-0 left-0 flex h-full w-full items-center justify-center"
|
||||
>
|
||||
|
|
@ -349,7 +414,7 @@
|
|||
</div>
|
||||
|
||||
<div class="flex" slot="title1">
|
||||
<Filter class="w-4 h-4"/>
|
||||
<Filter class="h-4 w-4" />
|
||||
<Tr t={Translations.t.general.menu.filter} />
|
||||
</div>
|
||||
|
||||
|
|
@ -373,7 +438,7 @@
|
|||
|
||||
<div class="flex" slot="title2">
|
||||
<If condition={state.featureSwitches.featureSwitchEnableExport}>
|
||||
<Download class="w-4 h-4"/>
|
||||
<Download class="h-4 w-4" />
|
||||
<Tr t={Translations.t.general.download.title} />
|
||||
</If>
|
||||
</div>
|
||||
|
|
@ -388,7 +453,7 @@
|
|||
<ToSvelte construct={() => new CopyrightPanel(state)} slot="content3" />
|
||||
|
||||
<div class="flex" slot="title4">
|
||||
<Share class="w-4 h-4"/>
|
||||
<Share class="h-4 w-4" />
|
||||
<Tr t={Translations.t.general.sharescreen.title} />
|
||||
</div>
|
||||
<div class="m-2" slot="content4">
|
||||
|
|
@ -440,17 +505,17 @@
|
|||
<Tr t={Translations.t.general.aboutMapComplete.intro} />
|
||||
|
||||
<a class="flex" href={Utils.HomepageLink()}>
|
||||
<Add class="h-6 w-6"/>
|
||||
<Add class="h-6 w-6" />
|
||||
<Tr t={Translations.t.general.backToIndex} />
|
||||
</a>
|
||||
|
||||
<a class="flex" href="https://github.com/pietervdvn/MapComplete/issues" target="_blank">
|
||||
<Bug class="h-6 w-6"/>
|
||||
<Bug class="h-6 w-6" />
|
||||
<Tr t={Translations.t.general.attribution.openIssueTracker} />
|
||||
</a>
|
||||
|
||||
<a class="flex" href="https://en.osm.town/@MapComplete" target="_blank">
|
||||
<Mastodon class="w-6 h-6" />
|
||||
<Mastodon class="h-6 w-6" />
|
||||
<Tr t={Translations.t.general.attribution.followOnMastodon} />
|
||||
</a>
|
||||
|
||||
|
|
@ -495,15 +560,15 @@
|
|||
|
||||
<div class="flex" slot="title2">
|
||||
<HeartIcon class="h-6 w-6" />
|
||||
Your favourites
|
||||
<Tr t={Translations.t.favouritePoi.tab}/>
|
||||
</div>
|
||||
|
||||
<div class="flex flex-col m-2" slot="content2">
|
||||
<h3>Your favourite locations</h3>
|
||||
<h3> <Tr t={Translations.t.favouritePoi.title}/></h3>
|
||||
<Favourites {state}/>
|
||||
</div>
|
||||
<div class="flex" slot="title3">
|
||||
<Community class="w-6 h-6"/>
|
||||
<Community class="h-6 w-6" />
|
||||
<Tr t={Translations.t.communityIndex.title} />
|
||||
</div>
|
||||
<div class="m-2" slot="content3">
|
||||
|
|
@ -521,7 +586,7 @@
|
|||
<div class="m-2 flex flex-col" slot="content5">
|
||||
<If condition={featureSwitches.featureSwitchEnableLogin}>
|
||||
<OpenIdEditor mapProperties={state.mapProperties} />
|
||||
<OpenJosm {state}/>
|
||||
<OpenJosm {state} />
|
||||
<MapillaryLink mapProperties={state.mapProperties} />
|
||||
</If>
|
||||
|
||||
|
|
|
|||
|
|
@ -1,25 +1,24 @@
|
|||
<script lang="ts">
|
||||
import BackButton from "../Base/BackButton.svelte";
|
||||
import NextButton from "../Base/NextButton.svelte";
|
||||
import { createEventDispatcher } from "svelte";
|
||||
import BackButton from "../Base/BackButton.svelte"
|
||||
import NextButton from "../Base/NextButton.svelte"
|
||||
import { createEventDispatcher } from "svelte"
|
||||
|
||||
const dispatch = createEventDispatcher<{ back; next }>();
|
||||
export let islast = false;
|
||||
export let isFirst = false;
|
||||
export let pageNumber: number = undefined;
|
||||
export let totalPages: number = undefined;
|
||||
const dispatch = createEventDispatcher<{ back; next }>()
|
||||
export let islast = false
|
||||
export let isFirst = false
|
||||
export let pageNumber: number = undefined
|
||||
export let totalPages: number = undefined
|
||||
</script>
|
||||
|
||||
<div class="flex h-full w-full flex-col justify-between link-underline">
|
||||
<div class="link-underline flex h-full w-full flex-col justify-between">
|
||||
<div class="overflow-y-auto">
|
||||
<slot />
|
||||
</div>
|
||||
|
||||
<div class="flex flex-col">
|
||||
|
||||
{#if pageNumber !== undefined && totalPages !== undefined}
|
||||
<div class="flex justify-end">
|
||||
<div class="subtle">{pageNumber+1}/{totalPages}</div>
|
||||
<div class="subtle">{pageNumber + 1}/{totalPages}</div>
|
||||
</div>
|
||||
{/if}
|
||||
<div class="flex w-full">
|
||||
|
|
@ -35,8 +34,6 @@
|
|||
Next
|
||||
{/if}
|
||||
</NextButton>
|
||||
|
||||
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
|
|
|||
|
|
@ -9,7 +9,7 @@
|
|||
import WikidataPreviewBox from "./WikidataPreviewBox"
|
||||
import Tr from "../Base/Tr.svelte"
|
||||
import Translations from "../i18n/Translations"
|
||||
import Wikipedia from "../../assets/svg/Wikipedia.svelte";
|
||||
import Wikipedia from "../../assets/svg/Wikipedia.svelte"
|
||||
|
||||
/**
|
||||
* Shows a wikipedia-article + wikidata preview for the given item
|
||||
|
|
@ -19,7 +19,7 @@
|
|||
|
||||
{#if $wikipediaDetails.articleUrl}
|
||||
<a class="flex" href={$wikipediaDetails.articleUrl} rel="noreferrer" target="_blank">
|
||||
<Wikipedia class="h-6 w-6"/>
|
||||
<Wikipedia class="h-6 w-6" />
|
||||
<Tr t={Translations.t.general.wikipedia.fromWikipedia} />
|
||||
</a>
|
||||
{/if}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue