forked from MapComplete/MapComplete
		
	Studio: more work on studio
This commit is contained in:
		
							parent
							
								
									81876fc5ed
								
							
						
					
					
						commit
						4e8dfc0026
					
				
					 20 changed files with 1842 additions and 94 deletions
				
			
		|  | @ -534,7 +534,7 @@ class MappedStore<TIn, T> extends Store<T> { | |||
|     private update(): void { | ||||
|         const newData = this._f(this._upstream.data) | ||||
|         this._upstreamPingCount = this._upstreamCallbackHandler?.pingCount | ||||
|         if (this._data == newData) { | ||||
|         if (this._data === newData) { | ||||
|             return | ||||
|         } | ||||
|         this._data = newData | ||||
|  |  | |||
|  | @ -645,6 +645,7 @@ export class AddEditingElements extends DesugaringStep<LayerConfigJson> { | |||
|         } | ||||
| 
 | ||||
|         if (json.allowSplit && !ValidationUtils.hasSpecialVisualisation(json, "split_button")) { | ||||
|             json.tagRenderings ??= [] | ||||
|             json.tagRenderings.push({ | ||||
|                 id: "split-button", | ||||
|                 render: { "*": "{split_button()}" }, | ||||
|  | @ -653,6 +654,7 @@ export class AddEditingElements extends DesugaringStep<LayerConfigJson> { | |||
|         } | ||||
| 
 | ||||
|         if (json.allowMove && !ValidationUtils.hasSpecialVisualisation(json, "move_button")) { | ||||
|             json.tagRenderings ??= [] | ||||
|             json.tagRenderings.push({ | ||||
|                 id: "move-button", | ||||
|                 render: { "*": "{move_button()}" }, | ||||
|  |  | |||
|  | @ -1033,7 +1033,7 @@ export class ValidateLayer extends DesugaringStep<LayerConfigJson> { | |||
|             } | ||||
| 
 | ||||
|             { | ||||
|                 const hasCondition = json.mapRendering?.filter( | ||||
|                 const hasCondition = json.pointRendering?.filter( | ||||
|                     (mr) => mr["icon"] !== undefined && mr["icon"]["condition"] !== undefined | ||||
|                 ) | ||||
|                 if (hasCondition?.length > 0) { | ||||
|  |  | |||
|  | @ -43,7 +43,6 @@ export interface LayerConfigJson { | |||
|     description?: Translatable | ||||
| 
 | ||||
|     /** | ||||
|      * | ||||
|      * Question: Where should the data be fetched from? | ||||
|      * title: Data Source | ||||
|      * | ||||
|  | @ -202,13 +201,13 @@ export interface LayerConfigJson { | |||
|     minzoomVisible?: number | ||||
| 
 | ||||
|     /** | ||||
|      * question: What title should be shown on the infobox? | ||||
|      * The title shown in a popup for elements of this layer. | ||||
|      * | ||||
|      * group: title | ||||
|      * question: What title should be shown on the infobox? | ||||
|      * types: use a fixed translation ; Use a dynamic tagRendering ; use a fixed string for all languages | ||||
|      * types: use a fixed translation ; Use a dynamic tagRendering ; hidden | ||||
|      * typesdefault: 1 | ||||
|      * | ||||
|      * type: translation | ||||
|      */ | ||||
|     title?: TagRenderingConfigJson | Translatable | ||||
| 
 | ||||
|  |  | |||
|  | @ -2,7 +2,17 @@ import { TagRenderingConfigJson } from "./TagRenderingConfigJson" | |||
| import { TagConfigJson } from "./TagConfigJson" | ||||
| 
 | ||||
| export interface IconConfigJson { | ||||
|     /** | ||||
|      * question: What icon should be used? | ||||
|      * type: icon | ||||
|      * suggestions: return ["pin","square","circle","checkmark","clock","close","crosshair","help","home","invalid","location","location_empty","location_locked","note","resolved","ring","scissors","teardrop","teardrop_with_hole_green","triangle"].map(i => ({if: "value="+i, then: i, icon: i})) | ||||
|      */ | ||||
|     icon: string | TagRenderingConfigJson | { builtin: string; override: any } | ||||
|     /** | ||||
|      * question: What colour should the icon be? | ||||
|      * This will only work for the default icons such as `pin`,`circle`,... | ||||
|      * type: color | ||||
|      */ | ||||
|     color?: string | TagRenderingConfigJson | { builtin: string; override: any } | ||||
| } | ||||
| 
 | ||||
|  | @ -23,17 +33,15 @@ export default interface PointRenderingConfigJson { | |||
|     location: ("point" | "centroid" | "start" | "end" | "projected_centerpoint" | string)[] | ||||
| 
 | ||||
|     /** | ||||
|    * question: What marker should be used to | ||||
|    * The icon for an element. | ||||
|    * Note that this also doubles as the icon for this layer (rendered with the overpass-tags) ánd the icon in the presets. | ||||
|      * The marker for an element. | ||||
|      * Note that this also defines the icon for this layer (rendered with the overpass-tags) <i>and</i> the icon in the presets. | ||||
|      * | ||||
|      * The result of the icon is rendered as follows: | ||||
|    * the resulting string is interpreted as a _list_ of items, separated by ";". The bottommost layer is the first layer. | ||||
|    * As a result, on could use a generic pin, then overlay it with a specific icon. | ||||
|    * To make things even more practical, one c    an use all SVG's from the folder "assets/svg" and _substitute the color_ in it. | ||||
|    * E.g. to draw a red pin, use "pin:#f00", to have a green circle with your icon on top, use `circle:#0f0;<path to my icon.svg>` | ||||
| 
 | ||||
|    * Type: icon | ||||
|      * - The first icon is rendered on the map | ||||
|      * - The second entry is overlayed on top of it | ||||
|      * - ... | ||||
|      * | ||||
|      * As a result, on could use a generic icon (`pin`, `circle`, `square`) with a color, then overlay it with a specific icon. | ||||
|      */ | ||||
|     marker?: IconConfigJson[] | ||||
| 
 | ||||
|  | @ -53,8 +61,9 @@ export default interface PointRenderingConfigJson { | |||
|     }[] | ||||
| 
 | ||||
|     /** | ||||
|      * A string containing "width,height" or "width,height,anchorpoint" where anchorpoint is any of 'center', 'top', 'bottom', 'left', 'right', 'bottomleft','topright', ... | ||||
|      * Default is '40,40,center' | ||||
|      * question: What size should the marker be on the map? | ||||
|      * A string containing "<width>,<height>" in pixels | ||||
|      * ifunset: Use the default size (<b>40,40</b> px) | ||||
|      */ | ||||
|     iconSize?: string | TagRenderingConfigJson | ||||
| 
 | ||||
|  | @ -69,13 +78,15 @@ export default interface PointRenderingConfigJson { | |||
|     anchor?: "center" | "top" | "bottom" | "left" | "right" | string | TagRenderingConfigJson | ||||
| 
 | ||||
|     /** | ||||
|      * The rotation of an icon, useful for e.g. directions. | ||||
|      * Usage: as if it were a css property for 'rotate', thus has to end with 'deg', e.g. `90deg`, `{direction}deg`, `calc(90deg - {camera:direction}deg)`` | ||||
|      * question: What rotation should be applied on the icon? | ||||
|      * This is mostly useful for items that face a specific direction, such as surveillance cameras | ||||
|      * This is interpreted as css property for 'rotate', thus has to end with 'deg', e.g. `90deg`, `{direction}deg`, `calc(90deg - {camera:direction}deg)`` | ||||
|      * ifunset: Do not rotate | ||||
|      */ | ||||
|     rotation?: string | TagRenderingConfigJson | ||||
|     /** | ||||
|      * question: What label should be shown beneath the marker? | ||||
|      * For example: <div style="background: white">{name}</div> | ||||
|      * For example: `<div style="background: white">{name}</div>` | ||||
|      * | ||||
|      * If the icon is undefined, then the label is shown in the center of the feature. | ||||
|      * types: Dynamic value | string | ||||
|  | @ -124,15 +135,15 @@ export default interface PointRenderingConfigJson { | |||
|     labelCssClasses?: string | TagRenderingConfigJson | ||||
| 
 | ||||
|     /** | ||||
|      * If the map is pitched, the marker will stay parallel to the screen. | ||||
|      * Set to 'map' if you want to put it flattened on the map | ||||
|      * question: If the map is pitched, should the icon stay parallel to the screen or to the groundplane? | ||||
|      * suggestions: return [{if: "value=canvas", then: "The icon will stay upward and not be transformed as if it sticks to the screen"}, {if: "value=map", then: "The icon will be transformed as if it were painted onto the ground. (Automatically sets rotationAlignment)"}] | ||||
|      */ | ||||
|     pitchAlignment?: "canvas" | "map" | TagRenderingConfigJson | ||||
| 
 | ||||
|     /** | ||||
|      * question: Should the icon be rotated or tilted if the map is rotated or tilted? | ||||
|      * question: Should the icon be rotated if the map is rotated? | ||||
|      * ifunset: Do not rotate or tilt icons. Always keep the icons straight | ||||
|      * suggestions: return [{if: "value=canvas", then: "If the map is tilted, tilt the icon as well. This gives the impression of an icon that is glued to the ground."}, {if: "value=map", then: "If the map is rotated, rotate the icon as well. This gives the impression of an icon that floats perpendicular above the ground."}] | ||||
|      * suggestions: return [{if: "value=canvas", then: "Never rotate the icon"}, {if: "value=map", then: "If the map is rotated, rotate the icon as well. This gives the impression of an icon that floats perpendicular above the ground."}] | ||||
|      */ | ||||
|     rotationAlignment?: "map" | "canvas" | TagRenderingConfigJson | ||||
| } | ||||
|  |  | |||
|  | @ -5,7 +5,7 @@ import { TagUtils } from "../../Logic/Tags/TagUtils" | |||
| import { Utils } from "../../Utils" | ||||
| import Svg from "../../Svg" | ||||
| import WithContextLoader from "./WithContextLoader" | ||||
| import { Store } from "../../Logic/UIEventSource" | ||||
| import { ImmutableStore, Store } from "../../Logic/UIEventSource" | ||||
| import BaseUIElement from "../../UI/BaseUIElement" | ||||
| import { FixedUiElement } from "../../UI/Base/FixedUiElement" | ||||
| import Img from "../../UI/Base/Img" | ||||
|  | @ -93,7 +93,7 @@ export default class PointRenderingConfig extends WithContextLoader { | |||
|                 ".location)" | ||||
|             ) | ||||
|         } | ||||
|         this.marker = (json.marker ?? []).map((m) => new IconConfig(m)) | ||||
|         this.marker = (json.marker ?? []).map((m) => new IconConfig(<any>m)) | ||||
|         if (json.css !== undefined) { | ||||
|             this.cssDef = this.tr("css", undefined) | ||||
|         } | ||||
|  | @ -192,7 +192,7 @@ export default class PointRenderingConfig extends WithContextLoader { | |||
|     } | ||||
| 
 | ||||
|     public GetBaseIcon(tags?: Record<string, string>): BaseUIElement { | ||||
|         return new SvelteUIElement(Marker, { icons: this.marker, tags }) | ||||
|         return new SvelteUIElement(Marker, { config: this, tags: new ImmutableStore(tags) }) | ||||
|     } | ||||
|     public RenderIcon( | ||||
|         tags: Store<Record<string, string>>, | ||||
|  |  | |||
|  | @ -20,6 +20,6 @@ | |||
|   <slot name="image" slot="image" /> | ||||
|   <div class="flex w-full items-center justify-between" slot="message"> | ||||
|     <slot /> | ||||
|     <ChevronRightIcon class="h-12 w-12 shrink-0" /> | ||||
|     <ChevronRightIcon class={clss?.indexOf("small") >= 0?  "h-4 w-4 shrink-0": "h-12 w-12 shrink-0" }/> | ||||
|   </div> | ||||
| </SubtleButton> | ||||
|  |  | |||
|  | @ -79,7 +79,7 @@ | |||
| 
 | ||||
|     onDestroy(_value.addCallbackAndRun((_) => setValues())) | ||||
|     onDestroy(value.addCallbackAndRunD(fromUpstream => { | ||||
|         if(_value.data !== fromUpstream){ | ||||
|         if(_value.data !== fromUpstream && fromUpstream !== ""){ | ||||
|             _value.setData(fromUpstream) | ||||
|         } | ||||
|     })) | ||||
|  |  | |||
|  | @ -25,6 +25,7 @@ import TranslationValidator from "./Validators/TranslationValidator" | |||
| import FediverseValidator from "./Validators/FediverseValidator" | ||||
| import IconValidator from "./Validators/IconValidator" | ||||
| import TagValidator from "./Validators/TagValidator" | ||||
| import IdValidator from "./Validators/IdValidator" | ||||
| 
 | ||||
| export type ValidatorType = (typeof Validators.availableTypes)[number] | ||||
| 
 | ||||
|  | @ -54,6 +55,7 @@ export default class Validators { | |||
|         "fediverse", | ||||
|         "tag", | ||||
|         "fediverse", | ||||
|         "id", | ||||
|     ] as const | ||||
| 
 | ||||
|     public static readonly AllValidators: ReadonlyArray<Validator> = [ | ||||
|  | @ -80,6 +82,7 @@ export default class Validators { | |||
|         new TranslationValidator(), | ||||
|         new IconValidator(), | ||||
|         new FediverseValidator(), | ||||
|         new IdValidator(), | ||||
|     ] | ||||
| 
 | ||||
|     private static _byType = Validators._byTypeConstructor() | ||||
|  |  | |||
|  | @ -11,7 +11,12 @@ export default class IconValidator extends Validator { | |||
|         super("icon", "Makes sure that a valid .svg-path is added") | ||||
|     } | ||||
| 
 | ||||
|     reformat(s: string, _?: () => string): string { | ||||
|         return s.trim() | ||||
|     } | ||||
| 
 | ||||
|     getFeedback(s: string, getCountry, sloppy?: boolean): Translation | undefined { | ||||
|         s = this.reformat(s) | ||||
|         if (!s.startsWith("http")) { | ||||
|             if (!IconValidator.allLicenses.has(s)) { | ||||
|                 const close = sloppy | ||||
|  |  | |||
|  | @ -0,0 +1,29 @@ | |||
| import { Translation } from "../../i18n/Translation" | ||||
| import { Validator } from "../Validator" | ||||
| import Translations from "../../i18n/Translations" | ||||
| 
 | ||||
| export default class IdValidator extends Validator { | ||||
|     constructor() { | ||||
|         super( | ||||
|             "id", | ||||
|             "Checks for valid identifiers for layers, will automatically replace spaces and uppercase" | ||||
|         ) | ||||
|     } | ||||
|     isValid(key: string, getCountry?: () => string): boolean { | ||||
|         return this.getFeedback(key, getCountry) === undefined | ||||
|     } | ||||
| 
 | ||||
|     reformat(s: string, _?: () => string): string { | ||||
|         return s.replaceAll(" ", "_").toLowerCase() | ||||
|     } | ||||
| 
 | ||||
|     getFeedback(s: string, _?: () => string): Translation | undefined { | ||||
|         if (s.length < 3) { | ||||
|             return Translations.t.validation.id.shouldBeLonger | ||||
|         } | ||||
|         if (!s.match(/^[a-zA-Z0-9_ ]+$/)) { | ||||
|             return Translations.t.validation.id.invalidCharacter | ||||
|         } | ||||
|         return undefined | ||||
|     } | ||||
| } | ||||
|  | @ -7,14 +7,15 @@ | |||
|   /** | ||||
|    * Renders a 'marker', which consists of multiple 'icons' | ||||
|    */ | ||||
|   export let config : PointRenderingConfig | ||||
|   export let config: PointRenderingConfig; | ||||
|   let icons: IconConfig[] = config.marker; | ||||
|   export let tags: Store<Record<string, string>>; | ||||
| 
 | ||||
| </script> | ||||
| 
 | ||||
| <div class="relative w-full h-full"> | ||||
| {#if config !== undefined} | ||||
|   <div class="relative w-full h-full"> | ||||
|     {#each icons as icon} | ||||
|       <Icon {icon} {tags} /> | ||||
|     {/each} | ||||
| </div> | ||||
|   </div> | ||||
| {/if} | ||||
|  |  | |||
|  | @ -7,10 +7,12 @@ | |||
|   import { Store, UIEventSource } from "../../Logic/UIEventSource"; | ||||
|   import type { ConfigMeta } from "./configMeta"; | ||||
|   import { Utils } from "../../Utils"; | ||||
|   import type { LayerConfigJson } from "../../Models/ThemeConfig/Json/LayerConfigJson"; | ||||
| 
 | ||||
|   const layerSchema: ConfigMeta[] = <any>layerSchemaRaw; | ||||
|   let state = new EditLayerState(layerSchema); | ||||
|   state.configuration.setData({}); | ||||
|   export let initialLayerConfig: Partial<LayerConfigJson> = {} | ||||
|   state.configuration.setData(initialLayerConfig); | ||||
|   const configuration = state.configuration; | ||||
|   new LayerStateSender("http://localhost:1235", state); | ||||
|   /** | ||||
|  |  | |||
|  | @ -129,7 +129,7 @@ export default class EditLayerState { | |||
|             } | ||||
|             entry = entry[breadcrumb] | ||||
|         } | ||||
|         if (v) { | ||||
|         if (v !== undefined && v !== null && v !== "") { | ||||
|             entry[path.at(-1)] = v | ||||
|         } else if (entry) { | ||||
|             delete entry[path.at(-1)] | ||||
|  |  | |||
|  | @ -72,11 +72,11 @@ | |||
|         configJson.mappings.push( | ||||
|             { | ||||
|                 if: "value=true", | ||||
|                 then: "Yes "+(schema.hints?.iftrue??"") | ||||
|                 then: "Yes: "+(schema.hints?.iftrue??"") | ||||
|             }, | ||||
|             { | ||||
|                 if: "value=false", | ||||
|                 then: "No "+(schema.hints?.iffalse??"") | ||||
|                 then: "No: "+(schema.hints?.iffalse??"") | ||||
|             } | ||||
|         ) | ||||
|     } | ||||
|  | @ -106,6 +106,15 @@ | |||
|             if (schema.type === "boolan") { | ||||
|                 return v === "true" || v === "yes" || v === "1" | ||||
|             } | ||||
|             if(mightBeBoolean(schema.type)){ | ||||
|               if(v === "true" || v === "yes" || v === "1"){ | ||||
|                 return true | ||||
|               } | ||||
|               if(v === "false" || v === "no" || v === "0"){ | ||||
|                 console.log("Setting false...") | ||||
|                 return false | ||||
|               } | ||||
|             } | ||||
|             if (schema.type === "number") { | ||||
|                 return Number(v) | ||||
|             } | ||||
|  |  | |||
|  | @ -4,9 +4,8 @@ | |||
|   import EditLayerState from "./EditLayerState"; | ||||
|   import SchemaBasedArray from "./SchemaBasedArray.svelte"; | ||||
|   import SchemaBasedMultiType from "./SchemaBasedMultiType.svelte"; | ||||
|   import SchemaBasedTranslationInput from "./SchemaBasedTranslationInput.svelte"; | ||||
|   import { ConfigMetaUtils } from "./configMeta.ts" | ||||
|   import ArrayMultiAnswer from "./ArrayMultiAnswer.svelte"; | ||||
| 
 | ||||
|   export let schema: ConfigMeta; | ||||
|   export let state: EditLayerState; | ||||
|   export let path: (string | number)[] = []; | ||||
|  |  | |||
|  | @ -13,6 +13,7 @@ | |||
|   import type { JsonSchemaType } from "./jsonSchema"; | ||||
|   // @ts-ignore | ||||
|   import nmd from "nano-markdown"; | ||||
|   import { writable } from "svelte/store"; | ||||
| 
 | ||||
|   /** | ||||
|    * If 'types' is defined: allow the user to pick one of the types to input. | ||||
|  | @ -84,7 +85,7 @@ | |||
|     ); | ||||
|   } | ||||
|   const config = new TagRenderingConfig(configJson, "config based on " + schema.path.join(".")); | ||||
|   let chosenOption: number = defaultOption; | ||||
|   let chosenOption: number = writable(defaultOption); | ||||
| 
 | ||||
| 
 | ||||
|   const existingValue = state.getCurrentValueFor(path); | ||||
|  | @ -126,8 +127,11 @@ | |||
|     } | ||||
|     possibleTypes.sort((a, b) => b.optionalMatches - a.optionalMatches); | ||||
|     possibleTypes.sort((a, b) => b.matchingPropertiesCount - a.matchingPropertiesCount); | ||||
|     console.log("Possible types are", possibleTypes) | ||||
|     if (possibleTypes.length > 0) { | ||||
|       tags.setData({ chosen_type_index: "" + possibleTypes[0].index }); | ||||
|       chosenOption = possibleTypes[0].index | ||||
|       tags.setData({ chosen_type_index: "" + chosenOption}); | ||||
|        | ||||
|     } | ||||
|   } else if (defaultOption !== undefined) { | ||||
|     tags.setData({ chosen_type_index: "" + defaultOption }); | ||||
|  | @ -150,13 +154,14 @@ | |||
|   let subSchemas: ConfigMeta[] = []; | ||||
| 
 | ||||
|   let subpath = path; | ||||
|   console.log("Initial chosen option is", chosenOption); | ||||
|   console.log("Initial chosen option for",path.join("."),"is", chosenOption); | ||||
|   onDestroy(tags.addCallbackAndRun(tags => { | ||||
|     if (tags["value"] !== "") { | ||||
|       chosenOption = undefined; | ||||
|       return; | ||||
|     } | ||||
|     const oldOption = chosenOption; | ||||
|     console.log("Updating chosenOption based on", tags, oldOption) | ||||
|     chosenOption = tags["chosen_type_index"] ? Number(tags["chosen_type_index"]) : defaultOption; | ||||
|     const type = schema.type[chosenOption]; | ||||
|     if (chosenOption !== oldOption) { | ||||
|  | @ -209,4 +214,5 @@ | |||
|                         path={[...subpath, (subschema?.path?.at(-1) ?? "???")]}></SchemaBasedInput> | ||||
|     {/each} | ||||
|   {/if} | ||||
|   {chosenOption} | ||||
| </div> | ||||
|  |  | |||
|  | @ -1,7 +1,87 @@ | |||
| <script lang="ts"> | ||||
| 
 | ||||
| 
 | ||||
| import EditLayer from "./Studio/EditLayer.svelte"; | ||||
| </script> | ||||
|   import NextButton from "./Base/NextButton.svelte"; | ||||
|   import { Utils } from "../Utils"; | ||||
|   import { UIEventSource } from "../Logic/UIEventSource"; | ||||
|   import Constants from "../Models/Constants"; | ||||
|   import ValidatedInput from "./InputElement/ValidatedInput.svelte"; | ||||
|   import EditLayerState from "./Studio/EditLayerState"; | ||||
|   import EditLayer from "./Studio/EditLayer.svelte"; | ||||
|   import Loading from "../assets/svg/Loading.svelte"; | ||||
| 
 | ||||
| <EditLayer/> | ||||
| 
 | ||||
|   export let studioUrl = "http://127.0.0.1:1235"; | ||||
|   let overview = UIEventSource.FromPromise<{ allFiles: string[] }>(Utils.downloadJson(studioUrl + "/overview")); | ||||
|   let layers = overview.map(overview => { | ||||
|     if (!overview) { | ||||
|       return []; | ||||
|     } | ||||
|     return overview.allFiles.filter(f => f.startsWith("layers/") | ||||
|     ).map(l => l.substring(l.lastIndexOf("/") + 1, l.length - ".json".length)) | ||||
|       .filter(layerId => Constants.priviliged_layers.indexOf(layerId) < 0); | ||||
|   }); | ||||
|   let state: undefined | "edit_layer" | "new_layer" | "edit_theme" | "new_theme" | "editing_layer" | "loading" = undefined; | ||||
| 
 | ||||
|   let initialLayerConfig: undefined; | ||||
|   let newLayerId = new UIEventSource<string>(""); | ||||
|   let layerIdFeedback = new UIEventSource<string>(undefined); | ||||
|   newLayerId.addCallbackD(layerId => { | ||||
|     if (layerId === "") { | ||||
|       return; | ||||
|     } | ||||
|     if (layers.data.indexOf(layerId) >= 0) { | ||||
|       layerIdFeedback.setData("This id is already used"); | ||||
|     } | ||||
|   }, [layers]); | ||||
| 
 | ||||
| 
 | ||||
|   let editLayerState = new EditLayerState(); | ||||
| 
 | ||||
| </script> | ||||
| {#if state === undefined} | ||||
|   <h1>MapComplete Studio</h1> | ||||
|   <div class="w-full flex flex-col"> | ||||
| 
 | ||||
|     <NextButton on:click={() => state = "edit_layer"}> | ||||
|       Edit an existing layer | ||||
|     </NextButton> | ||||
|     <NextButton on:click={() => state = "new_layer"}> | ||||
|       Create a new layer | ||||
|     </NextButton> | ||||
|     <NextButton on:click={() => state = "edit_theme"}> | ||||
|       Edit a theme | ||||
|     </NextButton> | ||||
|     <NextButton on:click={() => state = "new_theme"}> | ||||
|       Create a new theme | ||||
|     </NextButton> | ||||
|   </div> | ||||
| {:else if state === "edit_layer"} | ||||
|   <div class="flex flex-wrap"> | ||||
|     {#each $layers as layerId} | ||||
|       <NextButton clss="small" on:click={async () => { | ||||
|         console.log("Editing layer",layerId) | ||||
|         state = "loading" | ||||
|         initialLayerConfig = await Utils.downloadJson(studioUrl+"/layers/"+layerId+"/"+layerId+".json") | ||||
|         state = "editing_layer" | ||||
|        }}> | ||||
|         {layerId} | ||||
|       </NextButton> | ||||
|     {/each} | ||||
|   </div> | ||||
| {:else if state === "new_layer"} | ||||
|   <ValidatedInput type="id" value={newLayerId} feedback={layerIdFeedback} /> | ||||
|   {#if $layerIdFeedback !== undefined} | ||||
|     <div class="alert"> | ||||
|       {$layerIdFeedback} | ||||
|     </div> | ||||
|   {:else } | ||||
|     <NextButton on:click={() => {initialLayerConfig = ({id: newLayerId.data}); state = "editing_layer"}}> | ||||
|       Create this layer | ||||
|     </NextButton> | ||||
|   {/if} | ||||
| {:else if state === "loading"} | ||||
|   <Loading /> | ||||
| {:else if state === "editing_layer"} | ||||
|   <EditLayer {initialLayerConfig} /> | ||||
| {/if} | ||||
|  |  | |||
|  | @ -389,7 +389,8 @@ | |||
|     ], | ||||
|     "required": false, | ||||
|     "hints": { | ||||
|       "types": "use a fixed translation ; Use a dynamic tagRendering ; use a fixed string for all languages", | ||||
|       "typehint": "translation", | ||||
|       "types": "use a fixed translation ; Use a dynamic tagRendering ; hidden", | ||||
|       "typesdefault": "1", | ||||
|       "group": "title", | ||||
|       "question": "What title should be shown on the infobox?" | ||||
|  | @ -1621,12 +1622,318 @@ | |||
|       "marker" | ||||
|     ], | ||||
|     "required": false, | ||||
|     "hints": {}, | ||||
|     "type": "array", | ||||
|     "description": "The marker for an element.\nNote that this also defines the icon for this layer (rendered with the overpass-tags) <i>and</i> the icon in the presets.\nThe result of the icon is rendered as follows:\n- The first icon is rendered on the map\n- The second entry is overlayed on top of it\n- ...\nAs a result, on could use a generic icon (`pin`, `circle`, `square`) with a color, then overlay it with a specific icon." | ||||
|   }, | ||||
|   { | ||||
|     "path": [ | ||||
|       "pointRendering", | ||||
|       "marker", | ||||
|       "icon" | ||||
|     ], | ||||
|     "required": true, | ||||
|     "hints": { | ||||
|       "typehint": "icon", | ||||
|       "question": "What marker should be used to" | ||||
|       "question": "What icon should be used?", | ||||
|       "suggestions": [ | ||||
|         { | ||||
|           "if": "value=pin", | ||||
|           "then": "pin", | ||||
|           "icon": "pin" | ||||
|         }, | ||||
|         { | ||||
|           "if": "value=square", | ||||
|           "then": "square", | ||||
|           "icon": "square" | ||||
|         }, | ||||
|         { | ||||
|           "if": "value=circle", | ||||
|           "then": "circle", | ||||
|           "icon": "circle" | ||||
|         }, | ||||
|         { | ||||
|           "if": "value=checkmark", | ||||
|           "then": "checkmark", | ||||
|           "icon": "checkmark" | ||||
|         }, | ||||
|         { | ||||
|           "if": "value=clock", | ||||
|           "then": "clock", | ||||
|           "icon": "clock" | ||||
|         }, | ||||
|         { | ||||
|           "if": "value=close", | ||||
|           "then": "close", | ||||
|           "icon": "close" | ||||
|         }, | ||||
|         { | ||||
|           "if": "value=crosshair", | ||||
|           "then": "crosshair", | ||||
|           "icon": "crosshair" | ||||
|         }, | ||||
|         { | ||||
|           "if": "value=help", | ||||
|           "then": "help", | ||||
|           "icon": "help" | ||||
|         }, | ||||
|         { | ||||
|           "if": "value=home", | ||||
|           "then": "home", | ||||
|           "icon": "home" | ||||
|         }, | ||||
|         { | ||||
|           "if": "value=invalid", | ||||
|           "then": "invalid", | ||||
|           "icon": "invalid" | ||||
|         }, | ||||
|         { | ||||
|           "if": "value=location", | ||||
|           "then": "location", | ||||
|           "icon": "location" | ||||
|         }, | ||||
|         { | ||||
|           "if": "value=location_empty", | ||||
|           "then": "location_empty", | ||||
|           "icon": "location_empty" | ||||
|         }, | ||||
|         { | ||||
|           "if": "value=location_locked", | ||||
|           "then": "location_locked", | ||||
|           "icon": "location_locked" | ||||
|         }, | ||||
|         { | ||||
|           "if": "value=note", | ||||
|           "then": "note", | ||||
|           "icon": "note" | ||||
|         }, | ||||
|         { | ||||
|           "if": "value=resolved", | ||||
|           "then": "resolved", | ||||
|           "icon": "resolved" | ||||
|         }, | ||||
|         { | ||||
|           "if": "value=ring", | ||||
|           "then": "ring", | ||||
|           "icon": "ring" | ||||
|         }, | ||||
|         { | ||||
|           "if": "value=scissors", | ||||
|           "then": "scissors", | ||||
|           "icon": "scissors" | ||||
|         }, | ||||
|         { | ||||
|           "if": "value=teardrop", | ||||
|           "then": "teardrop", | ||||
|           "icon": "teardrop" | ||||
|         }, | ||||
|         { | ||||
|           "if": "value=teardrop_with_hole_green", | ||||
|           "then": "teardrop_with_hole_green", | ||||
|           "icon": "teardrop_with_hole_green" | ||||
|         }, | ||||
|         { | ||||
|           "if": "value=triangle", | ||||
|           "then": "triangle", | ||||
|           "icon": "triangle" | ||||
|         } | ||||
|       ] | ||||
|     }, | ||||
|     "type": [ | ||||
|       { | ||||
|         "description": "A TagRenderingConfigJson is a single piece of code which converts one ore more tags into a HTML-snippet.\nFor an _editable_ tagRendering, use 'QuestionableTagRenderingConfigJson' instead, which extends this one", | ||||
|         "type": "object", | ||||
|         "properties": { | ||||
|           "render": { | ||||
|             "description": "question: What text should be rendered?\n\nThis piece of text will be shown in the infobox.\nNote that \"&LBRACEkey&RBRACE\"-parts are substituted by the corresponding values of the element.\n\nThis text will be shown if:\n- there is no mapping which matches (or there are no matches)\n- no question, no mappings and no 'freeform' is set\n\nNote that this is a HTML-interpreted value, so you can add links as e.g. '<a href='{website}'>{website}</a>' or include images such as `This is of type A <br><img src='typeA-icon.svg' />`\ntype: rendered", | ||||
|             "anyOf": [ | ||||
|               { | ||||
|                 "$ref": "#/definitions/Record<string,string>" | ||||
|               }, | ||||
|               { | ||||
|                 "type": "object", | ||||
|                 "properties": { | ||||
|                   "special": { | ||||
|                     "allOf": [ | ||||
|                       { | ||||
|                         "$ref": "#/definitions/Record<string,string|Record<string,string>>" | ||||
|                       }, | ||||
|                       { | ||||
|                         "type": "object", | ||||
|                         "properties": { | ||||
|                           "type": { | ||||
|                             "type": "string" | ||||
|                           } | ||||
|                         }, | ||||
|                         "required": [ | ||||
|                           "type" | ||||
|                         ] | ||||
|                       } | ||||
|                     ] | ||||
|                   } | ||||
|                 }, | ||||
|                 "required": [ | ||||
|                   "special" | ||||
|                 ] | ||||
|               }, | ||||
|               { | ||||
|                 "type": "string" | ||||
|               } | ||||
|             ] | ||||
|           }, | ||||
|           "icon": { | ||||
|             "description": "question: what icon should be shown next to the 'render' value?\nAn icon shown next to the rendering; typically shown pretty small\nThis is only shown next to the \"render\" value\nType: icon", | ||||
|             "anyOf": [ | ||||
|               { | ||||
|                 "type": "object", | ||||
|                 "properties": { | ||||
|                   "path": { | ||||
|                     "description": "The path to the icon\nType: icon", | ||||
|                     "type": "string" | ||||
|                   }, | ||||
|                   "class": { | ||||
|                     "description": "A hint to mapcomplete on how to render this icon within the mapping.\nThis is translated to 'mapping-icon-<classtype>', so defining your own in combination with a custom CSS is possible (but discouraged)", | ||||
|                     "type": "string" | ||||
|                   } | ||||
|                 }, | ||||
|                 "required": [ | ||||
|                   "path" | ||||
|                 ] | ||||
|               }, | ||||
|               { | ||||
|                 "type": "string" | ||||
|               } | ||||
|             ] | ||||
|           }, | ||||
|           "condition": { | ||||
|             "description": "question: When should this item be shown?\n\nOnly show this tagrendering (or ask the question) if the selected object also matches the tags specified as `condition`.\n\nThis is useful to ask a follow-up question.\nFor example, within toilets, asking _where_ the diaper changing table is is only useful _if_ there is one.\nThis can be done by adding `\"condition\": \"changing_table=yes\"`\n\nA full example would be:\n```json\n    {\n      \"question\": \"Where is the changing table located?\",\n      \"render\": \"The changing table is located at {changing_table:location}\",\n      \"condition\": \"changing_table=yes\",\n      \"freeform\": {\n        \"key\": \"changing_table:location\",\n        \"inline\": true\n      },\n      \"mappings\": [\n        {\n          \"then\": \"The changing table is in the toilet for women.\",\n          \"if\": \"changing_table:location=female_toilet\"\n        },\n        {\n          \"then\": \"The changing table is in the toilet for men.\",\n          \"if\": \"changing_table:location=male_toilet\"\n        },\n        {\n          \"if\": \"changing_table:location=wheelchair_toilet\",\n          \"then\": \"The changing table is in the toilet for wheelchair users.\",\n        },\n        {\n          \"if\": \"changing_table:location=dedicated_room\",\n          \"then\": \"The changing table is in a dedicated room. \",\n        }\n      ],\n      \"id\": \"toilet-changing_table:location\"\n    },\n```", | ||||
|             "anyOf": [ | ||||
|               { | ||||
|                 "$ref": "#/definitions/{and:TagConfigJson[];}" | ||||
|               }, | ||||
|               { | ||||
|                 "$ref": "#/definitions/{or:TagConfigJson[];}" | ||||
|               }, | ||||
|               { | ||||
|                 "type": "string" | ||||
|               } | ||||
|             ] | ||||
|           }, | ||||
|           "metacondition": { | ||||
|             "description": "question: When should this item be shown (including special conditions)?\n\nIf set, this tag will be evaluated agains the _usersettings/application state_ table.\nEnable 'show debug info' in user settings to see available options.\nNote that values with an underscore depicts _application state_ (including metainfo about the user) whereas values without an underscore depict _user settings_", | ||||
|             "anyOf": [ | ||||
|               { | ||||
|                 "$ref": "#/definitions/{and:TagConfigJson[];}" | ||||
|               }, | ||||
|               { | ||||
|                 "$ref": "#/definitions/{or:TagConfigJson[];}" | ||||
|               }, | ||||
|               { | ||||
|                 "type": "string" | ||||
|               } | ||||
|             ] | ||||
|           }, | ||||
|           "freeform": { | ||||
|             "description": "question: Should a freeform text field be shown?\nAllow freeform text input from the user\nifunset: Do not add a freeform text field", | ||||
|             "type": "object", | ||||
|             "properties": { | ||||
|               "key": { | ||||
|                 "description": "What attribute should be filled out\nIf this key is present in the feature, then 'render' is used to display the value.\nIf this is undefined, the rendering is _always_ shown", | ||||
|                 "type": "string" | ||||
|               } | ||||
|             }, | ||||
|             "required": [ | ||||
|               "key" | ||||
|             ] | ||||
|           }, | ||||
|           "mappings": { | ||||
|             "description": "Allows fixed-tag inputs, shown either as radiobuttons or as checkboxes", | ||||
|             "type": "array", | ||||
|     "description": "The icon for an element.\nNote that this also doubles as the icon for this layer (rendered with the overpass-tags) ánd the icon in the presets.\nThe result of the icon is rendered as follows:\nthe resulting string is interpreted as a _list_ of items, separated by \";\". The bottommost layer is the first layer.\nAs a result, on could use a generic pin, then overlay it with a specific icon.\nTo make things even more practical, one c    an use all SVG's from the folder \"assets/svg\" and _substitute the color_ in it.\nE.g. to draw a red pin, use \"pin:#f00\", to have a green circle with your icon on top, use `circle:#0f0;<path to my icon.svg>`" | ||||
|             "items": { | ||||
|               "type": "object", | ||||
|               "properties": { | ||||
|                 "if": { | ||||
|                   "$ref": "#/definitions/TagConfigJson", | ||||
|                   "description": "question: When should this single mapping match?\n\nIf this condition is met, then the text under `then` will be shown.\nIf no value matches, and the user selects this mapping as an option, then these tags will be uploaded to OSM.\n\nFor example: {'if': 'diet:vegetarion=yes', 'then':'A vegetarian option is offered here'}\n\nThis can be an substituting-tag as well, e.g. {'if': 'addr:street:={_calculated_nearby_streetname}', 'then': '{_calculated_nearby_streetname}'}" | ||||
|                 }, | ||||
|                 "then": { | ||||
|                   "description": "question: What text should be shown?\n\nIf the condition `if` is met, the text `then` will be rendered.\nIf not known yet, the user will be presented with `then` as an option\nType: rendered", | ||||
|                   "anyOf": [ | ||||
|                     { | ||||
|                       "$ref": "#/definitions/Record<string,string>" | ||||
|                     }, | ||||
|                     { | ||||
|                       "type": "string" | ||||
|                     } | ||||
|                   ] | ||||
|                 }, | ||||
|                 "icon": { | ||||
|                   "description": "question: What icon should be added to this mapping?\nAn icon supporting this mapping; typically shown pretty small\ninline: <img src='{icon}' class=\"w-8 h-8\" /> {icon}\nType: icon", | ||||
|                   "anyOf": [ | ||||
|                     { | ||||
|                       "type": "object", | ||||
|                       "properties": { | ||||
|                         "path": { | ||||
|                           "description": "The path to the icon\nType: icon", | ||||
|                           "type": "string" | ||||
|                         }, | ||||
|                         "class": { | ||||
|                           "description": "A hint to mapcomplete on how to render this icon within the mapping.\nThis is translated to 'mapping-icon-<classtype>', so defining your own in combination with a custom CSS is possible (but discouraged)", | ||||
|                           "type": "string" | ||||
|                         } | ||||
|                       }, | ||||
|                       "required": [ | ||||
|                         "path" | ||||
|                       ] | ||||
|                     }, | ||||
|                     { | ||||
|                       "type": "string" | ||||
|                     } | ||||
|                   ] | ||||
|                 } | ||||
|               }, | ||||
|               "required": [ | ||||
|                 "if", | ||||
|                 "then" | ||||
|               ] | ||||
|             } | ||||
|           }, | ||||
|           "description": { | ||||
|             "description": "A human-readable text explaining what this tagRendering does.\nMostly used for the shared tagrenderings", | ||||
|             "anyOf": [ | ||||
|               { | ||||
|                 "$ref": "#/definitions/Record<string,string>" | ||||
|               }, | ||||
|               { | ||||
|                 "type": "string" | ||||
|               } | ||||
|             ] | ||||
|           }, | ||||
|           "classes": { | ||||
|             "description": "question: What css-classes should be applied to showing this attribute?\n\nA list of css-classes to apply to the entire tagRendering.\nThese classes are applied in 'answer'-mode, not in question mode\nThis is only for advanced users.\n\nValues are split on ` `  (space)", | ||||
|             "type": "string" | ||||
|           } | ||||
|         }, | ||||
|         "additionalProperties": false | ||||
|       }, | ||||
|       { | ||||
|         "type": "object", | ||||
|         "properties": { | ||||
|           "builtin": { | ||||
|             "type": "string" | ||||
|           }, | ||||
|           "override": {} | ||||
|         }, | ||||
|         "required": [ | ||||
|           "builtin", | ||||
|           "override" | ||||
|         ] | ||||
|       }, | ||||
|       { | ||||
|         "type": "string" | ||||
|       } | ||||
|     ], | ||||
|     "description": "" | ||||
|   }, | ||||
|   { | ||||
|     "path": [ | ||||
|  | @ -2131,6 +2438,213 @@ | |||
|     "type": "string", | ||||
|     "description": "A list of css-classes to apply to the entire tagRendering.\nThese classes are applied in 'answer'-mode, not in question mode\nThis is only for advanced users.\nValues are split on ` `  (space)" | ||||
|   }, | ||||
|   { | ||||
|     "path": [ | ||||
|       "pointRendering", | ||||
|       "marker", | ||||
|       "color" | ||||
|     ], | ||||
|     "required": false, | ||||
|     "hints": { | ||||
|       "typehint": "color", | ||||
|       "question": "What colour should the icon be?" | ||||
|     }, | ||||
|     "type": [ | ||||
|       { | ||||
|         "description": "A TagRenderingConfigJson is a single piece of code which converts one ore more tags into a HTML-snippet.\nFor an _editable_ tagRendering, use 'QuestionableTagRenderingConfigJson' instead, which extends this one", | ||||
|         "type": "object", | ||||
|         "properties": { | ||||
|           "render": { | ||||
|             "description": "question: What text should be rendered?\n\nThis piece of text will be shown in the infobox.\nNote that \"&LBRACEkey&RBRACE\"-parts are substituted by the corresponding values of the element.\n\nThis text will be shown if:\n- there is no mapping which matches (or there are no matches)\n- no question, no mappings and no 'freeform' is set\n\nNote that this is a HTML-interpreted value, so you can add links as e.g. '<a href='{website}'>{website}</a>' or include images such as `This is of type A <br><img src='typeA-icon.svg' />`\ntype: rendered", | ||||
|             "anyOf": [ | ||||
|               { | ||||
|                 "$ref": "#/definitions/Record<string,string>" | ||||
|               }, | ||||
|               { | ||||
|                 "type": "object", | ||||
|                 "properties": { | ||||
|                   "special": { | ||||
|                     "allOf": [ | ||||
|                       { | ||||
|                         "$ref": "#/definitions/Record<string,string|Record<string,string>>" | ||||
|                       }, | ||||
|                       { | ||||
|                         "type": "object", | ||||
|                         "properties": { | ||||
|                           "type": { | ||||
|                             "type": "string" | ||||
|                           } | ||||
|                         }, | ||||
|                         "required": [ | ||||
|                           "type" | ||||
|                         ] | ||||
|                       } | ||||
|                     ] | ||||
|                   } | ||||
|                 }, | ||||
|                 "required": [ | ||||
|                   "special" | ||||
|                 ] | ||||
|               }, | ||||
|               { | ||||
|                 "type": "string" | ||||
|               } | ||||
|             ] | ||||
|           }, | ||||
|           "icon": { | ||||
|             "description": "question: what icon should be shown next to the 'render' value?\nAn icon shown next to the rendering; typically shown pretty small\nThis is only shown next to the \"render\" value\nType: icon", | ||||
|             "anyOf": [ | ||||
|               { | ||||
|                 "type": "object", | ||||
|                 "properties": { | ||||
|                   "path": { | ||||
|                     "description": "The path to the icon\nType: icon", | ||||
|                     "type": "string" | ||||
|                   }, | ||||
|                   "class": { | ||||
|                     "description": "A hint to mapcomplete on how to render this icon within the mapping.\nThis is translated to 'mapping-icon-<classtype>', so defining your own in combination with a custom CSS is possible (but discouraged)", | ||||
|                     "type": "string" | ||||
|                   } | ||||
|                 }, | ||||
|                 "required": [ | ||||
|                   "path" | ||||
|                 ] | ||||
|               }, | ||||
|               { | ||||
|                 "type": "string" | ||||
|               } | ||||
|             ] | ||||
|           }, | ||||
|           "condition": { | ||||
|             "description": "question: When should this item be shown?\n\nOnly show this tagrendering (or ask the question) if the selected object also matches the tags specified as `condition`.\n\nThis is useful to ask a follow-up question.\nFor example, within toilets, asking _where_ the diaper changing table is is only useful _if_ there is one.\nThis can be done by adding `\"condition\": \"changing_table=yes\"`\n\nA full example would be:\n```json\n    {\n      \"question\": \"Where is the changing table located?\",\n      \"render\": \"The changing table is located at {changing_table:location}\",\n      \"condition\": \"changing_table=yes\",\n      \"freeform\": {\n        \"key\": \"changing_table:location\",\n        \"inline\": true\n      },\n      \"mappings\": [\n        {\n          \"then\": \"The changing table is in the toilet for women.\",\n          \"if\": \"changing_table:location=female_toilet\"\n        },\n        {\n          \"then\": \"The changing table is in the toilet for men.\",\n          \"if\": \"changing_table:location=male_toilet\"\n        },\n        {\n          \"if\": \"changing_table:location=wheelchair_toilet\",\n          \"then\": \"The changing table is in the toilet for wheelchair users.\",\n        },\n        {\n          \"if\": \"changing_table:location=dedicated_room\",\n          \"then\": \"The changing table is in a dedicated room. \",\n        }\n      ],\n      \"id\": \"toilet-changing_table:location\"\n    },\n```", | ||||
|             "anyOf": [ | ||||
|               { | ||||
|                 "$ref": "#/definitions/{and:TagConfigJson[];}" | ||||
|               }, | ||||
|               { | ||||
|                 "$ref": "#/definitions/{or:TagConfigJson[];}" | ||||
|               }, | ||||
|               { | ||||
|                 "type": "string" | ||||
|               } | ||||
|             ] | ||||
|           }, | ||||
|           "metacondition": { | ||||
|             "description": "question: When should this item be shown (including special conditions)?\n\nIf set, this tag will be evaluated agains the _usersettings/application state_ table.\nEnable 'show debug info' in user settings to see available options.\nNote that values with an underscore depicts _application state_ (including metainfo about the user) whereas values without an underscore depict _user settings_", | ||||
|             "anyOf": [ | ||||
|               { | ||||
|                 "$ref": "#/definitions/{and:TagConfigJson[];}" | ||||
|               }, | ||||
|               { | ||||
|                 "$ref": "#/definitions/{or:TagConfigJson[];}" | ||||
|               }, | ||||
|               { | ||||
|                 "type": "string" | ||||
|               } | ||||
|             ] | ||||
|           }, | ||||
|           "freeform": { | ||||
|             "description": "question: Should a freeform text field be shown?\nAllow freeform text input from the user\nifunset: Do not add a freeform text field", | ||||
|             "type": "object", | ||||
|             "properties": { | ||||
|               "key": { | ||||
|                 "description": "What attribute should be filled out\nIf this key is present in the feature, then 'render' is used to display the value.\nIf this is undefined, the rendering is _always_ shown", | ||||
|                 "type": "string" | ||||
|               } | ||||
|             }, | ||||
|             "required": [ | ||||
|               "key" | ||||
|             ] | ||||
|           }, | ||||
|           "mappings": { | ||||
|             "description": "Allows fixed-tag inputs, shown either as radiobuttons or as checkboxes", | ||||
|             "type": "array", | ||||
|             "items": { | ||||
|               "type": "object", | ||||
|               "properties": { | ||||
|                 "if": { | ||||
|                   "$ref": "#/definitions/TagConfigJson", | ||||
|                   "description": "question: When should this single mapping match?\n\nIf this condition is met, then the text under `then` will be shown.\nIf no value matches, and the user selects this mapping as an option, then these tags will be uploaded to OSM.\n\nFor example: {'if': 'diet:vegetarion=yes', 'then':'A vegetarian option is offered here'}\n\nThis can be an substituting-tag as well, e.g. {'if': 'addr:street:={_calculated_nearby_streetname}', 'then': '{_calculated_nearby_streetname}'}" | ||||
|                 }, | ||||
|                 "then": { | ||||
|                   "description": "question: What text should be shown?\n\nIf the condition `if` is met, the text `then` will be rendered.\nIf not known yet, the user will be presented with `then` as an option\nType: rendered", | ||||
|                   "anyOf": [ | ||||
|                     { | ||||
|                       "$ref": "#/definitions/Record<string,string>" | ||||
|                     }, | ||||
|                     { | ||||
|                       "type": "string" | ||||
|                     } | ||||
|                   ] | ||||
|                 }, | ||||
|                 "icon": { | ||||
|                   "description": "question: What icon should be added to this mapping?\nAn icon supporting this mapping; typically shown pretty small\ninline: <img src='{icon}' class=\"w-8 h-8\" /> {icon}\nType: icon", | ||||
|                   "anyOf": [ | ||||
|                     { | ||||
|                       "type": "object", | ||||
|                       "properties": { | ||||
|                         "path": { | ||||
|                           "description": "The path to the icon\nType: icon", | ||||
|                           "type": "string" | ||||
|                         }, | ||||
|                         "class": { | ||||
|                           "description": "A hint to mapcomplete on how to render this icon within the mapping.\nThis is translated to 'mapping-icon-<classtype>', so defining your own in combination with a custom CSS is possible (but discouraged)", | ||||
|                           "type": "string" | ||||
|                         } | ||||
|                       }, | ||||
|                       "required": [ | ||||
|                         "path" | ||||
|                       ] | ||||
|                     }, | ||||
|                     { | ||||
|                       "type": "string" | ||||
|                     } | ||||
|                   ] | ||||
|                 } | ||||
|               }, | ||||
|               "required": [ | ||||
|                 "if", | ||||
|                 "then" | ||||
|               ] | ||||
|             } | ||||
|           }, | ||||
|           "description": { | ||||
|             "description": "A human-readable text explaining what this tagRendering does.\nMostly used for the shared tagrenderings", | ||||
|             "anyOf": [ | ||||
|               { | ||||
|                 "$ref": "#/definitions/Record<string,string>" | ||||
|               }, | ||||
|               { | ||||
|                 "type": "string" | ||||
|               } | ||||
|             ] | ||||
|           }, | ||||
|           "classes": { | ||||
|             "description": "question: What css-classes should be applied to showing this attribute?\n\nA list of css-classes to apply to the entire tagRendering.\nThese classes are applied in 'answer'-mode, not in question mode\nThis is only for advanced users.\n\nValues are split on ` `  (space)", | ||||
|             "type": "string" | ||||
|           } | ||||
|         }, | ||||
|         "additionalProperties": false | ||||
|       }, | ||||
|       { | ||||
|         "type": "object", | ||||
|         "properties": { | ||||
|           "builtin": { | ||||
|             "type": "string" | ||||
|           }, | ||||
|           "override": {} | ||||
|         }, | ||||
|         "required": [ | ||||
|           "builtin", | ||||
|           "override" | ||||
|         ] | ||||
|       }, | ||||
|       { | ||||
|         "type": "string" | ||||
|       } | ||||
|     ], | ||||
|     "description": "This will only work for the default icons such as `pin`,`circle`,..." | ||||
|   }, | ||||
|   { | ||||
|     "path": [ | ||||
|       "pointRendering", | ||||
|  | @ -3380,7 +3894,10 @@ | |||
|       "iconSize" | ||||
|     ], | ||||
|     "required": false, | ||||
|     "hints": {}, | ||||
|     "hints": { | ||||
|       "question": "What size should the marker be on the map?", | ||||
|       "ifunset": "Use the default size (<b>40,40</b> px)" | ||||
|     }, | ||||
|     "type": [ | ||||
|       { | ||||
|         "description": "A TagRenderingConfigJson is a single piece of code which converts one ore more tags into a HTML-snippet.\nFor an _editable_ tagRendering, use 'QuestionableTagRenderingConfigJson' instead, which extends this one", | ||||
|  | @ -3562,7 +4079,7 @@ | |||
|         "type": "string" | ||||
|       } | ||||
|     ], | ||||
|     "description": "A string containing \"width,height\" or \"width,height,anchorpoint\" where anchorpoint is any of 'center', 'top', 'bottom', 'left', 'right', 'bottomleft','topright', ...\nDefault is '40,40,center'" | ||||
|     "description": "A string containing \"<width>,<height>\" in pixels" | ||||
|   }, | ||||
|   { | ||||
|     "path": [ | ||||
|  | @ -4751,7 +5268,10 @@ | |||
|       "rotation" | ||||
|     ], | ||||
|     "required": false, | ||||
|     "hints": {}, | ||||
|     "hints": { | ||||
|       "question": "What rotation should be applied on the icon?", | ||||
|       "ifunset": "Do not rotate" | ||||
|     }, | ||||
|     "type": [ | ||||
|       { | ||||
|         "description": "A TagRenderingConfigJson is a single piece of code which converts one ore more tags into a HTML-snippet.\nFor an _editable_ tagRendering, use 'QuestionableTagRenderingConfigJson' instead, which extends this one", | ||||
|  | @ -4933,7 +5453,7 @@ | |||
|         "type": "string" | ||||
|       } | ||||
|     ], | ||||
|     "description": "The rotation of an icon, useful for e.g. directions.\nUsage: as if it were a css property for 'rotate', thus has to end with 'deg', e.g. `90deg`, `{direction}deg`, `calc(90deg - {camera:direction}deg)``" | ||||
|     "description": "This is mostly useful for items that face a specific direction, such as surveillance cameras\nThis is interpreted as css property for 'rotate', thus has to end with 'deg', e.g. `90deg`, `{direction}deg`, `calc(90deg - {camera:direction}deg)``" | ||||
|   }, | ||||
|   { | ||||
|     "path": [ | ||||
|  | @ -5610,7 +6130,7 @@ | |||
|         "type": "string" | ||||
|       } | ||||
|     ], | ||||
|     "description": "For example: <div style=\"background: white\">{name}</div>\nIf the icon is undefined, then the label is shown in the center of the feature." | ||||
|     "description": "For example: `<div style=\"background: white\">{name}</div>`\nIf the icon is undefined, then the label is shown in the center of the feature." | ||||
|   }, | ||||
|   { | ||||
|     "path": [ | ||||
|  | @ -8809,7 +9329,19 @@ | |||
|       "pitchAlignment" | ||||
|     ], | ||||
|     "required": false, | ||||
|     "hints": {}, | ||||
|     "hints": { | ||||
|       "question": "If the map is pitched, should the icon stay parallel to the screen or to the groundplane?", | ||||
|       "suggestions": [ | ||||
|         { | ||||
|           "if": "value=canvas", | ||||
|           "then": "The icon will stay upward and not be transformed as if it sticks to the screen" | ||||
|         }, | ||||
|         { | ||||
|           "if": "value=map", | ||||
|           "then": "The icon will be transformed as if it were painted onto the ground. (Automatically sets rotationAlignment)" | ||||
|         } | ||||
|       ] | ||||
|     }, | ||||
|     "type": [ | ||||
|       { | ||||
|         "description": "A TagRenderingConfigJson is a single piece of code which converts one ore more tags into a HTML-snippet.\nFor an _editable_ tagRendering, use 'QuestionableTagRenderingConfigJson' instead, which extends this one", | ||||
|  | @ -8995,7 +9527,7 @@ | |||
|         "type": "string" | ||||
|       } | ||||
|     ], | ||||
|     "description": "If the map is pitched, the marker will stay parallel to the screen.\nSet to 'map' if you want to put it flattened on the map" | ||||
|     "description": "" | ||||
|   }, | ||||
|   { | ||||
|     "path": [ | ||||
|  | @ -9487,12 +10019,12 @@ | |||
|     ], | ||||
|     "required": false, | ||||
|     "hints": { | ||||
|       "question": "Should the icon be rotated or tilted if the map is rotated or tilted?", | ||||
|       "question": "Should the icon be rotated if the map is rotated?", | ||||
|       "ifunset": "Do not rotate or tilt icons. Always keep the icons straight", | ||||
|       "suggestions": [ | ||||
|         { | ||||
|           "if": "value=canvas", | ||||
|           "then": "If the map is tilted, tilt the icon as well. This gives the impression of an icon that is glued to the ground." | ||||
|           "then": "Never rotate the icon" | ||||
|         }, | ||||
|         { | ||||
|           "if": "value=map", | ||||
|  |  | |||
										
											
												File diff suppressed because it is too large
												Load diff
											
										
									
								
							
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue