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,43 +335,52 @@ export class OsmConnection {
|
|||
})
|
||||
}
|
||||
|
||||
public async uploadGpxTrack(gpx: string, options: {
|
||||
description: string,
|
||||
visibility: "private" | "public" | "trackable" | "identifiable",
|
||||
filename?: string
|
||||
/**
|
||||
* Some words to give some properties;
|
||||
*
|
||||
* 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 }> {
|
||||
public async uploadGpxTrack(
|
||||
gpx: string,
|
||||
options: {
|
||||
description: string
|
||||
visibility: "private" | "public" | "trackable" | "identifiable"
|
||||
filename?: string
|
||||
/**
|
||||
* Some words to give some properties;
|
||||
*
|
||||
* 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 }> {
|
||||
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',
|
||||
path: `/api/0.6/gpx/create`,
|
||||
options: {
|
||||
header:
|
||||
{
|
||||
auth.xhr(
|
||||
{
|
||||
method: "POST",
|
||||
path: `/api/0.6/gpx/create`,
|
||||
options: {
|
||||
header: {
|
||||
"Content-Type": "multipart/form-data; boundary=" + boundary,
|
||||
"Content-Length": body.length
|
||||
}
|
||||
"Content-Length": body.length,
|
||||
},
|
||||
},
|
||||
content: body,
|
||||
},
|
||||
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})
|
||||
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 })
|
||||
}
|
||||
}
|
||||
})
|
||||
)
|
||||
})
|
||||
|
||||
}
|
||||
|
||||
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[] } {
|
||||
const errors = []
|
||||
if(json["special"] !== undefined){
|
||||
errors.push("At "+context+": detected `special` on the top level. Did you mean `{\"render\":{ \"special\": ... }}`")
|
||||
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": ... }}`'
|
||||
)
|
||||
}
|
||||
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 button
|
||||
}
|
||||
return loading
|
||||
}))
|
||||
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(() => {
|
||||
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)
|
||||
await state?.osmConnection?.uploadGpxTrack(trace(title.GetValue().data), {
|
||||
visibility: dropdown.GetValue().data,
|
||||
description: descriptionStr,
|
||||
filename: titleStr +".gpx",
|
||||
labels: ["MapComplete", state?.layoutToUse?.id]
|
||||
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
|
||||
)
|
||||
await state?.osmConnection?.uploadGpxTrack(trace(title.GetValue().data), {
|
||||
visibility: dropdown.GetValue().data,
|
||||
description: descriptionStr,
|
||||
filename: titleStr + ".gpx",
|
||||
labels: ["MapComplete", state?.layoutToUse?.id],
|
||||
})
|
||||
|
||||
if (options?.whenUploaded !== undefined) {
|
||||
await options.whenUploaded()
|
||||
if (options?.whenUploaded !== undefined) {
|
||||
await options.whenUploaded()
|
||||
}
|
||||
uploadFinished.setData(true)
|
||||
}
|
||||
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,13 +152,13 @@ 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>
|
||||
}[],
|
||||
options?: {
|
||||
/*
|
||||
* If one single value can be selected (like a radio button) or if many values can be selected (like checkboxes)
|
||||
* If one single value can be selected (like a radio button) or if many values can be selected (like checkboxes)
|
||||
*/
|
||||
mode?: "select-one" | "select-many"
|
||||
/**
|
||||
|
@ -189,14 +189,14 @@ 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
|
||||
: new Combine([
|
||||
Svg.search_svg().SetClass("w-8 normal-background"),
|
||||
search.SetClass("w-full"),
|
||||
]).SetClass("flex items-center border-2 border-black m-2")
|
||||
Svg.search_svg().SetClass("w-8 normal-background"),
|
||||
search.SetClass("w-full"),
|
||||
]).SetClass("flex items-center border-2 border-black m-2")
|
||||
|
||||
const searchValue = search.GetValue().map((s) => s?.trim()?.toLowerCase())
|
||||
const selectedElements = options?.selectedElements ?? new UIEventSource<T[]>([])
|
||||
|
@ -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>,
|
||||
supportedLanguages?: Record<string, string> & { _meta?: { countries?: string[] } }
|
||||
}) {
|
||||
|
||||
mode?: "select-many" | "select-one"
|
||||
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",
|
||||
{
|
||||
mimetype: "{gpx=application/gpx+xml}",
|
||||
}
|
||||
)
|
||||
const title = matchingLayer.title?.GetRenderValue(tags)?.Subs(tags)?.txt ?? "gpx_track"
|
||||
Utils.offerContentsAsDownloadableFile(gpx, title + "_mapcomplete_export.gpx", {
|
||||
mimetype: "{gpx=application/gpx+xml}",
|
||||
})
|
||||
})
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,9 +1,9 @@
|
|||
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";
|
||||
|
||||
export class HistogramViz {
|
||||
export class HistogramViz {
|
||||
funcName = "histogram"
|
||||
docs = "Create a histogram for a list of given values, read from the properties."
|
||||
example =
|
||||
|
@ -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,73 +1,68 @@
|
|||
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"
|
||||
},
|
||||
{
|
||||
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()}"
|
||||
},
|
||||
{
|
||||
name: "render_single_language",
|
||||
doc: "What will be shown if the feature only supports a single language",
|
||||
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()}"
|
||||
},
|
||||
{
|
||||
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"
|
||||
},
|
||||
{
|
||||
name: 'mode',
|
||||
doc: "If one or many languages can be selected. Should be 'multi' or 'single'",
|
||||
defaultValue: 'multi'
|
||||
}
|
||||
|
||||
|
||||
]
|
||||
|
||||
|
||||
;
|
||||
{
|
||||
name: "question",
|
||||
required: true,
|
||||
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()}",
|
||||
},
|
||||
{
|
||||
name: "render_single_language",
|
||||
doc: "What will be shown if the feature only supports a single language",
|
||||
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()}",
|
||||
},
|
||||
{
|
||||
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",
|
||||
},
|
||||
{
|
||||
name: "mode",
|
||||
doc: "If one or many languages can be selected. Should be 'multi' or 'single'",
|
||||
defaultValue: "multi",
|
||||
},
|
||||
]
|
||||
|
||||
example: `
|
||||
\`\`\`json
|
||||
|
@ -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,40 +108,39 @@ export class LanguageElement implements SpecialVisualization {
|
|||
}
|
||||
|
||||
const prefix = key + ":"
|
||||
const foundLanguages = tagSource
|
||||
.map(tags => {
|
||||
const foundLanguages: string[] = []
|
||||
for (const k in tags) {
|
||||
const v = tags[k]
|
||||
if (v !== "yes") {
|
||||
continue
|
||||
}
|
||||
if (k.startsWith(prefix)) {
|
||||
foundLanguages.push(k.substring(prefix.length))
|
||||
}
|
||||
const foundLanguages = tagSource.map((tags) => {
|
||||
const foundLanguages: string[] = []
|
||||
for (const k in tags) {
|
||||
const v = tags[k]
|
||||
if (v !== "yes") {
|
||||
continue
|
||||
}
|
||||
return foundLanguages
|
||||
})
|
||||
const forceInputMode = new UIEventSource(false);
|
||||
if (k.startsWith(prefix)) {
|
||||
foundLanguages.push(k.substring(prefix.length))
|
||||
}
|
||||
}
|
||||
return foundLanguages
|
||||
})
|
||||
const forceInputMode = new UIEventSource(false)
|
||||
const inputEl = new Lazy(() => {
|
||||
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
|
||||
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
|
||||
.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, {
|
||||
theme: state?.layoutToUse?.id ?? "unkown",
|
||||
changeType: "answer",
|
||||
})
|
||||
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 => {
|
||||
|
||||
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")
|
||||
return new VariableUiElement(
|
||||
foundLanguages.map(
|
||||
(foundLanguages) => {
|
||||
if (forceInputMode.data) {
|
||||
return inputEl
|
||||
}
|
||||
return inputEl
|
||||
|
||||
}
|
||||
|
||||
let rendered: BaseUIElement;
|
||||
if (foundLanguages.length === 1) {
|
||||
const ln = foundLanguages[0]
|
||||
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
|
||||
)
|
||||
} else {
|
||||
|
||||
let mapping = new Map<string, BaseUIElement>();
|
||||
const languagesList = new List(
|
||||
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
|
||||
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"
|
||||
)
|
||||
})
|
||||
);
|
||||
mapping.set("list", languagesList)
|
||||
rendered = new SubstitutedTranslation(
|
||||
new Translation({'*': all_render}, undefined), tagSource,
|
||||
state, mapping
|
||||
)
|
||||
}
|
||||
return new Combine([rendered, editButton]).SetClass("flex justify-between")
|
||||
}
|
||||
return inputEl
|
||||
}
|
||||
|
||||
}, [forceInputMode]));
|
||||
let rendered: BaseUIElement
|
||||
if (foundLanguages.length === 1) {
|
||||
const ln = foundLanguages[0]
|
||||
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
|
||||
)
|
||||
} else {
|
||||
let mapping = new Map<string, BaseUIElement>()
|
||||
const languagesList = new List(
|
||||
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
|
||||
)
|
||||
})
|
||||
)
|
||||
mapping.set("list", languagesList)
|
||||
rendered = new SubstitutedTranslation(
|
||||
new Translation({ "*": all_render }, undefined),
|
||||
tagSource,
|
||||
state,
|
||||
mapping
|
||||
)
|
||||
}
|
||||
return new Combine([rendered, editButton]).SetClass("flex justify-between")
|
||||
},
|
||||
[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(
|
||||
{
|
||||
featureIds,
|
||||
keysToApply,
|
||||
text,
|
||||
autoapply,
|
||||
overwrite,
|
||||
tagsSource,
|
||||
state
|
||||
}
|
||||
);
|
||||
|
||||
return new MultiApply({
|
||||
featureIds,
|
||||
keysToApply,
|
||||
text,
|
||||
autoapply,
|
||||
overwrite,
|
||||
tagsSource,
|
||||
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,44 +36,40 @@ 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) => {
|
||||
selectedWikidata = Wikidata.ExtractKey(selectedWikidata)
|
||||
const change = new ChangeTagAction(
|
||||
tags.data.id,
|
||||
new And([
|
||||
new Tag("species:wikidata", selectedWikidata),
|
||||
new Tag("source:species:wikidata", "PlantNet.org AI"),
|
||||
]),
|
||||
tags.data,
|
||||
{
|
||||
theme: state.layoutToUse.id,
|
||||
changeType: "plantnet-ai-detection",
|
||||
}
|
||||
)
|
||||
await state.changes.applyAction(change)
|
||||
}
|
||||
)
|
||||
return new PlantNetSpeciesSearch(allImages, async (selectedWikidata) => {
|
||||
selectedWikidata = Wikidata.ExtractKey(selectedWikidata)
|
||||
const change = new ChangeTagAction(
|
||||
tags.data.id,
|
||||
new And([
|
||||
new Tag("species:wikidata", selectedWikidata),
|
||||
new Tag("source:species:wikidata", "PlantNet.org AI"),
|
||||
]),
|
||||
tags.data,
|
||||
{
|
||||
theme: state.layoutToUse.id,
|
||||
changeType: "plantnet-ai-detection",
|
||||
}
|
||||
)
|
||||
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."
|
||||
args = []
|
||||
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([])
|
||||
}
|
||||
},
|
||||
})
|
||||
}
|
||||
}
|
||||
|
|
32
test.ts
32
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, [
|
||||
"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")
|
||||
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")
|
||||
|
||||
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…
Reference in a new issue