forked from MapComplete/MapComplete
		
	Fix hiding and showing of features
This commit is contained in:
		
							parent
							
								
									d4f107c81a
								
							
						
					
					
						commit
						bc1863dcb6
					
				
					 12 changed files with 160 additions and 137 deletions
				
			
		| 
						 | 
					@ -1,6 +1,5 @@
 | 
				
			||||||
import {FixedUiElement} from "./UI/Base/FixedUiElement";
 | 
					import {FixedUiElement} from "./UI/Base/FixedUiElement";
 | 
				
			||||||
import CheckBox from "./UI/Input/CheckBox";
 | 
					import CheckBox from "./UI/Input/CheckBox";
 | 
				
			||||||
import Combine from "./UI/Base/Combine";
 | 
					 | 
				
			||||||
import {Basemap} from "./UI/BigComponents/Basemap";
 | 
					import {Basemap} from "./UI/BigComponents/Basemap";
 | 
				
			||||||
import State from "./State";
 | 
					import State from "./State";
 | 
				
			||||||
import LoadFromOverpass from "./Logic/Actors/UpdateFromOverpass";
 | 
					import LoadFromOverpass from "./Logic/Actors/UpdateFromOverpass";
 | 
				
			||||||
| 
						 | 
					@ -29,12 +28,12 @@ import LayerResetter from "./Logic/Actors/LayerResetter";
 | 
				
			||||||
import FullWelcomePaneWithTabs from "./UI/BigComponents/FullWelcomePaneWithTabs";
 | 
					import FullWelcomePaneWithTabs from "./UI/BigComponents/FullWelcomePaneWithTabs";
 | 
				
			||||||
import LayerControlPanel from "./UI/BigComponents/LayerControlPanel";
 | 
					import LayerControlPanel from "./UI/BigComponents/LayerControlPanel";
 | 
				
			||||||
import FeatureSwitched from "./UI/Base/FeatureSwitched";
 | 
					import FeatureSwitched from "./UI/Base/FeatureSwitched";
 | 
				
			||||||
import LayerConfig from "./Customizations/JSON/LayerConfig";
 | 
					 | 
				
			||||||
import ShowDataLayer from "./UI/ShowDataLayer";
 | 
					import ShowDataLayer from "./UI/ShowDataLayer";
 | 
				
			||||||
import Hash from "./Logic/Web/Hash";
 | 
					import Hash from "./Logic/Web/Hash";
 | 
				
			||||||
import FeaturePipeline from "./Logic/FeatureSource/FeaturePipeline";
 | 
					import FeaturePipeline from "./Logic/FeatureSource/FeaturePipeline";
 | 
				
			||||||
import HashHandler from "./Logic/Actors/SelectedFeatureHandler";
 | 
					 | 
				
			||||||
import SelectedFeatureHandler from "./Logic/Actors/SelectedFeatureHandler";
 | 
					import SelectedFeatureHandler from "./Logic/Actors/SelectedFeatureHandler";
 | 
				
			||||||
 | 
					import ScrollableFullScreen from "./UI/Base/ScrollableFullScreen";
 | 
				
			||||||
 | 
					import Translations from "./UI/i18n/Translations";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
export class InitUiElements {
 | 
					export class InitUiElements {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					@ -217,10 +216,10 @@ export class InitUiElements {
 | 
				
			||||||
        // ?-Button on Desktop, opens panel with close-X.
 | 
					        // ?-Button on Desktop, opens panel with close-X.
 | 
				
			||||||
        const help = Svg.help_svg().SetClass("open-welcome-button block");
 | 
					        const help = Svg.help_svg().SetClass("open-welcome-button block");
 | 
				
			||||||
        const checkbox = new CheckBox(
 | 
					        const checkbox = new CheckBox(
 | 
				
			||||||
                fullOptions
 | 
					            fullOptions
 | 
				
			||||||
                    .SetClass("welcomeMessage")
 | 
					                .SetClass("welcomeMessage")
 | 
				
			||||||
                    .onClick(() => {/*Catch the click*/
 | 
					                .onClick(() => {/*Catch the click*/
 | 
				
			||||||
                    }),
 | 
					                }),
 | 
				
			||||||
            help
 | 
					            help
 | 
				
			||||||
            , isOpened
 | 
					            , isOpened
 | 
				
			||||||
        ).AttachTo("messagesbox");
 | 
					        ).AttachTo("messagesbox");
 | 
				
			||||||
| 
						 | 
					@ -247,8 +246,8 @@ export class InitUiElements {
 | 
				
			||||||
            const layerControlPanel = new LayerControlPanel(
 | 
					            const layerControlPanel = new LayerControlPanel(
 | 
				
			||||||
                () => State.state.layerControlIsOpened.setData(false))
 | 
					                () => State.state.layerControlIsOpened.setData(false))
 | 
				
			||||||
                .SetClass("block p-1 rounded-full");
 | 
					                .SetClass("block p-1 rounded-full");
 | 
				
			||||||
              const checkbox = new CheckBox(
 | 
					            const checkbox = new CheckBox(
 | 
				
			||||||
                    layerControlPanel,
 | 
					                layerControlPanel,
 | 
				
			||||||
                Svg.layers_svg().SetClass("layer-selection-toggle"),
 | 
					                Svg.layers_svg().SetClass("layer-selection-toggle"),
 | 
				
			||||||
                State.state.layerControlIsOpened
 | 
					                State.state.layerControlIsOpened
 | 
				
			||||||
            ).AttachTo("layer-selection");
 | 
					            ).AttachTo("layer-selection");
 | 
				
			||||||
| 
						 | 
					@ -261,7 +260,7 @@ export class InitUiElements {
 | 
				
			||||||
                });
 | 
					                });
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            State.state.selectedElement.addCallbackAndRun(feature => {
 | 
					            State.state.selectedElement.addCallbackAndRun(feature => {
 | 
				
			||||||
                if(feature !== undefined){
 | 
					                if (feature !== undefined) {
 | 
				
			||||||
                    checkbox.isEnabled.setData(false);
 | 
					                    checkbox.isEnabled.setData(false);
 | 
				
			||||||
                }
 | 
					                }
 | 
				
			||||||
            })
 | 
					            })
 | 
				
			||||||
| 
						 | 
					@ -306,28 +305,27 @@ export class InitUiElements {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        const state = State.state;
 | 
					        const state = State.state;
 | 
				
			||||||
        const flayers: { layerDef: LayerConfig, isDisplayed: UIEventSource<boolean> }[] = []
 | 
					        state.filteredLayers = 
 | 
				
			||||||
        for (const layer of state.layoutToUse.data.layers) {
 | 
					            state.layoutToUse.map(layoutToUse => {
 | 
				
			||||||
 | 
					                const flayers = [];
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            if (typeof (layer) === "string") {
 | 
					                for (const layer of layoutToUse.layers) {
 | 
				
			||||||
                throw "Layer " + layer + " was not substituted";
 | 
					 | 
				
			||||||
            }
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
            const isDisplayed = QueryParameters.GetQueryParameter("layer-" + layer.id, "true", "Wether or not layer " + layer.id + " is shown")
 | 
					                    const isDisplayed = QueryParameters.GetQueryParameter("layer-" + layer.id, "true", "Wether or not layer " + layer.id + " is shown")
 | 
				
			||||||
                .map<boolean>((str) => str !== "false", [], (b) => b.toString());
 | 
					                        .map<boolean>((str) => str !== "false", [], (b) => b.toString());
 | 
				
			||||||
            const flayer = {
 | 
					                    const flayer = {
 | 
				
			||||||
                isDisplayed: isDisplayed,
 | 
					                        isDisplayed: isDisplayed,
 | 
				
			||||||
                layerDef: layer
 | 
					                        layerDef: layer
 | 
				
			||||||
            }
 | 
					                    }
 | 
				
			||||||
            flayers.push(flayer);
 | 
					                    flayers.push(flayer);
 | 
				
			||||||
        }
 | 
					                }
 | 
				
			||||||
 | 
					                return flayers;
 | 
				
			||||||
        State.state.filteredLayers.setData(flayers);
 | 
					            });
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        const updater = new LoadFromOverpass(state.locationControl, state.layoutToUse, state.leafletMap);
 | 
					        const updater = new LoadFromOverpass(state.locationControl, state.layoutToUse, state.leafletMap);
 | 
				
			||||||
        State.state.layerUpdater = updater;
 | 
					        State.state.layerUpdater = updater;
 | 
				
			||||||
        const source = new FeaturePipeline(flayers, updater, state.layoutToUse);
 | 
					        const source = new FeaturePipeline(state.filteredLayers.data, updater, state.layoutToUse, state.changes, state.locationControl);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        source.features.addCallbackAndRun((featuresFreshness: { feature: any, freshness: Date }[]) => {
 | 
					        source.features.addCallbackAndRun((featuresFreshness: { feature: any, freshness: Date }[]) => {
 | 
				
			||||||
| 
						 | 
					@ -337,18 +335,18 @@ export class InitUiElements {
 | 
				
			||||||
            let features = featuresFreshness.map(ff => ff.feature);
 | 
					            let features = featuresFreshness.map(ff => ff.feature);
 | 
				
			||||||
            features.forEach(feature => {
 | 
					            features.forEach(feature => {
 | 
				
			||||||
                State.state.allElements.addOrGetElement(feature);
 | 
					                State.state.allElements.addOrGetElement(feature);
 | 
				
			||||||
                
 | 
					
 | 
				
			||||||
                if(Hash.hash.data === feature.properties.id.replace("/","_")){
 | 
					                if (Hash.hash.data === feature.properties.id.replace("/", "_")) {
 | 
				
			||||||
                    State.state.selectedElement.setData(feature);
 | 
					                    State.state.selectedElement.setData(feature);
 | 
				
			||||||
                }
 | 
					                }
 | 
				
			||||||
                
 | 
					
 | 
				
			||||||
            })
 | 
					            })
 | 
				
			||||||
            MetaTagging.addMetatags(features);
 | 
					            MetaTagging.addMetatags(features);
 | 
				
			||||||
        })
 | 
					        })
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        new ShowDataLayer(source.features, State.state.leafletMap,
 | 
					        new ShowDataLayer(source.features, State.state.leafletMap,
 | 
				
			||||||
            State.state.layoutToUse.data);
 | 
					            State.state.layoutToUse);
 | 
				
			||||||
        
 | 
					
 | 
				
			||||||
        new SelectedFeatureHandler(Hash.hash, State.state.selectedElement, source);
 | 
					        new SelectedFeatureHandler(Hash.hash, State.state.selectedElement, source);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					@ -383,11 +381,11 @@ export class InitUiElements {
 | 
				
			||||||
                State.state.selectedElement,
 | 
					                State.state.selectedElement,
 | 
				
			||||||
                State.state.filteredLayers,
 | 
					                State.state.filteredLayers,
 | 
				
			||||||
                State.state.leafletMap,
 | 
					                State.state.leafletMap,
 | 
				
			||||||
                () => {
 | 
					                () =>
 | 
				
			||||||
                    return new SimpleAddUI(
 | 
					                    new ScrollableFullScreen(
 | 
				
			||||||
                        () => State.state.LastClickLocation.setData(undefined)
 | 
					                        Translations.t.general.add.title,
 | 
				
			||||||
                    );
 | 
					                        new SimpleAddUI(),
 | 
				
			||||||
                }
 | 
					                        () => State.state.LastClickLocation.setData(undefined))
 | 
				
			||||||
            );
 | 
					            );
 | 
				
			||||||
        });
 | 
					        });
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -44,10 +44,12 @@ export default class SelectedFeatureHandler {
 | 
				
			||||||
            // Feature already selected
 | 
					            // Feature already selected
 | 
				
			||||||
            return;
 | 
					            return;
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
 | 
					        console.log("Selecting a feature from the hash...")
 | 
				
			||||||
        for (const feature of features) {
 | 
					        for (const feature of features) {
 | 
				
			||||||
            const id = feature.feature?.properties?.id;
 | 
					            const id = feature.feature?.properties?.id;
 | 
				
			||||||
            if(id === this._hash.data){
 | 
					            if(id === this._hash.data){
 | 
				
			||||||
                this._selectedFeature.setData(feature.feature);
 | 
					                this._selectedFeature.setData(feature.feature);
 | 
				
			||||||
 | 
					                break;
 | 
				
			||||||
            }
 | 
					            }
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -52,9 +52,6 @@ export default class FeatureDuplicatorPerLayer implements FeatureSource {
 | 
				
			||||||
                        }
 | 
					                        }
 | 
				
			||||||
                    }
 | 
					                    }
 | 
				
			||||||
                }
 | 
					                }
 | 
				
			||||||
                if(!foundALayer){
 | 
					 | 
				
			||||||
                    console.error("LAYER DEDUP PANIC: no suitable layer found for ", f, JSON.stringify(f), "within layers", layers)
 | 
					 | 
				
			||||||
                }
 | 
					 | 
				
			||||||
            }
 | 
					            }
 | 
				
			||||||
            return newFeatures;
 | 
					            return newFeatures;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -1,5 +1,4 @@
 | 
				
			||||||
import FilteringFeatureSource from "../FeatureSource/FilteringFeatureSource";
 | 
					import FilteringFeatureSource from "../FeatureSource/FilteringFeatureSource";
 | 
				
			||||||
import State from "../../State";
 | 
					 | 
				
			||||||
import FeatureSourceMerger from "../FeatureSource/FeatureSourceMerger";
 | 
					import FeatureSourceMerger from "../FeatureSource/FeatureSourceMerger";
 | 
				
			||||||
import RememberingSource from "../FeatureSource/RememberingSource";
 | 
					import RememberingSource from "../FeatureSource/RememberingSource";
 | 
				
			||||||
import WayHandlingApplyingFeatureSource from "../FeatureSource/WayHandlingApplyingFeatureSource";
 | 
					import WayHandlingApplyingFeatureSource from "../FeatureSource/WayHandlingApplyingFeatureSource";
 | 
				
			||||||
| 
						 | 
					@ -11,6 +10,7 @@ import LocalStorageSaver from "./LocalStorageSaver";
 | 
				
			||||||
import LayerConfig from "../../Customizations/JSON/LayerConfig";
 | 
					import LayerConfig from "../../Customizations/JSON/LayerConfig";
 | 
				
			||||||
import LocalStorageSource from "./LocalStorageSource";
 | 
					import LocalStorageSource from "./LocalStorageSource";
 | 
				
			||||||
import LayoutConfig from "../../Customizations/JSON/LayoutConfig";
 | 
					import LayoutConfig from "../../Customizations/JSON/LayoutConfig";
 | 
				
			||||||
 | 
					import Loc from "../../Models/Loc";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
export default class FeaturePipeline implements FeatureSource {
 | 
					export default class FeaturePipeline implements FeatureSource {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					@ -18,7 +18,9 @@ export default class FeaturePipeline implements FeatureSource {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    constructor(flayers: { isDisplayed: UIEventSource<boolean>, layerDef: LayerConfig }[],
 | 
					    constructor(flayers: { isDisplayed: UIEventSource<boolean>, layerDef: LayerConfig }[],
 | 
				
			||||||
                updater: FeatureSource,
 | 
					                updater: FeatureSource,
 | 
				
			||||||
                layout: UIEventSource<LayoutConfig>) {
 | 
					                layout: UIEventSource<LayoutConfig>,
 | 
				
			||||||
 | 
					                newPoints: FeatureSource,
 | 
				
			||||||
 | 
					                locationControl: UIEventSource<Loc>) {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        const amendedOverpassSource =
 | 
					        const amendedOverpassSource =
 | 
				
			||||||
            new RememberingSource(
 | 
					            new RememberingSource(
 | 
				
			||||||
| 
						 | 
					@ -34,15 +36,18 @@ export default class FeaturePipeline implements FeatureSource {
 | 
				
			||||||
                    new NoOverlapSource(flayers, new FeatureDuplicatorPerLayer(flayers, new LocalStorageSource(layout)))
 | 
					                    new NoOverlapSource(flayers, new FeatureDuplicatorPerLayer(flayers, new LocalStorageSource(layout)))
 | 
				
			||||||
                ));
 | 
					                ));
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        newPoints = new FeatureDuplicatorPerLayer(flayers, newPoints);
 | 
				
			||||||
 | 
					        
 | 
				
			||||||
        const merged = new FeatureSourceMerger([
 | 
					        const merged = new FeatureSourceMerger([
 | 
				
			||||||
            amendedOverpassSource,
 | 
					            amendedOverpassSource,
 | 
				
			||||||
            new FeatureDuplicatorPerLayer(flayers, State.state.changes),
 | 
					            amendedLocalStorageSource,
 | 
				
			||||||
            amendedLocalStorageSource
 | 
					            newPoints
 | 
				
			||||||
        ]);
 | 
					        ]);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        const source =
 | 
					        const source =
 | 
				
			||||||
            new FilteringFeatureSource(
 | 
					            new FilteringFeatureSource(
 | 
				
			||||||
                flayers,
 | 
					                flayers,
 | 
				
			||||||
                State.state.locationControl,
 | 
					                locationControl,
 | 
				
			||||||
                merged
 | 
					                merged
 | 
				
			||||||
            );
 | 
					            );
 | 
				
			||||||
        this.features = source.features;
 | 
					        this.features = source.features;
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -13,29 +13,35 @@ export default class FilteringFeatureSource implements FeatureSource {
 | 
				
			||||||
                location: UIEventSource<Loc>,
 | 
					                location: UIEventSource<Loc>,
 | 
				
			||||||
                upstream: FeatureSource) {
 | 
					                upstream: FeatureSource) {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        const layerDict = {};
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        const self = this;
 | 
					        const self = this;
 | 
				
			||||||
 | 
					        
 | 
				
			||||||
 | 
					        const layerDict = {};
 | 
				
			||||||
 | 
					        for (const layer of layers) {
 | 
				
			||||||
 | 
					            layerDict[layer.layerDef.id] = layer;
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					       
 | 
				
			||||||
        function update() {
 | 
					        function update() {
 | 
				
			||||||
 | 
					            console.log("Updating the filtering layer")
 | 
				
			||||||
            const features: { feature: any, freshness: Date }[] = upstream.features.data;
 | 
					            const features: { feature: any, freshness: Date }[] = upstream.features.data;
 | 
				
			||||||
 | 
					            
 | 
				
			||||||
 | 
					            
 | 
				
			||||||
            const newFeatures = features.filter(f => {
 | 
					            const newFeatures = features.filter(f => {
 | 
				
			||||||
                const layerId = f.feature._matching_layer_id;
 | 
					                const layerId = f.feature._matching_layer_id;
 | 
				
			||||||
                if (layerId === undefined) {
 | 
					                if (layerId !== undefined) {
 | 
				
			||||||
                    console.error(f)
 | 
					                    const layer: {
 | 
				
			||||||
                    throw "feature._matching_layer_id is undefined"
 | 
					                        isDisplayed: UIEventSource<boolean>,
 | 
				
			||||||
 | 
					                        layerDef: LayerConfig
 | 
				
			||||||
 | 
					                    } = layerDict[layerId];
 | 
				
			||||||
 | 
					                    if (layer === undefined) {
 | 
				
			||||||
 | 
					                        console.error("No layer found with id ", layerId);
 | 
				
			||||||
 | 
					                        return true;
 | 
				
			||||||
 | 
					                    }
 | 
				
			||||||
 | 
					                    if (FilteringFeatureSource.showLayer(layer, location)) {
 | 
				
			||||||
 | 
					                        return true;
 | 
				
			||||||
 | 
					                    }
 | 
				
			||||||
                }
 | 
					                }
 | 
				
			||||||
                const layer: {
 | 
					                // Does it match any other layer - e.g. because of a switch?
 | 
				
			||||||
                    isDisplayed: UIEventSource<boolean>,
 | 
					 | 
				
			||||||
                    layerDef: LayerConfig
 | 
					 | 
				
			||||||
                } = layerDict[layerId];
 | 
					 | 
				
			||||||
                if (layer === undefined) {
 | 
					 | 
				
			||||||
                    throw "No layer found with id " + layerId;
 | 
					 | 
				
			||||||
                }
 | 
					 | 
				
			||||||
                if (FilteringFeatureSource.showLayer(layer, location)) {
 | 
					 | 
				
			||||||
                    return true;
 | 
					 | 
				
			||||||
                }
 | 
					 | 
				
			||||||
                // Does it match any other layer?
 | 
					 | 
				
			||||||
                for (const toCheck of layers) {
 | 
					                for (const toCheck of layers) {
 | 
				
			||||||
                    if (!FilteringFeatureSource.showLayer(toCheck, location)) {
 | 
					                    if (!FilteringFeatureSource.showLayer(toCheck, location)) {
 | 
				
			||||||
                        continue;
 | 
					                        continue;
 | 
				
			||||||
| 
						 | 
					@ -50,9 +56,7 @@ export default class FilteringFeatureSource implements FeatureSource {
 | 
				
			||||||
            self.features.setData(newFeatures);
 | 
					            self.features.setData(newFeatures);
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        for (const layer of layers) {
 | 
					
 | 
				
			||||||
            layerDict[layer.layerDef.id] = layer;
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
        upstream.features.addCallback(() => {
 | 
					        upstream.features.addCallback(() => {
 | 
				
			||||||
            update()
 | 
					            update()
 | 
				
			||||||
        });
 | 
					        });
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -4,7 +4,6 @@ import opening_hours from "opening_hours";
 | 
				
			||||||
import {And, Or, Tag} from "./Tags";
 | 
					import {And, Or, Tag} from "./Tags";
 | 
				
			||||||
import {Utils} from "../Utils";
 | 
					import {Utils} from "../Utils";
 | 
				
			||||||
import CountryCoder from "latlon2country"
 | 
					import CountryCoder from "latlon2country"
 | 
				
			||||||
import {UIEventSource} from "./UIEventSource";
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
class SimpleMetaTagger {
 | 
					class SimpleMetaTagger {
 | 
				
			||||||
    public readonly keys: string[];
 | 
					    public readonly keys: string[];
 | 
				
			||||||
| 
						 | 
					@ -135,7 +134,7 @@ export default class MetaTagging {
 | 
				
			||||||
                    }
 | 
					                    }
 | 
				
			||||||
                    updateTags();
 | 
					                    updateTags();
 | 
				
			||||||
                } catch (e) {
 | 
					                } catch (e) {
 | 
				
			||||||
                    console.error("Error while parsing opening hours of ", tags.id, e);
 | 
					                    console.warn("Error while parsing opening hours of ", tags.id, e);
 | 
				
			||||||
                    tags["_isOpen"] = "parse_error";
 | 
					                    tags["_isOpen"] = "parse_error";
 | 
				
			||||||
                }
 | 
					                }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -100,10 +100,12 @@ export class Changes implements FeatureSource{
 | 
				
			||||||
            }
 | 
					            }
 | 
				
			||||||
            changes.push({elementId: id, key: kv.key, value: kv.value})
 | 
					            changes.push({elementId: id, key: kv.key, value: kv.value})
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
        State.state.allElements.addOrGetElement(geojson).ping();
 | 
					       
 | 
				
			||||||
 | 
					        console.log("New feature added and pinged")
 | 
				
			||||||
        this.features.data.push({feature:geojson, freshness: new Date()});
 | 
					        this.features.data.push({feature:geojson, freshness: new Date()});
 | 
				
			||||||
        this.features.ping();
 | 
					        this.features.ping();
 | 
				
			||||||
 | 
					        
 | 
				
			||||||
 | 
					        State.state.allElements.addOrGetElement(geojson).ping();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        this.uploadAll([osmNode], changes);
 | 
					        this.uploadAll([osmNode], changes);
 | 
				
			||||||
        return geojson;
 | 
					        return geojson;
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -77,7 +77,7 @@ export class UIEventSource<T> {
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    public setData(t: T): UIEventSource<T> {
 | 
					    public setData(t: T): UIEventSource<T> {
 | 
				
			||||||
        if (this.data === t) {
 | 
					        if (this.data == t) { // MUST COMPARE BY REFERENCE!
 | 
				
			||||||
            return;
 | 
					            return;
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
        this.data = t;
 | 
					        this.data = t;
 | 
				
			||||||
| 
						 | 
					@ -86,8 +86,12 @@ export class UIEventSource<T> {
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    public ping(): void {
 | 
					    public ping(): void {
 | 
				
			||||||
 | 
					        const old = this.data;
 | 
				
			||||||
        for (const callback of this._callbacks) {
 | 
					        for (const callback of this._callbacks) {
 | 
				
			||||||
            callback(this.data);
 | 
					            callback(this.data);
 | 
				
			||||||
 | 
					            if(this.data === undefined && old !== undefined){
 | 
				
			||||||
 | 
					                throw "Something undefined the data!"
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -21,6 +21,7 @@ export default class ScrollableFullScreen extends UIElement {
 | 
				
			||||||
                Svg.close_svg().SetClass("hidden md:block")
 | 
					                Svg.close_svg().SetClass("hidden md:block")
 | 
				
			||||||
            ])
 | 
					            ])
 | 
				
			||||||
                .onClick(() => {
 | 
					                .onClick(() => {
 | 
				
			||||||
 | 
					                    console.log("Closing...")
 | 
				
			||||||
                    ScrollableFullScreen.RestoreLeaflet();
 | 
					                    ScrollableFullScreen.RestoreLeaflet();
 | 
				
			||||||
                    if (onClose !== undefined) {
 | 
					                    if (onClose !== undefined) {
 | 
				
			||||||
                        onClose();
 | 
					                        onClose();
 | 
				
			||||||
| 
						 | 
					@ -92,6 +93,19 @@ export default class ScrollableFullScreen extends UIElement {
 | 
				
			||||||
        if(htmlElement === undefined || htmlElement === null){
 | 
					        if(htmlElement === undefined || htmlElement === null){
 | 
				
			||||||
            return;
 | 
					            return;
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        let toHide = document.getElementsByClassName("leaflet-pane");
 | 
				
			||||||
 | 
					        for (let i = 0; i < toHide.length; ++i) {
 | 
				
			||||||
 | 
					            toHide[i].classList.add("no-transform");
 | 
				
			||||||
 | 
					            toHide[i].classList.add("scrollable-fullscreen-no-transform");
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        toHide = document.getElementsByClassName("leaflet-popup");
 | 
				
			||||||
 | 
					        for (let i = 0; i < toHide.length; ++i) {
 | 
				
			||||||
 | 
					            toHide[i].classList.add("no-transform");
 | 
				
			||||||
 | 
					            toHide[i].classList.add("scrollable-fullscreen-no-transform");
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					        
 | 
				
			||||||
        do {
 | 
					        do {
 | 
				
			||||||
            // A leaflet workaround: in order for fullscreen to work, we need to get the parent element which does a transform3d and remove/read the transform
 | 
					            // A leaflet workaround: in order for fullscreen to work, we need to get the parent element which does a transform3d and remove/read the transform
 | 
				
			||||||
            if (htmlElement.style.transform !== "") {
 | 
					            if (htmlElement.style.transform !== "") {
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -13,7 +13,6 @@ import {FixedUiElement} from "../Base/FixedUiElement";
 | 
				
			||||||
import Translations from "../i18n/Translations";
 | 
					import Translations from "../i18n/Translations";
 | 
				
			||||||
import Constants from "../../Models/Constants";
 | 
					import Constants from "../../Models/Constants";
 | 
				
			||||||
import LayerConfig from "../../Customizations/JSON/LayerConfig";
 | 
					import LayerConfig from "../../Customizations/JSON/LayerConfig";
 | 
				
			||||||
import ScrollableFullScreen from "../Base/ScrollableFullScreen";
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
export default class SimpleAddUI extends UIElement {
 | 
					export default class SimpleAddUI extends UIElement {
 | 
				
			||||||
    private readonly _loginButton: UIElement;
 | 
					    private readonly _loginButton: UIElement;
 | 
				
			||||||
| 
						 | 
					@ -37,7 +36,7 @@ export default class SimpleAddUI extends UIElement {
 | 
				
			||||||
    private readonly goToInboxButton: UIElement = new SubtleButton(Svg.envelope_ui(),
 | 
					    private readonly goToInboxButton: UIElement = new SubtleButton(Svg.envelope_ui(),
 | 
				
			||||||
        Translations.t.general.goToInbox, {url: "https://www.openstreetmap.org/messages/inbox", newTab: false});
 | 
					        Translations.t.general.goToInbox, {url: "https://www.openstreetmap.org/messages/inbox", newTab: false});
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    constructor(onClose: () => void) {
 | 
					    constructor() {
 | 
				
			||||||
        super(State.state.locationControl.map(loc => loc.zoom));
 | 
					        super(State.state.locationControl.map(loc => loc.zoom));
 | 
				
			||||||
        const self = this;
 | 
					        const self = this;
 | 
				
			||||||
        this.ListenTo(Locale.language);
 | 
					        this.ListenTo(Locale.language);
 | 
				
			||||||
| 
						 | 
					@ -64,14 +63,10 @@ export default class SimpleAddUI extends UIElement {
 | 
				
			||||||
            State.state.layerControlIsOpened.setData(true);
 | 
					            State.state.layerControlIsOpened.setData(true);
 | 
				
			||||||
        })
 | 
					        })
 | 
				
			||||||
        
 | 
					        
 | 
				
			||||||
        this._component = new ScrollableFullScreen(
 | 
					 | 
				
			||||||
            Translations.t.general.add.title,
 | 
					 | 
				
			||||||
            this.CreateContent(),
 | 
					 | 
				
			||||||
            onClose
 | 
					 | 
				
			||||||
        );
 | 
					 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    InnerRender(): string {
 | 
					    InnerRender(): string {
 | 
				
			||||||
 | 
					        this._component = this.CreateContent();
 | 
				
			||||||
        return this._component.Render();
 | 
					        return this._component.Render();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
| 
						 | 
					@ -109,7 +104,7 @@ export default class SimpleAddUI extends UIElement {
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        const presetButtons = this.CreatePresetButtons()
 | 
					        const presetButtons = this.CreatePresetButtons()
 | 
				
			||||||
        return new Combine(presetButtons).SetClass("add-popup-all-buttons")
 | 
					        return new Combine(presetButtons)
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -16,74 +16,67 @@ export default class ShowDataLayer {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    private readonly _layerDict;
 | 
					    private readonly _layerDict;
 | 
				
			||||||
    private readonly _leafletMap: UIEventSource<L.Map>;
 | 
					    private readonly _leafletMap: UIEventSource<L.Map>;
 | 
				
			||||||
    private readonly _onSelectedTrigger: any = {}; // osmId+geometry.type+matching_layer_id --> () => void
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
    constructor(features: UIEventSource<{ feature: any, freshness: Date }[]>,
 | 
					    constructor(features: UIEventSource<{ feature: any, freshness: Date }[]>,
 | 
				
			||||||
                leafletMap: UIEventSource<L.Map>,
 | 
					                leafletMap: UIEventSource<L.Map>,
 | 
				
			||||||
                layoutToUse: LayoutConfig) {
 | 
					                layoutToUse: UIEventSource<LayoutConfig>) {
 | 
				
			||||||
        this._leafletMap = leafletMap;
 | 
					        this._leafletMap = leafletMap;
 | 
				
			||||||
        const self = this;
 | 
					        const self = this;
 | 
				
			||||||
        const mp = leafletMap.data;
 | 
					        const mp = leafletMap.data;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        this._layerDict = {};
 | 
					        this._layerDict = {};
 | 
				
			||||||
        for (const layer of layoutToUse.layers) {
 | 
					 | 
				
			||||||
            this._layerDict[layer.id] = layer;
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        layoutToUse.addCallbackAndRun(layoutToUse => {
 | 
				
			||||||
        function openSelectedElementFeature(feature: any) {
 | 
					            for (const layer of layoutToUse.layers) {
 | 
				
			||||||
            if (feature === undefined) {
 | 
					                this._layerDict[layer.id] = layer;
 | 
				
			||||||
                return;
 | 
					 | 
				
			||||||
            }
 | 
					            }
 | 
				
			||||||
            const id = feature.properties.id + feature.geometry.type + feature._matching_layer_id;
 | 
					        });
 | 
				
			||||||
            const action = self._onSelectedTrigger[id];
 | 
					 | 
				
			||||||
            if (action) {
 | 
					 | 
				
			||||||
                action();
 | 
					 | 
				
			||||||
            }
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        let geoLayer = undefined;
 | 
				
			||||||
        const knownFeatureIds = new Set<string>();
 | 
					 | 
				
			||||||
        const geoLayer = self.CreateGeojsonLayer();
 | 
					 | 
				
			||||||
        mp.addLayer(geoLayer);
 | 
					 | 
				
			||||||
        let cluster = undefined;
 | 
					        let cluster = undefined;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        function update() {
 | 
					        function update() {
 | 
				
			||||||
 | 
					            console.log("Updating the data layer...", features.data.length, "objects loaded")
 | 
				
			||||||
            if (features.data === undefined) {
 | 
					            if (features.data === undefined) {
 | 
				
			||||||
                return;
 | 
					                return;
 | 
				
			||||||
            }
 | 
					            }
 | 
				
			||||||
            if (leafletMap.data === undefined) {
 | 
					            if (leafletMap.data === undefined) {
 | 
				
			||||||
                return;
 | 
					                return;
 | 
				
			||||||
            }
 | 
					            }
 | 
				
			||||||
            
 | 
					 | 
				
			||||||
            const feats = features.data.map(ff => ff.feature);
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
            for (const feat of feats) {
 | 
					            // clean all the old stuff away, if any
 | 
				
			||||||
 | 
					            if (geoLayer !== undefined) {
 | 
				
			||||||
 | 
					                mp.removeLayer(geoLayer);
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					            if (cluster !== undefined) {
 | 
				
			||||||
 | 
					                mp.removeLayer(cluster);
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            const allFeats = features.data.map(ff => ff.feature);
 | 
				
			||||||
 | 
					            console.log("AllFeats contain ", allFeats.length)
 | 
				
			||||||
 | 
					            geoLayer = self.CreateGeojsonLayer();
 | 
				
			||||||
 | 
					            let i = 0;
 | 
				
			||||||
 | 
					            for (const feat of allFeats) {
 | 
				
			||||||
                const key = feat.geometry.type + feat.properties.id + feat.layer;
 | 
					                const key = feat.geometry.type + feat.properties.id + feat.layer;
 | 
				
			||||||
                if (knownFeatureIds.has(key)) {
 | 
					 | 
				
			||||||
                    continue;
 | 
					 | 
				
			||||||
                }
 | 
					 | 
				
			||||||
                knownFeatureIds.add(key);
 | 
					 | 
				
			||||||
                // @ts-ignore
 | 
					                // @ts-ignore
 | 
				
			||||||
                geoLayer.addData(feat);
 | 
					                geoLayer.addData(feat);
 | 
				
			||||||
                console.log("Added ", feat)
 | 
					                i++;
 | 
				
			||||||
            }
 | 
					            }
 | 
				
			||||||
            if (cluster === undefined) {
 | 
					            if (layoutToUse.data.clustering.minNeededElements <= allFeats.length) {
 | 
				
			||||||
                if (layoutToUse.clustering.minNeededElements <= features.data.length) {
 | 
					                // Activate clustering if it wasn't already activated
 | 
				
			||||||
                    // Activate clustering if it wasn't already activated
 | 
					                const cl = window["L"]; // This is a dirty workaround, the clustering plugin binds to the L of the window, not of the namespace or something
 | 
				
			||||||
                    const cl = window["L"]; // This is a dirty workaround, the clustering plugin binds to the L of the window, not of the namespace or something
 | 
					                cluster = cl.markerClusterGroup({disableClusteringAtZoom: layoutToUse.data.clustering.maxZoom});
 | 
				
			||||||
                    cluster = cl.markerClusterGroup({disableClusteringAtZoom: layoutToUse.clustering.maxZoom});
 | 
					                cluster.addLayer(geoLayer);
 | 
				
			||||||
                    cluster.addLayer(geoLayer);
 | 
					                mp.addLayer(cluster);
 | 
				
			||||||
                    mp.removeLayer(geoLayer)
 | 
					                console.log("Added cluster", i)
 | 
				
			||||||
                    mp.addLayer(cluster);
 | 
					            } else {
 | 
				
			||||||
                }
 | 
					                mp.addLayer(geoLayer)
 | 
				
			||||||
 | 
					                console.log("Added geoLayer", i)
 | 
				
			||||||
            }
 | 
					            }
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        features.addCallback(() => update());
 | 
					        features.addCallback(() => update());
 | 
				
			||||||
        leafletMap.addCallback(() => update());
 | 
					        leafletMap.addCallback(() => update());
 | 
				
			||||||
 | 
					 | 
				
			||||||
        State.state.selectedElement.addCallbackAndRun(openSelectedElementFeature);
 | 
					 | 
				
			||||||
        update();
 | 
					        update();
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					@ -103,6 +96,10 @@ export default class ShowDataLayer {
 | 
				
			||||||
        const tagSource = State.state.allElements.addOrGetElement(feature)
 | 
					        const tagSource = State.state.allElements.addOrGetElement(feature)
 | 
				
			||||||
        const layer: LayerConfig = this._layerDict[feature._matching_layer_id];
 | 
					        const layer: LayerConfig = this._layerDict[feature._matching_layer_id];
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        if (layer === undefined) {
 | 
				
			||||||
 | 
					            return;
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        const style = layer.GenerateLeafletStyle(tagSource, !(layer.title === undefined && (layer.tagRenderings ?? []).length === 0));
 | 
					        const style = layer.GenerateLeafletStyle(tagSource, !(layer.title === undefined && (layer.tagRenderings ?? []).length === 0));
 | 
				
			||||||
        return L.marker(latLng, {
 | 
					        return L.marker(latLng, {
 | 
				
			||||||
            icon: L.divIcon({
 | 
					            icon: L.divIcon({
 | 
				
			||||||
| 
						 | 
					@ -122,13 +119,13 @@ export default class ShowDataLayer {
 | 
				
			||||||
            // No popup action defined -> Don't do anything
 | 
					            // No popup action defined -> Don't do anything
 | 
				
			||||||
            return;
 | 
					            return;
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
        const self = this;
 | 
					 | 
				
			||||||
        const popup = L.popup({
 | 
					        const popup = L.popup({
 | 
				
			||||||
            autoPan: true,
 | 
					            autoPan: true,
 | 
				
			||||||
            closeOnEscapeKey: true,
 | 
					            closeOnEscapeKey: true,
 | 
				
			||||||
            closeButton: false
 | 
					            closeButton: false
 | 
				
			||||||
        }, leafletLayer);
 | 
					        }, leafletLayer);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        let isOpen = false;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        const tags = State.state.allElements.getEventSourceFor(feature);
 | 
					        const tags = State.state.allElements.getEventSourceFor(feature);
 | 
				
			||||||
        const uiElement = new LazyElement(() =>
 | 
					        const uiElement = new LazyElement(() =>
 | 
				
			||||||
| 
						 | 
					@ -141,30 +138,45 @@ export default class ShowDataLayer {
 | 
				
			||||||
            "<div style='height: 90vh'>Rendering</div>"); // By setting 90vh, leaflet will attempt to fit the entire screen and move the feature down
 | 
					            "<div style='height: 90vh'>Rendering</div>"); // By setting 90vh, leaflet will attempt to fit the entire screen and move the feature down
 | 
				
			||||||
        popup.setContent(uiElement.Render());
 | 
					        popup.setContent(uiElement.Render());
 | 
				
			||||||
        popup.on('remove', () => {
 | 
					        popup.on('remove', () => {
 | 
				
			||||||
 | 
					            if (!isOpen) {
 | 
				
			||||||
 | 
					                return;
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					            console.log("Closing popup...")
 | 
				
			||||||
 | 
					            isOpen = false;
 | 
				
			||||||
            ScrollableFullScreen.RestoreLeaflet(); // Just in case...
 | 
					            ScrollableFullScreen.RestoreLeaflet(); // Just in case...
 | 
				
			||||||
            State.state.selectedElement.setData(undefined);
 | 
					            State.state.selectedElement.setData(undefined);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        });
 | 
					        });
 | 
				
			||||||
        leafletLayer.bindPopup(popup);
 | 
					        leafletLayer.bindPopup(popup);
 | 
				
			||||||
        // We first render the UIelement (which'll still need an update later on...)
 | 
					        // We first render the UIelement (which'll still need an update later on...)
 | 
				
			||||||
        // But at least it'll be visible already
 | 
					        // But at least it'll be visible already
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        leafletLayer.on("popupopen", () => {
 | 
					        leafletLayer.on("popupopen", () => {
 | 
				
			||||||
            console.log("Popup opened")
 | 
					            isOpen = true;
 | 
				
			||||||
            uiElement.Activate();
 | 
					 | 
				
			||||||
            State.state.selectedElement.setData(feature);
 | 
					            State.state.selectedElement.setData(feature);
 | 
				
			||||||
 | 
					            uiElement.Activate();
 | 
				
			||||||
        })
 | 
					        })
 | 
				
			||||||
        const id = feature.properties.id + feature.geometry.type + feature._matching_layer_id;
 | 
					
 | 
				
			||||||
        this._onSelectedTrigger[id]
 | 
					        State.state.selectedElement.addCallbackAndRun(selected => {
 | 
				
			||||||
            = () => {
 | 
					                if (selected === undefined) {
 | 
				
			||||||
            if (!popup.isOpen()) {
 | 
					                    if (popup.isOpen() && isOpen) {
 | 
				
			||||||
                console.log("Action triggered")
 | 
					                        popup.remove();
 | 
				
			||||||
                // Close all the popups which might still be opened
 | 
					                    }
 | 
				
			||||||
                self._leafletMap.data.closePopup();
 | 
					                } else if (selected == feature && selected.geometry.type == feature.geometry.type) {
 | 
				
			||||||
                leafletLayer.openPopup()
 | 
					                    // If wayhandling introduces a centerpoint and an area, this code might become unstable:
 | 
				
			||||||
                return;
 | 
					                    // The popup for the centerpoint would open, a bit later the area would close the first popup and open it's own
 | 
				
			||||||
 | 
					                    // In the process, the 'selectedElement' is set to undefined and to the other feature again, causing an infinite loop
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                    // This is why we check for the geometry-type too
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                    if (!popup.isOpen() && !isOpen) {
 | 
				
			||||||
 | 
					                        isOpen = true;
 | 
				
			||||||
 | 
					                        leafletLayer.openPopup();
 | 
				
			||||||
 | 
					                    }
 | 
				
			||||||
 | 
					                }
 | 
				
			||||||
            }
 | 
					            }
 | 
				
			||||||
        }
 | 
					        );
 | 
				
			||||||
        this._onSelectedTrigger[feature.properties.id.replace("/", "_")] = this._onSelectedTrigger[id];
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -359,15 +359,6 @@ a {
 | 
				
			||||||
    color: var(--foreground-color);
 | 
					    color: var(--foreground-color);
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					 | 
				
			||||||
.add-popup-all-buttons {
 | 
					 | 
				
			||||||
    max-height: 50vh;
 | 
					 | 
				
			||||||
    display: inline-block;
 | 
					 | 
				
			||||||
    overflow-y: auto;
 | 
					 | 
				
			||||||
    width: 100%;
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
/**************************************/
 | 
					/**************************************/
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue