2020-08-31 02:59:47 +02:00
import { UIElement } from "../UIElement" ;
import { UIEventSource } from "../../Logic/UIEventSource" ;
import { LayoutConfigJson } from "../../Customizations/JSON/LayoutConfigJson" ;
import SettingsTable from "./SettingsTable" ;
import SingleSetting from "./SingleSetting" ;
import { SubtleButton } from "../Base/SubtleButton" ;
import Combine from "../Base/Combine" ;
import { TextField } from "../Input/TextField" ;
import { InputElement } from "../Input/InputElement" ;
import MultiLingualTextFields from "../Input/MultiLingualTextFields" ;
import { CheckBox } from "../Input/CheckBox" ;
2020-09-02 11:37:34 +02:00
import { AndOrTagInput } from "../Input/AndOrTagInput" ;
import TagRenderingPanel from "./TagRenderingPanel" ;
import { DropDown } from "../Input/DropDown" ;
import { TagRenderingConfigJson } from "../../Customizations/JSON/TagRenderingConfigJson" ;
import { MultiInput } from "../Input/MultiInput" ;
import { LayerConfigJson } from "../../Customizations/JSON/LayerConfigJson" ;
2020-09-03 03:16:43 +02:00
import PresetInputPanel from "./PresetInputPanel" ;
2020-09-03 16:44:48 +02:00
import { UserDetails } from "../../Logic/Osm/OsmConnection" ;
import { State } from "../../State" ;
import { FixedUiElement } from "../Base/FixedUiElement" ;
2020-09-25 12:44:04 +02:00
import ValidatedTextField from "../Input/ValidatedTextField" ;
2020-08-31 02:59:47 +02:00
/ * *
* Shows the configuration for a single layer
* /
export default class LayerPanel extends UIElement {
2020-09-02 11:37:34 +02:00
private readonly _config : UIEventSource < LayoutConfigJson > ;
2020-08-31 02:59:47 +02:00
2020-09-02 11:37:34 +02:00
private readonly settingsTable : UIElement ;
2020-09-03 00:00:37 +02:00
private readonly mapRendering : UIElement ;
2020-08-31 02:59:47 +02:00
2020-09-02 11:37:34 +02:00
private readonly deleteButton : UIElement ;
2020-09-03 00:00:37 +02:00
public readonly titleRendering : UIElement ;
2020-09-02 11:37:34 +02:00
public readonly selectedTagRendering : UIEventSource < TagRenderingPanel >
= new UIEventSource < TagRenderingPanel > ( undefined ) ;
private tagRenderings : UIElement ;
2020-09-03 03:16:43 +02:00
private presetsPanel : UIElement ;
2020-08-31 02:59:47 +02:00
constructor ( config : UIEventSource < LayoutConfigJson > ,
languages : UIEventSource < string [ ] > ,
index : number ,
2020-09-03 16:44:48 +02:00
currentlySelected : UIEventSource < SingleSetting < any > > ,
userDetails : UserDetails ) {
2020-09-02 11:37:34 +02:00
super ( ) ;
2020-08-31 02:59:47 +02:00
this . _config = config ;
2020-09-03 16:44:48 +02:00
this . mapRendering = this . setupRenderOptions ( config , languages , index , currentlySelected , userDetails ) ;
2020-08-31 02:59:47 +02:00
const actualDeleteButton = new SubtleButton (
"./assets/delete.svg" ,
"Yes, delete this layer"
) . onClick ( ( ) = > {
config . data . layers . splice ( index , 1 ) ;
config . ping ( ) ;
} ) ;
this . deleteButton = new CheckBox (
new Combine (
[
"<h3>Confirm layer deletion</h3>" ,
new SubtleButton (
"./assets/close.svg" ,
"No, don't delete"
) ,
"<span class='alert'>Deleting a layer can not be undone!</span>" ,
actualDeleteButton
]
) ,
new SubtleButton (
"./assets/delete.svg" ,
"Remove this layer"
)
)
function setting ( input : InputElement < any > , path : string | string [ ] , name : string , description : string | UIElement ) : SingleSetting < any > {
let pathPre = [ "layers" , index ] ;
if ( typeof ( path ) === "string" ) {
pathPre . push ( path ) ;
} else {
pathPre = pathPre . concat ( path ) ;
}
return new SingleSetting < any > ( config , input , pathPre , name , description ) ;
}
this . settingsTable = new SettingsTable ( [
2020-09-25 12:44:04 +02:00
setting ( new TextField ( { placeholder : "Layer id" } ) , "id" , "Id" , "An identifier for this layer<br/>This should be a simple, lowercase, human readable string that is used to identify the layer." ) ,
2020-09-03 00:00:37 +02:00
setting ( new MultiLingualTextFields ( languages ) , "name" , "Name" , "The human-readable name of this layer<br/>Used in the layer control panel and the 'Personal theme'" ) ,
2020-08-31 02:59:47 +02:00
setting ( new MultiLingualTextFields ( languages , true ) , "description" , "Description" , "A description for this layer.<br/>Shown in the layer selections and in the personal theme" ) ,
2020-09-25 12:44:04 +02:00
setting ( ValidatedTextField . NumberInput ( "nat" , n = > n < 23 ) , "minzoom" , "Minimal zoom" ,
2020-09-02 11:37:34 +02:00
"The minimum zoomlevel needed to load and show this layer." ) ,
setting ( new DropDown ( "" , [
{ value : 0 , shown : "Show ways and areas as ways and lines" } ,
2020-09-03 02:18:23 +02:00
{ value : 2 , shown : "Show both the ways/areas and the centerpoints" } ,
{ value : 1 , shown : "Show everything as centerpoint" } ] ) , "wayHandling" , "Way handling" ,
2020-09-02 11:37:34 +02:00
"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" ) ,
2020-09-25 12:44:04 +02:00
setting ( ValidatedTextField . NumberInput ( "int" , n = > n <= 100 ) , "hideUnderlayingFeaturesMinPercentage" , "Max allowed overlap percentage" ,
2020-09-09 18:42:13 +02:00
"Consider that we want to show 'Nature Reserves' and 'Forests'. Now, ofter, there are pieces of forest mapped _in_ the nature reserve.<br/>" +
"Now, showing those pieces of forest overlapping with the nature reserve truly clutters the map and is very user-unfriendly.<br/>" +
"The features are placed layer by layer. If a feature below a feature on this layer overlaps for more then 'x'-percent, the underlying feature is hidden." ) ,
2020-09-02 11:37:34 +02:00
setting ( new AndOrTagInput ( ) , "overpassTags" , "Overpass query" ,
"The tags of the objects to load from overpass" ) ,
2020-08-31 02:59:47 +02:00
] ,
2020-09-02 11:37:34 +02:00
currentlySelected ) ;
const self = this ;
2020-09-03 16:44:48 +02:00
const popupTitleRendering = new TagRenderingPanel ( languages , currentlySelected , userDetails , {
2020-09-03 00:00:37 +02:00
title : "Popup title" ,
description : "This is the rendering shown as title in the popup for this element" ,
disableQuestions : true
} ) ;
new SingleSetting ( config , popupTitleRendering , [ "layers" , index , "title" ] , "Popup title" , "This is the rendering shown as title in the popup" ) ;
this . titleRendering = popupTitleRendering ;
this . registerTagRendering ( popupTitleRendering ) ;
2020-09-02 11:37:34 +02:00
const tagRenderings = new MultiInput < TagRenderingConfigJson > ( "Add a tag rendering/question" ,
( ) = > ( { } ) ,
( ) = > {
2020-09-03 16:44:48 +02:00
const tagPanel = new TagRenderingPanel ( languages , currentlySelected , userDetails )
2020-09-02 11:37:34 +02:00
self . registerTagRendering ( tagPanel ) ;
return tagPanel ;
2020-09-05 15:27:35 +02:00
} , undefined , { allowMovement :true } ) ;
2020-09-02 11:37:34 +02:00
tagRenderings . GetValue ( ) . addCallback (
tagRenderings = > {
( config . data . layers [ index ] as LayerConfigJson ) . tagRenderings = tagRenderings ;
config . ping ( ) ;
}
2020-08-31 02:59:47 +02:00
)
2020-09-02 11:37:34 +02:00
2020-09-03 16:44:48 +02:00
if ( userDetails . csCount >= State . userJourney . themeGeneratorFullUnlock ) {
const presetPanel = new MultiInput ( "Add a preset" ,
( ) = > ( { tags : [ ] , title : { } } ) ,
2020-09-05 15:27:35 +02:00
( ) = > new PresetInputPanel ( currentlySelected , languages ) ,
undefined , { allowMovement : true } ) ;
2020-09-03 16:44:48 +02:00
new SingleSetting ( config , presetPanel , [ "layers" , index , "presets" ] , "Presets" , "" )
this . presetsPanel = presetPanel ;
} else {
this . presetsPanel = new FixedUiElement ( ` Creating a custom theme which also edits OSM is only unlocked after ${ State . userJourney . themeGeneratorFullUnlock } changesets ` ) . SetClass ( "alert" ) ;
}
2020-09-03 03:16:43 +02:00
2020-09-02 11:37:34 +02:00
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 ,
2020-09-03 16:44:48 +02:00
currentlySelected : UIEventSource < SingleSetting < any > > ,
userDetails : UserDetails
) : UIElement {
2020-09-02 11:37:34 +02:00
const iconSelect = new TagRenderingPanel (
2020-09-03 16:44:48 +02:00
languages , currentlySelected , userDetails ,
2020-09-02 11:37:34 +02:00
{
title : "Icon" ,
description : "A visual representation for this layer and for the points on the map." ,
2020-09-05 17:43:30 +02:00
disableQuestions : true ,
noLanguage : true
2020-09-02 11:37:34 +02:00
} ) ;
2020-09-03 16:44:48 +02:00
const size = new TagRenderingPanel ( languages , currentlySelected , userDetails ,
2020-09-02 11:37:34 +02:00
{
title : "Icon Size" ,
description : "The size of the icons on the map in pixels. Can vary based on the tagging" ,
2020-09-05 17:43:30 +02:00
disableQuestions : true ,
noLanguage : true
2020-09-02 11:37:34 +02:00
} ) ;
2020-09-03 16:44:48 +02:00
const color = new TagRenderingPanel ( languages , currentlySelected , userDetails ,
2020-09-02 11:37:34 +02:00
{
title : "Way and area color" ,
description : "The color or a shown way or area. Can vary based on the tagging" ,
2020-09-05 17:43:30 +02:00
disableQuestions : true ,
noLanguage : true
2020-09-02 11:37:34 +02:00
} ) ;
2020-09-03 16:44:48 +02:00
const stroke = new TagRenderingPanel ( languages , currentlySelected , userDetails ,
2020-09-02 11:37:34 +02:00
{
title : "Stroke width" ,
description : "The width of lines representing ways and the outline of areas. Can vary based on the tags" ,
2020-09-05 17:43:30 +02:00
disableQuestions : true ,
noLanguage : true
2020-09-02 11:37:34 +02:00
} ) ;
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" ) ,
2020-09-05 17:43:30 +02:00
setting ( size , "iconSize" ) ,
2020-09-02 11:37:34 +02:00
setting ( color , "color" ) ,
2020-09-05 17:43:30 +02:00
setting ( stroke , "width" )
2020-09-02 11:37:34 +02:00
] , currentlySelected ) ;
}
private registerTagRendering (
tagRenderingPanel : TagRenderingPanel ) {
tagRenderingPanel . IsHovered ( ) . addCallback ( isHovering = > {
if ( ! isHovering ) {
return ;
}
this . selectedTagRendering . setData ( tagRenderingPanel ) ;
} )
2020-08-31 02:59:47 +02:00
}
InnerRender ( ) : string {
return new Combine ( [
2020-09-02 11:37:34 +02:00
"<h2>General layer settings</h2>" ,
2020-08-31 02:59:47 +02:00
this . settingsTable ,
2020-09-03 00:00:37 +02:00
"<h2>Popup contents</h2>" ,
this . titleRendering ,
2020-09-02 11:37:34 +02:00
this . tagRenderings ,
2020-09-03 03:16:43 +02:00
"<h2>Presets</h2>" ,
"Does this theme support adding a new point?<br/>If this should be the case, add a preset. Make sure that the preset tags do match the overpass-tags, otherwise it might seem like the newly added points dissapear " ,
this . presetsPanel ,
2020-09-03 00:00:37 +02:00
"<h2>Map rendering options</h2>" ,
this . mapRendering ,
2020-09-02 11:37:34 +02:00
"<h2>Layer delete</h2>" ,
2020-08-31 02:59:47 +02:00
this . deleteButton
] ) . Render ( ) ;
}
}