forked from MapComplete/MapComplete
Merge master
This commit is contained in:
commit
e060bd3288
67 changed files with 1417 additions and 1010 deletions
|
@ -5,9 +5,10 @@ import { Utils } from "../../Utils"
|
|||
import { Feature } from "geojson"
|
||||
|
||||
export default class PendingChangesUploader {
|
||||
|
||||
constructor(changes: Changes, selectedFeature: UIEventSource<Feature>) {
|
||||
changes.pendingChanges.stabilized(Constants.updateTimeoutSec * 1000).addCallback(() => changes.flushChanges("Flushing changes due to timeout"))
|
||||
changes.pendingChanges
|
||||
.stabilized(Constants.updateTimeoutSec * 1000)
|
||||
.addCallback(() => changes.flushChanges("Flushing changes due to timeout"))
|
||||
|
||||
selectedFeature.stabilized(1000).addCallback((feature) => {
|
||||
if (feature === undefined) {
|
||||
|
|
|
@ -1,47 +1,47 @@
|
|||
import LayoutConfig from "../../../Models/ThemeConfig/LayoutConfig";
|
||||
import { WritableFeatureSource } from "../FeatureSource";
|
||||
import { ImmutableStore, Store, UIEventSource } from "../../UIEventSource";
|
||||
import { Feature, Point } from "geojson";
|
||||
import { TagUtils } from "../../Tags/TagUtils";
|
||||
import BaseUIElement from "../../../UI/BaseUIElement";
|
||||
import { Utils } from "../../../Utils";
|
||||
import { OsmTags } from "../../../Models/OsmFeature";
|
||||
import LayoutConfig from "../../../Models/ThemeConfig/LayoutConfig"
|
||||
import { WritableFeatureSource } from "../FeatureSource"
|
||||
import { ImmutableStore, Store, UIEventSource } from "../../UIEventSource"
|
||||
import { Feature, Point } from "geojson"
|
||||
import { TagUtils } from "../../Tags/TagUtils"
|
||||
import BaseUIElement from "../../../UI/BaseUIElement"
|
||||
import { Utils } from "../../../Utils"
|
||||
import { OsmTags } from "../../../Models/OsmFeature"
|
||||
|
||||
/**
|
||||
* Highly specialized feature source.
|
||||
* Based on a lon/lat UIEVentSource, will generate the corresponding feature with the correct properties
|
||||
*/
|
||||
export class LastClickFeatureSource implements WritableFeatureSource {
|
||||
public readonly features: UIEventSource<Feature[]> = new UIEventSource<Feature[]>([]);
|
||||
public readonly hasNoteLayer: boolean;
|
||||
public readonly renderings: string[];
|
||||
public readonly hasPresets: boolean;
|
||||
private i: number = 0;
|
||||
public readonly features: UIEventSource<Feature[]> = new UIEventSource<Feature[]>([])
|
||||
public readonly hasNoteLayer: boolean
|
||||
public readonly renderings: string[]
|
||||
public readonly hasPresets: boolean
|
||||
private i: number = 0
|
||||
|
||||
constructor(location: Store<{ lon: number; lat: number }>, layout: LayoutConfig) {
|
||||
this.hasNoteLayer = layout.layers.some((l) => l.id === "note");
|
||||
this.hasPresets = layout.layers.some((l) => l.presets?.length > 0);
|
||||
const allPresets: BaseUIElement[] = [];
|
||||
this.hasNoteLayer = layout.layers.some((l) => l.id === "note")
|
||||
this.hasPresets = layout.layers.some((l) => l.presets?.length > 0)
|
||||
const allPresets: BaseUIElement[] = []
|
||||
for (const layer of layout.layers)
|
||||
for (let i = 0; i < (layer.presets ?? []).length; i++) {
|
||||
const preset = layer.presets[i];
|
||||
const tags = new ImmutableStore(TagUtils.KVtoProperties(preset.tags));
|
||||
const preset = layer.presets[i]
|
||||
const tags = new ImmutableStore(TagUtils.KVtoProperties(preset.tags))
|
||||
const { html } = layer.mapRendering[0].RenderIcon(tags, false, {
|
||||
noSize: true,
|
||||
includeBadges: false
|
||||
});
|
||||
allPresets.push(html);
|
||||
includeBadges: false,
|
||||
})
|
||||
allPresets.push(html)
|
||||
}
|
||||
|
||||
this.renderings = Utils.Dedup(
|
||||
allPresets.map((uiElem) =>
|
||||
Utils.runningFromConsole ? "" : uiElem.ConstructElement().innerHTML
|
||||
)
|
||||
);
|
||||
)
|
||||
|
||||
location.addCallbackAndRunD(({ lon, lat }) => {
|
||||
this.features.setData([this.createFeature(lon, lat)]);
|
||||
});
|
||||
this.features.setData([this.createFeature(lon, lat)])
|
||||
})
|
||||
}
|
||||
|
||||
public createFeature(lon: number, lat: number): Feature<Point, OsmTags> {
|
||||
|
@ -52,17 +52,17 @@ export class LastClickFeatureSource implements WritableFeatureSource {
|
|||
has_presets: this.hasPresets ? "yes" : "no",
|
||||
renderings: this.renderings.join(""),
|
||||
number_of_presets: "" + this.renderings.length,
|
||||
first_preset: this.renderings[0]
|
||||
};
|
||||
this.i++;
|
||||
first_preset: this.renderings[0],
|
||||
}
|
||||
this.i++
|
||||
|
||||
return <Feature<Point, OsmTags>>{
|
||||
type: "Feature",
|
||||
properties,
|
||||
geometry: {
|
||||
type: "Point",
|
||||
coordinates: [lon, lat]
|
||||
}
|
||||
};
|
||||
coordinates: [lon, lat],
|
||||
},
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -314,7 +314,7 @@ export class GeoOperations {
|
|||
return <any>way
|
||||
}
|
||||
|
||||
public static toCSV(features: any[]): string {
|
||||
public static toCSV(features: Feature[] | FeatureCollection): string {
|
||||
const headerValuesSeen = new Set<string>()
|
||||
const headerValuesOrdered: string[] = []
|
||||
|
||||
|
@ -330,7 +330,14 @@ export class GeoOperations {
|
|||
|
||||
const lines: string[] = []
|
||||
|
||||
for (const feature of features) {
|
||||
let _features
|
||||
if(Array.isArray(features)){
|
||||
_features = features
|
||||
}else{
|
||||
_features = features.features
|
||||
}
|
||||
|
||||
for (const feature of _features) {
|
||||
const properties = feature.properties
|
||||
for (const key in properties) {
|
||||
if (!properties.hasOwnProperty(key)) {
|
||||
|
@ -340,7 +347,7 @@ export class GeoOperations {
|
|||
}
|
||||
}
|
||||
headerValuesOrdered.sort()
|
||||
for (const feature of features) {
|
||||
for (const feature of _features) {
|
||||
const properties = feature.properties
|
||||
let line = ""
|
||||
for (const key of headerValuesOrdered) {
|
||||
|
|
|
@ -579,7 +579,7 @@ export class Changes {
|
|||
)
|
||||
|
||||
const result = await self.flushSelectChanges(pendingChanges, openChangeset)
|
||||
if(result){
|
||||
if (result) {
|
||||
this.errors.setData([])
|
||||
}
|
||||
return result
|
||||
|
|
|
@ -1,14 +1,42 @@
|
|||
import { Utils } from "../../Utils"
|
||||
/** This code is autogenerated - do not edit. Edit ./assets/layers/usersettings/usersettings.json instead */
|
||||
export class ThemeMetaTagging {
|
||||
public static readonly themeName = "usersettings"
|
||||
public static readonly themeName = "usersettings"
|
||||
|
||||
public metaTaggging_for_usersettings(feat: {properties: Record<string, string>}) {
|
||||
Utils.AddLazyProperty(feat.properties, '_mastodon_candidate_md', () => feat.properties._description.match(/\[[^\]]*\]\((.*(mastodon|en.osm.town).*)\).*/)?.at(1) )
|
||||
Utils.AddLazyProperty(feat.properties, '_d', () => feat.properties._description?.replace(/</g,'<')?.replace(/>/g,'>') ?? '' )
|
||||
Utils.AddLazyProperty(feat.properties, '_mastodon_candidate_a', () => (feat => {const e = document.createElement('div');e.innerHTML = feat.properties._d;return Array.from(e.getElementsByTagName("a")).filter(a => a.href.match(/mastodon|en.osm.town/) !== null)[0]?.href }) (feat) )
|
||||
Utils.AddLazyProperty(feat.properties, '_mastodon_link', () => (feat => {const e = document.createElement('div');e.innerHTML = feat.properties._d;return Array.from(e.getElementsByTagName("a")).filter(a => a.getAttribute("rel")?.indexOf('me') >= 0)[0]?.href})(feat) )
|
||||
Utils.AddLazyProperty(feat.properties, '_mastodon_candidate', () => feat.properties._mastodon_candidate_md ?? feat.properties._mastodon_candidate_a )
|
||||
feat.properties['__current_backgroun'] = 'initial_value'
|
||||
}
|
||||
}
|
||||
public metaTaggging_for_usersettings(feat: { properties: Record<string, string> }) {
|
||||
Utils.AddLazyProperty(feat.properties, "_mastodon_candidate_md", () =>
|
||||
feat.properties._description
|
||||
.match(/\[[^\]]*\]\((.*(mastodon|en.osm.town).*)\).*/)
|
||||
?.at(1)
|
||||
)
|
||||
Utils.AddLazyProperty(
|
||||
feat.properties,
|
||||
"_d",
|
||||
() => feat.properties._description?.replace(/</g, "<")?.replace(/>/g, ">") ?? ""
|
||||
)
|
||||
Utils.AddLazyProperty(feat.properties, "_mastodon_candidate_a", () =>
|
||||
((feat) => {
|
||||
const e = document.createElement("div")
|
||||
e.innerHTML = feat.properties._d
|
||||
return Array.from(e.getElementsByTagName("a")).filter(
|
||||
(a) => a.href.match(/mastodon|en.osm.town/) !== null
|
||||
)[0]?.href
|
||||
})(feat)
|
||||
)
|
||||
Utils.AddLazyProperty(feat.properties, "_mastodon_link", () =>
|
||||
((feat) => {
|
||||
const e = document.createElement("div")
|
||||
e.innerHTML = feat.properties._d
|
||||
return Array.from(e.getElementsByTagName("a")).filter(
|
||||
(a) => a.getAttribute("rel")?.indexOf("me") >= 0
|
||||
)[0]?.href
|
||||
})(feat)
|
||||
)
|
||||
Utils.AddLazyProperty(
|
||||
feat.properties,
|
||||
"_mastodon_candidate",
|
||||
() => feat.properties._mastodon_candidate_md ?? feat.properties._mastodon_candidate_a
|
||||
)
|
||||
feat.properties["__current_backgroun"] = "initial_value"
|
||||
}
|
||||
}
|
||||
|
|
|
@ -6,7 +6,7 @@ import { AuthConfig } from "../Logic/Osm/AuthConfig"
|
|||
export type PriviligedLayerType = (typeof Constants.priviliged_layers)[number]
|
||||
|
||||
export default class Constants {
|
||||
public static vNumber : string = packagefile.version
|
||||
public static vNumber: string = packagefile.version
|
||||
/**
|
||||
* API key for Maproulette
|
||||
*
|
||||
|
|
|
@ -94,7 +94,7 @@ export default class LayoutConfig implements LayoutInformation {
|
|||
}
|
||||
const context = this.id
|
||||
this.credits = json.credits
|
||||
if(!json.title){
|
||||
if (!json.title) {
|
||||
throw `The theme ${json.id} does not have a title defined.`
|
||||
}
|
||||
this.language = json.mustHaveLanguage ?? Object.keys(json.title)
|
||||
|
|
File diff suppressed because it is too large
Load diff
|
@ -12,28 +12,30 @@
|
|||
let id = Math.random() * 1000000000 + ""
|
||||
</script>
|
||||
|
||||
<form on:change|preventDefault={() => {
|
||||
drawAttention = false
|
||||
dispatcher("submit", inputElement.files)
|
||||
}}
|
||||
on:dragend={() => {
|
||||
console.log("Drag end")
|
||||
drawAttention = false
|
||||
}}
|
||||
on:dragenter|preventDefault|stopPropagation={(e) => {
|
||||
console.log("Dragging enter")
|
||||
drawAttention = true
|
||||
e.dataTransfer.drop = "copy"
|
||||
}}
|
||||
on:dragstart={() => {
|
||||
console.log("DragStart")
|
||||
drawAttention = false
|
||||
}}
|
||||
on:drop|preventDefault|stopPropagation={(e) => {
|
||||
console.log("Got a 'drop'")
|
||||
drawAttention = false
|
||||
dispatcher("submit", e.dataTransfer.files)
|
||||
}}>
|
||||
<form
|
||||
on:change|preventDefault={() => {
|
||||
drawAttention = false
|
||||
dispatcher("submit", inputElement.files)
|
||||
}}
|
||||
on:dragend={() => {
|
||||
console.log("Drag end")
|
||||
drawAttention = false
|
||||
}}
|
||||
on:dragenter|preventDefault|stopPropagation={(e) => {
|
||||
console.log("Dragging enter")
|
||||
drawAttention = true
|
||||
e.dataTransfer.drop = "copy"
|
||||
}}
|
||||
on:dragstart={() => {
|
||||
console.log("DragStart")
|
||||
drawAttention = false
|
||||
}}
|
||||
on:drop|preventDefault|stopPropagation={(e) => {
|
||||
console.log("Got a 'drop'")
|
||||
drawAttention = false
|
||||
dispatcher("submit", e.dataTransfer.files)
|
||||
}}
|
||||
>
|
||||
<label class={twMerge(cls, drawAttention ? "glowing-shadow" : "")} for={"fileinput" + id}>
|
||||
<slot />
|
||||
</label>
|
||||
|
@ -44,7 +46,6 @@
|
|||
id={"fileinput" + id}
|
||||
{multiple}
|
||||
name="file-input"
|
||||
|
||||
type="file"
|
||||
/>
|
||||
</form>
|
||||
|
|
|
@ -11,7 +11,9 @@
|
|||
<div
|
||||
class="absolute top-0 right-0 h-screen w-screen p-4 md:p-6"
|
||||
style="background-color: #00000088"
|
||||
on:click={() => {dispatch("close")}}
|
||||
on:click={() => {
|
||||
dispatch("close")
|
||||
}}
|
||||
>
|
||||
<div class="content normal-background" on:click|stopPropagation={() => {}}>
|
||||
<div class="h-full rounded-xl">
|
||||
|
|
|
@ -23,11 +23,11 @@ export default class Hotkeys {
|
|||
>([])
|
||||
|
||||
private static textElementSelected(event: KeyboardEvent): boolean {
|
||||
if(event.ctrlKey || event.altKey){
|
||||
if (event.ctrlKey || event.altKey) {
|
||||
// This is an event with a modifier-key, lets not ignore it
|
||||
return false
|
||||
}
|
||||
if(event.key === "Escape"){
|
||||
if (event.key === "Escape") {
|
||||
return false // Another not-printable character that should not be ignored
|
||||
}
|
||||
return ["input", "textarea"].includes(document?.activeElement?.tagName?.toLowerCase())
|
||||
|
|
|
@ -1,32 +1,32 @@
|
|||
<script lang="ts">
|
||||
/**
|
||||
* Thin wrapper around 'TabGroup' which binds the state
|
||||
*/
|
||||
/**
|
||||
* Thin wrapper around 'TabGroup' which binds the state
|
||||
*/
|
||||
|
||||
import { Tab, TabGroup, TabList, TabPanel, TabPanels } from "@rgossiaux/svelte-headlessui";
|
||||
import { ImmutableStore, Store, UIEventSource } from "../../Logic/UIEventSource";
|
||||
import { twJoin } from "tailwind-merge";
|
||||
import { Tab, TabGroup, TabList, TabPanel, TabPanels } from "@rgossiaux/svelte-headlessui"
|
||||
import { ImmutableStore, Store, UIEventSource } from "../../Logic/UIEventSource"
|
||||
import { twJoin } from "tailwind-merge"
|
||||
|
||||
/**
|
||||
* If a condition is given for a certain tab, it will only be shown if this condition is true.
|
||||
* E.g.
|
||||
* condition3 = new ImmutableStore(false) will always hide tab3 (the fourth tab)
|
||||
*/
|
||||
let tr = new ImmutableStore(true)
|
||||
export let condition0: Store<boolean> = tr
|
||||
export let condition1: Store<boolean> = tr
|
||||
export let condition2: Store<boolean> = tr
|
||||
export let condition3: Store<boolean> = tr
|
||||
export let condition4: Store<boolean> = tr
|
||||
|
||||
export let tab: UIEventSource<number>;
|
||||
let tabElements: HTMLElement[] = [];
|
||||
$: tabElements[$tab]?.click();
|
||||
$: {
|
||||
if (tabElements[tab.data]) {
|
||||
window.setTimeout(() => tabElements[tab.data].click(), 50);
|
||||
}
|
||||
/**
|
||||
* If a condition is given for a certain tab, it will only be shown if this condition is true.
|
||||
* E.g.
|
||||
* condition3 = new ImmutableStore(false) will always hide tab3 (the fourth tab)
|
||||
*/
|
||||
let tr = new ImmutableStore(true)
|
||||
export let condition0: Store<boolean> = tr
|
||||
export let condition1: Store<boolean> = tr
|
||||
export let condition2: Store<boolean> = tr
|
||||
export let condition3: Store<boolean> = tr
|
||||
export let condition4: Store<boolean> = tr
|
||||
|
||||
export let tab: UIEventSource<number>
|
||||
let tabElements: HTMLElement[] = []
|
||||
$: tabElements[$tab]?.click()
|
||||
$: {
|
||||
if (tabElements[tab.data]) {
|
||||
window.setTimeout(() => tabElements[tab.data].click(), 50)
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<div class="tabbedgroup flex h-full w-full">
|
||||
|
@ -41,27 +41,37 @@
|
|||
>
|
||||
<div class="interactive sticky top-0 flex items-center justify-between">
|
||||
<TabList class="flex flex-wrap">
|
||||
<Tab class={({ selected }) => twJoin("tab", selected && "primary", !$condition0 && "hidden")}>
|
||||
<Tab
|
||||
class={({ selected }) => twJoin("tab", selected && "primary", !$condition0 && "hidden")}
|
||||
>
|
||||
<div bind:this={tabElements[0]} class="flex">
|
||||
<slot name="title0">Tab 0</slot>
|
||||
</div>
|
||||
</Tab>
|
||||
<Tab class={({ selected }) => twJoin("tab", selected && "primary", !$condition1 && "hidden")}>
|
||||
<Tab
|
||||
class={({ selected }) => twJoin("tab", selected && "primary", !$condition1 && "hidden")}
|
||||
>
|
||||
<div bind:this={tabElements[1]} class="flex">
|
||||
<slot name="title1" />
|
||||
</div>
|
||||
</Tab>
|
||||
<Tab class={({ selected }) => twJoin("tab", selected && "primary", !$condition2 && "hidden")}>
|
||||
<Tab
|
||||
class={({ selected }) => twJoin("tab", selected && "primary", !$condition2 && "hidden")}
|
||||
>
|
||||
<div bind:this={tabElements[2]} class="flex">
|
||||
<slot name="title2" />
|
||||
</div>
|
||||
</Tab>
|
||||
<Tab class={({ selected }) => twJoin("tab", selected && "primary", !$condition3 && "hidden")}>
|
||||
<Tab
|
||||
class={({ selected }) => twJoin("tab", selected && "primary", !$condition3 && "hidden")}
|
||||
>
|
||||
<div bind:this={tabElements[3]} class="flex">
|
||||
<slot name="title3" />
|
||||
</div>
|
||||
</Tab>
|
||||
<Tab class={({ selected }) => twJoin("tab", selected && "primary", !$condition4 && "hidden")}>
|
||||
<Tab
|
||||
class={({ selected }) => twJoin("tab", selected && "primary", !$condition4 && "hidden")}
|
||||
>
|
||||
<div bind:this={tabElements[4]} class="flex">
|
||||
<slot name="title4" />
|
||||
</div>
|
||||
|
@ -102,44 +112,44 @@
|
|||
</div>
|
||||
|
||||
<style>
|
||||
.tabbedgroup {
|
||||
max-height: 100vh;
|
||||
height: 100%;
|
||||
}
|
||||
.tabbedgroup {
|
||||
max-height: 100vh;
|
||||
height: 100%;
|
||||
}
|
||||
|
||||
:global(.tabpanel) {
|
||||
height: 100%;
|
||||
}
|
||||
:global(.tabpanel) {
|
||||
height: 100%;
|
||||
}
|
||||
|
||||
:global(.tabpanels) {
|
||||
height: calc(100% - 2rem);
|
||||
}
|
||||
:global(.tabpanels) {
|
||||
height: calc(100% - 2rem);
|
||||
}
|
||||
|
||||
:global(.tab) {
|
||||
margin: 0.25rem;
|
||||
padding: 0.25rem;
|
||||
padding-left: 0.75rem;
|
||||
padding-right: 0.75rem;
|
||||
border-radius: 1rem;
|
||||
}
|
||||
:global(.tab) {
|
||||
margin: 0.25rem;
|
||||
padding: 0.25rem;
|
||||
padding-left: 0.75rem;
|
||||
padding-right: 0.75rem;
|
||||
border-radius: 1rem;
|
||||
}
|
||||
|
||||
:global(.tab .flex) {
|
||||
align-items: center;
|
||||
gap: 0.25rem;
|
||||
}
|
||||
:global(.tab .flex) {
|
||||
align-items: center;
|
||||
gap: 0.25rem;
|
||||
}
|
||||
|
||||
:global(.tab span|div) {
|
||||
align-items: center;
|
||||
gap: 0.25rem;
|
||||
display: flex;
|
||||
}
|
||||
:global(.tab span|div) {
|
||||
align-items: center;
|
||||
gap: 0.25rem;
|
||||
display: flex;
|
||||
}
|
||||
|
||||
:global(.tab-selected svg) {
|
||||
fill: var(--catch-detail-color-contrast);
|
||||
}
|
||||
:global(.tab-selected svg) {
|
||||
fill: var(--catch-detail-color-contrast);
|
||||
}
|
||||
|
||||
:global(.tab-unselected) {
|
||||
background-color: var(--background-color) !important;
|
||||
color: var(--foreground-color) !important;
|
||||
}
|
||||
:global(.tab-unselected) {
|
||||
background-color: var(--background-color) !important;
|
||||
color: var(--foreground-color) !important;
|
||||
}
|
||||
</style>
|
||||
|
|
|
@ -38,7 +38,7 @@
|
|||
if (value.data === undefined) {
|
||||
value.setData(coordinate)
|
||||
}
|
||||
if(coordinate === undefined){
|
||||
if (coordinate === undefined) {
|
||||
coordinate = value.data
|
||||
}
|
||||
export let snapToLayers: string[] | undefined
|
||||
|
@ -47,8 +47,6 @@
|
|||
|
||||
export let snappedTo: UIEventSource<string | undefined>
|
||||
|
||||
|
||||
|
||||
let preciseLocation: UIEventSource<{ lon: number; lat: number }> = new UIEventSource<{
|
||||
lon: number
|
||||
lat: number
|
||||
|
@ -75,7 +73,7 @@
|
|||
rasterLayer: UIEventSource.feedFrom(state.mapProperties.rasterLayer),
|
||||
}
|
||||
|
||||
if(targetLayer){
|
||||
if (targetLayer) {
|
||||
const featuresForLayer = state.perLayer.get(targetLayer.id)
|
||||
if (featuresForLayer) {
|
||||
new ShowDataLayer(map, {
|
||||
|
|
|
@ -1,21 +1,23 @@
|
|||
<script lang="ts">
|
||||
import type { SpecialVisualizationState } from "../SpecialVisualization"
|
||||
import { Store } from "../../Logic/UIEventSource"
|
||||
import { Changes } from "../../Logic/Osm/Changes"
|
||||
import Loading from "../Base/Loading.svelte"
|
||||
import Translations from "../i18n/Translations"
|
||||
import Tr from "../Base/Tr.svelte"
|
||||
import type { SpecialVisualizationState } from "../SpecialVisualization"
|
||||
import { Store } from "../../Logic/UIEventSource"
|
||||
import { Changes } from "../../Logic/Osm/Changes"
|
||||
import Loading from "../Base/Loading.svelte"
|
||||
import Translations from "../i18n/Translations"
|
||||
import Tr from "../Base/Tr.svelte"
|
||||
|
||||
export let state: SpecialVisualizationState
|
||||
export let state: SpecialVisualizationState
|
||||
|
||||
const changes: Changes = state.changes
|
||||
const isUploading: Store<boolean> = changes.isUploading
|
||||
const pendingChangesCount: Store<number> = changes.pendingChanges.map(ls => ls.length)
|
||||
const errors = changes.errors
|
||||
const changes: Changes = state.changes
|
||||
const isUploading: Store<boolean> = changes.isUploading
|
||||
const pendingChangesCount: Store<number> = changes.pendingChanges.map((ls) => ls.length)
|
||||
const errors = changes.errors
|
||||
</script>
|
||||
|
||||
|
||||
<div class="flex flex-col pointer-events-auto" on:click={() => changes.flushChanges("Pending changes indicator clicked")}>
|
||||
<div
|
||||
class="pointer-events-auto flex flex-col"
|
||||
on:click={() => changes.flushChanges("Pending changes indicator clicked")}
|
||||
>
|
||||
{#if $isUploading}
|
||||
<Loading>
|
||||
<Tr cls="thx" t={Translations.t.general.uploadingChanges} />
|
||||
|
@ -23,10 +25,13 @@
|
|||
{:else if $pendingChangesCount === 1}
|
||||
<Tr cls="alert" t={Translations.t.general.uploadPendingSingle} />
|
||||
{:else if $pendingChangesCount > 1}
|
||||
<Tr cls="alert" t={Translations.t.general.uploadPending.Subs({count: $pendingChangesCount})} />
|
||||
<Tr
|
||||
cls="alert"
|
||||
t={Translations.t.general.uploadPending.Subs({ count: $pendingChangesCount })}
|
||||
/>
|
||||
{/if}
|
||||
|
||||
{#each $errors as error}
|
||||
<Tr cls="alert" t={Translations.t.general.uploadError.Subs({error})} />
|
||||
<Tr cls="alert" t={Translations.t.general.uploadError.Subs({ error })} />
|
||||
{/each}
|
||||
</div>
|
||||
|
|
|
@ -1,48 +1,48 @@
|
|||
<script lang="ts">
|
||||
import Translations from "../i18n/Translations";
|
||||
import Svg from "../../Svg";
|
||||
import Tr from "../Base/Tr.svelte";
|
||||
import NextButton from "../Base/NextButton.svelte";
|
||||
import Geosearch from "./Geosearch.svelte";
|
||||
import ToSvelte from "../Base/ToSvelte.svelte";
|
||||
import ThemeViewState from "../../Models/ThemeViewState";
|
||||
import { Store, UIEventSource } from "../../Logic/UIEventSource";
|
||||
import { SearchIcon } from "@rgossiaux/svelte-heroicons/solid";
|
||||
import { twJoin } from "tailwind-merge";
|
||||
import { Utils } from "../../Utils";
|
||||
import type { GeolocationPermissionState } from "../../Logic/State/GeoLocationState";
|
||||
import If from "../Base/If.svelte";
|
||||
import Translations from "../i18n/Translations"
|
||||
import Svg from "../../Svg"
|
||||
import Tr from "../Base/Tr.svelte"
|
||||
import NextButton from "../Base/NextButton.svelte"
|
||||
import Geosearch from "./Geosearch.svelte"
|
||||
import ToSvelte from "../Base/ToSvelte.svelte"
|
||||
import ThemeViewState from "../../Models/ThemeViewState"
|
||||
import { Store, UIEventSource } from "../../Logic/UIEventSource"
|
||||
import { SearchIcon } from "@rgossiaux/svelte-heroicons/solid"
|
||||
import { twJoin } from "tailwind-merge"
|
||||
import { Utils } from "../../Utils"
|
||||
import type { GeolocationPermissionState } from "../../Logic/State/GeoLocationState"
|
||||
import If from "../Base/If.svelte"
|
||||
|
||||
/**
|
||||
* The theme introduction panel
|
||||
*/
|
||||
export let state: ThemeViewState;
|
||||
let layout = state.layout;
|
||||
let selectedElement = state.selectedElement;
|
||||
let selectedLayer = state.selectedLayer;
|
||||
/**
|
||||
* The theme introduction panel
|
||||
*/
|
||||
export let state: ThemeViewState
|
||||
let layout = state.layout
|
||||
let selectedElement = state.selectedElement
|
||||
let selectedLayer = state.selectedLayer
|
||||
|
||||
let triggerSearch: UIEventSource<any> = new UIEventSource<any>(undefined);
|
||||
let searchEnabled = false;
|
||||
let triggerSearch: UIEventSource<any> = new UIEventSource<any>(undefined)
|
||||
let searchEnabled = false
|
||||
|
||||
let geopermission: Store<GeolocationPermissionState> =
|
||||
state.geolocation.geolocationState.permission;
|
||||
let currentGPSLocation = state.geolocation.geolocationState.currentGPSLocation;
|
||||
let geopermission: Store<GeolocationPermissionState> =
|
||||
state.geolocation.geolocationState.permission
|
||||
let currentGPSLocation = state.geolocation.geolocationState.currentGPSLocation
|
||||
|
||||
geopermission.addCallback((perm) => console.log(">>>> Permission", perm));
|
||||
geopermission.addCallback((perm) => console.log(">>>> Permission", perm))
|
||||
|
||||
function jumpToCurrentLocation() {
|
||||
const glstate = state.geolocation.geolocationState;
|
||||
if (glstate.currentGPSLocation.data !== undefined) {
|
||||
const c: GeolocationCoordinates = glstate.currentGPSLocation.data;
|
||||
state.guistate.themeIsOpened.setData(false);
|
||||
const coor = { lon: c.longitude, lat: c.latitude };
|
||||
state.mapProperties.location.setData(coor);
|
||||
}
|
||||
if (glstate.permission.data !== "granted") {
|
||||
glstate.requestPermission();
|
||||
return;
|
||||
}
|
||||
function jumpToCurrentLocation() {
|
||||
const glstate = state.geolocation.geolocationState
|
||||
if (glstate.currentGPSLocation.data !== undefined) {
|
||||
const c: GeolocationCoordinates = glstate.currentGPSLocation.data
|
||||
state.guistate.themeIsOpened.setData(false)
|
||||
const coor = { lon: c.longitude, lat: c.latitude }
|
||||
state.mapProperties.location.setData(coor)
|
||||
}
|
||||
if (glstate.permission.data !== "granted") {
|
||||
glstate.requestPermission()
|
||||
return
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<div class="flex h-full flex-col justify-between">
|
||||
|
@ -63,7 +63,6 @@
|
|||
</div>
|
||||
</NextButton>
|
||||
|
||||
|
||||
<div class="flex w-full flex-wrap sm:flex-nowrap">
|
||||
<If condition={state.featureSwitches.featureSwitchGeolocation}>
|
||||
{#if $currentGPSLocation !== undefined || $geopermission === "prompt"}
|
||||
|
@ -73,12 +72,15 @@
|
|||
</button>
|
||||
<!-- No geolocation granted - we don't show the button -->
|
||||
{:else if $geopermission === "requested"}
|
||||
<button class="disabled flex w-full items-center gap-x-2" on:click={jumpToCurrentLocation}>
|
||||
<button
|
||||
class="disabled flex w-full items-center gap-x-2"
|
||||
on:click={jumpToCurrentLocation}
|
||||
>
|
||||
<!-- Even though disabled, when clicking we request the location again in case the contributor dismissed the location popup -->
|
||||
<ToSvelte
|
||||
construct={Svg.crosshair_svg()
|
||||
.SetClass("w-8 h-8")
|
||||
.SetStyle("animation: 3s linear 0s infinite normal none running spin;")}
|
||||
.SetClass("w-8 h-8")
|
||||
.SetStyle("animation: 3s linear 0s infinite normal none running spin;")}
|
||||
/>
|
||||
<Tr t={Translations.t.general.waitingForGeopermission} />
|
||||
</button>
|
||||
|
@ -91,24 +93,25 @@
|
|||
<button class="disabled flex w-full items-center gap-x-2">
|
||||
<ToSvelte
|
||||
construct={Svg.crosshair_svg()
|
||||
.SetClass("w-8 h-8")
|
||||
.SetStyle("animation: 3s linear 0s infinite normal none running spin;")}
|
||||
.SetClass("w-8 h-8")
|
||||
.SetStyle("animation: 3s linear 0s infinite normal none running spin;")}
|
||||
/>
|
||||
<Tr t={Translations.t.general.waitingForLocation} />
|
||||
</button>
|
||||
{/if}
|
||||
</If>
|
||||
|
||||
|
||||
<If condition={state.featureSwitches.featureSwitchSearch}>
|
||||
<div class=".button low-interaction m-1 flex w-full items-center gap-x-2 rounded border p-2">
|
||||
<div
|
||||
class=".button low-interaction m-1 flex w-full items-center gap-x-2 rounded border p-2"
|
||||
>
|
||||
<div class="w-full">
|
||||
<Geosearch
|
||||
bounds={state.mapProperties.bounds}
|
||||
on:searchCompleted={() => state.guistate.themeIsOpened.setData(false)}
|
||||
on:searchIsValid={(isValid) => {
|
||||
searchEnabled = isValid
|
||||
}}
|
||||
searchEnabled = isValid
|
||||
}}
|
||||
perLayer={state.perLayer}
|
||||
{selectedElement}
|
||||
{selectedLayer}
|
||||
|
@ -116,7 +119,10 @@
|
|||
/>
|
||||
</div>
|
||||
<button
|
||||
class={twJoin("flex items-center justify-between gap-x-2", !searchEnabled && "disabled")}
|
||||
class={twJoin(
|
||||
"flex items-center justify-between gap-x-2",
|
||||
!searchEnabled && "disabled"
|
||||
)}
|
||||
on:click={() => triggerSearch.ping()}
|
||||
>
|
||||
<Tr t={Translations.t.general.search.searchShort} />
|
||||
|
|
|
@ -1,24 +1,24 @@
|
|||
<script lang="ts">
|
||||
/**
|
||||
* Shows information about how much images are uploaded for the given feature
|
||||
*
|
||||
* Either pass in a store with tags or a featureId.
|
||||
*/
|
||||
/**
|
||||
* Shows information about how much images are uploaded for the given feature
|
||||
*
|
||||
* Either pass in a store with tags or a featureId.
|
||||
*/
|
||||
|
||||
import type { SpecialVisualizationState } from "../SpecialVisualization"
|
||||
import { Store } from "../../Logic/UIEventSource"
|
||||
import type { OsmTags } from "../../Models/OsmFeature"
|
||||
import Translations from "../i18n/Translations"
|
||||
import Tr from "../Base/Tr.svelte"
|
||||
import Loading from "../Base/Loading.svelte"
|
||||
import type { SpecialVisualizationState } from "../SpecialVisualization"
|
||||
import { Store } from "../../Logic/UIEventSource"
|
||||
import type { OsmTags } from "../../Models/OsmFeature"
|
||||
import Translations from "../i18n/Translations"
|
||||
import Tr from "../Base/Tr.svelte"
|
||||
import Loading from "../Base/Loading.svelte"
|
||||
|
||||
export let state: SpecialVisualizationState
|
||||
export let tags: Store<OsmTags>
|
||||
export let featureId = tags.data.id
|
||||
export let showThankYou: boolean = true
|
||||
const { uploadStarted, uploadFinished, retried, failed } =
|
||||
state.imageUploadManager.getCountsFor(featureId)
|
||||
const t = Translations.t.image
|
||||
export let state: SpecialVisualizationState
|
||||
export let tags: Store<OsmTags>
|
||||
export let featureId = tags.data.id
|
||||
export let showThankYou: boolean = true
|
||||
const { uploadStarted, uploadFinished, retried, failed } =
|
||||
state.imageUploadManager.getCountsFor(featureId)
|
||||
const t = Translations.t.image
|
||||
</script>
|
||||
|
||||
{#if $uploadStarted === 1}
|
||||
|
|
|
@ -1,14 +1,14 @@
|
|||
import { Store, UIEventSource } from "../../Logic/UIEventSource";
|
||||
import type { Map as MLMap } from "maplibre-gl";
|
||||
import { Map as MlMap, SourceSpecification } from "maplibre-gl";
|
||||
import { AvailableRasterLayers, RasterLayerPolygon } from "../../Models/RasterLayers";
|
||||
import { Utils } from "../../Utils";
|
||||
import { BBox } from "../../Logic/BBox";
|
||||
import { ExportableMap, MapProperties } from "../../Models/MapProperties";
|
||||
import SvelteUIElement from "../Base/SvelteUIElement";
|
||||
import MaplibreMap from "./MaplibreMap.svelte";
|
||||
import { RasterLayerProperties } from "../../Models/RasterLayerProperties";
|
||||
import * as htmltoimage from "html-to-image";
|
||||
import { Store, UIEventSource } from "../../Logic/UIEventSource"
|
||||
import type { Map as MLMap } from "maplibre-gl"
|
||||
import { Map as MlMap, SourceSpecification } from "maplibre-gl"
|
||||
import { AvailableRasterLayers, RasterLayerPolygon } from "../../Models/RasterLayers"
|
||||
import { Utils } from "../../Utils"
|
||||
import { BBox } from "../../Logic/BBox"
|
||||
import { ExportableMap, MapProperties } from "../../Models/MapProperties"
|
||||
import SvelteUIElement from "../Base/SvelteUIElement"
|
||||
import MaplibreMap from "./MaplibreMap.svelte"
|
||||
import { RasterLayerProperties } from "../../Models/RasterLayerProperties"
|
||||
import * as htmltoimage from "html-to-image"
|
||||
|
||||
/**
|
||||
* The 'MapLibreAdaptor' bridges 'MapLibre' with the various properties of the `MapProperties`
|
||||
|
|
|
@ -16,7 +16,7 @@ import { LayerConfigJson } from "../../Models/ThemeConfig/Json/LayerConfigJson"
|
|||
import PerLayerFeatureSourceSplitter from "../../Logic/FeatureSource/PerLayerFeatureSourceSplitter"
|
||||
import FilteredLayer from "../../Models/FilteredLayer"
|
||||
import SimpleFeatureSource from "../../Logic/FeatureSource/Sources/SimpleFeatureSource"
|
||||
import { CLIENT_RENEG_LIMIT } from "tls";
|
||||
import { CLIENT_RENEG_LIMIT } from "tls"
|
||||
|
||||
class PointRenderingLayer {
|
||||
private readonly _config: PointRenderingConfig
|
||||
|
@ -409,7 +409,7 @@ class LineRenderingLayer {
|
|||
this._listenerInstalledOn.add(id)
|
||||
tags.addCallbackAndRunD((properties) => {
|
||||
// Make sure to use 'getSource' here, the layer names are different!
|
||||
if(map.getSource(this._layername) === undefined){
|
||||
if (map.getSource(this._layername) === undefined) {
|
||||
return true
|
||||
}
|
||||
map.setFeatureState(
|
||||
|
|
|
@ -162,16 +162,16 @@
|
|||
<LoginButton osmConnection={state.osmConnection} slot="not-logged-in">
|
||||
<Tr slot="message" t={Translations.t.general.add.pleaseLogin} />
|
||||
</LoginButton>
|
||||
{#if $zoom < Constants.minZoomLevelToAddNewPoint}
|
||||
{#if $zoom < Constants.minZoomLevelToAddNewPoint}
|
||||
<div class="alert">
|
||||
<Tr t={Translations.t.general.add.zoomInFurther} />
|
||||
</div>
|
||||
{:else if $isLoading}
|
||||
<div class="alert">
|
||||
<Loading>
|
||||
<Tr t={Translations.t.general.add.stillLoading} />
|
||||
</Loading>
|
||||
</div>
|
||||
{:else if $isLoading}
|
||||
<div class="alert">
|
||||
<Loading>
|
||||
<Tr t={Translations.t.general.add.stillLoading} />
|
||||
</Loading>
|
||||
</div>
|
||||
{:else if selectedPreset === undefined}
|
||||
<!-- First, select the correct preset -->
|
||||
<PresetList
|
||||
|
|
|
@ -30,7 +30,12 @@
|
|||
if (flayer.isDisplayed.data === false) {
|
||||
// The layer is not displayed...
|
||||
if (!state.featureSwitches.featureSwitchFilter.data) {
|
||||
console.log("Not showing presets for layer", flayer.layerDef.id, "as not displayed and featureSwitchFilter.data is set",state.featureSwitches.featureSwitchFilter.data)
|
||||
console.log(
|
||||
"Not showing presets for layer",
|
||||
flayer.layerDef.id,
|
||||
"as not displayed and featureSwitchFilter.data is set",
|
||||
state.featureSwitches.featureSwitchFilter.data
|
||||
)
|
||||
// ...and we cannot enable the layer control -> we skip, as these presets can never be shown anyway
|
||||
continue
|
||||
}
|
||||
|
|
|
@ -2,50 +2,50 @@
|
|||
/**
|
||||
* UIcomponent to create a new note at the given location
|
||||
*/
|
||||
import type { SpecialVisualizationState } from "../SpecialVisualization";
|
||||
import { UIEventSource } from "../../Logic/UIEventSource";
|
||||
import { LocalStorageSource } from "../../Logic/Web/LocalStorageSource";
|
||||
import ValidatedInput from "../InputElement/ValidatedInput.svelte";
|
||||
import SubtleButton from "../Base/SubtleButton.svelte";
|
||||
import Tr from "../Base/Tr.svelte";
|
||||
import Translations from "../i18n/Translations.js";
|
||||
import type { Feature, Point } from "geojson";
|
||||
import LoginToggle from "../Base/LoginToggle.svelte";
|
||||
import FilteredLayer from "../../Models/FilteredLayer";
|
||||
import NewPointLocationInput from "../BigComponents/NewPointLocationInput.svelte";
|
||||
import ToSvelte from "../Base/ToSvelte.svelte";
|
||||
import Svg from "../../Svg";
|
||||
import type { SpecialVisualizationState } from "../SpecialVisualization"
|
||||
import { UIEventSource } from "../../Logic/UIEventSource"
|
||||
import { LocalStorageSource } from "../../Logic/Web/LocalStorageSource"
|
||||
import ValidatedInput from "../InputElement/ValidatedInput.svelte"
|
||||
import SubtleButton from "../Base/SubtleButton.svelte"
|
||||
import Tr from "../Base/Tr.svelte"
|
||||
import Translations from "../i18n/Translations.js"
|
||||
import type { Feature, Point } from "geojson"
|
||||
import LoginToggle from "../Base/LoginToggle.svelte"
|
||||
import FilteredLayer from "../../Models/FilteredLayer"
|
||||
import NewPointLocationInput from "../BigComponents/NewPointLocationInput.svelte"
|
||||
import ToSvelte from "../Base/ToSvelte.svelte"
|
||||
import Svg from "../../Svg"
|
||||
|
||||
export let coordinate: UIEventSource<{ lon: number; lat: number }>;
|
||||
export let state: SpecialVisualizationState;
|
||||
export let coordinate: UIEventSource<{ lon: number; lat: number }>
|
||||
export let state: SpecialVisualizationState
|
||||
|
||||
let comment: UIEventSource<string> = LocalStorageSource.Get("note-text");
|
||||
let created = false;
|
||||
let comment: UIEventSource<string> = LocalStorageSource.Get("note-text")
|
||||
let created = false
|
||||
|
||||
let notelayer: FilteredLayer = state.layerState.filteredLayers.get("note");
|
||||
let notelayer: FilteredLayer = state.layerState.filteredLayers.get("note")
|
||||
|
||||
let hasFilter = notelayer?.hasFilter;
|
||||
let isDisplayed = notelayer?.isDisplayed;
|
||||
let hasFilter = notelayer?.hasFilter
|
||||
let isDisplayed = notelayer?.isDisplayed
|
||||
|
||||
function enableNoteLayer() {
|
||||
state.guistate.closeAll();
|
||||
isDisplayed.setData(true);
|
||||
state.guistate.closeAll()
|
||||
isDisplayed.setData(true)
|
||||
}
|
||||
|
||||
async function uploadNote() {
|
||||
let txt = comment.data;
|
||||
let txt = comment.data
|
||||
if (txt === undefined || txt === "") {
|
||||
return;
|
||||
return
|
||||
}
|
||||
const loc = coordinate.data;
|
||||
txt += "\n\n #MapComplete #" + state?.layout?.id;
|
||||
const id = await state?.osmConnection?.openNote(loc.lat, loc.lon, txt);
|
||||
console.log("Created a note, got id", id);
|
||||
const loc = coordinate.data
|
||||
txt += "\n\n #MapComplete #" + state?.layout?.id
|
||||
const id = await state?.osmConnection?.openNote(loc.lat, loc.lon, txt)
|
||||
console.log("Created a note, got id", id)
|
||||
const feature = <Feature<Point>>{
|
||||
type: "Feature",
|
||||
geometry: {
|
||||
type: "Point",
|
||||
coordinates: [loc.lon, loc.lat]
|
||||
coordinates: [loc.lon, loc.lat],
|
||||
},
|
||||
properties: {
|
||||
id: "" + id.id,
|
||||
|
@ -56,22 +56,22 @@
|
|||
text: txt,
|
||||
html: txt,
|
||||
user: state.osmConnection?.userDetails?.data?.name,
|
||||
uid: state.osmConnection?.userDetails?.data?.uid
|
||||
}
|
||||
])
|
||||
}
|
||||
};
|
||||
// Normally, the 'Changes' will generate the new element. The 'notes' are an exception to this
|
||||
state.newFeatures.features.data.push(feature);
|
||||
state.newFeatures.features.ping();
|
||||
state.selectedElement?.setData(feature);
|
||||
if (state.featureProperties.trackFeature) {
|
||||
state.featureProperties.trackFeature(feature);
|
||||
uid: state.osmConnection?.userDetails?.data?.uid,
|
||||
},
|
||||
]),
|
||||
},
|
||||
}
|
||||
comment.setData("");
|
||||
created = true;
|
||||
state.selectedElement.setData(feature);
|
||||
state.selectedLayer.setData(state.layerState.filteredLayers.get("note"));
|
||||
// Normally, the 'Changes' will generate the new element. The 'notes' are an exception to this
|
||||
state.newFeatures.features.data.push(feature)
|
||||
state.newFeatures.features.ping()
|
||||
state.selectedElement?.setData(feature)
|
||||
if (state.featureProperties.trackFeature) {
|
||||
state.featureProperties.trackFeature(feature)
|
||||
}
|
||||
comment.setData("")
|
||||
created = true
|
||||
state.selectedElement.setData(feature)
|
||||
state.selectedLayer.setData(state.layerState.filteredLayers.get("note"))
|
||||
}
|
||||
</script>
|
||||
|
||||
|
@ -109,15 +109,14 @@
|
|||
<ValidatedInput type="text" value={comment} />
|
||||
</div>
|
||||
|
||||
<div class="w-full h-56">
|
||||
<NewPointLocationInput value={coordinate} {state} >
|
||||
<div class="h-56 w-full">
|
||||
<NewPointLocationInput value={coordinate} {state}>
|
||||
<div class="h-20 w-full pb-10" slot="image">
|
||||
<ToSvelte construct={Svg.note_svg().SetClass("h-10 w-full")}/>
|
||||
<ToSvelte construct={Svg.note_svg().SetClass("h-10 w-full")} />
|
||||
</div>
|
||||
</NewPointLocationInput>
|
||||
</div>
|
||||
|
||||
|
||||
<LoginToggle {state}>
|
||||
<span slot="loading"><!--empty: don't show a loading message--></span>
|
||||
<div slot="not-logged-in" class="alert">
|
||||
|
|
|
@ -112,7 +112,10 @@
|
|||
<button
|
||||
slot="save-button"
|
||||
on:click={onDelete}
|
||||
class={twJoin(selectedTags === undefined && "disabled", "primary flex bg-red-600 items-center")}
|
||||
class={twJoin(
|
||||
selectedTags === undefined && "disabled",
|
||||
"primary flex items-center bg-red-600"
|
||||
)}
|
||||
>
|
||||
<TrashIcon
|
||||
class={twJoin(
|
||||
|
|
|
@ -155,8 +155,8 @@
|
|||
<ToSvelte
|
||||
construct={() => new ExtraLinkButton(state, layout.extraLink).SetClass("pointer-events-auto")}
|
||||
/>
|
||||
<UploadingImageCounter {state} featureId="*" showThankYou={false}/>
|
||||
<PendingChangesIndicator {state}/>
|
||||
<UploadingImageCounter featureId="*" showThankYou={false} {state} />
|
||||
<PendingChangesIndicator {state} />
|
||||
<If condition={state.featureSwitchIsTesting}>
|
||||
<div class="alert w-fit">Testmode</div>
|
||||
</If>
|
||||
|
@ -174,7 +174,12 @@
|
|||
<div class="flex flex-col">
|
||||
<If condition={featureSwitches.featureSwitchEnableLogin}>
|
||||
{#if state.lastClickObject.hasPresets || state.lastClickObject.hasNoteLayer}
|
||||
<button class="w-fit pointer-events-auto" on:click={() => {state.openNewDialog()}}>
|
||||
<button
|
||||
class="pointer-events-auto w-fit"
|
||||
on:click={() => {
|
||||
state.openNewDialog()
|
||||
}}
|
||||
>
|
||||
{#if state.lastClickObject.hasPresets}
|
||||
<Tr t={Translations.t.general.add.title} />
|
||||
{:else}
|
||||
|
@ -197,9 +202,9 @@
|
|||
<a
|
||||
class="bg-black-transparent pointer-events-auto h-fit max-h-12 cursor-pointer self-end overflow-hidden rounded-2xl pl-1 pr-2 text-white opacity-50 hover:opacity-100"
|
||||
on:click={() => {
|
||||
state.guistate.themeViewTab.setData("copyright")
|
||||
state.guistate.themeIsOpened.setData(true)
|
||||
}}
|
||||
state.guistate.themeViewTab.setData("copyright")
|
||||
state.guistate.themeIsOpened.setData(true)
|
||||
}}
|
||||
>
|
||||
© OpenStreetMap, <span class="w-24">{rasterLayerName}</span>
|
||||
</a>
|
||||
|
@ -293,7 +298,10 @@
|
|||
<!-- Theme menu -->
|
||||
<FloatOver on:close={() => state.guistate.themeIsOpened.setData(false)}>
|
||||
<span slot="close-button"><!-- Disable the close button --></span>
|
||||
<TabbedGroup condition1={state.featureSwitches.featureSwitchFilter} tab={state.guistate.themeViewTabIndex}>
|
||||
<TabbedGroup
|
||||
condition1={state.featureSwitches.featureSwitchFilter}
|
||||
tab={state.guistate.themeViewTabIndex}
|
||||
>
|
||||
<div slot="post-tablist">
|
||||
<XCircleIcon
|
||||
class="mr-2 h-8 w-8"
|
||||
|
@ -362,7 +370,11 @@
|
|||
|
||||
<IfHidden condition={state.guistate.backgroundLayerSelectionIsOpened}>
|
||||
<!-- background layer selector -->
|
||||
<FloatOver on:close={() => {state.guistate.backgroundLayerSelectionIsOpened.setData(false)}}>
|
||||
<FloatOver
|
||||
on:close={() => {
|
||||
state.guistate.backgroundLayerSelectionIsOpened.setData(false)
|
||||
}}
|
||||
>
|
||||
<div class="h-full p-2">
|
||||
<RasterLayerOverview
|
||||
{availableLayers}
|
||||
|
@ -377,11 +389,13 @@
|
|||
|
||||
<If condition={state.guistate.menuIsOpened}>
|
||||
<!-- Menu page -->
|
||||
<FloatOver on:close={() => state.guistate.menuIsOpened.setData(false) }>
|
||||
<FloatOver on:close={() => state.guistate.menuIsOpened.setData(false)}>
|
||||
<span slot="close-button"><!-- Hide the default close button --></span>
|
||||
<TabbedGroup condition1={featureSwitches.featureSwitchEnableLogin}
|
||||
condition2={state.featureSwitches. featureSwitchCommunityIndex}
|
||||
tab={state.guistate.menuViewTabIndex}>
|
||||
<TabbedGroup
|
||||
condition1={featureSwitches.featureSwitchEnableLogin}
|
||||
condition2={state.featureSwitches.featureSwitchCommunityIndex}
|
||||
tab={state.guistate.menuViewTabIndex}
|
||||
>
|
||||
<div slot="post-tablist">
|
||||
<XCircleIcon
|
||||
class="mr-2 h-8 w-8"
|
||||
|
@ -470,7 +484,7 @@
|
|||
<OpenIdEditor mapProperties={state.mapProperties} />
|
||||
<ToSvelte
|
||||
construct={() =>
|
||||
new OpenJosm(state.osmConnection, state.mapProperties.bounds).SetClass("w-full")}
|
||||
new OpenJosm(state.osmConnection, state.mapProperties.bounds).SetClass("w-full")}
|
||||
/>
|
||||
<MapillaryLink mapProperties={state.mapProperties} />
|
||||
</If>
|
||||
|
|
|
@ -672,16 +672,21 @@ class SvgToPdfPage {
|
|||
}
|
||||
|
||||
public async PrepareLanguage(language: string) {
|
||||
let host = window.location.host
|
||||
if(host.startsWith("127.0.0.1")){
|
||||
host = "mapcomplete.org"
|
||||
}
|
||||
// Always fetch the remote data - it's cached anyway
|
||||
this.layerTranslations[language] = await Utils.downloadJsonCached(
|
||||
window.location.protocol +
|
||||
"//" +
|
||||
window.location.host +
|
||||
host +
|
||||
"/assets/langs/layers/" +
|
||||
language +
|
||||
".json",
|
||||
24 * 60 * 60 * 1000
|
||||
)
|
||||
|
||||
}
|
||||
|
||||
public async Prepare() {
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
{
|
||||
"contributors": [
|
||||
{
|
||||
"commits": 6092,
|
||||
"commits": 6139,
|
||||
"contributor": "Pieter Vander Vennet"
|
||||
},
|
||||
{
|
||||
|
@ -30,14 +30,14 @@
|
|||
},
|
||||
{
|
||||
"commits": 30,
|
||||
"contributor": "paunofu"
|
||||
},
|
||||
{
|
||||
"commits": 29,
|
||||
"contributor": "Hosted Weblate"
|
||||
},
|
||||
{
|
||||
"commits": 27,
|
||||
"commits": 30,
|
||||
"contributor": "paunofu"
|
||||
},
|
||||
{
|
||||
"commits": 28,
|
||||
"contributor": "riQQ"
|
||||
},
|
||||
{
|
||||
|
@ -53,7 +53,7 @@
|
|||
"contributor": "Ward"
|
||||
},
|
||||
{
|
||||
"commits": 21,
|
||||
"commits": 22,
|
||||
"contributor": "dependabot[bot]"
|
||||
},
|
||||
{
|
||||
|
|
|
@ -30,10 +30,6 @@
|
|||
"AT": [
|
||||
"de"
|
||||
],
|
||||
"AU": [
|
||||
"en",
|
||||
"en"
|
||||
],
|
||||
"AZ": [
|
||||
"az"
|
||||
],
|
||||
|
|
|
@ -3009,7 +3009,6 @@
|
|||
"_meta": {
|
||||
"countries": [
|
||||
"AG",
|
||||
"AU",
|
||||
"BB",
|
||||
"BI",
|
||||
"BN",
|
||||
|
|
|
@ -1,11 +1,11 @@
|
|||
{
|
||||
"contributors": [
|
||||
{
|
||||
"commits": 311,
|
||||
"commits": 314,
|
||||
"contributor": "kjon"
|
||||
},
|
||||
{
|
||||
"commits": 288,
|
||||
"commits": 292,
|
||||
"contributor": "Pieter Vander Vennet"
|
||||
},
|
||||
{
|
||||
|
@ -96,6 +96,10 @@
|
|||
"commits": 13,
|
||||
"contributor": "Joost"
|
||||
},
|
||||
{
|
||||
"commits": 11,
|
||||
"contributor": "Jaime Marquínez Ferrándiz"
|
||||
},
|
||||
{
|
||||
"commits": 11,
|
||||
"contributor": "Túllio Franca"
|
||||
|
@ -124,6 +128,10 @@
|
|||
"commits": 10,
|
||||
"contributor": "Irina"
|
||||
},
|
||||
{
|
||||
"commits": 9,
|
||||
"contributor": "Piotr Strebski"
|
||||
},
|
||||
{
|
||||
"commits": 9,
|
||||
"contributor": "Ettore Atalan"
|
||||
|
@ -132,10 +140,6 @@
|
|||
"commits": 9,
|
||||
"contributor": "deep map"
|
||||
},
|
||||
{
|
||||
"commits": 9,
|
||||
"contributor": "Jaime Marquínez Ferrándiz"
|
||||
},
|
||||
{
|
||||
"commits": 9,
|
||||
"contributor": "Fjuro"
|
||||
|
@ -152,6 +156,10 @@
|
|||
"commits": 8,
|
||||
"contributor": "Vinicius"
|
||||
},
|
||||
{
|
||||
"commits": 7,
|
||||
"contributor": "gallegonovato"
|
||||
},
|
||||
{
|
||||
"commits": 7,
|
||||
"contributor": "NetworkedPoncho"
|
||||
|
@ -172,6 +180,10 @@
|
|||
"commits": 7,
|
||||
"contributor": "Niels Elgaard Larsen"
|
||||
},
|
||||
{
|
||||
"commits": 6,
|
||||
"contributor": "macpac"
|
||||
},
|
||||
{
|
||||
"commits": 6,
|
||||
"contributor": "Juele juele"
|
||||
|
@ -216,14 +228,6 @@
|
|||
"commits": 6,
|
||||
"contributor": "lvgx"
|
||||
},
|
||||
{
|
||||
"commits": 5,
|
||||
"contributor": "Piotr Strebski"
|
||||
},
|
||||
{
|
||||
"commits": 5,
|
||||
"contributor": "gallegonovato"
|
||||
},
|
||||
{
|
||||
"commits": 5,
|
||||
"contributor": "ⵣⵓⵀⵉⵔ ⴰⵎⴰⵣⵉⵖ ZOUHIR DEHBI"
|
||||
|
@ -348,10 +352,6 @@
|
|||
"commits": 3,
|
||||
"contributor": "SiegbjornSitumeang"
|
||||
},
|
||||
{
|
||||
"commits": 2,
|
||||
"contributor": "macpac"
|
||||
},
|
||||
{
|
||||
"commits": 2,
|
||||
"contributor": "Peter Brodersen"
|
||||
|
@ -448,6 +448,10 @@
|
|||
"commits": 2,
|
||||
"contributor": "Leo Alcaraz"
|
||||
},
|
||||
{
|
||||
"commits": 1,
|
||||
"contributor": "Julio Salas"
|
||||
},
|
||||
{
|
||||
"commits": 1,
|
||||
"contributor": "Michal Čermák"
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue