forked from MapComplete/MapComplete
		
	Merge develop
This commit is contained in:
		
						commit
						c672fe7668
					
				
					 138 changed files with 14304 additions and 1299 deletions
				
			
		|  | @ -116,28 +116,42 @@ export default class Constants { | |||
|      * These are the values that are allowed to use as 'backdrop' icon for a map pin | ||||
|      */ | ||||
|     private static readonly _defaultPinIcons = [ | ||||
|         "pin", | ||||
|         "square", | ||||
|         "circle", | ||||
|         "none", | ||||
|         "pin", | ||||
|         "person", | ||||
|         "plus", | ||||
|         "ring", | ||||
|         "star", | ||||
|         "teardrop", | ||||
|         "triangle", | ||||
|         "checkmark", | ||||
|         "clock", | ||||
|         "close", | ||||
|         "crosshair", | ||||
|         "help", | ||||
|         "home", | ||||
|         "invalid", | ||||
|         "location", | ||||
|         "location_empty", | ||||
|         "location_locked", | ||||
|         "note", | ||||
|         "resolved", | ||||
|         "ring", | ||||
|         "scissors", | ||||
|         "teardrop", | ||||
|         "teardrop_with_hole_green", | ||||
|         "triangle", | ||||
|         "brick_wall_square", | ||||
|         "brick_wall_round", | ||||
|         "gps_arrow", | ||||
|         "checkmark", | ||||
|         "help", | ||||
|         "clock", | ||||
|         "invalid", | ||||
|         "close", | ||||
|         "invalid", | ||||
|         "heart", | ||||
|         "heart_outline", | ||||
|         "link", | ||||
|         "confirm", | ||||
|         "direction", | ||||
|         "not_found", | ||||
|         "mastodon", | ||||
|         "party", | ||||
|         "addSmall", | ||||
|     ] as const | ||||
|     public static readonly defaultPinIcons: string[] = <any>Constants._defaultPinIcons | ||||
| 
 | ||||
|  |  | |||
|  | @ -3,6 +3,7 @@ import { UIEventSource } from "../Logic/UIEventSource" | |||
| import UserRelatedState from "../Logic/State/UserRelatedState" | ||||
| import { Utils } from "../Utils" | ||||
| import { LocalStorageSource } from "../Logic/Web/LocalStorageSource" | ||||
| import Zoomcontrol from "../UI/Zoomcontrol" | ||||
| 
 | ||||
| export type ThemeViewTabStates = (typeof MenuState._themeviewTabs)[number] | ||||
| export type MenuViewTabStates = (typeof MenuState._menuviewTabs)[number] | ||||
|  | @ -114,7 +115,36 @@ export class MenuState { | |||
|                 name: "background", | ||||
|                 showOverOthers: true, | ||||
|             }, | ||||
|             { | ||||
|                 toggle: this.communityIndexPanelIsOpened, | ||||
|                 name: "community", | ||||
|                 showOverOthers: true, | ||||
|             }, | ||||
|             { | ||||
|                 toggle: this.privacyPanelIsOpened, | ||||
|                 name: "privacy", | ||||
|                 showOverOthers: true, | ||||
|             }, | ||||
|             { | ||||
|                 toggle: this.filtersPanelIsOpened, | ||||
|                 name: "filters", | ||||
|                 showOverOthers: true, | ||||
|             }, | ||||
|         ] | ||||
|         for (const toggle of this.allToggles) { | ||||
|             toggle.toggle.addCallback((isOpen) => { | ||||
|                 if (!isOpen) { | ||||
|                     this.resetZoomIfAllClosed() | ||||
|                 } | ||||
|             }) | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|     private resetZoomIfAllClosed() { | ||||
|         if (this.isSomethingOpen()) { | ||||
|             return | ||||
|         } | ||||
|         Zoomcontrol.resetzoom() | ||||
|     } | ||||
| 
 | ||||
|     public openFilterView(highlightLayer?: LayerConfig | string) { | ||||
|  | @ -146,27 +176,23 @@ export class MenuState { | |||
|         this.highlightedUserSetting.setData(highlightTagRendering) | ||||
|     } | ||||
| 
 | ||||
|     public isSomethingOpen(): boolean { | ||||
|         return this.allToggles.some((t) => t.toggle.data) | ||||
|     } | ||||
| 
 | ||||
|     /** | ||||
|      * Close all floatOvers. | ||||
|      * Returns 'true' if at least one menu was opened | ||||
|      */ | ||||
|     public closeAll(): boolean { | ||||
|         const toggles = [ | ||||
|             this.communityIndexPanelIsOpened, | ||||
|             this.privacyPanelIsOpened, | ||||
|             this.backgroundLayerSelectionIsOpened, | ||||
|             this.filtersPanelIsOpened, | ||||
|             this.menuIsOpened, | ||||
|             this.themeIsOpened, | ||||
|         ] | ||||
|         let somethingIsOpen = false | ||||
|         for (const t of toggles) { | ||||
|             somethingIsOpen = t.data | ||||
|             t.setData(false) | ||||
|             if (somethingIsOpen) { | ||||
|         let somethingWasOpen = false | ||||
|         for (const t of this.allToggles) { | ||||
|             somethingWasOpen = t.toggle.data | ||||
|             t.toggle.setData(false) | ||||
|             if (somethingWasOpen) { | ||||
|                 break | ||||
|             } | ||||
|         } | ||||
|         return somethingIsOpen | ||||
|         return somethingWasOpen | ||||
|     } | ||||
| } | ||||
|  |  | |||
|  | @ -1,14 +1,4 @@ | |||
| import { | ||||
|     Concat, | ||||
|     Conversion, | ||||
|     DesugaringContext, | ||||
|     DesugaringStep, | ||||
|     Each, | ||||
|     Fuse, | ||||
|     On, | ||||
|     Pass, | ||||
|     SetDefault, | ||||
| } from "./Conversion" | ||||
| import { Concat, Conversion, DesugaringContext, DesugaringStep, Each, Fuse, On, Pass, SetDefault } from "./Conversion" | ||||
| import { LayoutConfigJson } from "../Json/LayoutConfigJson" | ||||
| import { PrepareLayer } from "./PrepareLayer" | ||||
| import { LayerConfigJson } from "../Json/LayerConfigJson" | ||||
|  | @ -27,9 +17,9 @@ class SubstituteLayer extends Conversion<string | LayerConfigJson, LayerConfigJs | |||
| 
 | ||||
|     constructor(state: DesugaringContext) { | ||||
|         super( | ||||
|             "Converts the identifier of a builtin layer into the actual layer, or converts a 'builtin' syntax with override in the fully expanded form", | ||||
|             "Converts the identifier of a builtin layer into the actual layer, or converts a 'builtin' syntax with override in the fully expanded form. Note that 'tagRenderings+' will be inserted before 'leftover-questions'", | ||||
|             [], | ||||
|             "SubstituteLayer" | ||||
|             "SubstituteLayer", | ||||
|         ) | ||||
|         this._state = state | ||||
|     } | ||||
|  | @ -80,21 +70,35 @@ class SubstituteLayer extends Conversion<string | LayerConfigJson, LayerConfigJs | |||
|                 (found["tagRenderings"] ?? []).length > 0 | ||||
|             ) { | ||||
|                 context.err( | ||||
|                     `When overriding a layer, an override is not allowed to override into tagRenderings. Use "+tagRenderings" or "tagRenderings+" instead to prepend or append some questions.` | ||||
|                     `When overriding a layer, an override is not allowed to override into tagRenderings. Use "+tagRenderings" or "tagRenderings+" instead to prepend or append some questions.`, | ||||
|                 ) | ||||
|             } | ||||
|             try { | ||||
| 
 | ||||
|                 const trPlus = json["override"]["tagRenderings+"] | ||||
|                 if(trPlus){ | ||||
|                     let index = found.tagRenderings.findIndex(tr => tr["id"] === "leftover-questions") | ||||
|                     if(index < 0){ | ||||
|                         index = found.tagRenderings.length | ||||
|                     } | ||||
|                     found.tagRenderings.splice(index, 0, ...trPlus) | ||||
|                     delete json["override"]["tagRenderings+"] | ||||
|                 } | ||||
| 
 | ||||
|                 Utils.Merge(json["override"], found) | ||||
|                 layers.push(found) | ||||
|             } catch (e) { | ||||
|                 context.err( | ||||
|                     `Could not apply an override due to: ${e}.\nThe override is: ${JSON.stringify( | ||||
|                         json["override"] | ||||
|                     )}` | ||||
|                         json["override"], | ||||
|                     )}`,
 | ||||
|                 ) | ||||
|             } | ||||
| 
 | ||||
|             if (json["hideTagRenderingsWithLabels"]) { | ||||
|                 if (typeof json["hideTagRenderingsWithLabels"] === "string") { | ||||
|                     throw "At " + context + ".hideTagRenderingsWithLabels should be a list containing strings, you specified a string" | ||||
|                 } | ||||
|                 const hideLabels: Set<string> = new Set(json["hideTagRenderingsWithLabels"]) | ||||
|                 // These labels caused at least one deletion
 | ||||
|                 const usedLabels: Set<string> = new Set<string>() | ||||
|  | @ -107,9 +111,9 @@ class SubstituteLayer extends Conversion<string | LayerConfigJson, LayerConfigJs | |||
|                             usedLabels.add(labels[forbiddenLabel]) | ||||
|                             context.info( | ||||
|                                 "Dropping tagRendering " + | ||||
|                                     tr["id"] + | ||||
|                                     " as it has a forbidden label: " + | ||||
|                                     labels[forbiddenLabel] | ||||
|                                 tr["id"] + | ||||
|                                 " as it has a forbidden label: " + | ||||
|                                 labels[forbiddenLabel], | ||||
|                             ) | ||||
|                             continue | ||||
|                         } | ||||
|  | @ -118,7 +122,7 @@ class SubstituteLayer extends Conversion<string | LayerConfigJson, LayerConfigJs | |||
|                     if (hideLabels.has(tr["id"])) { | ||||
|                         usedLabels.add(tr["id"]) | ||||
|                         context.info( | ||||
|                             "Dropping tagRendering " + tr["id"] + " as its id is a forbidden label" | ||||
|                             "Dropping tagRendering " + tr["id"] + " as its id is a forbidden label", | ||||
|                         ) | ||||
|                         continue | ||||
|                     } | ||||
|  | @ -127,10 +131,10 @@ class SubstituteLayer extends Conversion<string | LayerConfigJson, LayerConfigJs | |||
|                         usedLabels.add(tr["group"]) | ||||
|                         context.info( | ||||
|                             "Dropping tagRendering " + | ||||
|                                 tr["id"] + | ||||
|                                 " as its group `" + | ||||
|                                 tr["group"] + | ||||
|                                 "` is a forbidden label" | ||||
|                             tr["id"] + | ||||
|                             " as its group `" + | ||||
|                             tr["group"] + | ||||
|                             "` is a forbidden label", | ||||
|                         ) | ||||
|                         continue | ||||
|                     } | ||||
|  | @ -141,8 +145,8 @@ class SubstituteLayer extends Conversion<string | LayerConfigJson, LayerConfigJs | |||
|                 if (unused.length > 0) { | ||||
|                     context.err( | ||||
|                         "This theme specifies that certain tagrenderings have to be removed based on forbidden layers. One or more of these layers did not match any tagRenderings and caused no deletions: " + | ||||
|                             unused.join(", ") + | ||||
|                             "\n   This means that this label can be removed or that the original tagRendering that should be deleted does not have this label anymore" | ||||
|                         unused.join(", ") + | ||||
|                         "\n   This means that this label can be removed or that the original tagRendering that should be deleted does not have this label anymore", | ||||
|                     ) | ||||
|                 } | ||||
|                 found.tagRenderings = filtered | ||||
|  | @ -159,7 +163,7 @@ class AddDefaultLayers extends DesugaringStep<LayoutConfigJson> { | |||
|         super( | ||||
|             "Adds the default layers, namely: " + Constants.added_by_default.join(", "), | ||||
|             ["layers"], | ||||
|             "AddDefaultLayers" | ||||
|             "AddDefaultLayers", | ||||
|         ) | ||||
|         this._state = state | ||||
|     } | ||||
|  | @ -183,10 +187,10 @@ class AddDefaultLayers extends DesugaringStep<LayoutConfigJson> { | |||
|             if (alreadyLoaded.has(v.id)) { | ||||
|                 context.warn( | ||||
|                     "Layout " + | ||||
|                         context + | ||||
|                         " already has a layer with name " + | ||||
|                         v.id + | ||||
|                         "; skipping inclusion of this builtin layer" | ||||
|                     context + | ||||
|                     " already has a layer with name " + | ||||
|                     v.id + | ||||
|                     "; skipping inclusion of this builtin layer", | ||||
|                 ) | ||||
|                 continue | ||||
|             } | ||||
|  | @ -202,14 +206,14 @@ class AddImportLayers extends DesugaringStep<LayoutConfigJson> { | |||
|         super( | ||||
|             "For every layer in the 'layers'-list, create a new layer which'll import notes. (Note that priviliged layers and layers which have a geojson-source set are ignored)", | ||||
|             ["layers"], | ||||
|             "AddImportLayers" | ||||
|             "AddImportLayers", | ||||
|         ) | ||||
|     } | ||||
| 
 | ||||
|     convert(json: LayoutConfigJson, context: ConversionContext): LayoutConfigJson { | ||||
|         if (!(json.enableNoteImports ?? true)) { | ||||
|             context.info( | ||||
|                 "Not creating a note import layers for theme " + json.id + " as they are disabled" | ||||
|                 "Not creating a note import layers for theme " + json.id + " as they are disabled", | ||||
|             ) | ||||
|             return json | ||||
|         } | ||||
|  | @ -244,7 +248,7 @@ class AddImportLayers extends DesugaringStep<LayoutConfigJson> { | |||
|             try { | ||||
|                 const importLayerResult = creator.convert( | ||||
|                     layer, | ||||
|                     context.inOperation(this.name).enter(i1) | ||||
|                     context.inOperation(this.name).enter(i1), | ||||
|                 ) | ||||
|                 if (importLayerResult !== undefined) { | ||||
|                     json.layers.push(importLayerResult) | ||||
|  | @ -263,7 +267,7 @@ class AddContextToTranslationsInLayout extends DesugaringStep<LayoutConfigJson> | |||
|         super( | ||||
|             "Adds context to translations, including the prefix 'themes:json.id'; this is to make sure terms in an 'overrides' or inline layer are linkable too", | ||||
|             ["_context"], | ||||
|             "AddContextToTranlationsInLayout" | ||||
|             "AddContextToTranlationsInLayout", | ||||
|         ) | ||||
|     } | ||||
| 
 | ||||
|  | @ -278,7 +282,7 @@ class ApplyOverrideAll extends DesugaringStep<LayoutConfigJson> { | |||
|         super( | ||||
|             "Applies 'overrideAll' onto every 'layer'. The 'overrideAll'-field is removed afterwards", | ||||
|             ["overrideAll", "layers"], | ||||
|             "ApplyOverrideAll" | ||||
|             "ApplyOverrideAll", | ||||
|         ) | ||||
|     } | ||||
| 
 | ||||
|  | @ -292,9 +296,29 @@ class ApplyOverrideAll extends DesugaringStep<LayoutConfigJson> { | |||
| 
 | ||||
|         delete json.overrideAll | ||||
|         const newLayers = [] | ||||
| 
 | ||||
|         let tagRenderingsPlus = undefined | ||||
|         if (overrideAll["tagRenderings+"] !== undefined) { | ||||
|             tagRenderingsPlus = overrideAll["tagRenderings+"] | ||||
|             delete overrideAll["tagRenderings+"] | ||||
|         } | ||||
| 
 | ||||
|         for (let layer of json.layers) { | ||||
|             layer = Utils.Clone(<LayerConfigJson>layer) | ||||
|             Utils.Merge(overrideAll, layer) | ||||
|             if (tagRenderingsPlus) { | ||||
|                 if (!layer.tagRenderings) { | ||||
|                     layer.tagRenderings = tagRenderingsPlus | ||||
|                 } else { | ||||
| 
 | ||||
|                     let index = layer.tagRenderings.findIndex(tr => tr["id"] === "leftover-questions") | ||||
|                     if (index < 0) { | ||||
|                         index = layer.tagRenderings.length - 1 | ||||
|                     } | ||||
|                     layer.tagRenderings.splice(index, 0, ...tagRenderingsPlus) | ||||
|                 } | ||||
|             } | ||||
| 
 | ||||
|             newLayers.push(layer) | ||||
|         } | ||||
|         json.layers = newLayers | ||||
|  | @ -314,7 +338,7 @@ class AddDependencyLayersToTheme extends DesugaringStep<LayoutConfigJson> { | |||
|             Some layers (e.g. \`all_buildings_and_walls\' or \'streets_with_a_name\') are invisible, so by default, \'force_load\' is set too.
 | ||||
|             `,
 | ||||
|             ["layers"], | ||||
|             "AddDependencyLayersToTheme" | ||||
|             "AddDependencyLayersToTheme", | ||||
|         ) | ||||
|         this._state = state | ||||
|     } | ||||
|  | @ -322,7 +346,7 @@ class AddDependencyLayersToTheme extends DesugaringStep<LayoutConfigJson> { | |||
|     private static CalculateDependencies( | ||||
|         alreadyLoaded: LayerConfigJson[], | ||||
|         allKnownLayers: Map<string, LayerConfigJson>, | ||||
|         themeId: string | ||||
|         themeId: string, | ||||
|     ): { config: LayerConfigJson; reason: string }[] { | ||||
|         const dependenciesToAdd: { config: LayerConfigJson; reason: string }[] = [] | ||||
|         const loadedLayerIds: Set<string> = new Set<string>(alreadyLoaded.map((l) => l.id)) | ||||
|  | @ -345,7 +369,7 @@ class AddDependencyLayersToTheme extends DesugaringStep<LayoutConfigJson> { | |||
|             for (const layerConfig of alreadyLoaded) { | ||||
|                 try { | ||||
|                     const layerDeps = DependencyCalculator.getLayerDependencies( | ||||
|                         new LayerConfig(layerConfig, themeId + "(dependencies)") | ||||
|                         new LayerConfig(layerConfig, themeId + "(dependencies)"), | ||||
|                     ) | ||||
|                     dependencies.push(...layerDeps) | ||||
|                 } catch (e) { | ||||
|  | @ -382,10 +406,10 @@ class AddDependencyLayersToTheme extends DesugaringStep<LayoutConfigJson> { | |||
|                 if (dep === undefined) { | ||||
|                     const message = [ | ||||
|                         "Loading a dependency failed: layer " + | ||||
|                             unmetDependency.neededLayer + | ||||
|                             " is not found, neither as layer of " + | ||||
|                             themeId + | ||||
|                             " nor as builtin layer.", | ||||
|                         unmetDependency.neededLayer + | ||||
|                         " is not found, neither as layer of " + | ||||
|                         themeId + | ||||
|                         " nor as builtin layer.", | ||||
|                         reason, | ||||
|                         "Loaded layers are: " + alreadyLoaded.map((l) => l.id).join(","), | ||||
|                     ] | ||||
|  | @ -401,7 +425,7 @@ class AddDependencyLayersToTheme extends DesugaringStep<LayoutConfigJson> { | |||
|                 }) | ||||
|                 loadedLayerIds.add(dep.id) | ||||
|                 unmetDependencies = unmetDependencies.filter( | ||||
|                     (d) => d.neededLayer !== unmetDependency.neededLayer | ||||
|                     (d) => d.neededLayer !== unmetDependency.neededLayer, | ||||
|                 ) | ||||
|             } | ||||
|         } while (unmetDependencies.length > 0) | ||||
|  | @ -422,14 +446,14 @@ class AddDependencyLayersToTheme extends DesugaringStep<LayoutConfigJson> { | |||
|         const dependencies = AddDependencyLayersToTheme.CalculateDependencies( | ||||
|             layers, | ||||
|             allKnownLayers, | ||||
|             theme.id | ||||
|             theme.id, | ||||
|         ) | ||||
|         for (const dependency of dependencies) { | ||||
|         } | ||||
|         if (dependencies.length > 0) { | ||||
|             for (const dependency of dependencies) { | ||||
|                 context.info( | ||||
|                     "Added " + dependency.config.id + " to the theme. " + dependency.reason | ||||
|                     "Added " + dependency.config.id + " to the theme. " + dependency.reason, | ||||
|                 ) | ||||
|             } | ||||
|         } | ||||
|  | @ -471,7 +495,7 @@ class WarnForUnsubstitutedLayersInTheme extends DesugaringStep<LayoutConfigJson> | |||
|         super( | ||||
|             "Generates a warning if a theme uses an unsubstituted layer", | ||||
|             ["layers"], | ||||
|             "WarnForUnsubstitutedLayersInTheme" | ||||
|             "WarnForUnsubstitutedLayersInTheme", | ||||
|         ) | ||||
|     } | ||||
| 
 | ||||
|  | @ -483,7 +507,7 @@ class WarnForUnsubstitutedLayersInTheme extends DesugaringStep<LayoutConfigJson> | |||
|             context | ||||
|                 .enter("layers") | ||||
|                 .err( | ||||
|                     "No layers are defined. You must define at least one layer to have a valid theme" | ||||
|                     "No layers are defined. You must define at least one layer to have a valid theme", | ||||
|                 ) | ||||
|             return json | ||||
|         } | ||||
|  | @ -507,10 +531,10 @@ class WarnForUnsubstitutedLayersInTheme extends DesugaringStep<LayoutConfigJson> | |||
| 
 | ||||
|             context.warn( | ||||
|                 "The theme " + | ||||
|                     json.id + | ||||
|                     " has an inline layer: " + | ||||
|                     layer["id"] + | ||||
|                     ". This is discouraged." | ||||
|                 json.id + | ||||
|                 " has an inline layer: " + | ||||
|                 layer["id"] + | ||||
|                 ". This is discouraged.", | ||||
|             ) | ||||
|         } | ||||
|         return json | ||||
|  | @ -519,11 +543,12 @@ class WarnForUnsubstitutedLayersInTheme extends DesugaringStep<LayoutConfigJson> | |||
| 
 | ||||
| export class PrepareTheme extends Fuse<LayoutConfigJson> { | ||||
|     private state: DesugaringContext | ||||
| 
 | ||||
|     constructor( | ||||
|         state: DesugaringContext, | ||||
|         options?: { | ||||
|             skipDefaultLayers: false | boolean | ||||
|         } | ||||
|         }, | ||||
|     ) { | ||||
|         super( | ||||
|             "Fully prepares and expands a theme", | ||||
|  | @ -536,6 +561,7 @@ export class PrepareTheme extends Fuse<LayoutConfigJson> { | |||
|             // We expand all tagrenderings first...
 | ||||
|             new On("layers", new Each(new PrepareLayer(state))), | ||||
|             // Then we apply the override all. We must first expand everything in case that we override something in an expanded tag
 | ||||
|             // Note that it'll cheat with tagRenderings+
 | ||||
|             new ApplyOverrideAll(), | ||||
|             // And then we prepare all the layers _again_ in case that an override all contained unexpanded tagrenderings!
 | ||||
|             new On("layers", new Each(new PrepareLayer(state))), | ||||
|  | @ -543,7 +569,7 @@ export class PrepareTheme extends Fuse<LayoutConfigJson> { | |||
|                 ? new Pass("AddDefaultLayers is disabled due to the set flag") | ||||
|                 : new AddDefaultLayers(state), | ||||
|             new AddDependencyLayersToTheme(state), | ||||
|             new AddImportLayers() | ||||
|             new AddImportLayers(), | ||||
|         ) | ||||
|         this.state = state | ||||
|     } | ||||
|  | @ -558,13 +584,13 @@ export class PrepareTheme extends Fuse<LayoutConfigJson> { | |||
|         const needsNodeDatabase = result.layers?.some((l: LayerConfigJson) => | ||||
|             l.tagRenderings?.some((tr) => | ||||
|                 ValidationUtils.getSpecialVisualisations(<any>tr)?.some( | ||||
|                     (special) => special.needsNodeDatabase | ||||
|                 ) | ||||
|             ) | ||||
|                     (special) => special.needsNodeDatabase, | ||||
|                 ), | ||||
|             ), | ||||
|         ) | ||||
|         if (needsNodeDatabase) { | ||||
|             context.info( | ||||
|                 "Setting 'enableNodeDatabase' as this theme uses a special visualisation which needs to keep track of _all_ nodes" | ||||
|                 "Setting 'enableNodeDatabase' as this theme uses a special visualisation which needs to keep track of _all_ nodes", | ||||
|             ) | ||||
|             result.enableNodeDatabase = true | ||||
|         } | ||||
|  |  | |||
|  | @ -24,6 +24,7 @@ import { ConversionContext } from "./ConversionContext" | |||
| import * as eli from "../../../assets/editor-layer-index.json" | ||||
| import { AvailableRasterLayers } from "../../RasterLayers" | ||||
| import Back from "../../../assets/svg/Back.svelte" | ||||
| import PointRenderingConfigJson from "../Json/PointRenderingConfigJson" | ||||
| 
 | ||||
| class ValidateLanguageCompleteness extends DesugaringStep<LayoutConfig> { | ||||
|     private readonly _languages: string[] | ||||
|  | @ -177,6 +178,9 @@ export class ValidateTheme extends DesugaringStep<LayoutConfigJson> { | |||
|         if (!json.title) { | ||||
|             context.enter("title").err(`The theme ${json.id} does not have a title defined.`) | ||||
|         } | ||||
|         if(!json.icon){ | ||||
|             context.enter("icon").err("A theme should have an icon") | ||||
|         } | ||||
|         if (this._isBuiltin && this._extractImages !== undefined) { | ||||
|             // Check images: are they local, are the licenses there, is the theme icon square, ...
 | ||||
|             const images = this._extractImages.convert(json, context.inOperation("ValidateTheme")) | ||||
|  | @ -243,7 +247,8 @@ export class ValidateTheme extends DesugaringStep<LayoutConfigJson> { | |||
|                 new ValidateLanguageCompleteness("en").convert(theme, context) | ||||
|             } | ||||
|         } catch (e) { | ||||
|             context.err(e) | ||||
|             console.error(e) | ||||
|             context.err("Could not validate the theme due to: " + e) | ||||
|         } | ||||
| 
 | ||||
|         if (theme.id !== "personal") { | ||||
|  | @ -411,7 +416,7 @@ export class DetectConflictingAddExtraTags extends DesugaringStep<TagRenderingCo | |||
| 
 | ||||
|             return json | ||||
|         } catch (e) { | ||||
|             context.err(e) | ||||
|             context.err("Could not check for conflicting extra tags due to: " + e) | ||||
|             return undefined | ||||
|         } | ||||
|     } | ||||
|  | @ -1016,6 +1021,7 @@ export class PrevalidateLayer extends DesugaringStep<LayerConfigJson> { | |||
|      */ | ||||
|     private readonly _path: string | ||||
|     private readonly _studioValidations: boolean | ||||
|     private readonly _validatePointRendering = new ValidatePointRendering() | ||||
| 
 | ||||
|     constructor(path: string, isBuiltin, doesImageExist, studioValidations) { | ||||
|         super("Runs various checks against common mistakes for a layer", [], "PrevalidateLayer") | ||||
|  | @ -1105,6 +1111,8 @@ export class PrevalidateLayer extends DesugaringStep<LayerConfigJson> { | |||
|             context.enter("pointRendering").err("There are no pointRenderings at all...") | ||||
|         } | ||||
| 
 | ||||
|         json.pointRendering?.forEach((pr,i) => this._validatePointRendering.convert(pr, context.enters("pointeRendering", i))) | ||||
| 
 | ||||
|         if (json["mapRendering"]) { | ||||
|             context.enter("mapRendering").err("This layer has a legacy 'mapRendering'") | ||||
|         } | ||||
|  | @ -1409,13 +1417,40 @@ export class ValidateLayerConfig extends DesugaringStep<LayerConfigJson> { | |||
|     } | ||||
| } | ||||
| 
 | ||||
| class ValidatePointRendering extends DesugaringStep<PointRenderingConfigJson> { | ||||
|     constructor() { | ||||
|         super("Various checks for pointRenderings", [], "ValidatePOintRendering") | ||||
|     } | ||||
| 
 | ||||
|     convert(json: PointRenderingConfigJson, context: ConversionContext): PointRenderingConfigJson { | ||||
|         if (json.marker === undefined && json.label === undefined) { | ||||
|             context.err(`A point rendering should define at least an marker or a label`) | ||||
|         } | ||||
| 
 | ||||
|         if (json["markers"]) { | ||||
|             context.enter("markers").err(`Detected a field 'markerS' in pointRendering. It is written as a singular case`) | ||||
|         } | ||||
|         if (json.marker && !Array.isArray(json.marker)) { | ||||
|             context.enter("marker").err( | ||||
|                 "The marker in a pointRendering should be an array" | ||||
|             ) | ||||
|         } | ||||
|         if (json.location.length == 0) { | ||||
|             context.enter("location").err ( | ||||
|                 "A pointRendering should have at least one 'location' to defined where it should be rendered. " | ||||
|             ) | ||||
|         } | ||||
|         return json | ||||
| 
 | ||||
| 
 | ||||
|     } | ||||
| } | ||||
| export class ValidateLayer extends Conversion< | ||||
|     LayerConfigJson, | ||||
|     { parsed: LayerConfig; raw: LayerConfigJson } | ||||
| > { | ||||
|     private readonly _skipDefaultLayers: boolean | ||||
|     private readonly _prevalidation: PrevalidateLayer | ||||
| 
 | ||||
|     constructor( | ||||
|         path: string, | ||||
|         isBuiltin: boolean, | ||||
|  |  | |||
|  | @ -506,7 +506,7 @@ export interface LayerConfigJson { | |||
|      * If the way is part of a relation, MapComplete will attempt to update this relation as well | ||||
|      * question: Should the contributor be able to split ways using this layer? | ||||
|      * iftrue: enable the 'split-roads'-component | ||||
|      * iffalse: don't enable the split-roads componenet | ||||
|      * iffalse: don't enable the split-roads component | ||||
|      * ifunset: don't enable the split-roads component | ||||
|      * group: editing | ||||
|      */ | ||||
|  |  | |||
|  | @ -5,7 +5,7 @@ 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})) | ||||
|      * suggestions: return Constants.defaultPinIcons.map(i => ({if: "value="+i, then: i, icon: i})) | ||||
|      */ | ||||
|     icon: string | MinimalTagRenderingConfigJson | { builtin: string; override: any } | ||||
|     /** | ||||
|  |  | |||
|  | @ -106,8 +106,12 @@ export interface MappingConfigJson { | |||
|     hideInAnswer?: boolean | TagConfigJson | ||||
| 
 | ||||
|     /** | ||||
|      * question: In what other cases should this item be rendered? | ||||
|      * | ||||
|      * Also show this 'then'-option if the feature matches these tags. | ||||
|      * Ideal for outdated tags. | ||||
|      * Ideal for outdated tags or default assumptions. The tags from this options will <b>not</b> be set if the option is chosen! | ||||
|      * | ||||
|      * ifunset: No other cases when this text is shown | ||||
|      */ | ||||
|     alsoShowIf?: TagConfigJson | ||||
| 
 | ||||
|  |  | |||
|  | @ -79,23 +79,7 @@ export default class PointRenderingConfig extends WithContextLoader { | |||
|             } | ||||
|         }) | ||||
| 
 | ||||
|         if (json.marker === undefined && json.label === undefined) { | ||||
|             throw `At ${context}: A point rendering should define at least an marker or a label` | ||||
|         } | ||||
| 
 | ||||
|         if (json["markers"]) { | ||||
|             throw `At ${context}.markers: detected a field 'markerS' in pointRendering. It is written as a singular case` | ||||
|         } | ||||
|         if (json.marker && !Array.isArray(json.marker)) { | ||||
|             throw `At ${context}.marker: the marker in a pointRendering should be an array` | ||||
|         } | ||||
|         if (this.location.size == 0) { | ||||
|             throw ( | ||||
|                 "A pointRendering should have at least one 'location' to defined where it should be rendered. (At " + | ||||
|                 context + | ||||
|                 ".location)" | ||||
|             ) | ||||
|         } | ||||
|         this.marker = (json.marker ?? []).map((m) => new IconConfig(<any>m)) | ||||
|         if (json.css !== undefined) { | ||||
|             this.cssDef = this.tr("css", undefined) | ||||
|  |  | |||
|  | @ -15,7 +15,6 @@ import { | |||
|     QuestionableTagRenderingConfigJson, | ||||
| } from "./Json/QuestionableTagRenderingConfigJson" | ||||
| import { FixedUiElement } from "../../UI/Base/FixedUiElement" | ||||
| import { Paragraph } from "../../UI/Base/Paragraph" | ||||
| import Validators, { ValidatorType } from "../../UI/InputElement/Validators" | ||||
| import { TagRenderingConfigJson } from "./Json/TagRenderingConfigJson" | ||||
| import Constants from "../Constants" | ||||
|  | @ -371,20 +370,9 @@ export default class TagRenderingConfig { | |||
|         let iconClass = commonSize | ||||
|         if (!!mapping.icon) { | ||||
|             if (typeof mapping.icon === "string" && mapping.icon !== "") { | ||||
|                 let stripped = mapping.icon | ||||
|                 if (stripped.endsWith(".svg")) { | ||||
|                     stripped = stripped.substring(0, stripped.length - 4) | ||||
|                 } | ||||
|                 if (Constants.defaultPinIcons.indexOf(stripped) >= 0) { | ||||
|                     icon = "./assets/svg/" + mapping.icon | ||||
|                     if (!icon.endsWith(".svg")) { | ||||
|                         icon += ".svg" | ||||
|                     } | ||||
|                 } else { | ||||
|                     icon = mapping.icon | ||||
|                 } | ||||
|                 icon = mapping.icon.trim() | ||||
|             } else if (mapping.icon["path"]) { | ||||
|                 icon = mapping.icon["path"] | ||||
|                 icon = mapping.icon["path"].trim() | ||||
|                 iconClass = mapping.icon["class"] ?? iconClass | ||||
|             } | ||||
|         } | ||||
|  | @ -754,12 +742,10 @@ export default class TagRenderingConfig { | |||
|             withRender = [ | ||||
|                 `This rendering asks information about the property `, | ||||
|                 Link.OsmWiki(this.freeform.key), | ||||
|                 new Paragraph( | ||||
|                     new Combine([ | ||||
|                         "This is rendered with ", | ||||
|                         new FixedUiElement(this.render.txt).SetClass("code font-bold"), | ||||
|                     ]) | ||||
|                 ), | ||||
|                 new Combine([ | ||||
|                     "This is rendered with ", | ||||
|                     new FixedUiElement(this.render.txt).SetClass("code font-bold"), | ||||
|                 ]), | ||||
|             ] | ||||
|         } | ||||
| 
 | ||||
|  |  | |||
|  | @ -61,6 +61,7 @@ import NearbyFeatureSource from "../Logic/FeatureSource/Sources/NearbyFeatureSou | |||
| import FavouritesFeatureSource from "../Logic/FeatureSource/Sources/FavouritesFeatureSource" | ||||
| import { ProvidedImage } from "../Logic/ImageProviders/ImageProvider" | ||||
| import { GeolocationControlState } from "../UI/BigComponents/GeolocationControl" | ||||
| import Zoomcontrol from "../UI/Zoomcontrol" | ||||
| 
 | ||||
| /** | ||||
|  * | ||||
|  | @ -481,6 +482,12 @@ export default class ThemeViewState implements SpecialVisualizationState { | |||
|             this.lastClickObject.features.setData([]) | ||||
|         }) | ||||
| 
 | ||||
|         this.selectedElement.addCallback((selected) => { | ||||
|             if (selected === undefined) { | ||||
|                 Zoomcontrol.resetzoom() | ||||
|             } | ||||
|         }) | ||||
| 
 | ||||
|         if (this.layout.customCss !== undefined && window.location.pathname.indexOf("theme") >= 0) { | ||||
|             Utils.LoadCustomCss(this.layout.customCss) | ||||
|         } | ||||
|  | @ -524,7 +531,10 @@ export default class ThemeViewState implements SpecialVisualizationState { | |||
|             } | ||||
|             this.selectedElement.setData(undefined) | ||||
|             this.guistate.closeAll() | ||||
|             this.focusOnMap() | ||||
|             if (!this.guistate.isSomethingOpen()) { | ||||
|                 Zoomcontrol.resetzoom() | ||||
|                 this.focusOnMap() | ||||
|             } | ||||
|         }) | ||||
| 
 | ||||
|         Hotkeys.RegisterHotkey({ nomod: "f" }, docs.selectFavourites, () => { | ||||
|  |  | |||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue