forked from MapComplete/MapComplete
		
	Formatting
This commit is contained in:
		
							parent
							
								
									d5d2c08706
								
							
						
					
					
						commit
						72ca67e3ab
					
				
					 34 changed files with 616 additions and 566 deletions
				
			
		|  | @ -27,10 +27,10 @@ export default class UserDetails { | |||
| 
 | ||||
| export class OsmConnection { | ||||
|     public static readonly oauth_configs = { | ||||
|         "osm": { | ||||
|             oauth_consumer_key: 'hivV7ec2o49Two8g9h8Is1VIiVOgxQ1iYexCbvem', | ||||
|             oauth_secret: 'wDBRTCem0vxD7txrg1y6p5r8nvmz8tAhET7zDASI', | ||||
|             url: "https://www.openstreetmap.org" | ||||
|         osm: { | ||||
|             oauth_consumer_key: "hivV7ec2o49Two8g9h8Is1VIiVOgxQ1iYexCbvem", | ||||
|             oauth_secret: "wDBRTCem0vxD7txrg1y6p5r8nvmz8tAhET7zDASI", | ||||
|             url: "https://www.openstreetmap.org", | ||||
|             // OAUTH 1.0 application
 | ||||
|             // https://www.openstreetmap.org/user/Pieter%20Vander%20Vennet/oauth_clients/7404
 | ||||
|         }, | ||||
|  | @ -335,9 +335,11 @@ export class OsmConnection { | |||
|         }) | ||||
|     } | ||||
| 
 | ||||
|     public async uploadGpxTrack(gpx: string, options: { | ||||
|         description: string, | ||||
|         visibility:  "private" | "public" | "trackable" | "identifiable", | ||||
|     public async uploadGpxTrack( | ||||
|         gpx: string, | ||||
|         options: { | ||||
|             description: string | ||||
|             visibility: "private" | "public" | "trackable" | "identifiable" | ||||
|             filename?: string | ||||
|             /** | ||||
|              * Some words to give some properties; | ||||
|  | @ -345,33 +347,40 @@ export class OsmConnection { | |||
|              * Note: these are called 'tags' on the wiki, but I opted to name them 'labels' instead as they aren't "key=value" tags, but just words. | ||||
|              */ | ||||
|             labels: string[] | ||||
|     }): Promise<{ id: number }> { | ||||
|         } | ||||
|     ): Promise<{ id: number }> { | ||||
|         if (this._dryRun.data) { | ||||
|             console.warn("Dryrun enabled - not actually uploading GPX ", gpx) | ||||
|             return new Promise<{ id: number }>((ok, error) => { | ||||
|                 window.setTimeout(() => ok({id: Math.floor(Math.random() * 1000)}), Math.random() * 5000) | ||||
|             }); | ||||
|                 window.setTimeout( | ||||
|                     () => ok({ id: Math.floor(Math.random() * 1000) }), | ||||
|                     Math.random() * 5000 | ||||
|                 ) | ||||
|             }) | ||||
|         } | ||||
| 
 | ||||
|         const contents = { | ||||
|             "file": gpx, | ||||
|             "description": options.description ?? "", | ||||
|             "tags": options.labels?.join(",") ?? "", | ||||
|             "visibility": options.visibility | ||||
|             file: gpx, | ||||
|             description: options.description ?? "", | ||||
|             tags: options.labels?.join(",") ?? "", | ||||
|             visibility: options.visibility, | ||||
|         } | ||||
| 
 | ||||
|         const extras = { | ||||
|             "file": "; filename=\""+(options.filename ?? ("gpx_track_mapcomplete_"+(new Date().toISOString())))+"\"\r\nContent-Type: application/gpx+xml" | ||||
|             file: | ||||
|                 '; filename="' + | ||||
|                 (options.filename ?? "gpx_track_mapcomplete_" + new Date().toISOString()) + | ||||
|                 '"\r\nContent-Type: application/gpx+xml', | ||||
|         } | ||||
| 
 | ||||
|         const auth = this.auth; | ||||
|         const boundary ="987654" | ||||
|         const auth = this.auth | ||||
|         const boundary = "987654" | ||||
| 
 | ||||
|         let body = "" | ||||
|         for (const key in contents) { | ||||
|             body += "--" + boundary + "\r\n" | ||||
|             body += "Content-Disposition: form-data; name=\"" + key + "\"" | ||||
|             if(extras[key] !== undefined){ | ||||
|             body += 'Content-Disposition: form-data; name="' + key + '"' | ||||
|             if (extras[key] !== undefined) { | ||||
|                 body += extras[key] | ||||
|             } | ||||
|             body += "\r\n\r\n" | ||||
|  | @ -379,34 +388,31 @@ export class OsmConnection { | |||
|         } | ||||
|         body += "--" + boundary + "--\r\n" | ||||
| 
 | ||||
| 
 | ||||
|         return new Promise((ok, error) => { | ||||
|             auth.xhr({ | ||||
|                 method: 'POST', | ||||
|             auth.xhr( | ||||
|                 { | ||||
|                     method: "POST", | ||||
|                     path: `/api/0.6/gpx/create`, | ||||
|                     options: { | ||||
|                     header: | ||||
|                         { | ||||
|                         header: { | ||||
|                             "Content-Type": "multipart/form-data; boundary=" + boundary, | ||||
|                             "Content-Length": body.length | ||||
|                         } | ||||
|                             "Content-Length": body.length, | ||||
|                         }, | ||||
|                 content: body | ||||
| 
 | ||||
|             }, function ( | ||||
|                 err, | ||||
|                 response: string) { | ||||
|                     }, | ||||
|                     content: body, | ||||
|                 }, | ||||
|                 function (err, response: string) { | ||||
|                     console.log("RESPONSE IS", response) | ||||
|                     if (err !== null) { | ||||
|                         error(err) | ||||
|                     } else { | ||||
|                         const parsed = JSON.parse(response) | ||||
|                         console.log("Uploaded GPX track", parsed) | ||||
|                     ok({id: parsed}) | ||||
|                         ok({ id: parsed }) | ||||
|                     } | ||||
|                 } | ||||
|             ) | ||||
|         }) | ||||
|         }) | ||||
| 
 | ||||
|     } | ||||
| 
 | ||||
|     public addCommentToNote(id: number | string, text: string): Promise<void> { | ||||
|  |  | |||
|  | @ -623,7 +623,9 @@ export class RewriteSpecial extends DesugaringStep<TagRenderingConfigJson> { | |||
|             } | ||||
|             const param = special[arg.name] | ||||
|             if (param === undefined) { | ||||
|                 errors.push(`At ${context}: Obligated parameter '${arg.name}' in special rendering of type ${vis.funcName} not found.\n${arg.doc}`) | ||||
|                 errors.push( | ||||
|                     `At ${context}: Obligated parameter '${arg.name}' in special rendering of type ${vis.funcName} not found.\n${arg.doc}` | ||||
|                 ) | ||||
|             } | ||||
|         } | ||||
| 
 | ||||
|  |  | |||
|  | @ -1,19 +1,19 @@ | |||
| import {DesugaringStep, Each, Fuse, On} from "./Conversion" | ||||
| import {LayerConfigJson} from "../Json/LayerConfigJson" | ||||
| import { DesugaringStep, Each, Fuse, On } from "./Conversion" | ||||
| import { LayerConfigJson } from "../Json/LayerConfigJson" | ||||
| import LayerConfig from "../LayerConfig" | ||||
| import {Utils} from "../../../Utils" | ||||
| import { Utils } from "../../../Utils" | ||||
| import Constants from "../../Constants" | ||||
| import {Translation} from "../../../UI/i18n/Translation" | ||||
| import {LayoutConfigJson} from "../Json/LayoutConfigJson" | ||||
| import { Translation } from "../../../UI/i18n/Translation" | ||||
| import { LayoutConfigJson } from "../Json/LayoutConfigJson" | ||||
| import LayoutConfig from "../LayoutConfig" | ||||
| import {TagRenderingConfigJson} from "../Json/TagRenderingConfigJson" | ||||
| import {TagUtils} from "../../../Logic/Tags/TagUtils" | ||||
| import {ExtractImages} from "./FixImages" | ||||
| import { TagRenderingConfigJson } from "../Json/TagRenderingConfigJson" | ||||
| import { TagUtils } from "../../../Logic/Tags/TagUtils" | ||||
| import { ExtractImages } from "./FixImages" | ||||
| import ScriptUtils from "../../../scripts/ScriptUtils" | ||||
| import {And} from "../../../Logic/Tags/And" | ||||
| import { And } from "../../../Logic/Tags/And" | ||||
| import Translations from "../../../UI/i18n/Translations" | ||||
| import Svg from "../../../Svg" | ||||
| import {QuestionableTagRenderingConfigJson} from "../Json/QuestionableTagRenderingConfigJson" | ||||
| import { QuestionableTagRenderingConfigJson } from "../Json/QuestionableTagRenderingConfigJson" | ||||
| import FilterConfigJson from "../Json/FilterConfigJson" | ||||
| import DeleteConfig from "../DeleteConfig" | ||||
| 
 | ||||
|  | @ -619,20 +619,31 @@ export class DetectMappingsWithImages extends DesugaringStep<TagRenderingConfigJ | |||
| 
 | ||||
| class MiscTagRenderingChecks extends DesugaringStep<TagRenderingConfigJson> { | ||||
|     constructor() { | ||||
|         super("Miscellanious checks on the tagrendering", ["special"], "MiscTagREnderingChecksRew"); | ||||
|         super("Miscellanious checks on the tagrendering", ["special"], "MiscTagREnderingChecksRew") | ||||
|     } | ||||
| 
 | ||||
|     convert(json: TagRenderingConfigJson, context: string): { result: TagRenderingConfigJson; errors?: string[]; warnings?: string[]; information?: string[] } { | ||||
|     convert( | ||||
|         json: TagRenderingConfigJson, | ||||
|         context: string | ||||
|     ): { | ||||
|         result: TagRenderingConfigJson | ||||
|         errors?: string[] | ||||
|         warnings?: string[] | ||||
|         information?: string[] | ||||
|     } { | ||||
|         const errors = [] | ||||
|         if(json["special"] !== undefined){ | ||||
|             errors.push("At "+context+": detected `special` on the top level. Did you mean `{\"render\":{ \"special\": ... }}`") | ||||
|         if (json["special"] !== undefined) { | ||||
|             errors.push( | ||||
|                 "At " + | ||||
|                     context + | ||||
|                     ': detected `special` on the top level. Did you mean `{"render":{ "special": ... }}`' | ||||
|             ) | ||||
|         } | ||||
|         return { | ||||
|             result: json, | ||||
|             errors | ||||
|         }; | ||||
|             errors, | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
| } | ||||
| 
 | ||||
| export class ValidateTagRenderings extends Fuse<TagRenderingConfigJson> { | ||||
|  |  | |||
|  | @ -2,7 +2,7 @@ import { SubstitutedTranslation } from "../../UI/SubstitutedTranslation" | |||
| import TagRenderingConfig from "./TagRenderingConfig" | ||||
| import { ExtraFuncParams, ExtraFunctions } from "../../Logic/ExtraFunctions" | ||||
| import LayerConfig from "./LayerConfig" | ||||
| import {SpecialVisualization} from "../../UI/SpecialVisualization"; | ||||
| import { SpecialVisualization } from "../../UI/SpecialVisualization" | ||||
| 
 | ||||
| export default class DependencyCalculator { | ||||
|     public static GetTagRenderingDependencies(tr: TagRenderingConfig): string[] { | ||||
|  |  | |||
|  | @ -68,7 +68,7 @@ export default class FilterConfig { | |||
|             for (const field of fields) { | ||||
|                 for (let ln in question.translations) { | ||||
|                     const txt = question.translations[ln] | ||||
|                     if(ln.startsWith("_")){ | ||||
|                     if (ln.startsWith("_")) { | ||||
|                         continue | ||||
|                     } | ||||
|                     if (txt.indexOf("{" + field.name + "}") < 0) { | ||||
|  |  | |||
|  | @ -239,7 +239,7 @@ export default class TagRenderingConfig { | |||
|                 throw `${context}: Detected a freeform key without rendering... Key: ${this.freeform.key} in ${context}` | ||||
|             } | ||||
|             for (const ln in this.render.translations) { | ||||
|                 if(ln.startsWith("_")){ | ||||
|                 if (ln.startsWith("_")) { | ||||
|                     continue | ||||
|                 } | ||||
|                 const txt: string = this.render.translations[ln] | ||||
|  |  | |||
|  | @ -73,11 +73,13 @@ export class SubtleButton extends UIElement { | |||
|             } | ||||
|         }) | ||||
|         const loading = new Lazy(() => new Loading(loadingText)) | ||||
|         return new VariableUiElement(state.map(st => { | ||||
|             if(st === "idle"){ | ||||
|         return new VariableUiElement( | ||||
|             state.map((st) => { | ||||
|                 if (st === "idle") { | ||||
|                     return button | ||||
|                 } | ||||
|                 return loading | ||||
|         })) | ||||
|             }) | ||||
|         ) | ||||
|     } | ||||
| } | ||||
|  |  | |||
|  | @ -1,20 +1,20 @@ | |||
| import Combine from "../Base/Combine" | ||||
| import {FlowPanelFactory, FlowStep} from "../ImportFlow/FlowStep" | ||||
| import {ImmutableStore, Store, UIEventSource} from "../../Logic/UIEventSource" | ||||
| import {InputElement} from "../Input/InputElement" | ||||
| import {SvgToPdf, SvgToPdfOptions} from "../../Utils/svgToPdf" | ||||
| import {FixedInputElement} from "../Input/FixedInputElement" | ||||
| import {FixedUiElement} from "../Base/FixedUiElement" | ||||
| import { FlowPanelFactory, FlowStep } from "../ImportFlow/FlowStep" | ||||
| import { ImmutableStore, Store, UIEventSource } from "../../Logic/UIEventSource" | ||||
| import { InputElement } from "../Input/InputElement" | ||||
| import { SvgToPdf, SvgToPdfOptions } from "../../Utils/svgToPdf" | ||||
| import { FixedInputElement } from "../Input/FixedInputElement" | ||||
| import { FixedUiElement } from "../Base/FixedUiElement" | ||||
| import FileSelectorButton from "../Input/FileSelectorButton" | ||||
| import InputElementMap from "../Input/InputElementMap" | ||||
| import {RadioButton} from "../Input/RadioButton" | ||||
| import {Utils} from "../../Utils" | ||||
| import {VariableUiElement} from "../Base/VariableUIElement" | ||||
| import { RadioButton } from "../Input/RadioButton" | ||||
| import { Utils } from "../../Utils" | ||||
| import { VariableUiElement } from "../Base/VariableUIElement" | ||||
| import Loading from "../Base/Loading" | ||||
| import BaseUIElement from "../BaseUIElement" | ||||
| import Img from "../Base/Img" | ||||
| import Title from "../Base/Title" | ||||
| import {CheckBox} from "../Input/Checkboxes" | ||||
| import { CheckBox } from "../Input/Checkboxes" | ||||
| import Minimap from "../Base/Minimap" | ||||
| import SearchAndGo from "./SearchAndGo" | ||||
| import Toggle from "../Input/Toggle" | ||||
|  | @ -25,7 +25,7 @@ import Toggleable from "../Base/Toggleable" | |||
| import Lazy from "../Base/Lazy" | ||||
| import LinkToWeblate from "../Base/LinkToWeblate" | ||||
| import Link from "../Base/Link" | ||||
| import {AllLanguagesSelector} from "../Popup/AllLanguagesSelector"; | ||||
| import { AllLanguagesSelector } from "../Popup/AllLanguagesSelector" | ||||
| 
 | ||||
| class SelectTemplate extends Combine implements FlowStep<{ title: string; pages: string[] }> { | ||||
|     readonly IsValid: Store<boolean> | ||||
|  | @ -201,7 +201,7 @@ class PreparePdf extends Combine implements FlowStep<{ svgToPdf: SvgToPdf; langu | |||
| 
 | ||||
|     constructor(title: string, pages: string[], options: SvgToPdfOptions) { | ||||
|         const svgToPdf = new SvgToPdf(title, pages, options) | ||||
|         const languageSelector = new AllLanguagesSelector(          ) | ||||
|         const languageSelector = new AllLanguagesSelector() | ||||
|         const isPrepared = UIEventSource.FromPromiseWithErr(svgToPdf.Prepare()) | ||||
| 
 | ||||
|         super([ | ||||
|  |  | |||
|  | @ -1,25 +1,23 @@ | |||
| import Toggle from "../Input/Toggle"; | ||||
| import {RadioButton} from "../Input/RadioButton"; | ||||
| import {FixedInputElement} from "../Input/FixedInputElement"; | ||||
| import Combine from "../Base/Combine"; | ||||
| import Translations from "../i18n/Translations"; | ||||
| import {TextField} from "../Input/TextField"; | ||||
| import {UIEventSource} from "../../Logic/UIEventSource"; | ||||
| import Title from "../Base/Title"; | ||||
| import {SubtleButton} from "../Base/SubtleButton"; | ||||
| import Svg from "../../Svg"; | ||||
| import {OsmConnection} from "../../Logic/Osm/OsmConnection"; | ||||
| import LayoutConfig from "../../Models/ThemeConfig/LayoutConfig"; | ||||
| import {Translation} from "../i18n/Translation"; | ||||
| 
 | ||||
| import Toggle from "../Input/Toggle" | ||||
| import { RadioButton } from "../Input/RadioButton" | ||||
| import { FixedInputElement } from "../Input/FixedInputElement" | ||||
| import Combine from "../Base/Combine" | ||||
| import Translations from "../i18n/Translations" | ||||
| import { TextField } from "../Input/TextField" | ||||
| import { UIEventSource } from "../../Logic/UIEventSource" | ||||
| import Title from "../Base/Title" | ||||
| import { SubtleButton } from "../Base/SubtleButton" | ||||
| import Svg from "../../Svg" | ||||
| import { OsmConnection } from "../../Logic/Osm/OsmConnection" | ||||
| import LayoutConfig from "../../Models/ThemeConfig/LayoutConfig" | ||||
| import { Translation } from "../i18n/Translation" | ||||
| 
 | ||||
| export default class UploadTraceToOsmUI extends Toggle { | ||||
| 
 | ||||
|     private static createDefault(s: string, defaultValue: string){ | ||||
|         if(defaultValue.length < 1){ | ||||
|     private static createDefault(s: string, defaultValue: string) { | ||||
|         if (defaultValue.length < 1) { | ||||
|             throw "Default value should have some characters" | ||||
|         } | ||||
|         if(s === undefined || s === null || s === ""){ | ||||
|         if (s === undefined || s === null || s === "") { | ||||
|             return defaultValue | ||||
|         } | ||||
|         return s | ||||
|  | @ -28,43 +26,50 @@ export default class UploadTraceToOsmUI extends Toggle { | |||
|     constructor( | ||||
|         trace: (title: string) => string, | ||||
|         state: { | ||||
|             layoutToUse: LayoutConfig; | ||||
|             layoutToUse: LayoutConfig | ||||
|             osmConnection: OsmConnection | ||||
|         }, options?: { | ||||
|         }, | ||||
|         options?: { | ||||
|             whenUploaded?: () => void | Promise<void> | ||||
|         }) { | ||||
|         } | ||||
|     ) { | ||||
|         const t = Translations.t.general.uploadGpx | ||||
|         const uploadFinished = new UIEventSource(false) | ||||
|         const traceVisibilities: { | ||||
|             key: "private" | "public", | ||||
|             name: Translation, | ||||
|             key: "private" | "public" | ||||
|             name: Translation | ||||
|             docs: Translation | ||||
|         }[] = [ | ||||
|             { | ||||
|                 key: "private", | ||||
|                 ...t.modes.private | ||||
|                 ...t.modes.private, | ||||
|             }, | ||||
|             { | ||||
|                 key: "public", | ||||
|                 ...t.modes.public | ||||
|             } | ||||
|                 ...t.modes.public, | ||||
|             }, | ||||
|         ] | ||||
| 
 | ||||
|         const dropdown = new RadioButton<"private" | "public">( | ||||
|             traceVisibilities.map(tv => new FixedInputElement<"private" | "public">( | ||||
|                 new Combine([Translations.W( | ||||
|                     tv.name | ||||
|                 ).SetClass("font-bold"), tv.docs]).SetClass("flex flex-col") | ||||
|                 , tv.key)), | ||||
|             traceVisibilities.map( | ||||
|                 (tv) => | ||||
|                     new FixedInputElement<"private" | "public">( | ||||
|                         new Combine([ | ||||
|                             Translations.W(tv.name).SetClass("font-bold"), | ||||
|                             tv.docs, | ||||
|                         ]).SetClass("flex flex-col"), | ||||
|                         tv.key | ||||
|                     ) | ||||
|             ), | ||||
|             { | ||||
|                 value: <any>state?.osmConnection?.GetPreference("gps.trace.visibility") | ||||
|                 value: <any>state?.osmConnection?.GetPreference("gps.trace.visibility"), | ||||
|             } | ||||
|         ) | ||||
|         const description = new TextField({ | ||||
|             placeholder: t.meta.descriptionPlaceHolder | ||||
|             placeholder: t.meta.descriptionPlaceHolder, | ||||
|         }) | ||||
|         const title = new TextField({ | ||||
|             placeholder: t.meta.titlePlaceholder | ||||
|             placeholder: t.meta.titlePlaceholder, | ||||
|         }) | ||||
|         const clicked = new UIEventSource<boolean>(false) | ||||
| 
 | ||||
|  | @ -81,38 +86,48 @@ export default class UploadTraceToOsmUI extends Toggle { | |||
|             t.meta.descriptionIntro, | ||||
|             description, | ||||
|             new Combine([ | ||||
|                 new SubtleButton(Svg.close_svg(), Translations.t.general.cancel).onClick(() => { | ||||
|                 new SubtleButton(Svg.close_svg(), Translations.t.general.cancel) | ||||
|                     .onClick(() => { | ||||
|                         clicked.setData(false) | ||||
|                 }).SetClass(""), | ||||
|                 new SubtleButton(Svg.upload_svg(), t.confirm).OnClickWithLoading(t.uploading, async () => { | ||||
|                     const titleStr = UploadTraceToOsmUI.createDefault(title.GetValue().data, "Track with mapcomplete") | ||||
|                     const descriptionStr = UploadTraceToOsmUI.createDefault(description.GetValue().data, "Track created with MapComplete with theme "+state?.layoutToUse?.id) | ||||
|                     }) | ||||
|                     .SetClass(""), | ||||
|                 new SubtleButton(Svg.upload_svg(), t.confirm).OnClickWithLoading( | ||||
|                     t.uploading, | ||||
|                     async () => { | ||||
|                         const titleStr = UploadTraceToOsmUI.createDefault( | ||||
|                             title.GetValue().data, | ||||
|                             "Track with mapcomplete" | ||||
|                         ) | ||||
|                         const descriptionStr = UploadTraceToOsmUI.createDefault( | ||||
|                             description.GetValue().data, | ||||
|                             "Track created with MapComplete with theme " + state?.layoutToUse?.id | ||||
|                         ) | ||||
|                         await state?.osmConnection?.uploadGpxTrack(trace(title.GetValue().data), { | ||||
|                             visibility: dropdown.GetValue().data, | ||||
|                             description: descriptionStr, | ||||
|                         filename: titleStr +".gpx", | ||||
|                         labels: ["MapComplete", state?.layoutToUse?.id] | ||||
|                             filename: titleStr + ".gpx", | ||||
|                             labels: ["MapComplete", state?.layoutToUse?.id], | ||||
|                         }) | ||||
| 
 | ||||
|                         if (options?.whenUploaded !== undefined) { | ||||
|                             await options.whenUploaded() | ||||
|                         } | ||||
|                         uploadFinished.setData(true) | ||||
| 
 | ||||
|                 }) | ||||
|             ]).SetClass("flex flex-wrap flex-wrap-reverse justify-between items-stretch") | ||||
|                     } | ||||
|                 ), | ||||
|             ]).SetClass("flex flex-wrap flex-wrap-reverse justify-between items-stretch"), | ||||
|         ]).SetClass("flex flex-col p-4 rounded border-2 m-2 border-subtle") | ||||
| 
 | ||||
| 
 | ||||
|         super( | ||||
|             new Combine([Svg.confirm_svg().SetClass("w-12 h-12 mr-2"), | ||||
|                 t.uploadFinished]) | ||||
|                 .SetClass("flex p-2 rounded-xl border-2 subtle-border items-center"), | ||||
|             new Combine([Svg.confirm_svg().SetClass("w-12 h-12 mr-2"), t.uploadFinished]).SetClass( | ||||
|                 "flex p-2 rounded-xl border-2 subtle-border items-center" | ||||
|             ), | ||||
|             new Toggle( | ||||
|                 confirmPanel, | ||||
|                 new SubtleButton(Svg.upload_svg(), t.title) | ||||
|                     .onClick(() => clicked.setData(true)), | ||||
|                 new SubtleButton(Svg.upload_svg(), t.title).onClick(() => clicked.setData(true)), | ||||
|                 clicked | ||||
|             ), uploadFinished) | ||||
|             ), | ||||
|             uploadFinished | ||||
|         ) | ||||
|     } | ||||
| } | ||||
|  |  | |||
|  | @ -13,8 +13,8 @@ export class RadioButton<T> extends InputElement<T> { | |||
|     constructor( | ||||
|         elements: InputElement<T>[], | ||||
|         options?: { | ||||
|             selectFirstAsDefault?: true | boolean, | ||||
|             dontStyle?: boolean, | ||||
|             selectFirstAsDefault?: true | boolean | ||||
|             dontStyle?: boolean | ||||
|             value?: UIEventSource<T> | ||||
|         } | ||||
|     ) { | ||||
|  |  | |||
|  | @ -1,13 +1,13 @@ | |||
| import {UIElement} from "../UIElement" | ||||
| import {InputElement} from "./InputElement" | ||||
| import { UIElement } from "../UIElement" | ||||
| import { InputElement } from "./InputElement" | ||||
| import BaseUIElement from "../BaseUIElement" | ||||
| import {Store, UIEventSource} from "../../Logic/UIEventSource" | ||||
| import { Store, UIEventSource } from "../../Logic/UIEventSource" | ||||
| import Translations from "../i18n/Translations" | ||||
| import Locale from "../i18n/Locale" | ||||
| import Combine from "../Base/Combine" | ||||
| import {TextField} from "./TextField" | ||||
| import { TextField } from "./TextField" | ||||
| import Svg from "../../Svg" | ||||
| import {VariableUiElement} from "../Base/VariableUIElement" | ||||
| import { VariableUiElement } from "../Base/VariableUIElement" | ||||
| 
 | ||||
| /** | ||||
|  * A single 'pill' which can hide itself if the search criteria is not met | ||||
|  | @ -28,7 +28,7 @@ class SelfHidingToggle extends UIElement implements InputElement<boolean> { | |||
|             searchTerms?: Record<string, string[]> | ||||
|             selected?: UIEventSource<boolean> | ||||
|             forceSelected?: UIEventSource<boolean> | ||||
|             squared?: boolean, | ||||
|             squared?: boolean | ||||
|             /* Hide, if not selected*/ | ||||
|             hide?: Store<boolean> | ||||
|         } | ||||
|  | @ -53,7 +53,7 @@ class SelfHidingToggle extends UIElement implements InputElement<boolean> { | |||
|         const selected = (this._selected = options?.selected ?? new UIEventSource<boolean>(false)) | ||||
|         const forceSelected = (this.forceSelected = | ||||
|             options?.forceSelected ?? new UIEventSource<boolean>(false)) | ||||
|         this.matchesSearchCriteria = search.map(s => { | ||||
|         this.matchesSearchCriteria = search.map((s) => { | ||||
|             if (s === undefined || s.length === 0) { | ||||
|                 return true | ||||
|             } | ||||
|  | @ -152,7 +152,7 @@ export class SearchablePillsSelector<T> extends Combine implements InputElement< | |||
|             show: BaseUIElement | ||||
|             value: T | ||||
|             mainTerm: Record<string, string> | ||||
|             searchTerms?: Record<string, string[]>, | ||||
|             searchTerms?: Record<string, string[]> | ||||
|             /* If there are more then 200 elements, should this element still be shown? */ | ||||
|             hasPriority?: Store<boolean> | ||||
|         }[], | ||||
|  | @ -189,7 +189,7 @@ export class SearchablePillsSelector<T> extends Combine implements InputElement< | |||
|             hideSearchBar?: false | boolean | ||||
|         } | ||||
|     ) { | ||||
|         const search = new TextField({value: options?.searchValue}) | ||||
|         const search = new TextField({ value: options?.searchValue }) | ||||
| 
 | ||||
|         const searchBar = options?.hideSearchBar | ||||
|             ? undefined | ||||
|  | @ -238,7 +238,10 @@ export class SearchablePillsSelector<T> extends Combine implements InputElement< | |||
|                 searchTerms: v.searchTerms, | ||||
|                 selected: vIsSelected, | ||||
|                 squared: mode === "select-many", | ||||
|                 hide: v.hasPriority === undefined ? forceHide : forceHide.map(fh => fh && !v.hasPriority?.data, [v.hasPriority]) | ||||
|                 hide: | ||||
|                     v.hasPriority === undefined | ||||
|                         ? forceHide | ||||
|                         : forceHide.map((fh) => fh && !v.hasPriority?.data, [v.hasPriority]), | ||||
|             }) | ||||
| 
 | ||||
|             return { | ||||
|  | @ -249,16 +252,17 @@ export class SearchablePillsSelector<T> extends Combine implements InputElement< | |||
| 
 | ||||
|         // The total number of elements that would be displayed based on the search criteria alone
 | ||||
|         let totalShown: Store<number> | ||||
|         totalShown = searchValue.map((_) => mappedValues.filter((mv) => mv.show.matchesSearchCriteria.data).length) | ||||
|         totalShown = searchValue.map( | ||||
|             (_) => mappedValues.filter((mv) => mv.show.matchesSearchCriteria.data).length | ||||
|         ) | ||||
|         const tooMuchElementsCutoff = 40 | ||||
|         totalShown.addCallbackAndRunD(shown => forceHide.setData(tooMuchElementsCutoff < shown)) | ||||
|         totalShown.addCallbackAndRunD((shown) => forceHide.setData(tooMuchElementsCutoff < shown)) | ||||
| 
 | ||||
|         super([ | ||||
|             searchBar, | ||||
|             new VariableUiElement( | ||||
|                 Locale.language.map( | ||||
|                     (lng) => { | ||||
| 
 | ||||
|                         if ( | ||||
|                             options?.onNoSearchMade !== undefined && | ||||
|                             (searchValue.data === undefined || searchValue.data.length === 0) | ||||
|  | @ -275,7 +279,10 @@ export class SearchablePillsSelector<T> extends Combine implements InputElement< | |||
|                             .SetClass(options?.searchAreaClass ?? "") | ||||
| 
 | ||||
|                         if (totalShown.data >= tooMuchElementsCutoff) { | ||||
|                             pills = new Combine([options?.onManyElements ?? Translations.t.general.useSearch, pills]) | ||||
|                             pills = new Combine([ | ||||
|                                 options?.onManyElements ?? Translations.t.general.useSearch, | ||||
|                                 pills, | ||||
|                             ]) | ||||
|                         } | ||||
|                         return pills | ||||
|                     }, | ||||
|  |  | |||
|  | @ -1,14 +1,14 @@ | |||
| import Translations from "../i18n/Translations"; | ||||
| import {TextField} from "../Input/TextField"; | ||||
| import {SubtleButton} from "../Base/SubtleButton"; | ||||
| import Svg from "../../Svg"; | ||||
| import NoteCommentElement from "./NoteCommentElement"; | ||||
| import {VariableUiElement} from "../Base/VariableUIElement"; | ||||
| import Toggle from "../Input/Toggle"; | ||||
| import {LoginToggle} from "./LoginButton"; | ||||
| import Combine from "../Base/Combine"; | ||||
| import Title from "../Base/Title"; | ||||
| import {SpecialVisualization} from "../SpecialVisualization"; | ||||
| import Translations from "../i18n/Translations" | ||||
| import { TextField } from "../Input/TextField" | ||||
| import { SubtleButton } from "../Base/SubtleButton" | ||||
| import Svg from "../../Svg" | ||||
| import NoteCommentElement from "./NoteCommentElement" | ||||
| import { VariableUiElement } from "../Base/VariableUIElement" | ||||
| import Toggle from "../Input/Toggle" | ||||
| import { LoginToggle } from "./LoginButton" | ||||
| import Combine from "../Base/Combine" | ||||
| import Title from "../Base/Title" | ||||
| import { SpecialVisualization } from "../SpecialVisualization" | ||||
| 
 | ||||
| export class AddNoteCommentViz implements SpecialVisualization { | ||||
|     funcName = "add_note_comment" | ||||
|  | @ -103,12 +103,8 @@ export class AddNoteCommentViz implements SpecialVisualization { | |||
|                     stateButtons.SetClass("sm:mr-2"), | ||||
|                     new Toggle( | ||||
|                         addCommentButton, | ||||
|                         new Combine([t.typeText]).SetClass( | ||||
|                             "flex items-center h-full subtle" | ||||
|                         ), | ||||
|                         textField | ||||
|                             .GetValue() | ||||
|                             .map((t) => t !== undefined && t.length >= 1) | ||||
|                         new Combine([t.typeText]).SetClass("flex items-center h-full subtle"), | ||||
|                         textField.GetValue().map((t) => t !== undefined && t.length >= 1) | ||||
|                     ).SetClass("sm:mr-2"), | ||||
|                 ]).SetClass("sm:flex sm:justify-between sm:items-stretch"), | ||||
|             ]).SetClass("border-2 border-black rounded-xl p-4 block"), | ||||
|  |  | |||
|  | @ -1,45 +1,46 @@ | |||
| import {SearchablePillsSelector} from "../Input/SearchableMappingsSelector"; | ||||
| import {Store} from "../../Logic/UIEventSource"; | ||||
| import BaseUIElement from "../BaseUIElement"; | ||||
| import * as all_languages from "../../assets/language_translations.json"; | ||||
| import {Translation} from "../i18n/Translation"; | ||||
| 
 | ||||
| export class AllLanguagesSelector extends SearchablePillsSelector <string> { | ||||
| import { SearchablePillsSelector } from "../Input/SearchableMappingsSelector" | ||||
| import { Store } from "../../Logic/UIEventSource" | ||||
| import BaseUIElement from "../BaseUIElement" | ||||
| import * as all_languages from "../../assets/language_translations.json" | ||||
| import { Translation } from "../i18n/Translation" | ||||
| 
 | ||||
| export class AllLanguagesSelector extends SearchablePillsSelector<string> { | ||||
|     constructor(options?: { | ||||
|         mode?: "select-many" | "select-one" | ||||
|                     currentCountry?: Store<string>, | ||||
|         currentCountry?: Store<string> | ||||
|         supportedLanguages?: Record<string, string> & { _meta?: { countries?: string[] } } | ||||
|     }) { | ||||
| 
 | ||||
|         const possibleValues: { | ||||
|             show: BaseUIElement | ||||
|             value: string | ||||
|             mainTerm: Record<string, string> | ||||
|             searchTerms?: Record<string, string[]>, | ||||
|             searchTerms?: Record<string, string[]> | ||||
|             hasPriority?: Store<boolean> | ||||
|         }[] = [] | ||||
| 
 | ||||
|         const langs = options?.supportedLanguages ?? all_languages["default"] ?? all_languages | ||||
|         for (const ln in langs) { | ||||
|             let languageInfo: Record<string, string> & { _meta?: { countries: string[] } } = all_languages[ln] | ||||
|             const countries = languageInfo._meta?.countries?.map(c => c.toLowerCase()) | ||||
|             languageInfo = {...languageInfo} | ||||
|             let languageInfo: Record<string, string> & { _meta?: { countries: string[] } } = | ||||
|                 all_languages[ln] | ||||
|             const countries = languageInfo._meta?.countries?.map((c) => c.toLowerCase()) | ||||
|             languageInfo = { ...languageInfo } | ||||
|             delete languageInfo._meta | ||||
|             const term = { | ||||
|                 show: new Translation(languageInfo), | ||||
|                 value: ln, | ||||
|                 mainTerm: languageInfo, | ||||
|                 searchTerms: {"*": [ln]}, | ||||
|                 hasPriority: countries === undefined ? undefined : options?.currentCountry?.map(country => countries?.indexOf(country.toLowerCase()) >= 0) | ||||
|                 searchTerms: { "*": [ln] }, | ||||
|                 hasPriority: | ||||
|                     countries === undefined | ||||
|                         ? undefined | ||||
|                         : options?.currentCountry?.map( | ||||
|                               (country) => countries?.indexOf(country.toLowerCase()) >= 0 | ||||
|                           ), | ||||
|             } | ||||
|             possibleValues.push(term) | ||||
| 
 | ||||
|         } | ||||
|         super(possibleValues, | ||||
|             { | ||||
|                 mode: options?.mode ?? 'select-many' | ||||
|             }); | ||||
|         super(possibleValues, { | ||||
|             mode: options?.mode ?? "select-many", | ||||
|         }) | ||||
|     } | ||||
| 
 | ||||
| } | ||||
|  |  | |||
|  | @ -23,7 +23,7 @@ import FilteredLayer from "../../Models/FilteredLayer" | |||
| import TagRenderingConfig from "../../Models/ThemeConfig/TagRenderingConfig" | ||||
| import Lazy from "../Base/Lazy" | ||||
| import List from "../Base/List" | ||||
| import {SpecialVisualization} from "../SpecialVisualization"; | ||||
| import { SpecialVisualization } from "../SpecialVisualization" | ||||
| 
 | ||||
| export interface AutoAction extends SpecialVisualization { | ||||
|     supportsAutoAction: boolean | ||||
|  |  | |||
|  | @ -1,13 +1,13 @@ | |||
| import FeaturePipelineState from "../../Logic/State/FeaturePipelineState"; | ||||
| import BaseUIElement from "../BaseUIElement"; | ||||
| import Translations from "../i18n/Translations"; | ||||
| import {Utils} from "../../Utils"; | ||||
| import Svg from "../../Svg"; | ||||
| import Img from "../Base/Img"; | ||||
| import {SubtleButton} from "../Base/SubtleButton"; | ||||
| import Toggle from "../Input/Toggle"; | ||||
| import {LoginToggle} from "./LoginButton"; | ||||
| import {SpecialVisualization} from "../SpecialVisualization"; | ||||
| import FeaturePipelineState from "../../Logic/State/FeaturePipelineState" | ||||
| import BaseUIElement from "../BaseUIElement" | ||||
| import Translations from "../i18n/Translations" | ||||
| import { Utils } from "../../Utils" | ||||
| import Svg from "../../Svg" | ||||
| import Img from "../Base/Img" | ||||
| import { SubtleButton } from "../Base/SubtleButton" | ||||
| import Toggle from "../Input/Toggle" | ||||
| import { LoginToggle } from "./LoginButton" | ||||
| import { SpecialVisualization } from "../SpecialVisualization" | ||||
| 
 | ||||
| export class CloseNoteButton implements SpecialVisualization { | ||||
|     public readonly funcName = "close_note" | ||||
|  |  | |||
|  | @ -10,7 +10,7 @@ import TagRenderingConfig from "../../Models/ThemeConfig/TagRenderingConfig" | |||
| import { Unit } from "../../Models/Unit" | ||||
| import Lazy from "../Base/Lazy" | ||||
| import { FixedUiElement } from "../Base/FixedUiElement" | ||||
| import {EditButton} from "./SaveButton"; | ||||
| import { EditButton } from "./SaveButton" | ||||
| 
 | ||||
| export default class EditableTagRendering extends Toggle { | ||||
|     constructor( | ||||
|  | @ -71,7 +71,7 @@ export default class EditableTagRendering extends Toggle { | |||
|             // We have a question and editing is enabled
 | ||||
|             const answerWithEditButton = new Combine([ | ||||
|                 answer, | ||||
|                 new EditButton(state.osmConnection,() => { | ||||
|                 new EditButton(state.osmConnection, () => { | ||||
|                     editMode.setData(true) | ||||
|                 }), | ||||
|             ]).SetClass("flex justify-between w-full") | ||||
|  |  | |||
|  | @ -1,10 +1,10 @@ | |||
| import Translations from "../i18n/Translations"; | ||||
| import {SubtleButton} from "../Base/SubtleButton"; | ||||
| import Svg from "../../Svg"; | ||||
| import Combine from "../Base/Combine"; | ||||
| import {GeoOperations} from "../../Logic/GeoOperations"; | ||||
| import {Utils} from "../../Utils"; | ||||
| import {SpecialVisualization} from "../SpecialVisualization"; | ||||
| import Translations from "../i18n/Translations" | ||||
| import { SubtleButton } from "../Base/SubtleButton" | ||||
| import Svg from "../../Svg" | ||||
| import Combine from "../Base/Combine" | ||||
| import { GeoOperations } from "../../Logic/GeoOperations" | ||||
| import { Utils } from "../../Utils" | ||||
| import { SpecialVisualization } from "../SpecialVisualization" | ||||
| 
 | ||||
| export class ExportAsGpxViz implements SpecialVisualization { | ||||
|     funcName = "export_as_gpx" | ||||
|  | @ -26,16 +26,10 @@ export class ExportAsGpxViz implements SpecialVisualization { | |||
|             const feature = state.allElements.ContainingFeatures.get(tags.id) | ||||
|             const matchingLayer = state?.layoutToUse?.getMatchingLayer(tags) | ||||
|             const gpx = GeoOperations.AsGpx(feature, matchingLayer) | ||||
|             const title = | ||||
|                 matchingLayer.title?.GetRenderValue(tags)?.Subs(tags)?.txt ?? | ||||
|                 "gpx_track" | ||||
|             Utils.offerContentsAsDownloadableFile( | ||||
|                 gpx, | ||||
|                 title + "_mapcomplete_export.gpx", | ||||
|                 { | ||||
|             const title = matchingLayer.title?.GetRenderValue(tags)?.Subs(tags)?.txt ?? "gpx_track" | ||||
|             Utils.offerContentsAsDownloadableFile(gpx, title + "_mapcomplete_export.gpx", { | ||||
|                 mimetype: "{gpx=application/gpx+xml}", | ||||
|                 } | ||||
|             ) | ||||
|             }) | ||||
|         }) | ||||
|     } | ||||
| } | ||||
|  |  | |||
|  | @ -1,5 +1,5 @@ | |||
| import {Store, UIEventSource} from "../../Logic/UIEventSource"; | ||||
| import {FixedUiElement} from "../Base/FixedUiElement"; | ||||
| import { Store, UIEventSource } from "../../Logic/UIEventSource" | ||||
| import { FixedUiElement } from "../Base/FixedUiElement" | ||||
| // import Histogram from "../BigComponents/Histogram";
 | ||||
| // import {SpecialVisualization} from "../SpecialVisualization";
 | ||||
| 
 | ||||
|  | @ -28,7 +28,7 @@ export class HistogramViz  { | |||
|             name: "colors*", | ||||
|             doc: "(Matches all resting arguments - optional) Matches a regex onto a color value, e.g. `3[a-zA-Z+-]*:#33cc33`", | ||||
|         }, | ||||
|     ]; | ||||
|     ] | ||||
| 
 | ||||
|     constr(state, tagSource: UIEventSource<any>, args: string[]) { | ||||
|         let assignColors = undefined | ||||
|  | @ -39,7 +39,7 @@ export class HistogramViz  { | |||
|                 const splitted = c.split(":") | ||||
|                 const value = splitted.pop() | ||||
|                 const regex = splitted.join(":") | ||||
|                 return {regex: "^" + regex + "$", color: value} | ||||
|                 return { regex: "^" + regex + "$", color: value } | ||||
|             }) | ||||
|             assignColors = (key) => { | ||||
|                 for (const kv of mapping) { | ||||
|  | @ -59,10 +59,7 @@ export class HistogramViz  { | |||
|                 } | ||||
|                 return JSON.parse(value) | ||||
|             } catch (e) { | ||||
|                 console.error( | ||||
|                     "Could not load histogram: parsing  of the list failed: ", | ||||
|                     e | ||||
|                 ) | ||||
|                 console.error("Could not load histogram: parsing  of the list failed: ", e) | ||||
|                 return undefined | ||||
|             } | ||||
|         }) | ||||
|  |  | |||
|  | @ -44,7 +44,7 @@ import { Changes } from "../../Logic/Osm/Changes" | |||
| import { ElementStorage } from "../../Logic/ElementStorage" | ||||
| import Hash from "../../Logic/Web/Hash" | ||||
| import { PreciseInput } from "../../Models/ThemeConfig/PresetConfig" | ||||
| import {SpecialVisualization} from "../SpecialVisualization"; | ||||
| import { SpecialVisualization } from "../SpecialVisualization" | ||||
| 
 | ||||
| /** | ||||
|  * A helper class for the various import-flows. | ||||
|  |  | |||
|  | @ -1,74 +1,69 @@ | |||
| import {SpecialVisualization} from "../SpecialVisualization"; | ||||
| import BaseUIElement from "../BaseUIElement"; | ||||
| import {UIEventSource} from "../../Logic/UIEventSource"; | ||||
| import FeaturePipelineState from "../../Logic/State/FeaturePipelineState"; | ||||
| import {VariableUiElement} from "../Base/VariableUIElement"; | ||||
| import {OsmTags} from "../../Models/OsmFeature"; | ||||
| import { SpecialVisualization } from "../SpecialVisualization" | ||||
| import BaseUIElement from "../BaseUIElement" | ||||
| import { UIEventSource } from "../../Logic/UIEventSource" | ||||
| import FeaturePipelineState from "../../Logic/State/FeaturePipelineState" | ||||
| import { VariableUiElement } from "../Base/VariableUIElement" | ||||
| import { OsmTags } from "../../Models/OsmFeature" | ||||
| import * as all_languages from "../../assets/language_translations.json" | ||||
| import {Translation} from "../i18n/Translation"; | ||||
| import Combine from "../Base/Combine"; | ||||
| import Title from "../Base/Title"; | ||||
| import Lazy from "../Base/Lazy"; | ||||
| import {SubstitutedTranslation} from "../SubstitutedTranslation"; | ||||
| import List from "../Base/List"; | ||||
| import {AllLanguagesSelector} from "./AllLanguagesSelector"; | ||||
| import ChangeTagAction from "../../Logic/Osm/Actions/ChangeTagAction"; | ||||
| import {And} from "../../Logic/Tags/And"; | ||||
| import {Tag} from "../../Logic/Tags/Tag"; | ||||
| import {EditButton, SaveButton} from "./SaveButton"; | ||||
| import {FixedUiElement} from "../Base/FixedUiElement"; | ||||
| import Translations from "../i18n/Translations"; | ||||
| import Toggle from "../Input/Toggle"; | ||||
| import {On} from "../../Models/ThemeConfig/Conversion/Conversion"; | ||||
| 
 | ||||
| import { Translation } from "../i18n/Translation" | ||||
| import Combine from "../Base/Combine" | ||||
| import Title from "../Base/Title" | ||||
| import Lazy from "../Base/Lazy" | ||||
| import { SubstitutedTranslation } from "../SubstitutedTranslation" | ||||
| import List from "../Base/List" | ||||
| import { AllLanguagesSelector } from "./AllLanguagesSelector" | ||||
| import ChangeTagAction from "../../Logic/Osm/Actions/ChangeTagAction" | ||||
| import { And } from "../../Logic/Tags/And" | ||||
| import { Tag } from "../../Logic/Tags/Tag" | ||||
| import { EditButton, SaveButton } from "./SaveButton" | ||||
| import { FixedUiElement } from "../Base/FixedUiElement" | ||||
| import Translations from "../i18n/Translations" | ||||
| import Toggle from "../Input/Toggle" | ||||
| import { On } from "../../Models/ThemeConfig/Conversion/Conversion" | ||||
| 
 | ||||
| export class LanguageElement implements SpecialVisualization { | ||||
|     funcName: string = "language_chooser" | ||||
| 
 | ||||
|     docs: string | BaseUIElement = "The language element allows to show and pick all known (modern) languages. The key can be set"; | ||||
|     docs: string | BaseUIElement = | ||||
|         "The language element allows to show and pick all known (modern) languages. The key can be set" | ||||
| 
 | ||||
|     args: { name: string; defaultValue?: string; doc: string; required?: boolean }[] = | ||||
|         [{ | ||||
|     args: { name: string; defaultValue?: string; doc: string; required?: boolean }[] = [ | ||||
|         { | ||||
|             name: "key", | ||||
|             required: true, | ||||
|             doc: "What key to use, e.g. `language`, `tactile_writing:braille:language`, ... If a language is supported, the language code will be appended to this key, resulting in `language:nl=yes` if nl is picked " | ||||
|             doc: "What key to use, e.g. `language`, `tactile_writing:braille:language`, ... If a language is supported, the language code will be appended to this key, resulting in `language:nl=yes` if nl is picked ", | ||||
|         }, | ||||
|         { | ||||
|             name: "question", | ||||
|             required: true, | ||||
|                 doc: "What to ask if no questions are known" | ||||
|             doc: "What to ask if no questions are known", | ||||
|         }, | ||||
|         { | ||||
|             name: "render_list_item", | ||||
|             doc: "How a single language will be shown in the list of languages. Use `{language}` to indicate the language (which it must contain).", | ||||
|                 defaultValue: "{language()}" | ||||
|             defaultValue: "{language()}", | ||||
|         }, | ||||
|         { | ||||
|             name: "render_single_language", | ||||
|             doc: "What will be shown if the feature only supports a single language", | ||||
|                 required: true | ||||
|             required: true, | ||||
|         }, | ||||
|         { | ||||
|             name: "render_all", | ||||
|             doc: "The full rendering. Use `{list}` to show where the list of languages must come. Optional if mode=single", | ||||
|                 defaultValue: "{list()}" | ||||
|             defaultValue: "{list()}", | ||||
|         }, | ||||
|         { | ||||
|             name: "no_known_languages", | ||||
|                 doc: "The text that is shown if no languages are known for this key. If this text is omitted, the languages will be prompted instead" | ||||
|             doc: "The text that is shown if no languages are known for this key. If this text is omitted, the languages will be prompted instead", | ||||
|         }, | ||||
|         { | ||||
|                 name: 'mode', | ||||
|             name: "mode", | ||||
|             doc: "If one or many languages can be selected. Should be 'multi' or 'single'", | ||||
|                 defaultValue: 'multi' | ||||
|             } | ||||
| 
 | ||||
| 
 | ||||
|             defaultValue: "multi", | ||||
|         }, | ||||
|     ] | ||||
| 
 | ||||
| 
 | ||||
|     ; | ||||
| 
 | ||||
|     example: ` | ||||
|     \`\`\`json
 | ||||
|      {"special": | ||||
|  | @ -83,8 +78,13 @@ export class LanguageElement implements SpecialVisualization { | |||
|      \`\`\` | ||||
|     ` | ||||
| 
 | ||||
|     constr(state: FeaturePipelineState, tagSource: UIEventSource<OsmTags>, argument: string[]): BaseUIElement { | ||||
|         let [key, question, item_render, single_render, all_render, on_no_known_languages, mode] = argument | ||||
|     constr( | ||||
|         state: FeaturePipelineState, | ||||
|         tagSource: UIEventSource<OsmTags>, | ||||
|         argument: string[] | ||||
|     ): BaseUIElement { | ||||
|         let [key, question, item_render, single_render, all_render, on_no_known_languages, mode] = | ||||
|             argument | ||||
|         if (mode === undefined || mode.length == 0) { | ||||
|             mode = "multi" | ||||
|         } | ||||
|  | @ -95,7 +95,10 @@ export class LanguageElement implements SpecialVisualization { | |||
|             all_render = "{list()}" | ||||
|         } | ||||
|         if (mode !== "single" && mode !== "multi") { | ||||
|             throw "Error while calling language_chooser: mode must be either 'single' or 'multi' but it is " + mode | ||||
|             throw ( | ||||
|                 "Error while calling language_chooser: mode must be either 'single' or 'multi' but it is " + | ||||
|                 mode | ||||
|             ) | ||||
|         } | ||||
|         if (single_render.indexOf("{language()") < 0 || item_render.indexOf("{language()") < 0) { | ||||
|             throw "Error while calling language_chooser: render_single_language and render_list_item must contain '{language()}'" | ||||
|  | @ -105,8 +108,7 @@ export class LanguageElement implements SpecialVisualization { | |||
|         } | ||||
| 
 | ||||
|         const prefix = key + ":" | ||||
|         const foundLanguages = tagSource | ||||
|             .map(tags => { | ||||
|         const foundLanguages = tagSource.map((tags) => { | ||||
|             const foundLanguages: string[] = [] | ||||
|             for (const k in tags) { | ||||
|                 const v = tags[k] | ||||
|  | @ -119,26 +121,26 @@ export class LanguageElement implements SpecialVisualization { | |||
|             } | ||||
|             return foundLanguages | ||||
|         }) | ||||
|         const forceInputMode = new UIEventSource(false); | ||||
|         const forceInputMode = new UIEventSource(false) | ||||
|         const inputEl = new Lazy(() => { | ||||
|             const selector = new AllLanguagesSelector( | ||||
|                 { | ||||
|             const selector = new AllLanguagesSelector({ | ||||
|                 mode: mode === "single" ? "select-one" : "select-many", | ||||
|                     currentCountry: tagSource.map(tgs => tgs["_country"]) | ||||
|                 } | ||||
|             ) | ||||
|             const cancelButton = Toggle.If(forceInputMode, | ||||
|                 () => Translations.t.general.cancel | ||||
|                 currentCountry: tagSource.map((tgs) => tgs["_country"]), | ||||
|             }) | ||||
|             const cancelButton = Toggle.If(forceInputMode, () => | ||||
|                 Translations.t.general.cancel | ||||
|                     .Clone() | ||||
|                     .SetClass("btn btn-secondary").onClick(() => forceInputMode.setData(false))) | ||||
|                     .SetClass("btn btn-secondary") | ||||
|                     .onClick(() => forceInputMode.setData(false)) | ||||
|             ) | ||||
| 
 | ||||
|             const saveButton = new SaveButton( | ||||
|                 selector.GetValue().map(lngs => lngs.length > 0 ? "true" : undefined), | ||||
|                 state.osmConnection, | ||||
|                 selector.GetValue().map((lngs) => (lngs.length > 0 ? "true" : undefined)), | ||||
|                 state.osmConnection | ||||
|             ).onClick(() => { | ||||
|                 const selectedLanguages = selector.GetValue().data | ||||
|                 const currentLanguages = foundLanguages.data | ||||
|                 const selection: Tag[] = selectedLanguages.map(ln => new Tag(prefix + ln, "yes")); | ||||
|                 const selection: Tag[] = selectedLanguages.map((ln) => new Tag(prefix + ln, "yes")) | ||||
| 
 | ||||
|                 for (const currentLanguage of currentLanguages) { | ||||
|                     if (selectedLanguages.indexOf(currentLanguage) >= 0) { | ||||
|  | @ -148,19 +150,23 @@ export class LanguageElement implements SpecialVisualization { | |||
|                     selection.push(new Tag(prefix + currentLanguage, "")) | ||||
|                 } | ||||
| 
 | ||||
| 
 | ||||
|                 if (state.featureSwitchIsTesting.data) { | ||||
|                     for (const tag of selection) { | ||||
|                         tagSource.data[tag.key] = tag.value | ||||
|                     } | ||||
|                     tagSource.ping() | ||||
|                 } else { | ||||
|                     (state?.changes) | ||||
|                     ;(state?.changes) | ||||
|                         .applyAction( | ||||
|                             new ChangeTagAction(tagSource.data.id, new And(selection), tagSource.data, { | ||||
|                             new ChangeTagAction( | ||||
|                                 tagSource.data.id, | ||||
|                                 new And(selection), | ||||
|                                 tagSource.data, | ||||
|                                 { | ||||
|                                     theme: state?.layoutToUse?.id ?? "unkown", | ||||
|                                     changeType: "answer", | ||||
|                             }) | ||||
|                                 } | ||||
|                             ) | ||||
|                         ) | ||||
|                         .then((_) => { | ||||
|                             console.log("Tagchanges applied") | ||||
|  | @ -169,60 +175,72 @@ export class LanguageElement implements SpecialVisualization { | |||
|                 forceInputMode.setData(false) | ||||
|             }) | ||||
| 
 | ||||
|             return new Combine([new Title(question), selector, | ||||
|                 new Combine([cancelButton, saveButton]).SetClass("flex justify-end") | ||||
|             ]).SetClass("flex flex-col question disable-links"); | ||||
|             return new Combine([ | ||||
|                 new Title(question), | ||||
|                 selector, | ||||
|                 new Combine([cancelButton, saveButton]).SetClass("flex justify-end"), | ||||
|             ]).SetClass("flex flex-col question disable-links") | ||||
|         }) | ||||
| 
 | ||||
|         const editButton = new EditButton(state.osmConnection, () => forceInputMode.setData(true)) | ||||
| 
 | ||||
|         return new VariableUiElement(foundLanguages | ||||
|             .map(foundLanguages => { | ||||
| 
 | ||||
|         return new VariableUiElement( | ||||
|             foundLanguages.map( | ||||
|                 (foundLanguages) => { | ||||
|                     if (forceInputMode.data) { | ||||
|                         return inputEl | ||||
|                     } | ||||
| 
 | ||||
|                     if (foundLanguages.length === 0) { | ||||
|                         // No languages found - we show the question and the input element
 | ||||
|                     if (on_no_known_languages !== undefined && on_no_known_languages.length > 0) { | ||||
|                         return new Combine([on_no_known_languages, editButton]).SetClass("flex justify-end") | ||||
|                         if ( | ||||
|                             on_no_known_languages !== undefined && | ||||
|                             on_no_known_languages.length > 0 | ||||
|                         ) { | ||||
|                             return new Combine([on_no_known_languages, editButton]).SetClass( | ||||
|                                 "flex justify-end" | ||||
|                             ) | ||||
|                         } | ||||
|                         return inputEl | ||||
| 
 | ||||
|                     } | ||||
| 
 | ||||
|                 let rendered: BaseUIElement; | ||||
|                     let rendered: BaseUIElement | ||||
|                     if (foundLanguages.length === 1) { | ||||
|                         const ln = foundLanguages[0] | ||||
|                     let mapping = new Map<string, BaseUIElement>(); | ||||
|                         let mapping = new Map<string, BaseUIElement>() | ||||
|                         mapping.set("language", new Translation(all_languages[ln])) | ||||
|                         rendered = new SubstitutedTranslation( | ||||
|                         new Translation({"*": single_render}, undefined), | ||||
|                         tagSource, state, mapping | ||||
|                             new Translation({ "*": single_render }, undefined), | ||||
|                             tagSource, | ||||
|                             state, | ||||
|                             mapping | ||||
|                         ) | ||||
|                     } else { | ||||
| 
 | ||||
|                     let mapping = new Map<string, BaseUIElement>(); | ||||
|                         let mapping = new Map<string, BaseUIElement>() | ||||
|                         const languagesList = new List( | ||||
|                         foundLanguages.map(ln => { | ||||
|                             let mappingLn = new Map<string, BaseUIElement>(); | ||||
|                             foundLanguages.map((ln) => { | ||||
|                                 let mappingLn = new Map<string, BaseUIElement>() | ||||
|                                 mappingLn.set("language", new Translation(all_languages[ln])) | ||||
|                                 return new SubstitutedTranslation( | ||||
|                                 new Translation({"*": item_render}, undefined), | ||||
|                                 tagSource, state, mappingLn | ||||
|                                     new Translation({ "*": item_render }, undefined), | ||||
|                                     tagSource, | ||||
|                                     state, | ||||
|                                     mappingLn | ||||
|                                 ) | ||||
|                             }) | ||||
|                     ); | ||||
|                         ) | ||||
|                         mapping.set("list", languagesList) | ||||
|                         rendered = new SubstitutedTranslation( | ||||
|                         new Translation({'*': all_render}, undefined), tagSource, | ||||
|                         state, mapping | ||||
|                             new Translation({ "*": all_render }, undefined), | ||||
|                             tagSource, | ||||
|                             state, | ||||
|                             mapping | ||||
|                         ) | ||||
|                     } | ||||
|                     return new Combine([rendered, editButton]).SetClass("flex justify-between") | ||||
| 
 | ||||
|             }, [forceInputMode])); | ||||
|                 }, | ||||
|                 [forceInputMode] | ||||
|             ) | ||||
|         ) | ||||
|     } | ||||
| } | ||||
| 
 | ||||
|  |  | |||
|  | @ -1,8 +1,8 @@ | |||
| import {GeoOperations} from "../../Logic/GeoOperations"; | ||||
| import {MapillaryLink} from "../BigComponents/MapillaryLink"; | ||||
| import {UIEventSource} from "../../Logic/UIEventSource"; | ||||
| import Loc from "../../Models/Loc"; | ||||
| import {SpecialVisualization} from "../SpecialVisualization"; | ||||
| import { GeoOperations } from "../../Logic/GeoOperations" | ||||
| import { MapillaryLink } from "../BigComponents/MapillaryLink" | ||||
| import { UIEventSource } from "../../Logic/UIEventSource" | ||||
| import Loc from "../../Models/Loc" | ||||
| import { SpecialVisualization } from "../SpecialVisualization" | ||||
| 
 | ||||
| export class MapillaryLinkVis implements SpecialVisualization { | ||||
|     funcName = "mapillary_link" | ||||
|  |  | |||
|  | @ -1,9 +1,9 @@ | |||
| import {Store, UIEventSource} from "../../Logic/UIEventSource"; | ||||
| import Loc from "../../Models/Loc"; | ||||
| import Minimap from "../Base/Minimap"; | ||||
| import ShowDataMultiLayer from "../ShowDataLayer/ShowDataMultiLayer"; | ||||
| import StaticFeatureSource from "../../Logic/FeatureSource/Sources/StaticFeatureSource"; | ||||
| import {SpecialVisualization} from "../SpecialVisualization"; | ||||
| import { Store, UIEventSource } from "../../Logic/UIEventSource" | ||||
| import Loc from "../../Models/Loc" | ||||
| import Minimap from "../Base/Minimap" | ||||
| import ShowDataMultiLayer from "../ShowDataLayer/ShowDataMultiLayer" | ||||
| import StaticFeatureSource from "../../Logic/FeatureSource/Sources/StaticFeatureSource" | ||||
| import { SpecialVisualization } from "../SpecialVisualization" | ||||
| 
 | ||||
| export class MinimapViz implements SpecialVisualization { | ||||
|     funcName = "minimap" | ||||
|  | @ -20,8 +20,7 @@ export class MinimapViz implements SpecialVisualization { | |||
|             defaultValue: "id", | ||||
|         }, | ||||
|     ] | ||||
|     example: | ||||
|         "`{minimap()}`, `{minimap(17, id, _list_of_embedded_feature_ids_calculated_by_calculated_tag):height:10rem; border: 2px solid black}`" | ||||
|     example: "`{minimap()}`, `{minimap(17, id, _list_of_embedded_feature_ids_calculated_by_calculated_tag):height:10rem; border: 2px solid black}`" | ||||
| 
 | ||||
|     constr(state, tagSource, args, _) { | ||||
|         if (state === undefined) { | ||||
|  | @ -30,8 +29,8 @@ export class MinimapViz implements SpecialVisualization { | |||
|         const keys = [...args] | ||||
|         keys.splice(0, 1) | ||||
|         const featureStore = state.allElements.ContainingFeatures | ||||
|         const featuresToShow: Store<{ freshness: Date; feature: any }[]> = | ||||
|             tagSource.map((properties) => { | ||||
|         const featuresToShow: Store<{ freshness: Date; feature: any }[]> = tagSource.map( | ||||
|             (properties) => { | ||||
|                 const features: { freshness: Date; feature: any }[] = [] | ||||
|                 for (const key of keys) { | ||||
|                     const value = properties[key] | ||||
|  | @ -58,7 +57,8 @@ export class MinimapViz implements SpecialVisualization { | |||
|                     } | ||||
|                 } | ||||
|                 return features | ||||
|             }) | ||||
|             } | ||||
|         ) | ||||
|         const properties = tagSource.data | ||||
|         let zoom = 18 | ||||
|         if (args[0]) { | ||||
|  |  | |||
|  | @ -1,10 +1,11 @@ | |||
| import {Store} from "../../Logic/UIEventSource"; | ||||
| import MultiApply from "./MultiApply"; | ||||
| import {SpecialVisualization} from "../SpecialVisualization"; | ||||
| import { Store } from "../../Logic/UIEventSource" | ||||
| import MultiApply from "./MultiApply" | ||||
| import { SpecialVisualization } from "../SpecialVisualization" | ||||
| 
 | ||||
| export class MultiApplyViz implements SpecialVisualization { | ||||
|     funcName = "multi_apply" | ||||
|     docs = "A button to apply the tagging of this object onto a list of other features. This is an advanced feature for which you'll need calculatedTags" | ||||
|     docs = | ||||
|         "A button to apply the tagging of this object onto a list of other features. This is an advanced feature for which you'll need calculatedTags" | ||||
|     args = [ | ||||
|         { | ||||
|             name: "feature_ids", | ||||
|  | @ -52,17 +53,14 @@ export class MultiApplyViz implements SpecialVisualization { | |||
|                 return [] | ||||
|             } | ||||
|         }) | ||||
|         return new MultiApply( | ||||
|             { | ||||
|         return new MultiApply({ | ||||
|             featureIds, | ||||
|             keysToApply, | ||||
|             text, | ||||
|             autoapply, | ||||
|             overwrite, | ||||
|             tagsSource, | ||||
|                 state | ||||
|             } | ||||
|         ); | ||||
| 
 | ||||
|             state, | ||||
|         }) | ||||
|     } | ||||
| } | ||||
|  |  | |||
|  | @ -1,25 +1,25 @@ | |||
| import FeaturePipelineState from "../../Logic/State/FeaturePipelineState"; | ||||
| import {UIEventSource} from "../../Logic/UIEventSource"; | ||||
| import {DefaultGuiState} from "../DefaultGuiState"; | ||||
| import BaseUIElement from "../BaseUIElement"; | ||||
| import Translations from "../i18n/Translations"; | ||||
| import {GeoOperations} from "../../Logic/GeoOperations"; | ||||
| import NearbyImages, {NearbyImageOptions, P4CPicture, SelectOneNearbyImage} from "./NearbyImages"; | ||||
| import {SubstitutedTranslation} from "../SubstitutedTranslation"; | ||||
| import {Tag} from "../../Logic/Tags/Tag"; | ||||
| import ChangeTagAction from "../../Logic/Osm/Actions/ChangeTagAction"; | ||||
| import {And} from "../../Logic/Tags/And"; | ||||
| import {SaveButton} from "./SaveButton"; | ||||
| import Lazy from "../Base/Lazy"; | ||||
| import {CheckBox} from "../Input/Checkboxes"; | ||||
| import Slider from "../Input/Slider"; | ||||
| import AllImageProviders from "../../Logic/ImageProviders/AllImageProviders"; | ||||
| import Combine from "../Base/Combine"; | ||||
| import {VariableUiElement} from "../Base/VariableUIElement"; | ||||
| import Toggle from "../Input/Toggle"; | ||||
| import Title from "../Base/Title"; | ||||
| import {MapillaryLinkVis} from "./MapillaryLinkVis"; | ||||
| import {SpecialVisualization} from "../SpecialVisualization"; | ||||
| import FeaturePipelineState from "../../Logic/State/FeaturePipelineState" | ||||
| import { UIEventSource } from "../../Logic/UIEventSource" | ||||
| import { DefaultGuiState } from "../DefaultGuiState" | ||||
| import BaseUIElement from "../BaseUIElement" | ||||
| import Translations from "../i18n/Translations" | ||||
| import { GeoOperations } from "../../Logic/GeoOperations" | ||||
| import NearbyImages, { NearbyImageOptions, P4CPicture, SelectOneNearbyImage } from "./NearbyImages" | ||||
| import { SubstitutedTranslation } from "../SubstitutedTranslation" | ||||
| import { Tag } from "../../Logic/Tags/Tag" | ||||
| import ChangeTagAction from "../../Logic/Osm/Actions/ChangeTagAction" | ||||
| import { And } from "../../Logic/Tags/And" | ||||
| import { SaveButton } from "./SaveButton" | ||||
| import Lazy from "../Base/Lazy" | ||||
| import { CheckBox } from "../Input/Checkboxes" | ||||
| import Slider from "../Input/Slider" | ||||
| import AllImageProviders from "../../Logic/ImageProviders/AllImageProviders" | ||||
| import Combine from "../Base/Combine" | ||||
| import { VariableUiElement } from "../Base/VariableUIElement" | ||||
| import Toggle from "../Input/Toggle" | ||||
| import Title from "../Base/Title" | ||||
| import { MapillaryLinkVis } from "./MapillaryLinkVis" | ||||
| import { SpecialVisualization } from "../SpecialVisualization" | ||||
| 
 | ||||
| export class NearbyImageVis implements SpecialVisualization { | ||||
|     args: { name: string; defaultValue?: string; doc: string; required?: boolean }[] = [ | ||||
|  | @ -116,7 +116,7 @@ export class NearbyImageVis implements SpecialVisualization { | |||
|                 towardsCenter, | ||||
|                 new Combine([ | ||||
|                     new VariableUiElement( | ||||
|                         radius.GetValue().map((radius) => t.withinRadius.Subs({radius})) | ||||
|                         radius.GetValue().map((radius) => t.withinRadius.Subs({ radius })) | ||||
|                     ), | ||||
|                     radius, | ||||
|                 ]).SetClass("flex justify-between"), | ||||
|  |  | |||
|  | @ -1,29 +1,30 @@ | |||
| import {Store, UIEventSource} from "../../Logic/UIEventSource"; | ||||
| import Toggle from "../Input/Toggle"; | ||||
| import Lazy from "../Base/Lazy"; | ||||
| import {ProvidedImage} from "../../Logic/ImageProviders/ImageProvider"; | ||||
| import PlantNetSpeciesSearch from "../BigComponents/PlantNetSpeciesSearch"; | ||||
| import Wikidata from "../../Logic/Web/Wikidata"; | ||||
| import ChangeTagAction from "../../Logic/Osm/Actions/ChangeTagAction"; | ||||
| import {And} from "../../Logic/Tags/And"; | ||||
| import {Tag} from "../../Logic/Tags/Tag"; | ||||
| import {SubtleButton} from "../Base/SubtleButton"; | ||||
| import Combine from "../Base/Combine"; | ||||
| import Svg from "../../Svg"; | ||||
| import Translations from "../i18n/Translations"; | ||||
| import AllImageProviders from "../../Logic/ImageProviders/AllImageProviders"; | ||||
| import {SpecialVisualization} from "../SpecialVisualization"; | ||||
| import { Store, UIEventSource } from "../../Logic/UIEventSource" | ||||
| import Toggle from "../Input/Toggle" | ||||
| import Lazy from "../Base/Lazy" | ||||
| import { ProvidedImage } from "../../Logic/ImageProviders/ImageProvider" | ||||
| import PlantNetSpeciesSearch from "../BigComponents/PlantNetSpeciesSearch" | ||||
| import Wikidata from "../../Logic/Web/Wikidata" | ||||
| import ChangeTagAction from "../../Logic/Osm/Actions/ChangeTagAction" | ||||
| import { And } from "../../Logic/Tags/And" | ||||
| import { Tag } from "../../Logic/Tags/Tag" | ||||
| import { SubtleButton } from "../Base/SubtleButton" | ||||
| import Combine from "../Base/Combine" | ||||
| import Svg from "../../Svg" | ||||
| import Translations from "../i18n/Translations" | ||||
| import AllImageProviders from "../../Logic/ImageProviders/AllImageProviders" | ||||
| import { SpecialVisualization } from "../SpecialVisualization" | ||||
| 
 | ||||
| export class PlantNetDetectionViz implements SpecialVisualization { | ||||
|     funcName = "plantnet_detection" | ||||
| 
 | ||||
|     docs = "Sends the images linked to the current object to plantnet.org and asks it what plant species is shown on it. The user can then select the correct species; the corresponding wikidata-identifier will then be added to the object (together with `source:species:wikidata=plantnet.org AI`). " | ||||
|     docs = | ||||
|         "Sends the images linked to the current object to plantnet.org and asks it what plant species is shown on it. The user can then select the correct species; the corresponding wikidata-identifier will then be added to the object (together with `source:species:wikidata=plantnet.org AI`). " | ||||
|     args = [ | ||||
|         { | ||||
|             name: "image_key", | ||||
|             defaultValue: AllImageProviders.defaultKeys.join(","), | ||||
|             doc: "The keys given to the images, e.g. if <span class='literal-code'>image</span> is given, the first picture URL will be added as <span class='literal-code'>image</span>, the second as <span class='literal-code'>image:0</span>, the third as <span class='literal-code'>image:1</span>, etc... Multiple values are allowed if ';'-separated ", | ||||
|         } | ||||
|         }, | ||||
|     ] | ||||
| 
 | ||||
|     public constr(state, tags, args) { | ||||
|  | @ -35,14 +36,14 @@ export class PlantNetDetectionViz implements SpecialVisualization { | |||
|         const detect = new UIEventSource(false) | ||||
|         const toggle = new Toggle( | ||||
|             new Lazy(() => { | ||||
|                 const allProvidedImages: Store<ProvidedImage[]> = | ||||
|                     AllImageProviders.LoadImagesFor(tags, imagePrefixes) | ||||
|                 const allProvidedImages: Store<ProvidedImage[]> = AllImageProviders.LoadImagesFor( | ||||
|                     tags, | ||||
|                     imagePrefixes | ||||
|                 ) | ||||
|                 const allImages: Store<string[]> = allProvidedImages.map((pi) => | ||||
|                     pi.map((pi) => pi.url) | ||||
|                 ) | ||||
|                 return new PlantNetSpeciesSearch( | ||||
|                     allImages, | ||||
|                     async (selectedWikidata) => { | ||||
|                 return new PlantNetSpeciesSearch(allImages, async (selectedWikidata) => { | ||||
|                     selectedWikidata = Wikidata.ExtractKey(selectedWikidata) | ||||
|                     const change = new ChangeTagAction( | ||||
|                         tags.data.id, | ||||
|  | @ -57,22 +58,18 @@ export class PlantNetDetectionViz implements SpecialVisualization { | |||
|                         } | ||||
|                     ) | ||||
|                     await state.changes.applyAction(change) | ||||
|                     } | ||||
|                 ) | ||||
|                 }) | ||||
|             }), | ||||
|             new SubtleButton( | ||||
|                 undefined, | ||||
|                 "Detect plant species with plantnet.org" | ||||
|             ).onClick(() => detect.setData(true)), | ||||
|             new SubtleButton(undefined, "Detect plant species with plantnet.org").onClick(() => | ||||
|                 detect.setData(true) | ||||
|             ), | ||||
|             detect | ||||
|         ) | ||||
| 
 | ||||
|         return new Combine([ | ||||
|             toggle, | ||||
|             new Combine([ | ||||
|                 Svg.plantnet_logo_svg().SetClass( | ||||
|                     "w-10 h-10 p-1 mr-1 bg-white rounded-full" | ||||
|                 ), | ||||
|                 Svg.plantnet_logo_svg().SetClass("w-10 h-10 p-1 mr-1 bg-white rounded-full"), | ||||
|                 Translations.t.plantDetection.poweredByPlantnet, | ||||
|             ]).SetClass("flex p-2 bg-gray-200 rounded-xl self-end"), | ||||
|         ]).SetClass("flex flex-col") | ||||
|  |  | |||
|  | @ -3,8 +3,8 @@ import Translations from "../i18n/Translations" | |||
| import { OsmConnection } from "../../Logic/Osm/OsmConnection" | ||||
| import Toggle from "../Input/Toggle" | ||||
| import BaseUIElement from "../BaseUIElement" | ||||
| import Combine from "../Base/Combine"; | ||||
| import Svg from "../../Svg"; | ||||
| import Combine from "../Base/Combine" | ||||
| import Svg from "../../Svg" | ||||
| 
 | ||||
| export class EditButton extends Toggle { | ||||
|     constructor(osmConnection: OsmConnection, onClick: () => void) { | ||||
|  |  | |||
|  | @ -1,9 +1,9 @@ | |||
| import {UIEventSource} from "../../Logic/UIEventSource"; | ||||
| import LayerConfig from "../../Models/ThemeConfig/LayerConfig"; | ||||
| import ShareButton from "../BigComponents/ShareButton"; | ||||
| import Svg from "../../Svg"; | ||||
| import {FixedUiElement} from "../Base/FixedUiElement"; | ||||
| import {SpecialVisualization} from "../SpecialVisualization"; | ||||
| import { UIEventSource } from "../../Logic/UIEventSource" | ||||
| import LayerConfig from "../../Models/ThemeConfig/LayerConfig" | ||||
| import ShareButton from "../BigComponents/ShareButton" | ||||
| import Svg from "../../Svg" | ||||
| import { FixedUiElement } from "../Base/FixedUiElement" | ||||
| import { SpecialVisualization } from "../SpecialVisualization" | ||||
| 
 | ||||
| export class ShareLinkViz implements SpecialVisualization { | ||||
|     funcName = "share_link" | ||||
|  | @ -45,10 +45,7 @@ export class ShareLinkViz implements SpecialVisualization { | |||
|                 } | ||||
|             } | ||||
| 
 | ||||
|             return new ShareButton( | ||||
|                 Svg.share_svg().SetClass("w-8 h-8"), | ||||
|                 generateShareData | ||||
|             ) | ||||
|             return new ShareButton(Svg.share_svg().SetClass("w-8 h-8"), generateShareData) | ||||
|         } else { | ||||
|             return new FixedUiElement("") | ||||
|         } | ||||
|  |  | |||
|  | @ -1,15 +1,16 @@ | |||
| import {UIEventSource} from "../../Logic/UIEventSource"; | ||||
| import Loc from "../../Models/Loc"; | ||||
| import Minimap from "../Base/Minimap"; | ||||
| import ShowDataLayer from "../ShowDataLayer/ShowDataLayer"; | ||||
| import LayerConfig from "../../Models/ThemeConfig/LayerConfig"; | ||||
| import * as left_right_style_json from "../../assets/layers/left_right_style/left_right_style.json"; | ||||
| import StaticFeatureSource from "../../Logic/FeatureSource/Sources/StaticFeatureSource"; | ||||
| import {SpecialVisualization} from "../SpecialVisualization"; | ||||
| import { UIEventSource } from "../../Logic/UIEventSource" | ||||
| import Loc from "../../Models/Loc" | ||||
| import Minimap from "../Base/Minimap" | ||||
| import ShowDataLayer from "../ShowDataLayer/ShowDataLayer" | ||||
| import LayerConfig from "../../Models/ThemeConfig/LayerConfig" | ||||
| import * as left_right_style_json from "../../assets/layers/left_right_style/left_right_style.json" | ||||
| import StaticFeatureSource from "../../Logic/FeatureSource/Sources/StaticFeatureSource" | ||||
| import { SpecialVisualization } from "../SpecialVisualization" | ||||
| 
 | ||||
| export class SidedMinimap implements SpecialVisualization { | ||||
|     funcName = "sided_minimap" | ||||
|     docs = "A small map showing _only one side_ the selected feature. *This features requires to have linerenderings with offset* as only linerenderings with a postive or negative offset will be shown. Note: in most cases, this map will be automatically introduced" | ||||
|     docs = | ||||
|         "A small map showing _only one side_ the selected feature. *This features requires to have linerenderings with offset* as only linerenderings with a postive or negative offset will be shown. Note: in most cases, this map will be automatically introduced" | ||||
|     args = [ | ||||
|         { | ||||
|             doc: "The side to show, either `left` or `right`", | ||||
|  | @ -33,18 +34,14 @@ export class SidedMinimap implements SpecialVisualization { | |||
|         }) | ||||
|         const side = args[0] | ||||
|         const feature = state.allElements.ContainingFeatures.get(tagSource.data.id) | ||||
|         const copy = {...feature} | ||||
|         const copy = { ...feature } | ||||
|         copy.properties = { | ||||
|             id: side, | ||||
|         } | ||||
|         new ShowDataLayer({ | ||||
|             leafletMap: minimap["leafletMap"], | ||||
|             zoomToFeatures: true, | ||||
|             layerToShow: new LayerConfig( | ||||
|                 left_right_style_json, | ||||
|                 "all_known_layers", | ||||
|                 true | ||||
|             ), | ||||
|             layerToShow: new LayerConfig(left_right_style_json, "all_known_layers", true), | ||||
|             features: StaticFeatureSource.fromGeojson([copy]), | ||||
|             state, | ||||
|         }) | ||||
|  |  | |||
|  | @ -1,10 +1,10 @@ | |||
| import LayerConfig from "../../Models/ThemeConfig/LayerConfig"; | ||||
| import TagRenderingConfig from "../../Models/ThemeConfig/TagRenderingConfig"; | ||||
| import {VariableUiElement} from "../Base/VariableUIElement"; | ||||
| import BaseUIElement from "../BaseUIElement"; | ||||
| import EditableTagRendering from "./EditableTagRendering"; | ||||
| import Combine from "../Base/Combine"; | ||||
| import {SpecialVisualization} from "../SpecialVisualization"; | ||||
| import LayerConfig from "../../Models/ThemeConfig/LayerConfig" | ||||
| import TagRenderingConfig from "../../Models/ThemeConfig/TagRenderingConfig" | ||||
| import { VariableUiElement } from "../Base/VariableUIElement" | ||||
| import BaseUIElement from "../BaseUIElement" | ||||
| import EditableTagRendering from "./EditableTagRendering" | ||||
| import Combine from "../Base/Combine" | ||||
| import { SpecialVisualization } from "../SpecialVisualization" | ||||
| 
 | ||||
| export class StealViz implements SpecialVisualization { | ||||
|     funcName = "steal" | ||||
|  | @ -27,9 +27,7 @@ export class StealViz implements SpecialVisualization { | |||
|         for (const layerAndTagRenderingId of layerAndtagRenderingIds.split(";")) { | ||||
|             const [layerId, tagRenderingId] = layerAndTagRenderingId.trim().split(".") | ||||
|             const layer = state.layoutToUse.layers.find((l) => l.id === layerId) | ||||
|             const tagRendering = layer.tagRenderings.find( | ||||
|                 (tr) => tr.id === tagRenderingId | ||||
|             ) | ||||
|             const tagRendering = layer.tagRenderings.find((tr) => tr.id === tagRenderingId) | ||||
|             tagRenderings.push([layer, tagRendering]) | ||||
|         } | ||||
|         if (tagRenderings.length === 0) { | ||||
|  |  | |||
|  | @ -14,9 +14,9 @@ import { Tag } from "../../Logic/Tags/Tag" | |||
| import FeaturePipelineState from "../../Logic/State/FeaturePipelineState" | ||||
| import LayoutConfig from "../../Models/ThemeConfig/LayoutConfig" | ||||
| import { Changes } from "../../Logic/Osm/Changes" | ||||
| import {SpecialVisualization} from "../SpecialVisualization"; | ||||
| import { SpecialVisualization } from "../SpecialVisualization" | ||||
| 
 | ||||
| export default class TagApplyButton implements AutoAction , SpecialVisualization{ | ||||
| export default class TagApplyButton implements AutoAction, SpecialVisualization { | ||||
|     public readonly funcName = "tag_apply" | ||||
|     public readonly docs = | ||||
|         "Shows a big button; clicking this button will apply certain tags onto the feature.\n\nThe first argument takes a specification of which tags to add.\n" + | ||||
|  |  | |||
|  | @ -105,7 +105,7 @@ export default class TagRenderingQuestion extends Combine { | |||
|                 TagUtils.FlattenAnd(inputElement.GetValue().data, tags.data) | ||||
|             ) | ||||
|             if (selection) { | ||||
|                 (state?.changes) | ||||
|                 ;(state?.changes) | ||||
|                     .applyAction( | ||||
|                         new ChangeTagAction(tags.data.id, selection, tags.data, { | ||||
|                             theme: state?.layoutToUse?.id ?? "unkown", | ||||
|  | @ -288,7 +288,7 @@ export default class TagRenderingQuestion extends Combine { | |||
|         value: number | ||||
|         mainTerm: Record<string, string> | ||||
|         searchTerms?: Record<string, string[]> | ||||
|         original: Mapping, | ||||
|         original: Mapping | ||||
|         hasPriority?: Store<boolean> | ||||
|     }[] { | ||||
|         const values: { | ||||
|  | @ -296,7 +296,7 @@ export default class TagRenderingQuestion extends Combine { | |||
|             value: number | ||||
|             mainTerm: Record<string, string> | ||||
|             searchTerms?: Record<string, string[]> | ||||
|             original: Mapping, | ||||
|             original: Mapping | ||||
|             hasPriority?: Store<boolean> | ||||
|         }[] = [] | ||||
|         const addIcons = applicableMappings.some((m) => m.icon !== undefined) | ||||
|  | @ -319,7 +319,7 @@ export default class TagRenderingQuestion extends Combine { | |||
|                 mainTerm: tr.translations, | ||||
|                 searchTerms: mapping.searchTerms, | ||||
|                 original: mapping, | ||||
|                 hasPriority: tagsSource.map(tags => mapping.priorityIf?.matchesProperties(tags)) | ||||
|                 hasPriority: tagsSource.map((tags) => mapping.priorityIf?.matchesProperties(tags)), | ||||
|             }) | ||||
|         } | ||||
|         return values | ||||
|  | @ -400,7 +400,7 @@ export default class TagRenderingQuestion extends Combine { | |||
|         const values = TagRenderingQuestion.MappingToPillValue( | ||||
|             applicableMappings, | ||||
|             tagsSource, | ||||
|             state, | ||||
|             state | ||||
|         ) | ||||
| 
 | ||||
|         const searchValue: UIEventSource<string> = | ||||
|  | @ -419,7 +419,7 @@ export default class TagRenderingQuestion extends Combine { | |||
|             mode, | ||||
|             searchValue, | ||||
|             onNoMatches: onEmpty?.SetClass(classes).SetClass("flex justify-center items-center"), | ||||
|             searchAreaClass: classes | ||||
|             searchAreaClass: classes, | ||||
|         }) | ||||
|         const fallbackTag = searchValue.map((s) => { | ||||
|             if (s === undefined || ff?.key === undefined) { | ||||
|  |  | |||
|  | @ -1,27 +1,28 @@ | |||
| import {Utils} from "../../Utils"; | ||||
| import {Feature} from "geojson"; | ||||
| import {Point} from "@turf/turf"; | ||||
| import {GeoLocationPointProperties} from "../../Logic/Actors/GeoLocationHandler"; | ||||
| import UploadTraceToOsmUI from "../BigComponents/UploadTraceToOsmUI"; | ||||
| import {SpecialVisualization} from "../SpecialVisualization"; | ||||
| import { Utils } from "../../Utils" | ||||
| import { Feature } from "geojson" | ||||
| import { Point } from "@turf/turf" | ||||
| import { GeoLocationPointProperties } from "../../Logic/Actors/GeoLocationHandler" | ||||
| import UploadTraceToOsmUI from "../BigComponents/UploadTraceToOsmUI" | ||||
| import { SpecialVisualization } from "../SpecialVisualization" | ||||
| 
 | ||||
| /** | ||||
|  * Wrapper  around 'UploadTraceToOsmUI' | ||||
|  */ | ||||
| export class UploadToOsmViz implements SpecialVisualization { | ||||
|     funcName = "upload_to_osm" | ||||
|     docs = "Uploads the GPS-history as GPX to OpenStreetMap.org; clears the history afterwards. The actual feature is ignored." | ||||
|     docs = | ||||
|         "Uploads the GPS-history as GPX to OpenStreetMap.org; clears the history afterwards. The actual feature is ignored." | ||||
|     args = [] | ||||
| 
 | ||||
|     constr(state, featureTags, args) { | ||||
| 
 | ||||
|         function getTrace(title: string) { | ||||
|             title = title?.trim() | ||||
|             if (title === undefined || title === "") { | ||||
|                 title = "Uploaded with MapComplete" | ||||
|             } | ||||
|             title = Utils.EncodeXmlValue(title) | ||||
|             const userLocations: Feature<Point, GeoLocationPointProperties>[] = state.historicalUserLocations.features.data.map(f => f.feature) | ||||
|             const userLocations: Feature<Point, GeoLocationPointProperties>[] = | ||||
|                 state.historicalUserLocations.features.data.map((f) => f.feature) | ||||
|             const trackPoints: string[] = [] | ||||
|             for (const l of userLocations) { | ||||
|                 let trkpt = `    <trkpt lat="${l.geometry.coordinates[1]}" lon="${l.geometry.coordinates[0]}">` | ||||
|  | @ -32,14 +33,22 @@ export class UploadToOsmViz implements SpecialVisualization { | |||
|                 trkpt += "    </trkpt>" | ||||
|                 trackPoints.push(trkpt) | ||||
|             } | ||||
|             const header = '<gpx version="1.1" creator="MapComplete track uploader" xmlns="http://www.topografix.com/GPX/1/1" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://www.topografix.com/GPX/1/1 http://www.topografix.com/GPX/1/1/gpx.xsd">' | ||||
|             return header + "\n<name>" + title + "</name>\n<trk><trkseg>\n" + trackPoints.join("\n") + "\n</trkseg></trk></gpx>" | ||||
|             const header = | ||||
|                 '<gpx version="1.1" creator="MapComplete track uploader" xmlns="http://www.topografix.com/GPX/1/1" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://www.topografix.com/GPX/1/1 http://www.topografix.com/GPX/1/1/gpx.xsd">' | ||||
|             return ( | ||||
|                 header + | ||||
|                 "\n<name>" + | ||||
|                 title + | ||||
|                 "</name>\n<trk><trkseg>\n" + | ||||
|                 trackPoints.join("\n") + | ||||
|                 "\n</trkseg></trk></gpx>" | ||||
|             ) | ||||
|         } | ||||
| 
 | ||||
|         return new UploadTraceToOsmUI(getTrace, state, { | ||||
|             whenUploaded: async () => { | ||||
|                 state.historicalUserLocations.features.setData([]) | ||||
|             } | ||||
|             }, | ||||
|         }) | ||||
|     } | ||||
| } | ||||
|  |  | |||
							
								
								
									
										24
									
								
								test.ts
									
										
									
									
									
								
							
							
						
						
									
										24
									
								
								test.ts
									
										
									
									
									
								
							|  | @ -1,24 +1,26 @@ | |||
| import {LanguageElement} from "./UI/Popup/LanguageElement"; | ||||
| import {ImmutableStore, UIEventSource} from "./Logic/UIEventSource"; | ||||
| import {VariableUiElement} from "./UI/Base/VariableUIElement"; | ||||
| import Locale from "./UI/i18n/Locale"; | ||||
| import {OsmConnection} from "./Logic/Osm/OsmConnection"; | ||||
| import { LanguageElement } from "./UI/Popup/LanguageElement" | ||||
| import { ImmutableStore, UIEventSource } from "./Logic/UIEventSource" | ||||
| import { VariableUiElement } from "./UI/Base/VariableUIElement" | ||||
| import Locale from "./UI/i18n/Locale" | ||||
| import { OsmConnection } from "./Logic/Osm/OsmConnection" | ||||
| 
 | ||||
| const tgs = new UIEventSource({ | ||||
|     "name": "xyz", | ||||
|     "id": "node/1234", | ||||
|     "_country" : "BE", | ||||
|     name: "xyz", | ||||
|     id: "node/1234", | ||||
|     _country: "BE", | ||||
| }) | ||||
| Locale.language.setData("nl") | ||||
| console.log(tgs) | ||||
| console.log("Locale", Locale.language) | ||||
| const conn = new OsmConnection({}) | ||||
| new LanguageElement().constr(<any> {osmConnection: conn, featureSwitchIsTesting: new ImmutableStore(true)}, tgs, [ | ||||
| new LanguageElement() | ||||
|     .constr(<any>{ osmConnection: conn, featureSwitchIsTesting: new ImmutableStore(true) }, tgs, [ | ||||
|         "language", | ||||
|         "What languages are spoken here?", | ||||
|         "{language()} is spoken here", | ||||
|         "{language()} is the only language spoken here", | ||||
|     "The following languages are spoken here: {list()}" | ||||
| ]).AttachTo("maindiv") | ||||
|         "The following languages are spoken here: {list()}", | ||||
|     ]) | ||||
|     .AttachTo("maindiv") | ||||
| 
 | ||||
| new VariableUiElement(tgs.map(JSON.stringify)).AttachTo("extradiv") | ||||
|  |  | |||
|  | @ -12,8 +12,11 @@ describe("SpecialVisualisations", () => { | |||
|                     "A special visualisation is not allowed to be named 'type', as this will conflict with the 'special'-blocks" | ||||
|                 ) | ||||
| 
 | ||||
|                 if(special.args === undefined){ | ||||
|                     throw "The field 'args' is undefined for special visualisation "+special.funcName | ||||
|                 if (special.args === undefined) { | ||||
|                     throw ( | ||||
|                         "The field 'args' is undefined for special visualisation " + | ||||
|                         special.funcName | ||||
|                     ) | ||||
|                 } | ||||
| 
 | ||||
|                 for (const arg of special.args) { | ||||
|  |  | |||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue