forked from MapComplete/MapComplete
		
	Way to much fixes and improvements
This commit is contained in:
		
							parent
							
								
									e68d9d99a5
								
							
						
					
					
						commit
						5ed0bb431c
					
				
					 41 changed files with 1244 additions and 402 deletions
				
			
		|  | @ -3,34 +3,30 @@ import {TabbedComponent} from "../Base/TabbedComponent"; | |||
| import {SubtleButton} from "../Base/SubtleButton"; | ||||
| import {UIEventSource} from "../../Logic/UIEventSource"; | ||||
| import {LayoutConfigJson} from "../../Customizations/JSON/LayoutConfigJson"; | ||||
| import {LayerConfigJson} from "../../Customizations/JSON/LayerConfigJson"; | ||||
| import LayerPanel from "./LayerPanel"; | ||||
| import SingleSetting from "./SingleSetting"; | ||||
| import Combine from "../Base/Combine"; | ||||
| import {GenerateEmpty} from "./GenerateEmpty"; | ||||
| import PageSplit from "../Base/PageSplit"; | ||||
| import {VariableUiElement} from "../Base/VariableUIElement"; | ||||
| import HelpText from "../../Customizations/HelpText"; | ||||
| import {MultiTagInput} from "../Input/MultiTagInput"; | ||||
| import {FromJSON} from "../../Customizations/JSON/FromJSON"; | ||||
| import {TagRenderingConfigJson} from "../../Customizations/JSON/TagRenderingConfigJson"; | ||||
| import {FixedUiElement} from "../Base/FixedUiElement"; | ||||
| import TagRenderingPanel from "./TagRenderingPanel"; | ||||
| 
 | ||||
