mapcomplete/UI/CustomGenerator/TagRenderingPanel.ts

148 lines
6.9 KiB
TypeScript

import {UIElement} from "../UIElement";
import {UIEventSource} from "../../Logic/UIEventSource";
import {InputElement} from "../Input/InputElement";
import SingleSetting from "./SingleSetting";
import SettingsTable from "./SettingsTable";
import {TextField} from "../Input/TextField";
import Combine from "../Base/Combine";
import MultiLingualTextFields from "../Input/MultiLingualTextFields";
import AndOrTagInput from "../Input/AndOrTagInput";
import {MultiTagInput} from "../Input/MultiTagInput";
import {MultiInput} from "../Input/MultiInput";
import MappingInput from "./MappingInput";
import {AndOrTagConfigJson} from "../../Customizations/JSON/TagConfigJson";
import {TagRenderingConfigJson} from "../../Customizations/JSON/TagRenderingConfigJson";
import UserDetails from "../../Logic/Osm/OsmConnection";
import {VariableUiElement} from "../Base/VariableUIElement";
import ValidatedTextField from "../Input/ValidatedTextField";
import SpecialVisualizations from "../SpecialVisualizations";
import TagRenderingConfig from "../../Customizations/JSON/TagRenderingConfig";
import Constants from "../../Models/Constants";
export default class TagRenderingPanel extends InputElement<TagRenderingConfigJson> {
private intro: UIElement;
private settingsTable: UIElement;
public IsImage = false;
private readonly _value: UIEventSource<TagRenderingConfigJson>;
public options: { title?: string; description?: string; disableQuestions?: boolean; isImage?: boolean; };
public readonly validText : UIElement;
constructor(languages: UIEventSource<string[]>,
currentlySelected: UIEventSource<SingleSetting<any>>,
userDetails: UserDetails,
options?: {
title?: string,
description?: string,
disableQuestions?: boolean,
isImage?: boolean,
noLanguage?: boolean
}) {
super();
this.SetClass("bordered");
this.SetClass("min-height");
this.options = options ?? {};
const questionsNotUnlocked = userDetails.csCount < Constants.userJourney.themeGeneratorFullUnlock;
this.options.disableQuestions =
(this.options.disableQuestions ?? false) ||
questionsNotUnlocked;
this.intro = new Combine(["<h3>", options?.title ?? "TagRendering", "</h3>",
options?.description ?? "A tagrendering converts OSM-tags into a value on screen. Fill out the field 'render' with the text that should appear. Note that `{key}` will be replaced with the corresponding `value`, if present.<br/>For specific known tags (e.g. if `foo=bar`, make a mapping). "])
this.IsImage = options?.isImage ?? false;
const value = new UIEventSource<TagRenderingConfigJson>({});
this._value = value;
function setting(input: InputElement<any>, id: string | string[], name: string, description: string | UIElement): SingleSetting<any> {
return new SingleSetting<any>(value, input, id, name, description);
}
this._value.addCallback(value => {
if(value?.freeform?.key == ""){
value.freeform = undefined;
this._value.ping();
}
})
const questionSettings = [
setting(options?.noLanguage ? new TextField({placeholder:"question"}) : new MultiLingualTextFields(languages)
, "question", "Question", "If the key or mapping doesn't match, this question is asked"),
"<h3>Freeform key</h3>",
setting(ValidatedTextField.KeyInput(true), ["freeform", "key"], "Freeform key<br/>",
"If specified, the rendering will search if this key is present." +
"If it is, the rendering above will be used to display the element.<br/>" +
"The rendering will go into question mode if <ul><li>this key is not present</li><li>No single mapping matches</li><li>A question is given</li>"),
setting(ValidatedTextField.TypeDropdown(), ["freeform", "type"], "Freeform type",
"The type of this freeform text field, in order to validate"),
setting(new MultiTagInput(), ["freeform", "addExtraTags"], "Extra tags on freeform",
"When the freeform text field is used, the user might mean a predefined key. This field allows to add extra tags, e.g. <span class='literal-code'>fixme=User used a freeform field - to check</span>"),
];
const settings: (string | SingleSetting<any>)[] = [
setting(
options?.noLanguage ? new TextField({placeholder:"Rendering"}) :
new MultiLingualTextFields(languages), "render", "Value to show",
"Renders this value. Note that <span class='literal-code'>{key}</span>-parts are substituted by the corresponding values of the element. If neither 'textFieldQuestion' nor 'mappings' are defined, this text is simply shown as default value." +
"<br/><br/>" +
"Furhtermore, some special functions are supported:"+SpecialVisualizations.HelpMessage.Render()),
questionsNotUnlocked ? `You need at least ${Constants.userJourney.themeGeneratorFullUnlock} changesets to unlock the 'question'-field and to use your theme to edit OSM data` : "",
...(options?.disableQuestions ? [] : questionSettings),
"<h3>Mappings</h3>",
setting(new MultiInput<{ if: AndOrTagConfigJson, then: (string | any), hideInAnswer?: boolean }>("Add a mapping",
() => ({if: {and: []}, then: {}}),
() => new MappingInput(languages, options?.disableQuestions ?? false),
undefined, {allowMovement: true}), "mappings",
"If a tag matches, then show the first respective text", ""),
"<h3>Condition</h3>",
setting(new AndOrTagInput(), "condition", "Only show this tagrendering if the following condition applies",
"Only show this tag rendering if these tags matches. Optional field.<br/>Note that the Overpass-tags are already always included in this object"),
];
this.settingsTable = new SettingsTable(settings, currentlySelected);
this.validText = new VariableUiElement(value.map((json: TagRenderingConfigJson) => {
try{
new TagRenderingConfig(json,undefined, options?.title ?? "");
return "";
}catch(e){
return "<span class='alert'>"+e+"</span>"
}
}));
}
InnerRender(): string {
return new Combine([
this.intro,
this.settingsTable,
this.validText]).Render();
}
GetValue(): UIEventSource<TagRenderingConfigJson> {
return this._value;
}
IsSelected: UIEventSource<boolean> = new UIEventSource<boolean>(false);
IsValid(t: TagRenderingConfigJson): boolean {
return false;
}
}