diff --git a/Customizations/JSON/LayerConfig.ts b/Customizations/JSON/LayerConfig.ts index 0aa1fc811..8352331f8 100644 --- a/Customizations/JSON/LayerConfig.ts +++ b/Customizations/JSON/LayerConfig.ts @@ -34,7 +34,8 @@ export default class LayerConfig { passAllFeatures: boolean; isShown: TagRenderingConfig; minzoom: number; - maxzoom: number; + minzoomVisible: number; + maxzoom:number; title?: TagRenderingConfig; titleIcons: TagRenderingConfig[]; icon: TagRenderingConfig; @@ -143,7 +144,7 @@ export default class LayerConfig { this.doNotDownload = json.doNotDownload ?? false; this.passAllFeatures = json.passAllFeatures ?? false; this.minzoom = json.minzoom ?? 0; - this.maxzoom = json.maxzoom ?? 1000; + this.minzoomVisible = json.minzoomVisible ?? this.minzoom; this.wayHandling = json.wayHandling ?? 0; this.presets = (json.presets ?? []).map((pr, i) => { if (pr.preciseInput === true) { diff --git a/Customizations/JSON/LayerConfigJson.ts b/Customizations/JSON/LayerConfigJson.ts index b2dd13c74..b74181c2c 100644 --- a/Customizations/JSON/LayerConfigJson.ts +++ b/Customizations/JSON/LayerConfigJson.ts @@ -92,16 +92,16 @@ export interface LayerConfigJson { /** - * The zoomlevel at which point the data is shown and loaded. + * The minimum needed zoomlevel required before loading of the data start * Default: 0 */ minzoom?: number; /** - * The zoomlevel at which point the data is hidden again - * Default: 100 (thus: always visible + * If zoomed out below this zoomlevel, the data will be hidden. + * Default: minzoom */ - maxzoom?: number; + minzoomVisible?: number; /** * The title shown in a popup for elements of this layer. diff --git a/InitUiElements.ts b/InitUiElements.ts index 518e23d66..6e068aca1 100644 --- a/InitUiElements.ts +++ b/InitUiElements.ts @@ -1,19 +1,19 @@ -import { CenterFlexedElement } from "./UI/Base/CenterFlexedElement"; -import { FixedUiElement } from "./UI/Base/FixedUiElement"; +import {CenterFlexedElement} from "./UI/Base/CenterFlexedElement"; +import {FixedUiElement} from "./UI/Base/FixedUiElement"; import Toggle from "./UI/Input/Toggle"; -import { Basemap } from "./UI/BigComponents/Basemap"; +import {Basemap} from "./UI/BigComponents/Basemap"; import State from "./State"; import LoadFromOverpass from "./Logic/Actors/OverpassFeatureSource"; -import { UIEventSource } from "./Logic/UIEventSource"; -import { QueryParameters } from "./Logic/Web/QueryParameters"; +import {UIEventSource} from "./Logic/UIEventSource"; +import {QueryParameters} from "./Logic/Web/QueryParameters"; import StrayClickHandler from "./Logic/Actors/StrayClickHandler"; import SimpleAddUI from "./UI/BigComponents/SimpleAddUI"; import CenterMessageBox from "./UI/CenterMessageBox"; import UserBadge from "./UI/BigComponents/UserBadge"; import SearchAndGo from "./UI/BigComponents/SearchAndGo"; import GeoLocationHandler from "./Logic/Actors/GeoLocationHandler"; -import { LocalStorageSource } from "./Logic/Web/LocalStorageSource"; -import { Utils } from "./Utils"; +import {LocalStorageSource} from "./Logic/Web/LocalStorageSource"; +import {Utils} from "./Utils"; import Svg from "./Svg"; import Link from "./UI/Base/Link"; import * as personal from "./assets/themes/personalLayout/personalLayout.json"; @@ -34,17 +34,16 @@ import MapControlButton from "./UI/MapControlButton"; import Combine from "./UI/Base/Combine"; import SelectedFeatureHandler from "./Logic/Actors/SelectedFeatureHandler"; import LZString from "lz-string"; -import { LayoutConfigJson } from "./Customizations/JSON/LayoutConfigJson"; +import {LayoutConfigJson} from "./Customizations/JSON/LayoutConfigJson"; import AttributionPanel from "./UI/BigComponents/AttributionPanel"; import ContributorCount from "./Logic/ContributorCount"; import FeatureSource from "./Logic/FeatureSource/FeatureSource"; import AllKnownLayers from "./Customizations/AllKnownLayers"; import LayerConfig from "./Customizations/JSON/LayerConfig"; import AvailableBaseLayers from "./Logic/Actors/AvailableBaseLayers"; -import { SimpleMapScreenshoter } from "leaflet-simple-map-screenshoter"; +import {SimpleMapScreenshoter} from "leaflet-simple-map-screenshoter"; import jsPDF from "jspdf"; -import FilterView from "./UI/BigComponents/FilterView"; -import { TagsFilter } from "./Logic/Tags/TagsFilter"; +import {TagsFilter} from "./Logic/Tags/TagsFilter"; export class InitUiElements { static InitAll( diff --git a/Logic/FeatureSource/FeatureDuplicatorPerLayer.ts b/Logic/FeatureSource/FeatureDuplicatorPerLayer.ts index 0771314af..72c370bce 100644 --- a/Logic/FeatureSource/FeatureDuplicatorPerLayer.ts +++ b/Logic/FeatureSource/FeatureDuplicatorPerLayer.ts @@ -1,6 +1,6 @@ import FeatureSource from "./FeatureSource"; import {UIEventSource} from "../UIEventSource"; -import LayerConfig from "../../Customizations/JSON/LayerConfig"; +import FilteredLayer from "../../Models/FilteredLayer"; /** @@ -13,7 +13,7 @@ export default class FeatureDuplicatorPerLayer implements FeatureSource { public readonly name; - constructor(layers: UIEventSource<{ layerDef: LayerConfig }[]>, upstream: FeatureSource) { + constructor(layers: UIEventSource, upstream: FeatureSource) { this.name = "FeatureDuplicator of "+upstream.name; this.features = upstream.features.map(features => { const newFeatures: { feature: any, freshness: Date }[] = []; diff --git a/Logic/FeatureSource/FeaturePipeline.ts b/Logic/FeatureSource/FeaturePipeline.ts index 351ccdc3e..c63fe89a3 100644 --- a/Logic/FeatureSource/FeaturePipeline.ts +++ b/Logic/FeatureSource/FeaturePipeline.ts @@ -13,6 +13,7 @@ import Loc from "../../Models/Loc"; import GeoJsonSource from "./GeoJsonSource"; import MetaTaggingFeatureSource from "./MetaTaggingFeatureSource"; import RegisteringFeatureSource from "./RegisteringFeatureSource"; +import FilteredLayer from "../../Models/FilteredLayer"; export default class FeaturePipeline implements FeatureSource { @@ -20,7 +21,7 @@ export default class FeaturePipeline implements FeatureSource { public readonly name = "FeaturePipeline" - constructor(flayers: UIEventSource<{ isDisplayed: UIEventSource, layerDef: LayerConfig }[]>, + constructor(flayers: UIEventSource, updater: FeatureSource, fromOsmApi: FeatureSource, layout: UIEventSource, diff --git a/Logic/FeatureSource/FilteringFeatureSource.ts b/Logic/FeatureSource/FilteringFeatureSource.ts index 61e781113..899cc1d5f 100644 --- a/Logic/FeatureSource/FilteringFeatureSource.ts +++ b/Logic/FeatureSource/FilteringFeatureSource.ts @@ -1,173 +1,169 @@ import FeatureSource from "./FeatureSource"; -import { UIEventSource } from "../UIEventSource"; +import {UIEventSource} from "../UIEventSource"; import LayerConfig from "../../Customizations/JSON/LayerConfig"; import Loc from "../../Models/Loc"; import Hash from "../Web/Hash"; -import { TagsFilter } from "../Tags/TagsFilter"; +import {TagsFilter} from "../Tags/TagsFilter"; export default class FilteringFeatureSource implements FeatureSource { - public features: UIEventSource<{ feature: any; freshness: Date }[]> = - new UIEventSource<{ feature: any; freshness: Date }[]>([]); - public readonly name = "FilteringFeatureSource"; + public features: UIEventSource<{ feature: any; freshness: Date }[]> = + new UIEventSource<{ feature: any; freshness: Date }[]>([]); + public readonly name = "FilteringFeatureSource"; - constructor( - layers: UIEventSource< - { - isDisplayed: UIEventSource; - layerDef: LayerConfig; - appliedFilters: UIEventSource; - }[] - >, - location: UIEventSource, - selectedElement: UIEventSource, - upstream: FeatureSource - ) { - const self = this; - - function update() { - const layerDict = {}; - if (layers.data.length == 0) { - console.warn("No layers defined!"); - return; - } - for (const layer of layers.data) { - layerDict[layer.layerDef.id] = layer; - } - - const features: { feature: any; freshness: Date }[] = - upstream.features.data; - - const missingLayers = new Set(); - - const newFeatures = features.filter((f) => { - const layerId = f.feature._matching_layer_id; - - if ( - selectedElement.data?.id === f.feature.id || - f.feature.id === Hash.hash.data - ) { - // This is the selected object - it gets a free pass even if zoom is not sufficient - return true; - } - - if (layerId !== undefined) { - const layer: { + constructor( + layers: UIEventSource<{ isDisplayed: UIEventSource; layerDef: LayerConfig; appliedFilters: UIEventSource; - } = layerDict[layerId]; - if (layer === undefined) { - missingLayers.add(layerId); - return true; - } + }[]>, + location: UIEventSource, + selectedElement: UIEventSource, + upstream: FeatureSource + ) { + const self = this; - const isShown = layer.layerDef.isShown; - const tags = f.feature.properties; - if (isShown.IsKnown(tags)) { - const result = layer.layerDef.isShown.GetRenderValue( - f.feature.properties - ).txt; - if (result !== "yes") { - return false; + function update() { + const layerDict = {}; + if (layers.data.length == 0) { + console.warn("No layers defined!"); + return; } - } - - if (FilteringFeatureSource.showLayer(layer, location)) { - const tagsFilter = layer.appliedFilters.data; - if (tagsFilter) { - const properties = f.feature.properties; - if (!tagsFilter.matchesProperties(properties)) { - return false; - } + for (const layer of layers.data) { + const prev = layerDict[layer.layerDef.id] + if (prev !== undefined) { + // We have seen this layer before! + // We prefer the one which has a name + if (layer.layerDef.name === undefined) { + // This one is hidden, so we skip it + console.log("Ignoring layer selection from ", layer) + continue; + } + } + layerDict[layer.layerDef.id] = layer; } - return true; - } + const features: { feature: any; freshness: Date }[] = + upstream.features.data; + + const missingLayers = new Set(); + + const newFeatures = features.filter((f) => { + const layerId = f.feature._matching_layer_id; + + if ( + selectedElement.data?.id === f.feature.id || + f.feature.id === Hash.hash.data) { + // This is the selected object - it gets a free pass even if zoom is not sufficient or it is filtered away + return true; + } + + if (layerId === undefined) { + return false; + } + const layer: { + isDisplayed: UIEventSource; + layerDef: LayerConfig; + appliedFilters: UIEventSource; + } = layerDict[layerId]; + if (layer === undefined) { + missingLayers.add(layerId); + return false; + } + + const isShown = layer.layerDef.isShown; + const tags = f.feature.properties; + if (isShown.IsKnown(tags)) { + const result = layer.layerDef.isShown.GetRenderValue( + f.feature.properties + ).txt; + if (result !== "yes") { + return false; + } + } + + const tagsFilter = layer.appliedFilters.data; + if (tagsFilter) { + if (!tagsFilter.matchesProperties(f.feature.properties)) { + // Hidden by the filter on the layer itself - we want to hide it no matter wat + return false; + } + } + if (!FilteringFeatureSource.showLayer(layer, location)) { + // The layer itself is either disabled or hidden due to zoom constraints + // We should return true, but it might still match some other layer + return false; + } + + + + return true; + }); + console.log( + "Filtering layer source: input: ", + upstream.features.data?.length, + "output:", + newFeatures.length + ); + self.features.setData(newFeatures); + if (missingLayers.size > 0) { + console.error( + "Some layers were not found: ", + Array.from(missingLayers) + ); + } } - // Does it match any other layer - e.g. because of a switch? - for (const toCheck of layers.data) { - if (!FilteringFeatureSource.showLayer(toCheck, location)) { - continue; - } - if ( - toCheck.layerDef.source.osmTags.matchesProperties( - f.feature.properties - ) - ) { - return true; - } - } - return false; - }); - console.log( - "Filtering layer source: input: ", - upstream.features.data?.length, - "output:", - newFeatures.length - ); - self.features.setData(newFeatures); - if (missingLayers.size > 0) { - console.error( - "Some layers were not found: ", - Array.from(missingLayers) - ); - } + + upstream.features.addCallback(() => { + update(); + }); + location + .map((l) => { + // We want something that is stable for the shown layers + const displayedLayerIndexes = []; + for (let i = 0; i < layers.data.length; i++) { + const layer = layers.data[i]; + if (l.zoom < layer.layerDef.minzoom) { + continue; + } + + if (!layer.isDisplayed.data) { + continue; + } + displayedLayerIndexes.push(i); + } + return displayedLayerIndexes.join(","); + }) + .addCallback(() => { + update(); + }); + + layers.addCallback(update); + + const registered = new Set>(); + layers.addCallbackAndRun((layers) => { + for (const layer of layers) { + if (registered.has(layer.isDisplayed)) { + continue; + } + registered.add(layer.isDisplayed); + layer.isDisplayed.addCallback(() => update()); + layer.appliedFilters.addCallback(() => update()); + } + }); + + update(); } - upstream.features.addCallback(() => { - update(); - }); - location - .map((l) => { - // We want something that is stable for the shown layers - const displayedLayerIndexes = []; - for (let i = 0; i < layers.data.length; i++) { - const layer = layers.data[i]; - if (l.zoom < layer.layerDef.minzoom) { - continue; - } - if (l.zoom > layer.layerDef.maxzoom) { - continue; - } - if (!layer.isDisplayed.data) { - continue; - } - displayedLayerIndexes.push(i); - } - return displayedLayerIndexes.join(","); - }) - .addCallback(() => { - update(); - }); - - layers.addCallback(update); - - const registered = new Set>(); - layers.addCallbackAndRun((layers) => { - for (const layer of layers) { - if (registered.has(layer.isDisplayed)) { - continue; - } - registered.add(layer.isDisplayed); - layer.isDisplayed.addCallback(() => update()); - layer.appliedFilters.addCallback(() => update()); - } - }); - - update(); - } - - private static showLayer( - layer: { - isDisplayed: UIEventSource; - layerDef: LayerConfig; - }, - location: UIEventSource - ) { - return ( - layer.isDisplayed.data && - layer.layerDef.minzoom <= location.data.zoom && - layer.layerDef.maxzoom >= location.data.zoom - ); - } + private static showLayer( + layer: { + isDisplayed: UIEventSource; + layerDef: LayerConfig; + }, + location: UIEventSource + ) { + return ( + layer.isDisplayed.data && + layer.layerDef.minzoomVisible <= location.data.zoom + ); + } } diff --git a/Logic/FeatureSource/GeoJsonSource.ts b/Logic/FeatureSource/GeoJsonSource.ts index 3da47bd04..c2a9db3d6 100644 --- a/Logic/FeatureSource/GeoJsonSource.ts +++ b/Logic/FeatureSource/GeoJsonSource.ts @@ -50,7 +50,7 @@ export default class GeoJsonSource implements FeatureSource { * @param locationControl * @constructor */ - public static ConstructMultiSource(flayers: { isDisplayed: UIEventSource, layerDef: LayerConfig }[], locationControl: UIEventSource): FeatureSource[] { + public static ConstructMultiSource(flayers: { isDisplayed: UIEventSource, layerDef: LayerConfig }[], locationControl: UIEventSource): GeoJsonSource[] { const flayersPerSource = new Map, layerDef: LayerConfig }[]>(); for (const flayer of flayers) { @@ -65,7 +65,7 @@ export default class GeoJsonSource implements FeatureSource { flayersPerSource.get(url).push(flayer) } - const sources: FeatureSource[] = [] + const sources: GeoJsonSource[] = [] flayersPerSource.forEach((flayers, key) => { if (flayers.length == 1) { @@ -118,8 +118,7 @@ export default class GeoJsonSource implements FeatureSource { return undefined; } - if (location.zoom < flayer.layerDef.minzoom || - location.zoom > flayer.layerDef.maxzoom) { + if (location.zoom < flayer.layerDef.minzoom) { // No need to download! - the layer is disabled return undefined; } diff --git a/Logic/FeatureSource/ZoomRespectingFeatureSource.ts b/Logic/FeatureSource/ZoomRespectingFeatureSource.ts deleted file mode 100644 index 09a757b55..000000000 --- a/Logic/FeatureSource/ZoomRespectingFeatureSource.ts +++ /dev/null @@ -1,25 +0,0 @@ -import FeatureSource from "./FeatureSource"; -import {UIEventSource} from "../UIEventSource"; -import LayerConfig from "../../Customizations/JSON/LayerConfig"; - -export default class ZoomRespectingFeatureSource implements FeatureSource{ - public readonly features: UIEventSource<{ feature: any; freshness: Date }[]>; - public readonly name: string; - - constructor(layerConfig: LayerConfig, location: UIEventSource<{zoom: number}>, upstream: FeatureSource) { - this.name = "zoomrespecting("+upstream.name+")" - const empty = [] - this.features = upstream.features.map( - features => { - const z = location.data.zoom - - if(layerConfig.minzoom < z || layerConfig.maxzoom > z){ - return empty - } - - - return features - },[location] - ) - } -} \ No newline at end of file diff --git a/Models/FilteredLayer.ts b/Models/FilteredLayer.ts new file mode 100644 index 000000000..9bd72ae8d --- /dev/null +++ b/Models/FilteredLayer.ts @@ -0,0 +1,9 @@ +import {UIEventSource} from "../Logic/UIEventSource"; +import {TagsFilter} from "../Logic/Tags/TagsFilter"; +import LayerConfig from "../Customizations/JSON/LayerConfig"; + +export default interface FilteredLayer { + readonly isDisplayed: UIEventSource; + readonly appliedFilters: UIEventSource; + readonly layerDef: LayerConfig; +} \ No newline at end of file diff --git a/State.ts b/State.ts index 1a51bebb8..c3dcca45f 100644 --- a/State.ts +++ b/State.ts @@ -21,6 +21,7 @@ import {Relation} from "./Logic/Osm/ExtractRelations"; import OsmApiFeatureSource from "./Logic/FeatureSource/OsmApiFeatureSource"; import FeaturePipeline from "./Logic/FeatureSource/FeaturePipeline"; import { TagsFilter } from "./Logic/Tags/TagsFilter"; +import FilteredLayer from "./Models/FilteredLayer"; /** * Contains the global state: a bunch of UI-event sources @@ -61,15 +62,7 @@ export default class State { public osmApiFeatureSource: OsmApiFeatureSource; - public filteredLayers: UIEventSource<{ - readonly isDisplayed: UIEventSource; - readonly appliedFilters: UIEventSource; - readonly layerDef: LayerConfig; - }[]> = new UIEventSource<{ - readonly isDisplayed: UIEventSource; - readonly appliedFilters: UIEventSource; - readonly layerDef: LayerConfig; - }[]>([]); + public filteredLayers: UIEventSource = new UIEventSource([]); /** The latest element that was selected diff --git a/UI/Base/VariableUIElement.ts b/UI/Base/VariableUIElement.ts index abab84bba..600f1517e 100644 --- a/UI/Base/VariableUIElement.ts +++ b/UI/Base/VariableUIElement.ts @@ -23,7 +23,7 @@ export class VariableUiElement extends BaseUIElement { el.innerHTML = contents; } else if (contents instanceof Array) { for (const content of contents) { - const c = content.ConstructElement(); + const c = content?.ConstructElement(); if (c !== undefined && c !== null) { el.appendChild(c); } diff --git a/UI/BigComponents/FilterView.ts b/UI/BigComponents/FilterView.ts index a89533bc8..1f1ac2251 100644 --- a/UI/BigComponents/FilterView.ts +++ b/UI/BigComponents/FilterView.ts @@ -13,6 +13,9 @@ import {TagsFilter} from "../../Logic/Tags/TagsFilter"; import {And} from "../../Logic/Tags/And"; import {UIEventSource} from "../../Logic/UIEventSource"; import BaseUIElement from "../BaseUIElement"; +import State from "../../State"; +import {control} from "leaflet"; + /** * Shows the filter @@ -22,12 +25,16 @@ export default class FilterView extends VariableUiElement { constructor(filteredLayer) { super( filteredLayer.map((filteredLayers) => - filteredLayers.map(FilterView.createOneFilteredLayerElement) + filteredLayers.map(l => FilterView.createOneFilteredLayerElement(l)) ) ); } - static createOneFilteredLayerElement(filteredLayer) { + private static createOneFilteredLayerElement(filteredLayer) { + if(filteredLayer.layerDef.name === undefined){ + // Name is not defined: we hide this one + return undefined; + } const iconStyle = "width:1.5rem;height:1.5rem;margin-left:1.25rem"; const icon = new Combine([Svg.checkbox_filled]).SetStyle(iconStyle); @@ -52,9 +59,19 @@ export default class FilterView extends VariableUiElement { .Clone() .SetStyle("font-size:large;padding-left:1.25rem"); + const zoomStatus = + new Toggle( + undefined, + Translations.t.general.layerSelection.zoomInToSeeThisLayer.Clone() + .SetClass("alert") + .SetStyle("display: block ruby;width:min-content;"), + State.state.locationControl.map(location =>location.zoom > filteredLayer.layerDef.minzoom ) + ) + + const style = "display:flex;align-items:center;color:#007759;padding:0.5rem 0;"; - const layerChecked = new Combine([icon, styledNameChecked]) + const layerChecked = new Combine([icon, styledNameChecked, zoomStatus]) .SetStyle(style) .onClick(() => filteredLayer.isDisplayed.setData(false)); @@ -62,25 +79,27 @@ export default class FilterView extends VariableUiElement { .SetStyle(style) .onClick(() => filteredLayer.isDisplayed.setData(true)); - + const filterPanel: BaseUIElement = FilterView.createFilterPanel(filteredLayer) - + + + return new Toggle( new Combine([layerChecked, filterPanel]), layerNotChecked, filteredLayer.isDisplayed ); } - + static createFilterPanel(flayer: { layerDef: LayerConfig, appliedFilters: UIEventSource - }): BaseUIElement{ + }): BaseUIElement { const layer = flayer.layerDef - if(layer.filters.length === 0){ + if (layer.filters.length === 0) { return undefined; } - + let listFilterElements: [BaseUIElement, UIEventSource][] = layer.filters.map( FilterView.createFilter ); diff --git a/UI/BigComponents/LayerControlPanel.ts b/UI/BigComponents/LayerControlPanel.ts index c87e97cee..395dcaacc 100644 --- a/UI/BigComponents/LayerControlPanel.ts +++ b/UI/BigComponents/LayerControlPanel.ts @@ -1,6 +1,5 @@ import State from "../../State"; import BackgroundSelector from "./BackgroundSelector"; -import LayerSelection from "./LayerSelection"; import Combine from "../Base/Combine"; import ScrollableFullScreen from "../Base/ScrollableFullScreen"; import Translations from "../i18n/Translations"; diff --git a/UI/BigComponents/LayerSelection.ts b/UI/BigComponents/LayerSelection.ts deleted file mode 100644 index 3c7f108e8..000000000 --- a/UI/BigComponents/LayerSelection.ts +++ /dev/null @@ -1,81 +0,0 @@ -import {UIEventSource} from "../../Logic/UIEventSource"; -import {VariableUiElement} from "../Base/VariableUIElement"; -import State from "../../State"; -import Toggle from "../Input/Toggle"; -import Combine from "../Base/Combine"; -import Translations from "../i18n/Translations"; -import LayerConfig from "../../Customizations/JSON/LayerConfig"; -import BaseUIElement from "../BaseUIElement"; -import {Translation} from "../i18n/Translation"; - -/** - * Shows the panel with all layers and a toggle for each of them - */ -export default class LayerSelection extends Combine { - - - constructor(activeLayers: UIEventSource<{ - readonly isDisplayed: UIEventSource, - readonly layerDef: LayerConfig; - }[]>) { - - if (activeLayers === undefined) { - throw "ActiveLayers should be defined..." - } - - - const checkboxes: BaseUIElement[] = []; - - for (const layer of activeLayers.data) { - const leafletStyle = layer.layerDef.GenerateLeafletStyle( - new UIEventSource({id: "node/-1"}), - false) - const leafletStyleNa = layer.layerDef.GenerateLeafletStyle( - new UIEventSource({id: "node/-1"}), - false) - const icon = new Combine([leafletStyle.icon.html]).SetClass("single-layer-selection-toggle") - let iconUnselected: BaseUIElement = new Combine([leafletStyleNa.icon.html]) - .SetClass("single-layer-selection-toggle") - .SetStyle("opacity:0.2;"); - - if (layer.layerDef.name === undefined) { - continue; - } - - const name: Translation = Translations.WT(layer.layerDef.name)?.Clone() - name.SetStyle("font-size:large;margin-left: 0.5em;"); - - const zoomStatus = new VariableUiElement(State.state.locationControl.map(location => { - if (location.zoom < layer.layerDef.minzoom) { - return Translations.t.general.layerSelection.zoomInToSeeThisLayer.Clone() - .SetClass("alert") - .SetStyle("display: block ruby;width:min-content;") - } - return "" - })) - const zoomStatusNonActive = new VariableUiElement(State.state.locationControl.map(location => { - if (location.zoom < layer.layerDef.minzoom) { - return Translations.t.general.layerSelection.zoomInToSeeThisLayer.Clone() - .SetClass("alert") - .SetStyle("display: block ruby;width:min-content;") - } - return "" - })) - - const style = "display:flex;align-items:center;" - const styleWhole = "display:flex; flex-wrap: wrap" - checkboxes.push(new Toggle( - new Combine([new Combine([icon, name.Clone()]).SetStyle(style), zoomStatus]) - .SetStyle(styleWhole), - new Combine([new Combine([iconUnselected, "", name.Clone(), ""]).SetStyle(style), zoomStatusNonActive]) - .SetStyle(styleWhole), - layer.isDisplayed).ToggleOnClick() - .SetStyle("margin:0.3em;") - ); - } - - super(checkboxes) - this.SetStyle("display:flex;flex-direction:column;") - - } -} \ No newline at end of file diff --git a/UI/Input/RadioButton.ts b/UI/Input/RadioButton.ts index eb48ab3e6..fa951c5ca 100644 --- a/UI/Input/RadioButton.ts +++ b/UI/Input/RadioButton.ts @@ -118,7 +118,7 @@ export class RadioButton extends InputElement { const label = document.createElement("label"); label.appendChild(labelHtml); label.htmlFor = input.id; - label.classList.add("block", "w-full", "cursor-pointer", "bg-red"); + label.classList.add("flex", "w-full", "cursor-pointer", "bg-red"); if (!this._dontStyle) { labelHtml.classList.add("p-2") diff --git a/UI/i18n/Translations.ts b/UI/i18n/Translations.ts index eb91b6cd8..d54414988 100644 --- a/UI/i18n/Translations.ts +++ b/UI/i18n/Translations.ts @@ -19,7 +19,7 @@ export default class Translations { static T(t: string | any, context = undefined): Translation { - if(t === undefined){ + if(t === undefined || t === null){ return undefined; } if(typeof t === "string"){ @@ -38,7 +38,7 @@ export default class Translations { private static wtcache = {} public static WT(s: string | Translation): Translation { - if(s === undefined){ + if(s === undefined || s === null){ return undefined; } if (typeof (s) === "string") { diff --git a/index.css b/index.css index 2b7e866d0..0bd790f55 100644 --- a/index.css +++ b/index.css @@ -48,7 +48,7 @@ :root { - --subtle-detail-color: #007759; + --subtle-detail-color: #e5f5ff; --subtle-detail-color-contrast: black; --subtle-detail-color-light-contrast: lightgrey;