| export default class AllLayersPanel extends UIElement { | ||||
| 
 | ||||
| 
 | ||||
|     private panel: UIElement; | ||||
|     private _config: UIEventSource<LayoutConfigJson>; | ||||
|     private _currentlySelected: UIEventSource<SingleSetting<any>>; | ||||
|     private languages: UIEventSource<string[]>; | ||||
|     private readonly _config: UIEventSource<LayoutConfigJson>; | ||||
|     private readonly languages: UIEventSource<string[]>; | ||||
| 
 | ||||
|     private static createEmptyLayer(): LayerConfigJson { | ||||
|         return { | ||||
|             id: undefined, | ||||
|             name: undefined, | ||||
|             minzoom: 0, | ||||
|             overpassTags: undefined, | ||||
|             title: undefined, | ||||
|             description: {} | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|     constructor(config: UIEventSource<LayoutConfigJson>, currentlySelected: UIEventSource<SingleSetting<any>>, | ||||
|     constructor(config: UIEventSource<LayoutConfigJson>, | ||||
|                 languages: UIEventSource<any>) { | ||||
|         super(undefined); | ||||
|         this._config = config; | ||||
|         this._currentlySelected = currentlySelected; | ||||
|         this.languages = languages; | ||||
| 
 | ||||
|         this.createPanels(); | ||||
|  | @ -46,23 +42,85 @@ export default class AllLayersPanel extends UIElement { | |||
| 
 | ||||
|         const layers = this._config.data.layers; | ||||
|         for (let i = 0; i < layers.length; i++) { | ||||
|             const currentlySelected = new UIEventSource<(SingleSetting<any>)>(undefined); | ||||
|             const layer = new LayerPanel(this._config, this.languages, i, currentlySelected); | ||||
|             const helpText = new HelpText(currentlySelected); | ||||
| 
 | ||||
|             const previewTagInput = new MultiTagInput(); | ||||
|             previewTagInput.GetValue().setData(["id=123456"]); | ||||
|             const previewTagValue = previewTagInput.GetValue().map(tags => { | ||||
|                 const properties = {}; | ||||
|                 for (const str of tags) { | ||||
|                     const tag = FromJSON.SimpleTag(str); | ||||
|                     if (tag !== undefined) { | ||||
|                         properties[tag.key] = tag.value; | ||||
|                     } | ||||
|                 } | ||||
|                 return properties; | ||||
|             }); | ||||
| 
 | ||||
|             const preview = new VariableUiElement(layer.selectedTagRendering.map( | ||||
|                 (tagRenderingPanel: TagRenderingPanel) => { | ||||
|                     if (tagRenderingPanel === undefined) { | ||||
|                         return "No tag rendering selected at the moment"; | ||||
|                     } | ||||
| 
 | ||||
|                     let es = tagRenderingPanel.GetValue(); | ||||
|                     let tagRenderingConfig: TagRenderingConfigJson = es.data; | ||||
| 
 | ||||
|                     let rendering: UIElement; | ||||
|                     try { | ||||
|                         rendering = FromJSON.TagRendering(tagRenderingConfig) | ||||
|                             .construct({tags: previewTagValue}) | ||||
|                     } catch (e) { | ||||
|                         console.error("User defined tag rendering incorrect:", e); | ||||
|                         rendering = new FixedUiElement(e).SetClass("alert"); | ||||
|                     } | ||||
| 
 | ||||
|                     return new Combine([ | ||||
|                         "<h3>", | ||||
|                         tagRenderingPanel.options.title ?? "Extra tag rendering", | ||||
|                         "</h3>", | ||||
|                         tagRenderingPanel.options.description ?? "This tag rendering will appear in the popup", | ||||
|                         "<br/>", | ||||
|                         rendering]).Render(); | ||||
| 
 | ||||
|                 }, | ||||
|                 [this._config] | ||||
|             )).ListenTo(layer.selectedTagRendering); | ||||
| 
 | ||||
|             tabs.push({ | ||||
|                 header: "<img src='./assets/bug.svg'>", | ||||
|                 content: new LayerPanel(this._config, this.languages, i, this._currentlySelected) | ||||
|                 content: | ||||
|                     new PageSplit( | ||||
|                         layer.SetClass("scrollable"), | ||||
|                         new Combine([ | ||||
|                             helpText, | ||||
|                             "</br>", | ||||
|                             "<h2>Testing tags</h2>", | ||||
|                             previewTagInput, | ||||
|                             "<h2>Tag Rendering preview</h2>", | ||||
|                             preview | ||||
| 
 | ||||
|                         ]), 60 | ||||
|                     ) | ||||
|             }); | ||||
|         } | ||||
|         tabs.push({ | ||||
|             header: "<img src='./assets/add.svg'>", | ||||
|             content: new SubtleButton( | ||||
|                 "./assets/add.svg", | ||||
|                 "Add a new layer" | ||||
|             ).onClick(() => { | ||||
|                 self._config.data.layers.push(AllLayersPanel.createEmptyLayer()) | ||||
|                 self._config.ping(); | ||||
|             }) | ||||
|             content: new Combine([ | ||||
|                 "<h2>Layer editor</h2>", | ||||
|                 "In this tab page, you can add and edit the layers of the theme. Click the layers above or add a new layer to get started.", | ||||
|                 new SubtleButton( | ||||
|                     "./assets/add.svg", | ||||
|                     "Add a new layer" | ||||
|                 ).onClick(() => { | ||||
|                     self._config.data.layers.push(GenerateEmpty.createEmptyLayer()) | ||||
|                     self._config.ping(); | ||||
|                 })]) | ||||
|         }) | ||||
|          | ||||
|         this.panel = new TabbedComponent(tabs, new UIEventSource<number>(Math.max(0, layers.length-1))); | ||||
| 
 | ||||
|         this.panel = new TabbedComponent(tabs, new UIEventSource<number>(Math.max(0, layers.length - 1))); | ||||
|         this.Update(); | ||||
|     } | ||||
| 
 | ||||
|  |  | |||
							
								
								
									
										67
									
								
								UI/CustomGenerator/GenerateEmpty.ts
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										67
									
								
								UI/CustomGenerator/GenerateEmpty.ts
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,67 @@ | |||
| import {LayerConfigJson} from "../../Customizations/JSON/LayerConfigJson"; | ||||
| import {LayoutConfigJson} from "../../Customizations/JSON/LayoutConfigJson"; | ||||
| import {TagRenderingConfigJson} from "../../Customizations/JSON/TagRenderingConfigJson"; | ||||
| 
 | ||||
| export class GenerateEmpty { | ||||
|     public static createEmptyLayer(): LayerConfigJson { | ||||
|         return { | ||||
|             id: undefined, | ||||
|             name: undefined, | ||||
|             minzoom: 0, | ||||
|             overpassTags: {and: [""]}, | ||||
|             title: undefined, | ||||
|             description: {}, | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|     public static createEmptyLayout(): LayoutConfigJson { | ||||
|         return { | ||||
|             id: "", | ||||
|             title: {}, | ||||
|             description: {}, | ||||
|             language: [], | ||||
|             maintainer: "", | ||||
|             icon: "./assets/bug.svg", | ||||
|             version: "0", | ||||
|             startLat: 0, | ||||
|             startLon: 0, | ||||
|             startZoom: 1, | ||||
|             socialImage: "", | ||||
|             layers: [] | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|     public static createTestLayout(): LayoutConfigJson { | ||||
|         return { | ||||
|             id: "test", | ||||
|             title: {"en": "Test layout"}, | ||||
|             description: {"en": "A layout for testing"}, | ||||
|             language: ["en"], | ||||
|             maintainer: "Pieter Vander Vennet", | ||||
|             icon: "./assets/bug.svg", | ||||
|             version: "0", | ||||
|             startLat: 0, | ||||
|             startLon: 0, | ||||
|             startZoom: 1, | ||||
|             widenFactor: 0.05, | ||||
|             socialImage: "", | ||||
|             layers: [{ | ||||
|                 id: "testlayer", | ||||
|                 name: "Testing layer", | ||||
|                 minzoom: 15, | ||||
|                 overpassTags: {and: ["highway=residential"]}, | ||||
|                 title: "Some Title", | ||||
|                 description: {"en": "Some Description"}, | ||||
|                 icon: {render: {en: "./assets/pencil.svg"}}, | ||||
|                 width: {render: {en: "5"}}, | ||||
|                 tagRenderings: [{ | ||||
|                     render: {"en":"Test Rendering"} | ||||
|                 }] | ||||
|             }] | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|     public static createEmptyTagRendering(): TagRenderingConfigJson { | ||||
|         return {}; | ||||
|     } | ||||
| } | ||||
|  | @ -9,24 +9,37 @@ import {TextField} from "../Input/TextField"; | |||
| import {InputElement} from "../Input/InputElement"; | ||||
| import MultiLingualTextFields from "../Input/MultiLingualTextFields"; | ||||
| import {CheckBox} from "../Input/CheckBox"; | ||||
| import {MultiTagInput} from "../Input/MultiTagInput"; | ||||
| import {AndOrTagInput} from "../Input/AndOrTagInput"; | ||||
| import TagRenderingPanel from "./TagRenderingPanel"; | ||||
| import {GenerateEmpty} from "./GenerateEmpty"; | ||||
| import {DropDown} from "../Input/DropDown"; | ||||
| import {TagRenderingConfigJson} from "../../Customizations/JSON/TagRenderingConfigJson"; | ||||
| import {MultiInput} from "../Input/MultiInput"; | ||||
| import {Tag} from "../../Logic/Tags"; | ||||
| import {LayerConfigJson} from "../../Customizations/JSON/LayerConfigJson"; | ||||
| 
 | ||||
| /** | ||||
|  * Shows the configuration for a single layer | ||||
|  */ | ||||
| export default class LayerPanel extends UIElement { | ||||
|     private _config: UIEventSource<LayoutConfigJson>; | ||||
|     private readonly _config: UIEventSource<LayoutConfigJson>; | ||||
| 
 | ||||
|     private settingsTable: UIElement; | ||||
|     private readonly settingsTable: UIElement; | ||||
|     private readonly renderingOptions: UIElement; | ||||
| 
 | ||||
|     private deleteButton: UIElement; | ||||
|     private readonly deleteButton: UIElement; | ||||
| 
 | ||||
|     public readonly selectedTagRendering: UIEventSource<TagRenderingPanel> | ||||
|         = new UIEventSource<TagRenderingPanel>(undefined); | ||||
|     private tagRenderings: UIElement; | ||||
| 
 | ||||
|     constructor(config: UIEventSource<LayoutConfigJson>, | ||||
|                 languages: UIEventSource<string[]>, | ||||
|                 index: number, | ||||
|                 currentlySelected: UIEventSource<SingleSetting<any>>) { | ||||
|         super(undefined); | ||||
|         super(); | ||||
|         this._config = config; | ||||
|         this.renderingOptions = this.setupRenderOptions(config, languages, index, currentlySelected); | ||||
| 
 | ||||
|         const actualDeleteButton = new SubtleButton( | ||||
|             "./assets/delete.svg", | ||||
|  | @ -70,17 +83,120 @@ export default class LayerPanel extends UIElement { | |||
|                 setting(TextField.StringInput(), "id", "Id", "An identifier for this layer<br/>This should be a simple, lowercase, human readable string that is used to identify the layer."), | ||||
|                 setting(new MultiLingualTextFields(languages), "title", "Title", "The human-readable name of this layer<br/>Used in the layer control panel and the 'Personal theme'"), | ||||
|                 setting(new MultiLingualTextFields(languages, true), "description", "Description", "A description for this layer.<br/>Shown in the layer selections and in the personal theme"), | ||||
|                 setting(new MultiTagInput(), "overpassTags","Overpass query", | ||||
|                     new Combine(["The tags to load from overpass. ", MultiTagInput.tagExplanation])) | ||||
|                 setting(TextField.NumberInput("nat", n => n < 23), "minzoom", "Minimal zoom", | ||||
|                     "The minimum zoomlevel needed to load and show this layer."), | ||||
|                 setting(new DropDown("", [ | ||||
|                         {value: 0, shown: "Show ways and areas as ways and lines"}, | ||||
|                         {value: 1, shown: "Show both the ways/areas and the centerpoints"}, | ||||
|                         {value: 2, shown: "Show everything as centerpoint"}]), "wayHandling", "Way handling", | ||||
|                     "Describes how ways and areas are represented on the map: areas can be represented as the area itself, or it can be converted into the centerpoint"), | ||||
| 
 | ||||
|                 setting(new AndOrTagInput(), "overpassTags", "Overpass query", | ||||
|                     "The tags of the objects to load from overpass"), | ||||
| 
 | ||||
|             ], | ||||
|             currentlySelected | ||||
|             currentlySelected); | ||||
|         const self = this; | ||||
| 
 | ||||
|         const tagRenderings = new MultiInput<TagRenderingConfigJson>("Add a tag rendering/question", | ||||
|             () => ({}), | ||||
|             () => { | ||||
|                 const tagPanel = new TagRenderingPanel(languages, currentlySelected) | ||||
|                 self.registerTagRendering(tagPanel); | ||||
|                 return tagPanel; | ||||
|             }); | ||||
|         tagRenderings.GetValue().addCallback( | ||||
|             tagRenderings => { | ||||
|                 (config.data.layers[index] as LayerConfigJson).tagRenderings = tagRenderings; | ||||
|                 config.ping(); | ||||
|             } | ||||
|         ) | ||||
|         ; | ||||
| 
 | ||||
|         function loadTagRenderings() { | ||||
|             const values = (config.data.layers[index] as LayerConfigJson).tagRenderings; | ||||
|             const renderings: TagRenderingConfigJson[] = []; | ||||
|             for (const value of values) { | ||||
|                 if (typeof (value) !== "string") { | ||||
|                     renderings.push(value); | ||||
|                 } | ||||
| 
 | ||||
|             } | ||||
|             tagRenderings.GetValue().setData(renderings); | ||||
|         } | ||||
| 
 | ||||
|         loadTagRenderings(); | ||||
| 
 | ||||
|         this.tagRenderings = tagRenderings; | ||||
| 
 | ||||
| 
 | ||||
|     } | ||||
| 
 | ||||
|     private setupRenderOptions(config: UIEventSource<LayoutConfigJson>, | ||||
|                                languages: UIEventSource<string[]>, | ||||
|                                index: number, | ||||
|                                currentlySelected: UIEventSource<SingleSetting<any>>): UIElement { | ||||
|         const iconSelect = new TagRenderingPanel( | ||||
|             languages, currentlySelected, | ||||
|             { | ||||
|                 title: "Icon", | ||||
|                 description: "A visual representation for this layer and for the points on the map.", | ||||
|                 disableQuestions: true | ||||
|             }); | ||||
|         const size = new TagRenderingPanel(languages, currentlySelected, | ||||
|             { | ||||
|                 title: "Icon Size", | ||||
|                 description: "The size of the icons on the map in pixels. Can vary based on the tagging", | ||||
|                 disableQuestions: true | ||||
|             }); | ||||
|         const color = new TagRenderingPanel(languages, currentlySelected, | ||||
|             { | ||||
|                 title: "Way and area color", | ||||
|                 description: "The color or a shown way or area. Can vary based on the tagging", | ||||
|                 disableQuestions: true | ||||
|             }); | ||||
|         const stroke = new TagRenderingPanel(languages, currentlySelected, | ||||
|             { | ||||
|                 title: "Stroke width", | ||||
|                 description: "The width of lines representing ways and the outline of areas. Can vary based on the tags", | ||||
|                 disableQuestions: true | ||||
|             }); | ||||
|         this.registerTagRendering(iconSelect); | ||||
|         this.registerTagRendering(size); | ||||
|         this.registerTagRendering(color); | ||||
|         this.registerTagRendering(stroke); | ||||
| 
 | ||||
|         function setting(input: InputElement<any>, path, isIcon: boolean = false): SingleSetting<TagRenderingConfigJson> { | ||||
|             return new SingleSetting(config, input, ["layers", index, path], undefined, undefined) | ||||
|         } | ||||
| 
 | ||||
|         return new SettingsTable([ | ||||
|             setting(iconSelect, "icon"), | ||||
|             setting(size, "size"), | ||||
|             setting(color, "color"), | ||||
|             setting(stroke, "stroke") | ||||
|         ], currentlySelected); | ||||
|     } | ||||
| 
 | ||||
|     private registerTagRendering( | ||||
|         tagRenderingPanel: TagRenderingPanel) { | ||||
| 
 | ||||
|         tagRenderingPanel.IsHovered().addCallback(isHovering => { | ||||
|             if (!isHovering) { | ||||
|                 return; | ||||
|             } | ||||
|             this.selectedTagRendering.setData(tagRenderingPanel); | ||||
|         }) | ||||
|     } | ||||
| 
 | ||||
|     InnerRender(): string { | ||||
|         return new Combine([ | ||||
|             "<h2>General layer settings</h2>", | ||||
|             this.settingsTable, | ||||
|             "<h2>Map rendering options</h2>", | ||||
|             this.renderingOptions, | ||||
|             "<h2>Tag rendering and questions</h2>", | ||||
|             this.tagRenderings, | ||||
|             "<h2>Layer delete</h2>", | ||||
|             this.deleteButton | ||||
|         ]).Render(); | ||||
|     } | ||||
|  |  | |||
							
								
								
									
										64
									
								
								UI/CustomGenerator/MappingInput.ts
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										64
									
								
								UI/CustomGenerator/MappingInput.ts
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,64 @@ | |||
| import {InputElement} from "../Input/InputElement"; | ||||
| import {AndOrTagConfigJson} from "../../Customizations/JSON/TagConfigJson"; | ||||
| import {UIEventSource} from "../../Logic/UIEventSource"; | ||||
| import {UIElement} from "../UIElement"; | ||||
| import SettingsTable from "./SettingsTable"; | ||||
| import SingleSetting from "./SingleSetting"; | ||||
| import {AndOrTagInput} from "../Input/AndOrTagInput"; | ||||
| import MultiLingualTextFields from "../Input/MultiLingualTextFields"; | ||||
| import {DropDown} from "../Input/DropDown"; | ||||
| 
 | ||||
| export default class MappingInput extends InputElement<{ if: AndOrTagConfigJson, then: (string | any), hideInAnswer?: boolean }> { | ||||
| 
 | ||||
|     private readonly _value: UIEventSource<{ if: AndOrTagConfigJson; then: any; hideInAnswer?: boolean }>; | ||||
|     private readonly _panel: UIElement; | ||||
| 
 | ||||
|     constructor(languages: UIEventSource<any>, disableQuestions: boolean = false) { | ||||
|         super(); | ||||
|         const currentSelected = new UIEventSource<SingleSetting<any>>(undefined); | ||||
|         this._value = new UIEventSource<{ if: AndOrTagConfigJson, then: any, hideInAnswer?: boolean }>({ | ||||
|             if: undefined, | ||||
|             then: undefined | ||||
|         }); | ||||
|         const self = this; | ||||
| 
 | ||||
|         function setting(inputElement: InputElement<any>, path: string, name: string, description: string | UIElement) { | ||||
|             return new SingleSetting(self._value, inputElement, path, name, description); | ||||
|         } | ||||
| 
 | ||||
|         const withQuestions = [setting(new DropDown("", | ||||
|             [{value: false, shown: "Can be used as answer"}, {value: true, shown: "Not an answer option"}]), | ||||
|             "hideInAnswer", "Answer option", | ||||
|             "Sometimes, multiple tags for the same meaning are used (e.g. <span class='literal-code'>access=yes</span> and <span class='literal-code'>access=public</span>)." + | ||||
|             "Use this toggle to disable an anwer. Alternatively an implied/assumed rendering can be used. In order to do this:" + | ||||
|             "use a single tag in the 'if' with <i>no</i> value defined, e.g. <span class='literal-code'>indoor=</span>. The mapping will then be shown as default until explicitly changed" | ||||
|         )]; | ||||
|          | ||||
|         this._panel = new SettingsTable([ | ||||
|             setting(new AndOrTagInput(), "if", "If matches", "If this condition matches, the template <b>then</b> below will be used"), | ||||
|             setting(new MultiLingualTextFields(languages), | ||||
|                 "then", "Then show", "If the condition above matches, this template <b>then</b> below will be shown to the user."), | ||||
|             ...(disableQuestions ? [] : withQuestions) | ||||
| 
 | ||||
|         ], currentSelected).SetClass("bordered tag-mapping"); | ||||
| 
 | ||||
|     } | ||||
| 
 | ||||
| 
 | ||||
|     InnerRender(): string { | ||||
|         return this._panel.Render(); | ||||
|     } | ||||
| 
 | ||||
| 
 | ||||
|     GetValue(): UIEventSource<{ if: AndOrTagConfigJson; then: any; hideInAnswer?: boolean }> { | ||||
|         return this._value; | ||||
|     } | ||||
| 
 | ||||
| 
 | ||||
|     IsSelected: UIEventSource<boolean> = new UIEventSource<boolean>(false); | ||||
| 
 | ||||
|     IsValid(t: { if: AndOrTagConfigJson; then: any; hideInAnswer: boolean }): boolean { | ||||
|         return false; | ||||
|     } | ||||
| 
 | ||||
| } | ||||
|  | @ -1,27 +1,33 @@ | |||
| import SingleSetting from "./SingleSetting"; | ||||
| import {UIElement} from "../UIElement"; | ||||
| import {FixedUiElement} from "../Base/FixedUiElement"; | ||||
| import {InputElement} from "../Input/InputElement"; | ||||
| import {UIEventSource} from "../../Logic/UIEventSource"; | ||||
| import PageSplit from "../Base/PageSplit"; | ||||
| import Combine from "../Base/Combine"; | ||||
| import {VariableUiElement} from "../Base/VariableUIElement"; | ||||
| 
 | ||||
| export default class SettingsTable extends UIElement { | ||||
| 
 | ||||
|     private _col1: UIElement[] = []; | ||||
|     private _col2: InputElement<any>[] = []; | ||||
|     private _col2: UIElement[] = []; | ||||
| 
 | ||||
|     public selectedSetting: UIEventSource<SingleSetting<any>>; | ||||
| 
 | ||||
|     constructor(elements: SingleSetting<any>[], | ||||
|     constructor(elements: (SingleSetting<any> | string)[], | ||||
|                 currentSelectedSetting: UIEventSource<SingleSetting<any>>) { | ||||
|         super(undefined); | ||||
|         const self = this; | ||||
|         this.selectedSetting = currentSelectedSetting ?? new UIEventSource<SingleSetting<any>>(undefined); | ||||
|         for (const element of elements) { | ||||
|             let title: UIElement = new FixedUiElement(element._name); | ||||
|             if(typeof element === "string"){ | ||||
|                 this._col1.push(new FixedUiElement(element)); | ||||
|                 this._col2.push(null); | ||||
|                 continue; | ||||
|             } | ||||
|              | ||||
|             let title: UIElement = element._name === undefined ? null : new FixedUiElement(element._name); | ||||
|             this._col1.push(title); | ||||
|             this._col2.push(element._value); | ||||
|             element._value.SetStyle("display:block"); | ||||
|             element._value.IsSelected.addCallback(isSelected => { | ||||
|                 if (isSelected) { | ||||
|                     self.selectedSetting.setData(element); | ||||
|  | @ -34,13 +40,19 @@ export default class SettingsTable extends UIElement { | |||
|     } | ||||
| 
 | ||||
|     InnerRender(): string { | ||||
|         let html = ""; | ||||
|         let elements = []; | ||||
| 
 | ||||
|         for (let i = 0; i < this._col1.length; i++) { | ||||
|             html += `<tr><td>${this._col1[i].Render()}</td><td>${this._col2[i].Render()}</td></tr>` | ||||
|             if(this._col1[i] !== null && this._col2[i] !== null){ | ||||
|                 elements.push(new PageSplit(this._col1[i], this._col2[i], 25)); | ||||
|             }else if(this._col1[i] !== null){ | ||||
|                 elements.push(this._col1[i]) | ||||
|             }else{ | ||||
|                 elements.push(this._col2[i]) | ||||
|             } | ||||
|         } | ||||
| 
 | ||||
|         return `<table><tr>${html}</tr></table>`; | ||||
|         return new Combine(elements).Render(); | ||||
|     } | ||||
| 
 | ||||
|      | ||||
| } | ||||
|  | @ -1,4 +1,3 @@ | |||
| import {LayoutConfigJson} from "../../Customizations/JSON/LayoutConfigJson"; | ||||
| import {UIEventSource} from "../../Logic/UIEventSource"; | ||||
| import {InputElement} from "../Input/InputElement"; | ||||
| import {UIElement} from "../UIElement"; | ||||
|  | @ -12,7 +11,7 @@ export default class SingleSetting<T> { | |||
|     public _description: UIElement; | ||||
|     public _options: { showIconPreview?: boolean }; | ||||
| 
 | ||||
|     constructor(config: UIEventSource<LayoutConfigJson>, | ||||
|     constructor(config: UIEventSource<any>, | ||||
|                 value: InputElement<T>, | ||||
|                 path: string | (string | number)[], | ||||
|                 name: string, | ||||
|  | @ -47,11 +46,17 @@ export default class SingleSetting<T> { | |||
|             // We have to rewalk every time as parts might be new
 | ||||
|             let configPart = config.data; | ||||
|             for (const pathPart of path) { | ||||
|                 configPart = configPart[pathPart]; | ||||
|                 if (configPart === undefined) { | ||||
|                     console.warn("Lost the way for path ", path) | ||||
|                     return; | ||||
|                 let newConfigPart = configPart[pathPart]; | ||||
|                 if (newConfigPart === undefined) { | ||||
|                     console.warn("Lost the way for path ", path, " - creating entry") | ||||
|                     if (typeof (pathPart) === "string") { | ||||
|                         configPart[pathPart] = {}; | ||||
|                     } else { | ||||
|                         configPart[pathPart] = []; | ||||
|                     } | ||||
|                     newConfigPart = configPart[pathPart]; | ||||
|                 } | ||||
|                 configPart = newConfigPart; | ||||
|             } | ||||
|             configPart[lastPart] = value; | ||||
|             config.ping(); | ||||
|  | @ -66,7 +71,6 @@ export default class SingleSetting<T> { | |||
|                 } | ||||
|             } | ||||
|             const loadedValue = configPart[lastPart]; | ||||
| 
 | ||||
|             if (loadedValue !== undefined) { | ||||
|                 value.GetValue().setData(loadedValue); | ||||
|             } | ||||
|  | @ -79,6 +83,8 @@ export default class SingleSetting<T> { | |||
|          | ||||
| 
 | ||||
|     } | ||||
|      | ||||
|      | ||||
| 
 | ||||
| 
 | ||||
| } | ||||
							
								
								
									
										103
									
								
								UI/CustomGenerator/TagRenderingPanel.ts
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										103
									
								
								UI/CustomGenerator/TagRenderingPanel.ts
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,103 @@ | |||
| import {UIElement} from "../UIElement"; | ||||
| import {UIEventSource} from "../../Logic/UIEventSource"; | ||||
| import {InputElement} from "../Input/InputElement"; | ||||
| import SingleSetting from "./SingleSetting"; | ||||
| import SettingsTable from "./SettingsTable"; | ||||
| import {TextField, ValidatedTextField} 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"; | ||||
| 
 | ||||
| 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; }; | ||||
| 
 | ||||
|     constructor(languages: UIEventSource<string[]>, | ||||
|                 currentlySelected: UIEventSource<SingleSetting<any>>, | ||||
|                 options?: { | ||||
|                     title?: string, | ||||
|                     description?: string, | ||||
|                     disableQuestions?: boolean, | ||||
|                     isImage?: boolean | ||||
|                 }) { | ||||
|         super(); | ||||
| 
 | ||||
|         this.SetClass("bordered"); | ||||
|         this.SetClass("min-height"); | ||||
| 
 | ||||
|         this.options = options ?? {}; | ||||
| 
 | ||||
|         this.intro = new Combine(["<h3>", options?.title ?? "TagRendering", "</h3>", options?.description ?? ""]) | ||||
|         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); | ||||
|         } | ||||
| 
 | ||||
| 
 | ||||
|         const questionSettings = [ | ||||
| 
 | ||||
|             setting(new MultiLingualTextFields(languages), "question", "Question", "If the key or mapping doesn't match, this question is asked"), | ||||
| 
 | ||||
|             setting(new AndOrTagInput(), "condition", "Condition", | ||||
|                 "Only show this tag rendering if these tags matches. Optional field.<br/>Note that the Overpass-tags are already always included in this object"), | ||||
| 
 | ||||
|             "<h3>Freeform key</h3>", | ||||
|             setting(TextField.KeyInput(), ["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(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."), | ||||
|             ...(options?.disableQuestions ? [] : questionSettings), | ||||
| 
 | ||||
|             "<h3>Mappings</h3>", | ||||
|             setting(new MultiInput<{ if: AndOrTagConfigJson, then: (string | any), hideInAnswer?: boolean }>("Add a mapping", | ||||
|                 () => ({if: undefined, then: undefined}), | ||||
|                 () => new MappingInput(languages, options?.disableQuestions ?? false)), "mappings", | ||||
|                 "Mappings", "") | ||||
| 
 | ||||
|         ]; | ||||
| 
 | ||||
|         this.settingsTable = new SettingsTable(settings, currentlySelected); | ||||
|     } | ||||
| 
 | ||||
|     InnerRender(): string { | ||||
|         return new Combine([ | ||||
|             this.intro, | ||||
|             this.settingsTable]).Render(); | ||||
|     } | ||||
| 
 | ||||
|     GetValue(): UIEventSource<TagRenderingConfigJson> { | ||||
|         return this._value; | ||||
|     } | ||||
| 
 | ||||
|     IsSelected: UIEventSource<boolean> = new UIEventSource<boolean>(false); | ||||
| 
 | ||||
|     IsValid(t: TagRenderingConfigJson): boolean { | ||||
|         return false; | ||||
|     } | ||||
| 
 | ||||
| 
 | ||||
| } | ||||
							
								
								
									
										15
									
								
								UI/CustomGenerator/TagRenderingPreview.ts
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										15
									
								
								UI/CustomGenerator/TagRenderingPreview.ts
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,15 @@ | |||
| import {UIElement} from "../UIElement"; | ||||
| import {UIEventSource} from "../../Logic/UIEventSource"; | ||||
| import TagRenderingPanel from "./TagRenderingPanel"; | ||||
| 
 | ||||
| export default class TagRenderingPreview extends UIElement{ | ||||
|      | ||||
|     constructor(selectedTagRendering: UIEventSource<TagRenderingPanel>) { | ||||
|         super(selectedTagRendering); | ||||
|     } | ||||
|      | ||||
|     InnerRender(): string { | ||||
|         return ""; | ||||
|     } | ||||
|      | ||||
| } | ||||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue