forked from MapComplete/MapComplete
		
	Refactoring fullscreenhandling
This commit is contained in:
		
							parent
							
								
									e1a4c75391
								
							
						
					
					
						commit
						00f610c589
					
				
					 23 changed files with 346 additions and 245 deletions
				
			
		| 
						 | 
					@ -12,7 +12,6 @@ import CenterMessageBox from "./UI/CenterMessageBox";
 | 
				
			||||||
import {AllKnownLayouts} from "./Customizations/AllKnownLayouts";
 | 
					import {AllKnownLayouts} from "./Customizations/AllKnownLayouts";
 | 
				
			||||||
import UserBadge from "./UI/BigComponents/UserBadge";
 | 
					import UserBadge from "./UI/BigComponents/UserBadge";
 | 
				
			||||||
import SearchAndGo from "./UI/BigComponents/SearchAndGo";
 | 
					import SearchAndGo from "./UI/BigComponents/SearchAndGo";
 | 
				
			||||||
import FullScreenMessageBox from "./UI/FullScreenMessageBoxHandler";
 | 
					 | 
				
			||||||
import GeoLocationHandler from "./Logic/Actors/GeoLocationHandler";
 | 
					import GeoLocationHandler from "./Logic/Actors/GeoLocationHandler";
 | 
				
			||||||
import {LocalStorageSource} from "./Logic/Web/LocalStorageSource";
 | 
					import {LocalStorageSource} from "./Logic/Web/LocalStorageSource";
 | 
				
			||||||
import {Utils} from "./Utils";
 | 
					import {Utils} from "./Utils";
 | 
				
			||||||
| 
						 | 
					@ -33,7 +32,6 @@ import FeatureSwitched from "./UI/Base/FeatureSwitched";
 | 
				
			||||||
import LayerConfig from "./Customizations/JSON/LayerConfig";
 | 
					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 HistoryHandling from "./Logic/Actors/HistoryHandling";
 | 
					 | 
				
			||||||
import FeaturePipeline from "./Logic/FeatureSource/FeaturePipeline";
 | 
					import FeaturePipeline from "./Logic/FeatureSource/FeaturePipeline";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
export class InitUiElements {
 | 
					export class InitUiElements {
 | 
				
			||||||
| 
						 | 
					@ -118,8 +116,6 @@ export class InitUiElements {
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        new HistoryHandling(Hash.hash, State.state.fullScreenMessage);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        InitUiElements.OnlyIf(State.state.featureSwitchUserbadge, () => {
 | 
					        InitUiElements.OnlyIf(State.state.featureSwitchUserbadge, () => {
 | 
				
			||||||
            new UserBadge().AttachTo('userbadge');
 | 
					            new UserBadge().AttachTo('userbadge');
 | 
				
			||||||
        });
 | 
					        });
 | 
				
			||||||
| 
						 | 
					@ -129,9 +125,6 @@ export class InitUiElements {
 | 
				
			||||||
        });
 | 
					        });
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        new FullScreenMessageBox().AttachTo("messagesboxmobile");
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        InitUiElements.OnlyIf(State.state.featureSwitchWelcomeMessage, () => {
 | 
					        InitUiElements.OnlyIf(State.state.featureSwitchWelcomeMessage, () => {
 | 
				
			||||||
            InitUiElements.InitWelcomeMessage()
 | 
					            InitUiElements.InitWelcomeMessage()
 | 
				
			||||||
        });
 | 
					        });
 | 
				
			||||||
| 
						 | 
					@ -214,18 +207,18 @@ export class InitUiElements {
 | 
				
			||||||
    private static InitWelcomeMessage() {
 | 
					    private static InitWelcomeMessage() {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        const isOpened = new UIEventSource<boolean>(true);
 | 
					        const isOpened = new UIEventSource<boolean>(true);
 | 
				
			||||||
        const fullOptions = new FullWelcomePaneWithTabs(() => isOpened.setData(false));
 | 
					        const fullOptions = new FullWelcomePaneWithTabs(() => {
 | 
				
			||||||
 | 
					            console.log("Closing the welcome message...")
 | 
				
			||||||
 | 
					            isOpened.setData(false);
 | 
				
			||||||
 | 
					        });
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        // ?-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 close = Svg.close_svg().SetClass("close-welcome-button");
 | 
					 | 
				
			||||||
        const checkbox = new CheckBox(
 | 
					        const checkbox = new CheckBox(
 | 
				
			||||||
            new Combine([
 | 
					 | 
				
			||||||
                close,
 | 
					 | 
				
			||||||
                fullOptions
 | 
					                fullOptions
 | 
				
			||||||
                    .SetClass("welcomeMessage")
 | 
					                    .SetClass("welcomeMessage")
 | 
				
			||||||
                    .onClick(() => {/*Catch the click*/
 | 
					                    .onClick(() => {/*Catch the click*/
 | 
				
			||||||
                    })]),
 | 
					                    }),
 | 
				
			||||||
            help
 | 
					            help
 | 
				
			||||||
            , isOpened
 | 
					            , isOpened
 | 
				
			||||||
        ).AttachTo("messagesbox");
 | 
					        ).AttachTo("messagesbox");
 | 
				
			||||||
| 
						 | 
					@ -252,15 +245,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 closeButton = Svg.close_svg()
 | 
					              const checkbox = new CheckBox(
 | 
				
			||||||
                .SetClass("layer-selection-toggle")
 | 
					                    layerControlPanel,
 | 
				
			||||||
                .SetStyle("  background: var(--subtle-detail-color);")
 | 
					 | 
				
			||||||
            const checkbox = new CheckBox(
 | 
					 | 
				
			||||||
                new Combine([
 | 
					 | 
				
			||||||
                    closeButton,
 | 
					 | 
				
			||||||
                    layerControlPanel])
 | 
					 | 
				
			||||||
                    .SetClass("flex flex-row")
 | 
					 | 
				
			||||||
                ,
 | 
					 | 
				
			||||||
                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");
 | 
				
			||||||
| 
						 | 
					@ -388,7 +374,6 @@ export class InitUiElements {
 | 
				
			||||||
                State.state.selectedElement,
 | 
					                State.state.selectedElement,
 | 
				
			||||||
                State.state.filteredLayers,
 | 
					                State.state.filteredLayers,
 | 
				
			||||||
                State.state.leafletMap,
 | 
					                State.state.leafletMap,
 | 
				
			||||||
                State.state.fullScreenMessage,
 | 
					 | 
				
			||||||
                () => {
 | 
					                () => {
 | 
				
			||||||
                    return new SimpleAddUI(
 | 
					                    return new SimpleAddUI(
 | 
				
			||||||
                        () => State.state.LastClickLocation.setData(undefined)
 | 
					                        () => State.state.LastClickLocation.setData(undefined)
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -17,7 +17,6 @@ export default class StrayClickHandler {
 | 
				
			||||||
        selectedElement: UIEventSource<string>,
 | 
					        selectedElement: UIEventSource<string>,
 | 
				
			||||||
        filteredLayers: UIEventSource<{ readonly isDisplayed: UIEventSource<boolean> }[]>,
 | 
					        filteredLayers: UIEventSource<{ readonly isDisplayed: UIEventSource<boolean> }[]>,
 | 
				
			||||||
        leafletMap: UIEventSource<L.Map>,
 | 
					        leafletMap: UIEventSource<L.Map>,
 | 
				
			||||||
        fullscreenMessage: UIEventSource<{content: UIElement, hashText: string}>,
 | 
					 | 
				
			||||||
        uiToShow: (() => UIElement)) {
 | 
					        uiToShow: (() => UIElement)) {
 | 
				
			||||||
        this._uiToShow = uiToShow;
 | 
					        this._uiToShow = uiToShow;
 | 
				
			||||||
        const self = this;
 | 
					        const self = this;
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -3,31 +3,70 @@ import LayoutConfig from "../../Customizations/JSON/LayoutConfig";
 | 
				
			||||||
import Translations from "../../UI/i18n/Translations";
 | 
					import Translations from "../../UI/i18n/Translations";
 | 
				
			||||||
import Locale from "../../UI/i18n/Locale";
 | 
					import Locale from "../../UI/i18n/Locale";
 | 
				
			||||||
import {UIElement} from "../../UI/UIElement";
 | 
					import {UIElement} from "../../UI/UIElement";
 | 
				
			||||||
 | 
					import TagRenderingAnswer from "../../UI/Popup/TagRenderingAnswer";
 | 
				
			||||||
 | 
					import {ElementStorage} from "../ElementStorage";
 | 
				
			||||||
 | 
					import Combine from "../../UI/Base/Combine";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
export default class TitleHandler{
 | 
					class TitleElement extends UIElement {
 | 
				
			||||||
    constructor(layoutToUse: UIEventSource<LayoutConfig>, fullScreenMessage: UIEventSource<{ content: UIElement, hashText: string, titleText?: UIElement }>) {
 | 
					    private readonly _layoutToUse: UIEventSource<LayoutConfig>;
 | 
				
			||||||
 | 
					    private readonly _selectedFeature: UIEventSource<any>;
 | 
				
			||||||
 | 
					    private readonly _allElementsStorage: ElementStorage;
 | 
				
			||||||
 | 
					    
 | 
				
			||||||
 | 
					    constructor(layoutToUse: UIEventSource<LayoutConfig>,
 | 
				
			||||||
 | 
					                selectedFeature: UIEventSource<any>,
 | 
				
			||||||
 | 
					                allElementsStorage : ElementStorage) {
 | 
				
			||||||
 | 
					        super(layoutToUse);
 | 
				
			||||||
 | 
					        this._layoutToUse = layoutToUse;
 | 
				
			||||||
 | 
					        this._selectedFeature = selectedFeature;
 | 
				
			||||||
 | 
					        this._allElementsStorage = allElementsStorage;
 | 
				
			||||||
 | 
					        this.ListenTo(Locale.language);
 | 
				
			||||||
 | 
					        this.dumbMode = false;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    
 | 
				
			||||||
 | 
					    InnerRender(): string {
 | 
				
			||||||
 | 
					        
 | 
				
			||||||
 | 
					        const defaultTitle = Translations.WT(this._layoutToUse.data?.title)?.txt ?? "MapComplete"
 | 
				
			||||||
 | 
					        const feature = this._selectedFeature.data;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        if(feature === undefined){
 | 
				
			||||||
 | 
					            return defaultTitle;
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        layoutToUse.map((layoutToUse) => {
 | 
					        const layout = this._layoutToUse.data;
 | 
				
			||||||
                return Translations.WT(layoutToUse?.title)?.txt ?? "MapComplete"
 | 
					        const properties = this._selectedFeature.data.properties;
 | 
				
			||||||
            }, [Locale.language]
 | 
					         for (const layer of layout.layers) {
 | 
				
			||||||
        ).addCallbackAndRun((title) => {
 | 
					             if(layer.title === undefined){
 | 
				
			||||||
            document.title = title
 | 
					                 continue;
 | 
				
			||||||
        });
 | 
					             }
 | 
				
			||||||
 | 
					            if (layer.overpassTags.matchesProperties(properties)) {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        fullScreenMessage.addCallbackAndRun(selected => {
 | 
					                const title = new TagRenderingAnswer(
 | 
				
			||||||
            const title = Translations.WT(layoutToUse.data?.title)?.txt ?? "MapComplete"
 | 
					                    this._allElementsStorage.getEventSourceFor(feature),
 | 
				
			||||||
            if(selected?.titleText?.data === undefined){
 | 
					                    layer.title
 | 
				
			||||||
                document.title = title
 | 
					                )
 | 
				
			||||||
            }else{
 | 
					                return new Combine([defaultTitle," | ", title]).Render();
 | 
				
			||||||
                selected.titleText.Update();
 | 
					 | 
				
			||||||
                var d = document.createElement('div');
 | 
					 | 
				
			||||||
                d.innerHTML = selected.titleText.InnerRender();
 | 
					 | 
				
			||||||
                const poi = (d.textContent || d.innerText)
 | 
					 | 
				
			||||||
                document.title = title + " | " + poi;
 | 
					 | 
				
			||||||
            }
 | 
					            }
 | 
				
			||||||
        })
 | 
					        }
 | 
				
			||||||
 | 
					        return defaultTitle;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
    
 | 
					    
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					export default class TitleHandler {
 | 
				
			||||||
 | 
					    constructor(layoutToUse: UIEventSource<LayoutConfig>,
 | 
				
			||||||
 | 
					                selectedFeature: UIEventSource<any>,
 | 
				
			||||||
 | 
					                allElementsStorage : ElementStorage) {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        new TitleElement(layoutToUse, selectedFeature, allElementsStorage)
 | 
				
			||||||
 | 
					            .addCallbackAndRun(contents => {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                const d = document.createElement('div');
 | 
				
			||||||
 | 
					                d.innerHTML = contents;
 | 
				
			||||||
 | 
					                // We pass everything into a div to strip out images etc...
 | 
				
			||||||
 | 
					                document.title = (d.textContent || d.innerText);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            });
 | 
				
			||||||
        
 | 
					        
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
| 
						 | 
					@ -74,7 +74,7 @@ export default class MetaTagging {
 | 
				
			||||||
                    const tagsSource = State.state.allElements.getEventSourceFor(feature);
 | 
					                    const tagsSource = State.state.allElements.getEventSourceFor(feature);
 | 
				
			||||||
                    tagsSource.ping();
 | 
					                    tagsSource.ping();
 | 
				
			||||||
                } catch (e) {
 | 
					                } catch (e) {
 | 
				
			||||||
                    console.error(e)
 | 
					                    console.warn(e)
 | 
				
			||||||
                }
 | 
					                }
 | 
				
			||||||
            });
 | 
					            });
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -4,7 +4,7 @@ import {Utils} from "../../Utils";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
export class OsmPreferences {
 | 
					export class OsmPreferences {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    public preferences = new UIEventSource<any>({});
 | 
					    public preferences = new UIEventSource<any>({}, "all-osm-preferences");
 | 
				
			||||||
    public preferenceSources: any = {}
 | 
					    public preferenceSources: any = {}
 | 
				
			||||||
    private auth: any;
 | 
					    private auth: any;
 | 
				
			||||||
    private userDetails: UIEventSource<UserDetails>;
 | 
					    private userDetails: UIEventSource<UserDetails>;
 | 
				
			||||||
| 
						 | 
					@ -29,7 +29,7 @@ export class OsmPreferences {
 | 
				
			||||||
            return this.longPreferences[prefix + key];
 | 
					            return this.longPreferences[prefix + key];
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        const source = new UIEventSource<string>(undefined);
 | 
					        const source = new UIEventSource<string>(undefined, "long-osm-preference:"+prefix+key);
 | 
				
			||||||
        this.longPreferences[prefix + key] = source;
 | 
					        this.longPreferences[prefix + key] = source;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        const allStartWith = prefix + key + "-combined";
 | 
					        const allStartWith = prefix + key + "-combined";
 | 
				
			||||||
| 
						 | 
					@ -106,7 +106,7 @@ export class OsmPreferences {
 | 
				
			||||||
        if (this.userDetails.data.loggedIn && this.preferences.data[key] === undefined) {
 | 
					        if (this.userDetails.data.loggedIn && this.preferences.data[key] === undefined) {
 | 
				
			||||||
            this.UpdatePreferences();
 | 
					            this.UpdatePreferences();
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
        const pref = new UIEventSource<string>(this.preferences.data[key]);
 | 
					        const pref = new UIEventSource<string>(this.preferences.data[key],"osm-preference:"+key);
 | 
				
			||||||
        pref.addCallback((v) => {
 | 
					        pref.addCallback((v) => {
 | 
				
			||||||
            this.SetPreference(key, v);
 | 
					            this.SetPreference(key, v);
 | 
				
			||||||
        });
 | 
					        });
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -1,14 +1,64 @@
 | 
				
			||||||
export class UIEventSource<T>{
 | 
					export class UIEventSource<T> {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    public data: T;
 | 
					    public data: T;
 | 
				
			||||||
 | 
					    private readonly tag: string;
 | 
				
			||||||
    private _callbacks = [];
 | 
					    private _callbacks = [];
 | 
				
			||||||
    
 | 
					    
 | 
				
			||||||
    constructor(data: T) {
 | 
					    private static allSources : UIEventSource<any>[] = UIEventSource.PrepPerf();
 | 
				
			||||||
 | 
					    
 | 
				
			||||||
 | 
					    static PrepPerf(){
 | 
				
			||||||
 | 
					        // @ts-ignore
 | 
				
			||||||
 | 
					        window.mcperf = () => {
 | 
				
			||||||
 | 
					            console.log(UIEventSource.allSources.length, "uieventsources created");
 | 
				
			||||||
 | 
					            const copy = [...UIEventSource.allSources];
 | 
				
			||||||
 | 
					            copy.sort((a,b) => b._callbacks.length - a._callbacks.length);
 | 
				
			||||||
 | 
					            console.log("Topten is:")
 | 
				
			||||||
 | 
					            for (let i = 0; i < 10; i++) {
 | 
				
			||||||
 | 
					                console.log(copy[i].tag, copy[i]);
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					        return [];
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    constructor(data: T, tag: string = "") {
 | 
				
			||||||
 | 
					        this.tag = tag;
 | 
				
			||||||
        this.data = data;
 | 
					        this.data = data;
 | 
				
			||||||
 | 
					        UIEventSource.allSources.push(this);
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    public static flatten<X>(source: UIEventSource<UIEventSource<X>>, possibleSources: UIEventSource<any>[]): UIEventSource<X> {
 | 
				
			||||||
 | 
					        const sink = new UIEventSource<X>(source.data?.data);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        source.addCallback((latestData) => {
 | 
				
			||||||
 | 
					            sink.setData(latestData?.data);
 | 
				
			||||||
 | 
					        });
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        for (const possibleSource of possibleSources) {
 | 
				
			||||||
 | 
					            possibleSource?.addCallback(() => {
 | 
				
			||||||
 | 
					                sink.setData(source.data?.data);
 | 
				
			||||||
 | 
					            })
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        return sink;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    public static Chronic(millis: number, asLong: () => boolean = undefined): UIEventSource<Date> {
 | 
				
			||||||
 | 
					        const source = new UIEventSource<Date>(undefined);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        function run() {
 | 
				
			||||||
 | 
					            source.setData(new Date());
 | 
				
			||||||
 | 
					            if (asLong === undefined || asLong()) {
 | 
				
			||||||
 | 
					                window.setTimeout(run, millis);
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        run();
 | 
				
			||||||
 | 
					        return source;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    public addCallback(callback: ((latestData: T) => void)): UIEventSource<T> {
 | 
					    public addCallback(callback: ((latestData: T) => void)): UIEventSource<T> {
 | 
				
			||||||
        if(callback === console.log){
 | 
					        if (callback === console.log) {
 | 
				
			||||||
            // This ^^^ actually works!
 | 
					            // This ^^^ actually works!
 | 
				
			||||||
            throw "Don't add console.log directly as a callback - you'll won't be able to find it afterwards. Wrap it in a lambda instead."
 | 
					            throw "Don't add console.log directly as a callback - you'll won't be able to find it afterwards. Wrap it in a lambda instead."
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
| 
						 | 
					@ -36,25 +86,9 @@ export class UIEventSource<T>{
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    public static flatten<X>(source: UIEventSource<UIEventSource<X>>, possibleSources: UIEventSource<any>[]): UIEventSource<X> {
 | 
					 | 
				
			||||||
        const sink = new UIEventSource<X>(source.data?.data);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        source.addCallback((latestData) => {
 | 
					 | 
				
			||||||
           sink.setData(latestData?.data);
 | 
					 | 
				
			||||||
        });
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        for (const possibleSource of possibleSources) {
 | 
					 | 
				
			||||||
            possibleSource?.addCallback(() => {
 | 
					 | 
				
			||||||
                sink.setData(source.data?.data);
 | 
					 | 
				
			||||||
            })
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        return sink;
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    public map<J>(f: ((T) => J),
 | 
					    public map<J>(f: ((T) => J),
 | 
				
			||||||
                  extraSources: UIEventSource<any>[] = [],
 | 
					                  extraSources: UIEventSource<any>[] = [],
 | 
				
			||||||
                  g: ((J) => T) = undefined ): UIEventSource<J> {
 | 
					                  g: ((J) => T) = undefined): UIEventSource<J> {
 | 
				
			||||||
        const self = this;
 | 
					        const self = this;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        const newSource = new UIEventSource<J>(
 | 
					        const newSource = new UIEventSource<J>(
 | 
				
			||||||
| 
						 | 
					@ -70,7 +104,7 @@ export class UIEventSource<T>{
 | 
				
			||||||
            extraSource?.addCallback(update);
 | 
					            extraSource?.addCallback(update);
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        if(g !== undefined) {
 | 
					        if (g !== undefined) {
 | 
				
			||||||
            newSource.addCallback((latest) => {
 | 
					            newSource.addCallback((latest) => {
 | 
				
			||||||
                self.setData(g(latest));
 | 
					                self.setData(g(latest));
 | 
				
			||||||
            })
 | 
					            })
 | 
				
			||||||
| 
						 | 
					@ -79,7 +113,6 @@ export class UIEventSource<T>{
 | 
				
			||||||
        return newSource;
 | 
					        return newSource;
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					 | 
				
			||||||
    public syncWith(otherSource: UIEventSource<T>, reverseOverride = false): UIEventSource<T> {
 | 
					    public syncWith(otherSource: UIEventSource<T>, reverseOverride = false): UIEventSource<T> {
 | 
				
			||||||
        this.addCallback((latest) => otherSource.setData(latest));
 | 
					        this.addCallback((latest) => otherSource.setData(latest));
 | 
				
			||||||
        const self = this;
 | 
					        const self = this;
 | 
				
			||||||
| 
						 | 
					@ -94,7 +127,7 @@ export class UIEventSource<T>{
 | 
				
			||||||
        return this;
 | 
					        return this;
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    public stabilized(millisToStabilize) : UIEventSource<T>{
 | 
					    public stabilized(millisToStabilize): UIEventSource<T> {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        const newSource = new UIEventSource<T>(this.data);
 | 
					        const newSource = new UIEventSource<T>(this.data);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					@ -103,7 +136,7 @@ export class UIEventSource<T>{
 | 
				
			||||||
            currentCallback++;
 | 
					            currentCallback++;
 | 
				
			||||||
            const thisCallback = currentCallback;
 | 
					            const thisCallback = currentCallback;
 | 
				
			||||||
            window.setTimeout(() => {
 | 
					            window.setTimeout(() => {
 | 
				
			||||||
                if(thisCallback === currentCallback){
 | 
					                if (thisCallback === currentCallback) {
 | 
				
			||||||
                    newSource.setData(latestData);
 | 
					                    newSource.setData(latestData);
 | 
				
			||||||
                }
 | 
					                }
 | 
				
			||||||
            }, millisToStabilize)
 | 
					            }, millisToStabilize)
 | 
				
			||||||
| 
						 | 
					@ -112,19 +145,4 @@ export class UIEventSource<T>{
 | 
				
			||||||
        return newSource;
 | 
					        return newSource;
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    public static Chronic(millis: number, asLong: () => boolean = undefined): UIEventSource<Date> {
 | 
					 | 
				
			||||||
        const source = new UIEventSource<Date>(undefined);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        function run() {
 | 
					 | 
				
			||||||
            source.setData(new Date());
 | 
					 | 
				
			||||||
            if (asLong === undefined || asLong()) {
 | 
					 | 
				
			||||||
                window.setTimeout(run, millis);
 | 
					 | 
				
			||||||
            }
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        run();
 | 
					 | 
				
			||||||
        return source;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
| 
						 | 
					@ -5,7 +5,7 @@ export class LocalStorageSource {
 | 
				
			||||||
    static Get(key: string, defaultValue: string = undefined): UIEventSource<string> {
 | 
					    static Get(key: string, defaultValue: string = undefined): UIEventSource<string> {
 | 
				
			||||||
        try {
 | 
					        try {
 | 
				
			||||||
            const saved = localStorage.getItem(key);
 | 
					            const saved = localStorage.getItem(key);
 | 
				
			||||||
            const source = new UIEventSource<string>(saved ?? defaultValue);
 | 
					            const source = new UIEventSource<string>(saved ?? defaultValue, "localstorage:"+key);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            source.addCallback((data) => {
 | 
					            source.addCallback((data) => {
 | 
				
			||||||
                localStorage.setItem(key, data);
 | 
					                localStorage.setItem(key, data);
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
							
								
								
									
										6
									
								
								State.ts
									
										
									
									
									
								
							
							
						
						
									
										6
									
								
								State.ts
									
										
									
									
									
								
							| 
						 | 
					@ -72,10 +72,6 @@ export default class State {
 | 
				
			||||||
     *  The message that should be shown at the center of the screen
 | 
					     *  The message that should be shown at the center of the screen
 | 
				
			||||||
     */
 | 
					     */
 | 
				
			||||||
    public readonly centerMessage = new UIEventSource<string>("");
 | 
					    public readonly centerMessage = new UIEventSource<string>("");
 | 
				
			||||||
    /**
 | 
					 | 
				
			||||||
     This message is shown full screen on mobile devices
 | 
					 | 
				
			||||||
     */
 | 
					 | 
				
			||||||
    public readonly fullScreenMessage = new UIEventSource<{ content: UIElement, hashText: string, titleText?: UIElement }>(undefined)
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
    /**
 | 
					    /**
 | 
				
			||||||
     The latest element that was selected - used to generate the right UI at the right place
 | 
					     The latest element that was selected - used to generate the right UI at the right place
 | 
				
			||||||
| 
						 | 
					@ -244,7 +240,7 @@ export default class State {
 | 
				
			||||||
            }
 | 
					            }
 | 
				
			||||||
        }).ping()
 | 
					        }).ping()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        new TitleHandler(this.layoutToUse, this.fullScreenMessage);
 | 
					        new TitleHandler(this.layoutToUse, this.selectedElement, this.allElements);
 | 
				
			||||||
        
 | 
					        
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        this.allElements = new ElementStorage();
 | 
					        this.allElements = new ElementStorage();
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -1,22 +1,25 @@
 | 
				
			||||||
import {UIElement} from "../UIElement";
 | 
					import {UIElement} from "../UIElement";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
export default class LazyElement extends UIElement {
 | 
					export default class LazyElement<T extends UIElement> extends UIElement {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    private _content: UIElement = undefined;
 | 
					    public Activate: (onElement?: (element: T) => void) => void;
 | 
				
			||||||
 | 
					    private _content: T = undefined;
 | 
				
			||||||
 | 
					    private readonly _loadingContent: string;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    public Activate: () => void;
 | 
					    constructor(content: (() => T), loadingContent = "Rendering...") {
 | 
				
			||||||
    private _loadingContent: string;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    constructor(content: (() => UIElement), loadingContent = "Rendering...") {
 | 
					 | 
				
			||||||
        super();
 | 
					        super();
 | 
				
			||||||
        this._loadingContent = loadingContent;
 | 
					        this._loadingContent = loadingContent;
 | 
				
			||||||
        this.dumbMode = false;
 | 
					        this.dumbMode = false;
 | 
				
			||||||
        const self = this;
 | 
					        const self = this;
 | 
				
			||||||
        this.Activate = () => {
 | 
					        this.Activate = (onElement?: (element: T) => void) => {
 | 
				
			||||||
 | 
					            console.log("ACTIVATED")
 | 
				
			||||||
            if (this._content === undefined) {
 | 
					            if (this._content === undefined) {
 | 
				
			||||||
                self._content = content();
 | 
					                self._content = content();
 | 
				
			||||||
            }
 | 
					            }
 | 
				
			||||||
 | 
					            if (onElement) {
 | 
				
			||||||
 | 
					                onElement(self._content)
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
            self.Update();
 | 
					            self.Update();
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
| 
						 | 
					@ -28,4 +31,6 @@ export default class LazyElement extends UIElement {
 | 
				
			||||||
        return this._content.InnerRender();
 | 
					        return this._content.InnerRender();
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
    
 | 
					    
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
| 
						 | 
					@ -1,6 +1,5 @@
 | 
				
			||||||
import {UIElement} from "../UIElement";
 | 
					import {UIElement} from "../UIElement";
 | 
				
			||||||
import Svg from "../../Svg";
 | 
					import Svg from "../../Svg";
 | 
				
			||||||
import State from "../../State";
 | 
					 | 
				
			||||||
import Combine from "./Combine";
 | 
					import Combine from "./Combine";
 | 
				
			||||||
import Ornament from "./Ornament";
 | 
					import Ornament from "./Ornament";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					@ -8,63 +7,150 @@ import Ornament from "./Ornament";
 | 
				
			||||||
 * Wraps some contents into a panel that scrolls the content _under_ the title
 | 
					 * Wraps some contents into a panel that scrolls the content _under_ the title
 | 
				
			||||||
 */
 | 
					 */
 | 
				
			||||||
export default class ScrollableFullScreen extends UIElement {
 | 
					export default class ScrollableFullScreen extends UIElement {
 | 
				
			||||||
 | 
					    private static _isInited = false;
 | 
				
			||||||
 | 
					    private title: UIElement;
 | 
				
			||||||
 | 
					    private content: UIElement;
 | 
				
			||||||
    private _component: UIElement;
 | 
					    private _component: UIElement;
 | 
				
			||||||
    private elementsToRestore: Set<HTMLElement> = new Set<HTMLElement>();
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
    constructor(title: UIElement, content: UIElement, onClose: (() => void)) {
 | 
					    constructor(title: UIElement, content: UIElement, onClose: (() => void)) {
 | 
				
			||||||
        super();
 | 
					        super();
 | 
				
			||||||
 | 
					        this.content = content;
 | 
				
			||||||
 | 
					        this.title = title;
 | 
				
			||||||
 | 
					        if (!ScrollableFullScreen._isInited) {
 | 
				
			||||||
 | 
					            ScrollableFullScreen._isInited = ScrollableFullScreen.PreparePatchesForFullscreen();
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					        if (onClose === undefined) {
 | 
				
			||||||
 | 
					            console.error("ScrollableFullScreen initialized without onClose!")
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
        this.dumbMode = false;
 | 
					        this.dumbMode = false;
 | 
				
			||||||
        const returnToTheMap = Svg.back_svg().onClick(() => {
 | 
					        const returnToTheMap =
 | 
				
			||||||
            console.log("Clicked back!");
 | 
					            new Combine([
 | 
				
			||||||
            this.RestoreLeaflet();
 | 
					                Svg.back_svg().SetClass("block sm:hidden"),
 | 
				
			||||||
            if (onClose() !== undefined) {
 | 
					                Svg.close_svg().SetClass("hidden sm:block")
 | 
				
			||||||
                console.error("WARNING: onClose is not defined")
 | 
					            ])
 | 
				
			||||||
                onClose();
 | 
					                .onClick(() => {
 | 
				
			||||||
            }
 | 
					                    console.log("Clicked back!");
 | 
				
			||||||
        }).SetClass("block sm:hidden mb-2 bg-blue-50 rounded-full w-12 h-12 p-1.5")
 | 
					                    ScrollableFullScreen.RestoreLeaflet();
 | 
				
			||||||
 | 
					                    if (onClose !== undefined) {
 | 
				
			||||||
 | 
					                        onClose();
 | 
				
			||||||
 | 
					                    } else {
 | 
				
			||||||
 | 
					                        console.error("WARNING: onClose is not defined")
 | 
				
			||||||
 | 
					                    }
 | 
				
			||||||
 | 
					                }).SetClass("mb-2 bg-blue-50 rounded-full w-12 h-12 p-1.5")
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        title.SetClass("block w-full")
 | 
					        title.SetClass("block w-full text-2xl font-bold p-2 pl-4")
 | 
				
			||||||
        const ornament = new Combine([new Ornament().SetStyle("height:5em;")])
 | 
					        const ornament = new Combine([new Ornament().SetStyle("height:5em;")])
 | 
				
			||||||
            .SetClass("block sm:hidden h-5")
 | 
					            .SetClass("block sm:hidden h-5")
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        this._component =
 | 
					        this._component =
 | 
				
			||||||
            new Combine([
 | 
					            new Combine([
 | 
				
			||||||
            new Combine([
 | 
					                new Combine([
 | 
				
			||||||
            new Combine([returnToTheMap, title])
 | 
					                    new Combine([returnToTheMap, title])
 | 
				
			||||||
                .AddClass("border-b-2 border-black shadow sm:shadow-none bg-white p-2 pb-0 sm:p-0 flex overflow-x-hidden flex-shrink-0 max-h-20vh"),
 | 
					                        .SetClass("border-b-2 border-black shadow sm:shadow-none bg-white p-2 pb-0 sm:p-0 flex overflow-x-hidden flex-shrink-0 max-h-20vh"),
 | 
				
			||||||
            new Combine(["<span>", content, "</span>", ornament])
 | 
					                    new Combine(["<span>", content, "</span>", ornament])
 | 
				
			||||||
                .SetClass("block p-2 sm:pt-4 w-full h-screen landscape:h-screen  sm:h-full sm:w-full overflow-y-auto overflow-x-hidden"),
 | 
					                        .SetClass("block p-2 sm:pt-4 w-full h-screen landscape:h-screen  sm:h-full sm:w-full overflow-y-auto overflow-x-hidden"),
 | 
				
			||||||
            // We add an ornament which takes around 5em. This is in order to make sure the Web UI doesn't hide
 | 
					                    // We add an ornament which takes around 5em. This is in order to make sure the Web UI doesn't hide
 | 
				
			||||||
        ]).SetClass("block flex flex-col  relative bg-white")
 | 
					                ]).SetClass("block flex flex-col  relative bg-white")
 | 
				
			||||||
        ]).SetClass("fixed top-0 left-0 right-0 h-screen w-screen sm:max-h-65vh sm:w-auto");
 | 
					            ]).SetClass("fixed top-0 left-0 right-0 h-screen w-screen sm:max-h-65vh sm:w-auto sm:relative");
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    private static HideClutter(htmlElement: HTMLElement) {
 | 
				
			||||||
 | 
					        const whiteList = new Set<Element>();
 | 
				
			||||||
 | 
					        do {
 | 
				
			||||||
 | 
					            if(htmlElement === null){
 | 
				
			||||||
 | 
					                break;
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					            if (htmlElement.classList.contains("clutter")) {
 | 
				
			||||||
 | 
					                // Don't hide the parent element
 | 
				
			||||||
 | 
					                whiteList.add(htmlElement)
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					            htmlElement = htmlElement.parentElement;
 | 
				
			||||||
 | 
					        } while (htmlElement != null)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        const clutter = document.getElementsByClassName("clutter");
 | 
				
			||||||
 | 
					        for (let i = 0; i < clutter.length; ++i) {
 | 
				
			||||||
 | 
					            if (whiteList.has(clutter[i])) {
 | 
				
			||||||
 | 
					                continue;
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					            const classlist = clutter[i].classList;
 | 
				
			||||||
 | 
					            if (classlist.contains("clutter-hidden")) {
 | 
				
			||||||
 | 
					                continue;
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					            classlist.add("clutter-hidden");
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * Adds the 'clutter' class (which merely acts as a tag) onto some elements, e.g. the leaflet attributions
 | 
				
			||||||
 | 
					     * @constructor
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    private static PreparePatchesForFullscreen(): boolean {
 | 
				
			||||||
 | 
					        const toHide = document.getElementsByClassName("leaflet-control-container");
 | 
				
			||||||
 | 
					        for (let i = 0; i < toHide.length; ++i) {
 | 
				
			||||||
 | 
					            toHide[i].classList.add("clutter");
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					        return true;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    private static PatchLeaflet(htmlElement) {
 | 
				
			||||||
 | 
					        if(htmlElement === null){
 | 
				
			||||||
 | 
					            return;
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					        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
 | 
				
			||||||
 | 
					            if (htmlElement.style.transform !== "") {
 | 
				
			||||||
 | 
					                if (!htmlElement.classList.contains("no-transform")) {
 | 
				
			||||||
 | 
					                    htmlElement.classList.add("no-transform");
 | 
				
			||||||
 | 
					                    htmlElement.classList.add("scrollable-fullscreen-no-transform")
 | 
				
			||||||
 | 
					                }
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					            htmlElement = htmlElement.parentElement;
 | 
				
			||||||
 | 
					        } while (htmlElement != null)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    private static RestoreLeaflet() {
 | 
				
			||||||
 | 
					        console.log("Restoring")
 | 
				
			||||||
 | 
					        const noTransf = document.getElementsByClassName("scrollable-fullscreen-no-transform");
 | 
				
			||||||
 | 
					        for (let i = 0; i < noTransf.length; ++i) {
 | 
				
			||||||
 | 
					            noTransf[i].classList.remove("no-transform");
 | 
				
			||||||
 | 
					            noTransf[i].classList.remove("scrollable-fullscreen-no-transform");
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					        let clutter = document.getElementsByClassName("clutter-hidden");
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        do {
 | 
				
			||||||
 | 
					            for (let i = 0; i < clutter.length; ++i) {
 | 
				
			||||||
 | 
					                clutter[i].classList.remove("clutter-hidden");
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					            clutter = document.getElementsByClassName("clutter-hidden");
 | 
				
			||||||
 | 
					        } while (clutter.length > 0)
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    InnerRender(): string {
 | 
					    InnerRender(): string {
 | 
				
			||||||
        return this._component.Render();
 | 
					        return this._component.Render();
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
    
 | 
					    
 | 
				
			||||||
    protected InnerUpdate(htmlElement: HTMLElement) {
 | 
					    Update() {
 | 
				
			||||||
 | 
					        console.log("Updating  the scrollableFullScreen")
 | 
				
			||||||
        do {
 | 
					        super.Update();
 | 
				
			||||||
            // 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
 | 
					        this._component.Update();
 | 
				
			||||||
            if (htmlElement.style.transform !== "") {
 | 
					 | 
				
			||||||
                this.elementsToRestore.add(htmlElement);
 | 
					 | 
				
			||||||
                htmlElement.classList.add("no-transform")
 | 
					 | 
				
			||||||
            }
 | 
					 | 
				
			||||||
            htmlElement = htmlElement.parentElement;
 | 
					 | 
				
			||||||
        } while (htmlElement != null)
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        super.InnerUpdate(htmlElement);
 | 
					 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    private RestoreLeaflet() {
 | 
					    public PrepFullscreen(htmlElement = undefined) {
 | 
				
			||||||
        this.elementsToRestore.forEach(
 | 
					        htmlElement = htmlElement ?? document.getElementById(this.id);
 | 
				
			||||||
            el => el.classList.remove("no-transform")
 | 
					        ScrollableFullScreen.PatchLeaflet(htmlElement);
 | 
				
			||||||
        );
 | 
					        ScrollableFullScreen.HideClutter(htmlElement);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    protected InnerUpdate(htmlElement: HTMLElement) {
 | 
				
			||||||
 | 
					        this.PrepFullscreen(htmlElement)
 | 
				
			||||||
 | 
					        super.InnerUpdate(htmlElement);
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
| 
						 | 
					@ -24,9 +24,9 @@ export class SubtleButton extends UIElement{
 | 
				
			||||||
        } else {
 | 
					        } else {
 | 
				
			||||||
            img = imageUrl;
 | 
					            img = imageUrl;
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
        img.AddClass("block flex items-center justify-center h-11 w-11 flex-shrink0")
 | 
					        img.SetClass("block flex items-center justify-center h-11 w-11 flex-shrink0")
 | 
				
			||||||
        this.image = new Combine([img])
 | 
					        this.image = new Combine([img])
 | 
				
			||||||
            .AddClass("flex-shrink-0");
 | 
					            .SetClass("flex-shrink-0");
 | 
				
			||||||
        
 | 
					        
 | 
				
			||||||
       
 | 
					       
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
| 
						 | 
					@ -53,7 +53,7 @@ export class SubtleButton extends UIElement{
 | 
				
			||||||
        return new Combine([
 | 
					        return new Combine([
 | 
				
			||||||
            this.image,
 | 
					            this.image,
 | 
				
			||||||
            this.message,
 | 
					            this.message,
 | 
				
			||||||
        ]).AddClass("block flex p-3 my-2 bg-blue-100 rounded-lg hover:shadow-xl hover:bg-blue-200")
 | 
					        ]).SetClass("block flex p-3 my-2 bg-blue-100 rounded-lg hover:shadow-xl hover:bg-blue-200")
 | 
				
			||||||
            .Render();
 | 
					            .Render();
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -7,22 +7,22 @@ export default class IndexText extends Combine {
 | 
				
			||||||
    constructor() {
 | 
					    constructor() {
 | 
				
			||||||
        super([
 | 
					        super([
 | 
				
			||||||
            new FixedUiElement(`<img class="w-12 h-12 sm:h-24 sm:w-24" src="./assets/svg/logo.svg" alt="MapComplete Logo">`)
 | 
					            new FixedUiElement(`<img class="w-12 h-12 sm:h-24 sm:w-24" src="./assets/svg/logo.svg" alt="MapComplete Logo">`)
 | 
				
			||||||
                .AddClass("flex-none m-3"),
 | 
					                .SetClass("flex-none m-3"),
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            new Combine([
 | 
					            new Combine([
 | 
				
			||||||
                Translations.t.index.title
 | 
					                Translations.t.index.title
 | 
				
			||||||
                    .AddClass("text-2xl tracking-tight font-extrabold text-gray-900 sm:text-5xl md:text-6xl block text-gray-800 xl:inline"),
 | 
					                    .SetClass("text-2xl tracking-tight font-extrabold text-gray-900 sm:text-5xl md:text-6xl block text-gray-800 xl:inline"),
 | 
				
			||||||
 | 
					
 | 
				
			||||||
                Translations.t.index.intro.AddClass(
 | 
					                Translations.t.index.intro.SetClass(
 | 
				
			||||||
                    "mt-3 text-base font-semibold text-gray-500 sm:mt-5 sm:text-lg sm:max-w-xl sm:mx-auto md:mt-5 md:text-xl lg:mx-0"),
 | 
					                    "mt-3 text-base font-semibold text-gray-500 sm:mt-5 sm:text-lg sm:max-w-xl sm:mx-auto md:mt-5 md:text-xl lg:mx-0"),
 | 
				
			||||||
 | 
					
 | 
				
			||||||
                Translations.t.index.pickTheme.AddClass("mt-3 text-base text-green-600 sm:mt-5 sm:text-lg sm:max-w-xl sm:mx-auto md:mt-5 md:text-xl lg:mx-0")
 | 
					                Translations.t.index.pickTheme.SetClass("mt-3 text-base text-green-600 sm:mt-5 sm:text-lg sm:max-w-xl sm:mx-auto md:mt-5 md:text-xl lg:mx-0")
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            ]).AddClass("flex flex-col sm:text-center lg:text-left m-1 mt-2 md:m-2 md:mt-4")
 | 
					            ]).SetClass("flex flex-col sm:text-center lg:text-left m-1 mt-2 md:m-2 md:mt-4")
 | 
				
			||||||
        ]);
 | 
					        ]);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        this.AddClass("flex flex-row");
 | 
					        this.SetClass("flex flex-row");
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
| 
						 | 
					@ -22,7 +22,7 @@ export default class SearchAndGo extends UIElement {
 | 
				
			||||||
    );
 | 
					    );
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    private _foundEntries = new UIEventSource([]);
 | 
					    private _foundEntries = new UIEventSource([]);
 | 
				
			||||||
    private _goButton = Svg.search_ui().AddClass('w-8 h-8 full-rounded border-black float-right');
 | 
					    private _goButton = Svg.search_ui().SetClass('w-8 h-8 full-rounded border-black float-right');
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    constructor() {
 | 
					    constructor() {
 | 
				
			||||||
        super(undefined);
 | 
					        super(undefined);
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -1,32 +0,0 @@
 | 
				
			||||||
import {UIElement} from "./UIElement";
 | 
					 | 
				
			||||||
import State from "../State";
 | 
					 | 
				
			||||||
import Combine from "./Base/Combine";
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
/**
 | 
					 | 
				
			||||||
 * Handles the full screen popup on mobile
 | 
					 | 
				
			||||||
 */
 | 
					 | 
				
			||||||
export default class FullScreenMessageBox extends UIElement {
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    private _content: UIElement;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    constructor() {
 | 
					 | 
				
			||||||
        super(State.state.fullScreenMessage);
 | 
					 | 
				
			||||||
        this.HideOnEmpty(true);
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    InnerRender(): string {
 | 
					 | 
				
			||||||
        if (State.state.fullScreenMessage.data === undefined) {
 | 
					 | 
				
			||||||
            return "";
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
        this._content = State.state.fullScreenMessage.data.content;
 | 
					 | 
				
			||||||
        return new Combine([this._content])
 | 
					 | 
				
			||||||
            .SetClass("block max-h-screen h-screen overflow-x-hidden overflow-y-auto bg-white p-0").Render();
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    protected InnerUpdate(htmlElement: HTMLElement) {
 | 
					 | 
				
			||||||
        super.InnerUpdate(htmlElement);
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
| 
						 | 
					@ -9,29 +9,41 @@ import State from "../../State";
 | 
				
			||||||
import TagRenderingConfig from "../../Customizations/JSON/TagRenderingConfig";
 | 
					import TagRenderingConfig from "../../Customizations/JSON/TagRenderingConfig";
 | 
				
			||||||
import ScrollableFullScreen from "../Base/ScrollableFullScreen";
 | 
					import ScrollableFullScreen from "../Base/ScrollableFullScreen";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
export default class FeatureInfoBox extends UIElement {
 | 
					export default class FeatureInfoBox extends ScrollableFullScreen {
 | 
				
			||||||
    private _component: UIElement;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    public title: UIElement ;
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
    constructor(
 | 
					    constructor(
 | 
				
			||||||
        tags: UIEventSource<any>,
 | 
					        tags: UIEventSource<any>,
 | 
				
			||||||
        layerConfig: LayerConfig,
 | 
					        layerConfig: LayerConfig,
 | 
				
			||||||
        onClose: () => {}
 | 
					        onClose: () => void
 | 
				
			||||||
    ) {
 | 
					    ) {
 | 
				
			||||||
        super();
 | 
					        super(
 | 
				
			||||||
 | 
					            FeatureInfoBox.GenerateTitleBar(tags, layerConfig),
 | 
				
			||||||
 | 
					            FeatureInfoBox.GenerateContent(tags, layerConfig),
 | 
				
			||||||
 | 
					            onClose
 | 
				
			||||||
 | 
					        );
 | 
				
			||||||
        if (layerConfig === undefined) {
 | 
					        if (layerConfig === undefined) {
 | 
				
			||||||
            throw "Undefined layerconfig"
 | 
					            throw "Undefined layerconfig"
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
    
 | 
					    
 | 
				
			||||||
 | 
					    private static GenerateTitleBar( tags: UIEventSource<any>,
 | 
				
			||||||
 | 
					                                     layerConfig: LayerConfig): UIElement{
 | 
				
			||||||
        const title = new TagRenderingAnswer(tags, layerConfig.title ?? new TagRenderingConfig("POI", undefined))
 | 
					        const title = new TagRenderingAnswer(tags, layerConfig.title ?? new TagRenderingConfig("POI", undefined))
 | 
				
			||||||
            .AddClass("text-2xl break-words font-bold p-2");
 | 
					            .SetClass("text-2xl break-words font-bold p-2");
 | 
				
			||||||
        this.title = title;
 | 
					 | 
				
			||||||
        const titleIcons = new Combine(
 | 
					        const titleIcons = new Combine(
 | 
				
			||||||
            layerConfig.titleIcons.map(icon => new TagRenderingAnswer(tags, icon)
 | 
					            layerConfig.titleIcons.map(icon => new TagRenderingAnswer(tags, icon)
 | 
				
			||||||
                .AddClass("block w-8 h-8 align-baseline box-content p-0.5")))
 | 
					                .SetClass("block w-8 h-8 align-baseline box-content p-0.5")))
 | 
				
			||||||
            .AddClass("flex flex-row flex-wrap pt-1 items-center mr-2");
 | 
					            .SetClass("flex flex-row flex-wrap pt-1 items-center mr-2");
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					      return new Combine([
 | 
				
			||||||
 | 
					            new Combine([title, titleIcons]).SetClass("flex flex-grow justify-between")
 | 
				
			||||||
 | 
					        ])
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    
 | 
				
			||||||
 | 
					    private static GenerateContent(tags: UIEventSource<any>,
 | 
				
			||||||
 | 
					                                   layerConfig: LayerConfig): UIElement{
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        let questionBox: UIElement = undefined;
 | 
					        let questionBox: UIElement = undefined;
 | 
				
			||||||
        if (State.state.featureSwitchUserbadge.data) {
 | 
					        if (State.state.featureSwitchUserbadge.data) {
 | 
				
			||||||
| 
						 | 
					@ -53,20 +65,12 @@ export default class FeatureInfoBox extends UIElement {
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
        const tail = new Combine([]).SetClass("only-on-mobile");
 | 
					        const tail = new Combine([]).SetClass("only-on-mobile");
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        const content = new Combine([
 | 
					        return new Combine([
 | 
				
			||||||
                ...renderings,
 | 
					                ...renderings,
 | 
				
			||||||
                tail.SetClass("featureinfobox-tail")
 | 
					                tail.SetClass("featureinfobox-tail")
 | 
				
			||||||
            ]
 | 
					            ]
 | 
				
			||||||
        )
 | 
					        )
 | 
				
			||||||
        const titleBar = new Combine([
 | 
					 | 
				
			||||||
            new Combine([title, titleIcons]).SetClass("flex flex-grow justify-between")
 | 
					 | 
				
			||||||
        ])
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
        this._component = new ScrollableFullScreen(titleBar, content, onClose)
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    InnerRender(): string {
 | 
					 | 
				
			||||||
        return this._component.Render();
 | 
					 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -21,7 +21,7 @@ export default class TagRenderingAnswer extends UIElement {
 | 
				
			||||||
        if (configuration === undefined) {
 | 
					        if (configuration === undefined) {
 | 
				
			||||||
            throw "Trying to generate a tagRenderingAnswer without configuration..."
 | 
					            throw "Trying to generate a tagRenderingAnswer without configuration..."
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
        this.AddClass("flex items-center flex-row text-lg")
 | 
					        this.SetClass("flex items-center flex-row text-lg")
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    InnerRender(): string {
 | 
					    InnerRender(): string {
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -88,7 +88,7 @@ export default class TagRenderingQuestion extends UIElement {
 | 
				
			||||||
                    return tags.asHumanString(true, true);
 | 
					                    return tags.asHumanString(true, true);
 | 
				
			||||||
                }
 | 
					                }
 | 
				
			||||||
            )
 | 
					            )
 | 
				
			||||||
        ).AddClass("block")
 | 
					        ).SetClass("block")
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    private GenerateInputElement(): InputElement<TagsFilter> {
 | 
					    private GenerateInputElement(): InputElement<TagsFilter> {
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -44,10 +44,10 @@ export default class ShowDataLayer {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            let geoLayer = self.CreateGeojsonLayer(feats)
 | 
					            let geoLayer = self.CreateGeojsonLayer(feats)
 | 
				
			||||||
            if (layoutToUse.clustering.minNeededElements <= features.data.length) {
 | 
					            if (layoutToUse.clustering.minNeededElements <= features.data.length) {
 | 
				
			||||||
                    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
 | 
				
			||||||
                    const cluster = cl.markerClusterGroup({ disableClusteringAtZoom: layoutToUse.clustering.maxZoom });
 | 
					                const cluster = cl.markerClusterGroup({disableClusteringAtZoom: layoutToUse.clustering.maxZoom});
 | 
				
			||||||
                    cluster.addLayer(geoLayer);
 | 
					                cluster.addLayer(geoLayer);
 | 
				
			||||||
                    geoLayer = cluster;
 | 
					                geoLayer = cluster;
 | 
				
			||||||
            }
 | 
					            }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            if (oldGeoLayer) {
 | 
					            if (oldGeoLayer) {
 | 
				
			||||||
| 
						 | 
					@ -60,22 +60,22 @@ export default class ShowDataLayer {
 | 
				
			||||||
        features.addCallbackAndRun(() => update());
 | 
					        features.addCallbackAndRun(() => update());
 | 
				
			||||||
        leafletMap.addCallback(() => update());
 | 
					        leafletMap.addCallback(() => update());
 | 
				
			||||||
        State.state.selectedElement.addCallback(feature => {
 | 
					        State.state.selectedElement.addCallback(feature => {
 | 
				
			||||||
            if(feature === undefined){
 | 
					            if (feature === undefined) {
 | 
				
			||||||
                return;
 | 
					                return;
 | 
				
			||||||
            }
 | 
					            }
 | 
				
			||||||
            const id = feature.properties.id+feature.geometry.type+feature._matching_layer_id;
 | 
					            const id = feature.properties.id + feature.geometry.type + feature._matching_layer_id;
 | 
				
			||||||
            const action = self._onSelectedTrigger[id];
 | 
					            const action = self._onSelectedTrigger[id];
 | 
				
			||||||
            if(action){
 | 
					            if (action) {
 | 
				
			||||||
                action();
 | 
					                action();
 | 
				
			||||||
            }
 | 
					            }
 | 
				
			||||||
        });
 | 
					        });
 | 
				
			||||||
        Hash.hash.addCallbackAndRun(id => {
 | 
					        Hash.hash.addCallbackAndRun(id => {
 | 
				
			||||||
            // This is a bit of an edge case: if the hash becomes an id to search, we have to show the corresponding popup
 | 
					            // This is a bit of an edge case: if the hash becomes an id to search, we have to show the corresponding popup
 | 
				
			||||||
            if(State.state.selectedElement !== undefined){
 | 
					            if (State.state.selectedElement !== undefined) {
 | 
				
			||||||
                return; // Something is already selected, we don't have to apply this fix
 | 
					                return; // Something is already selected, we don't have to apply this fix
 | 
				
			||||||
            }
 | 
					            }
 | 
				
			||||||
            const action = self._onSelectedTrigger[id];
 | 
					            const action = self._onSelectedTrigger[id];
 | 
				
			||||||
            if(action){
 | 
					            if (action) {
 | 
				
			||||||
                action();
 | 
					                action();
 | 
				
			||||||
            }
 | 
					            }
 | 
				
			||||||
        })
 | 
					        })
 | 
				
			||||||
| 
						 | 
					@ -126,41 +126,48 @@ export default class ShowDataLayer {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        const tags = State.state.allElements.getEventSourceFor(feature);
 | 
					        const tags = State.state.allElements.getEventSourceFor(feature);
 | 
				
			||||||
        const uiElement: LazyElement = new LazyElement(() => new FeatureInfoBox(tags, layer, () => popup.closePopup()),
 | 
					        const uiElement: LazyElement<FeatureInfoBox> = new LazyElement(() => new FeatureInfoBox(tags, layer, () => {
 | 
				
			||||||
 | 
					                console.log("Closing the popup!")
 | 
				
			||||||
 | 
					                State.state.selectedElement.setData(undefined);
 | 
				
			||||||
 | 
					                popup.remove();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            }),
 | 
				
			||||||
            "<div style='height: 90vh'>Rendering</div>");
 | 
					            "<div style='height: 90vh'>Rendering</div>");
 | 
				
			||||||
        popup.setContent(uiElement.Render());
 | 
					        popup.setContent(uiElement.Render());
 | 
				
			||||||
        popup.on('remove', () => {
 | 
					        popup.on('remove', () => {
 | 
				
			||||||
            if(!popup.isOpen()){
 | 
					            if (!popup.isOpen()) {
 | 
				
			||||||
                return;
 | 
					                return;
 | 
				
			||||||
            }
 | 
					            }
 | 
				
			||||||
           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("click", (e) => {
 | 
					        leafletLayer.on("click", () => {
 | 
				
			||||||
            // We set the element as selected...
 | 
					            // We set the element as selected...
 | 
				
			||||||
            uiElement.Activate();
 | 
					            
 | 
				
			||||||
 | 
					            uiElement.Activate(e => e.PrepFullscreen());
 | 
				
			||||||
            State.state.selectedElement.setData(feature);
 | 
					            State.state.selectedElement.setData(feature);
 | 
				
			||||||
        });
 | 
					        });
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        const id = feature.properties.id+feature.geometry.type+feature._matching_layer_id;
 | 
					        const id = feature.properties.id + feature.geometry.type + feature._matching_layer_id;
 | 
				
			||||||
        this._onSelectedTrigger[id]
 | 
					        this._onSelectedTrigger[id]
 | 
				
			||||||
         = () => {
 | 
					            = () => {
 | 
				
			||||||
            if(popup.isOpen()){
 | 
					            if (popup.isOpen()) {
 | 
				
			||||||
                return;
 | 
					                return;
 | 
				
			||||||
            }
 | 
					            }
 | 
				
			||||||
            leafletLayer.openPopup();
 | 
					            leafletLayer.openPopup();
 | 
				
			||||||
            uiElement.Activate();
 | 
					            uiElement.Activate(e => e.PrepFullscreen());
 | 
				
			||||||
            State.state.selectedElement.setData(feature);
 | 
					            State.state.selectedElement.setData(feature);
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
        this._onSelectedTrigger[feature.properties.id.replace("/","_")] = this._onSelectedTrigger[id];
 | 
					        this._onSelectedTrigger[feature.properties.id.replace("/", "_")] = this._onSelectedTrigger[id];
 | 
				
			||||||
        if (feature.properties.id.replace(/\//g, "_") === Hash.hash.data) {
 | 
					        if (feature.properties.id.replace(/\//g, "_") === Hash.hash.data && State.state.selectedElement.data === undefined) {
 | 
				
			||||||
            // This element is in the URL, so this is a share link
 | 
					            // This element is in the URL, so this is a share link
 | 
				
			||||||
            // We open the relevant popup straight away
 | 
					            // We open the relevant popup straight away
 | 
				
			||||||
            uiElement.Activate();
 | 
					            console.log("Opening the popup due to sharelink")
 | 
				
			||||||
 | 
					            uiElement.Activate(e => e.PrepFullscreen());
 | 
				
			||||||
            popup.setContent(uiElement.Render());
 | 
					            popup.setContent(uiElement.Render());
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            const center = GeoOperations.centerpoint(feature).geometry.coordinates;
 | 
					            const center = GeoOperations.centerpoint(feature).geometry.coordinates;
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -149,16 +149,12 @@ export abstract class UIElement extends UIEventSource<string> {
 | 
				
			||||||
        return this.InnerRender() === "";
 | 
					        return this.InnerRender() === "";
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    public SetClass(clss: string): UIElement {
 | 
					 | 
				
			||||||
        return this.AddClass(clss);
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    /**
 | 
					    /**
 | 
				
			||||||
     * Adds all the relevant classes, space seperated
 | 
					     * Adds all the relevant classes, space seperated
 | 
				
			||||||
     * @param clss
 | 
					     * @param clss
 | 
				
			||||||
     * @constructor
 | 
					     * @constructor
 | 
				
			||||||
     */
 | 
					     */
 | 
				
			||||||
    public AddClass(clss: string) {
 | 
					    public SetClass(clss: string) {
 | 
				
			||||||
        this.dumbMode = false;
 | 
					        this.dumbMode = false;
 | 
				
			||||||
        const all = clss.split(" ");
 | 
					        const all = clss.split(" ");
 | 
				
			||||||
        let recordedChange = false;
 | 
					        let recordedChange = false;
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -79,10 +79,3 @@
 | 
				
			||||||
    max-height: 30vh;
 | 
					    max-height: 30vh;
 | 
				
			||||||
    border-radius: 1em;
 | 
					    border-radius: 1em;
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
.hidden {
 | 
					 | 
				
			||||||
    /* This is used by the slideshow, to hide non-active slides*/
 | 
					 | 
				
			||||||
    display: none !important;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
							
								
								
									
										16
									
								
								index.css
									
										
									
									
									
								
							
							
						
						
									
										16
									
								
								index.css
									
										
									
									
									
								
							| 
						 | 
					@ -32,15 +32,21 @@
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@media only screen and (max-width: 640px), only screen and (max-height: 640px) {
 | 
					@media only screen and (max-width: 640px) {
 | 
				
			||||||
    .no-transform {
 | 
					    .no-transform {
 | 
				
			||||||
        /*This is a workaround to let popup contents escape the popup on mobile*/
 | 
					        /*This is a workaround to let popup contents escape the popup on mobile - see scrollableFullScreen.ts*/
 | 
				
			||||||
        transform: none !important;
 | 
					        transform: none !important;
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    .clutter-hidden {
 | 
				
			||||||
 | 
					        /*This is a workaround to let popup contents escape the popup on mobile - see scrollableFullScreen.ts*/
 | 
				
			||||||
 | 
					        visibility: hidden !important;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
:root {
 | 
					:root {
 | 
				
			||||||
    --subtle-detail-color: #e5f5ff;
 | 
					    --subtle-detail-color: #e5f5ff;
 | 
				
			||||||
    --subtle-detail-color-contrast: black;
 | 
					    --subtle-detail-color-contrast: black;
 | 
				
			||||||
| 
						 | 
					@ -57,6 +63,11 @@
 | 
				
			||||||
    --return-to-the-map-height: 2em;
 | 
					    --return-to-the-map-height: 2em;
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					.hide-when-fullscreen-is-shown {
 | 
				
			||||||
 | 
					    /*Clutter is actually a class indicating that the element should be hidden when a scrollableFullScreen is opened
 | 
				
			||||||
 | 
					    It doesn't actually define any rules*/
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
html, body {
 | 
					html, body {
 | 
				
			||||||
    height: 100%;
 | 
					    height: 100%;
 | 
				
			||||||
    min-height: 100vh;
 | 
					    min-height: 100vh;
 | 
				
			||||||
| 
						 | 
					@ -333,7 +344,6 @@ a {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
.welcomeMessage {
 | 
					.welcomeMessage {
 | 
				
			||||||
    display: block;
 | 
					    display: block;
 | 
				
			||||||
    margin-left: 4em;
 | 
					 | 
				
			||||||
    max-width: calc(100vw - 5em);
 | 
					    max-width: calc(100vw - 5em);
 | 
				
			||||||
    width: 40em;
 | 
					    width: 40em;
 | 
				
			||||||
    max-height: calc(100vh - 15em);
 | 
					    max-height: calc(100vh - 15em);
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
							
								
								
									
										10
									
								
								index.html
									
										
									
									
									
								
							
							
						
						
									
										10
									
								
								index.html
									
										
									
									
									
								
							| 
						 | 
					@ -44,11 +44,7 @@
 | 
				
			||||||
    <!-- DECORATION 0 END -->
 | 
					    <!-- DECORATION 0 END -->
 | 
				
			||||||
</div>
 | 
					</div>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
<div class="only-on-mobile">
 | 
					<div id="topleft-tools" class="z-index-above-map clutter">
 | 
				
			||||||
    <div id="messagesboxmobile"></div>
 | 
					 | 
				
			||||||
</div>
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
<div id="topleft-tools" class="z-index-above-map">
 | 
					 | 
				
			||||||
    <div id="userbadge-and-search" class="p-3">
 | 
					    <div id="userbadge-and-search" class="p-3">
 | 
				
			||||||
        <div id="userbadge" class="shadow rounded-3xl overflow-hidden"></div>
 | 
					        <div id="userbadge" class="shadow rounded-3xl overflow-hidden"></div>
 | 
				
			||||||
        <div id="searchbox" class="shadow rounded-3xl overflow-hidden"></div>
 | 
					        <div id="searchbox" class="shadow rounded-3xl overflow-hidden"></div>
 | 
				
			||||||
| 
						 | 
					@ -57,13 +53,13 @@
 | 
				
			||||||
    <div id="help-button-mobile"></div>
 | 
					    <div id="help-button-mobile"></div>
 | 
				
			||||||
</div>
 | 
					</div>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
<div id="layer-selection" class="absolute bottom-3 left-3 rounded-3xl overflow-hidden"></div>
 | 
					<div id="layer-selection" class="absolute bottom-3 left-3 rounded-3xl overflow-hidden clutter"></div>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
<div id="centermessage" class="absolute rounded-3xl h-24 left-24 right-24 top-56 bg-white p-3 pt-5 sm:pt-8 text-xl font-bold text-center">
 | 
					<div id="centermessage" class="absolute rounded-3xl h-24 left-24 right-24 top-56 bg-white p-3 pt-5 sm:pt-8 text-xl font-bold text-center">
 | 
				
			||||||
    Loading MapComplete, hang on...
 | 
					    Loading MapComplete, hang on...
 | 
				
			||||||
</div>
 | 
					</div>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
<div id="geolocate-button"></div>
 | 
					<div id="geolocate-button" class="clutter"></div>
 | 
				
			||||||
<div id="leafletDiv"></div>
 | 
					<div id="leafletDiv"></div>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
<script src="./index.ts"></script>
 | 
					<script src="./index.ts"></script>
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
							
								
								
									
										3
									
								
								index.ts
									
										
									
									
									
								
							
							
						
						
									
										3
									
								
								index.ts
									
										
									
									
									
								
							| 
						 | 
					@ -119,11 +119,10 @@ if (layoutFromBase64.startsWith("wiki:")) {
 | 
				
			||||||
} else {
 | 
					} else {
 | 
				
			||||||
    // We fall through: no theme loaded: just show a few buttons
 | 
					    // We fall through: no theme loaded: just show a few buttons
 | 
				
			||||||
    State.state = new State(undefined);
 | 
					    State.state = new State(undefined);
 | 
				
			||||||
    document.getElementById("messagesboxmobile").remove();
 | 
					 | 
				
			||||||
    new Combine([new MoreScreen(true)
 | 
					    new Combine([new MoreScreen(true)
 | 
				
			||||||
        .SetStyle("pointer-events: all;"),
 | 
					        .SetStyle("pointer-events: all;"),
 | 
				
			||||||
        Translations.t.general.openStreetMapIntro
 | 
					        Translations.t.general.openStreetMapIntro
 | 
				
			||||||
    ]).AddClass("block m-5 lg:w-3/4 lg:ml-40")
 | 
					    ]).SetClass("block m-5 lg:w-3/4 lg:ml-40")
 | 
				
			||||||
        .AttachTo("topleft-tools");
 | 
					        .AttachTo("topleft-tools");
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
window.addEventListener('contextmenu', function (e) { // Not compatible with IE < 9
 | 
					window.addEventListener('contextmenu', function (e) { // Not compatible with IE < 9
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue