From 00f610c589f8b950fcaccedd4882610c43f7f968 Mon Sep 17 00:00:00 2001 From: pietervdvn Date: Mon, 25 Jan 2021 03:12:09 +0100 Subject: [PATCH] Refactoring fullscreenhandling --- InitUiElements.ts | 29 ++---- Logic/Actors/StrayClickHandler.ts | 1 - Logic/Actors/TitleHandler.ts | 77 +++++++++++---- Logic/MetaTagging.ts | 2 +- Logic/Osm/OsmPreferences.ts | 6 +- Logic/UIEventSource.ts | 96 ++++++++++-------- Logic/Web/LocalStorageSource.ts | 2 +- State.ts | 6 +- UI/Base/LazyElement.ts | 19 ++-- UI/Base/ScrollableFullScreen.ts | 156 +++++++++++++++++++++++------- UI/Base/SubtleButton.ts | 6 +- UI/BigComponents/IndexText.ts | 12 +-- UI/BigComponents/SearchAndGo.ts | 2 +- UI/FullScreenMessageBoxHandler.ts | 32 ------ UI/Popup/FeatureInfoBox.ts | 46 +++++---- UI/Popup/TagRenderingAnswer.ts | 2 +- UI/Popup/TagRenderingQuestion.ts | 2 +- UI/ShowDataLayer.ts | 51 +++++----- UI/UIElement.ts | 6 +- css/slideshow.css | 9 +- index.css | 16 ++- index.html | 10 +- index.ts | 3 +- 23 files changed, 346 insertions(+), 245 deletions(-) delete mode 100644 UI/FullScreenMessageBoxHandler.ts diff --git a/InitUiElements.ts b/InitUiElements.ts index c34a360..45137c1 100644 --- a/InitUiElements.ts +++ b/InitUiElements.ts @@ -12,7 +12,6 @@ import CenterMessageBox from "./UI/CenterMessageBox"; import {AllKnownLayouts} from "./Customizations/AllKnownLayouts"; import UserBadge from "./UI/BigComponents/UserBadge"; import SearchAndGo from "./UI/BigComponents/SearchAndGo"; -import FullScreenMessageBox from "./UI/FullScreenMessageBoxHandler"; import GeoLocationHandler from "./Logic/Actors/GeoLocationHandler"; import {LocalStorageSource} from "./Logic/Web/LocalStorageSource"; import {Utils} from "./Utils"; @@ -33,7 +32,6 @@ import FeatureSwitched from "./UI/Base/FeatureSwitched"; import LayerConfig from "./Customizations/JSON/LayerConfig"; import ShowDataLayer from "./UI/ShowDataLayer"; import Hash from "./Logic/Web/Hash"; -import HistoryHandling from "./Logic/Actors/HistoryHandling"; import FeaturePipeline from "./Logic/FeatureSource/FeaturePipeline"; export class InitUiElements { @@ -118,8 +116,6 @@ export class InitUiElements { } - new HistoryHandling(Hash.hash, State.state.fullScreenMessage); - InitUiElements.OnlyIf(State.state.featureSwitchUserbadge, () => { new UserBadge().AttachTo('userbadge'); }); @@ -129,9 +125,6 @@ export class InitUiElements { }); - new FullScreenMessageBox().AttachTo("messagesboxmobile"); - - InitUiElements.OnlyIf(State.state.featureSwitchWelcomeMessage, () => { InitUiElements.InitWelcomeMessage() }); @@ -214,18 +207,18 @@ export class InitUiElements { private static InitWelcomeMessage() { const isOpened = new UIEventSource(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. const help = Svg.help_svg().SetClass("open-welcome-button block"); - const close = Svg.close_svg().SetClass("close-welcome-button"); const checkbox = new CheckBox( - new Combine([ - close, fullOptions .SetClass("welcomeMessage") .onClick(() => {/*Catch the click*/ - })]), + }), help , isOpened ).AttachTo("messagesbox"); @@ -252,15 +245,8 @@ export class InitUiElements { const layerControlPanel = new LayerControlPanel( () => State.state.layerControlIsOpened.setData(false)) .SetClass("block p-1 rounded-full"); - const closeButton = Svg.close_svg() - .SetClass("layer-selection-toggle") - .SetStyle(" background: var(--subtle-detail-color);") - const checkbox = new CheckBox( - new Combine([ - closeButton, - layerControlPanel]) - .SetClass("flex flex-row") - , + const checkbox = new CheckBox( + layerControlPanel, Svg.layers_svg().SetClass("layer-selection-toggle"), State.state.layerControlIsOpened ).AttachTo("layer-selection"); @@ -388,7 +374,6 @@ export class InitUiElements { State.state.selectedElement, State.state.filteredLayers, State.state.leafletMap, - State.state.fullScreenMessage, () => { return new SimpleAddUI( () => State.state.LastClickLocation.setData(undefined) diff --git a/Logic/Actors/StrayClickHandler.ts b/Logic/Actors/StrayClickHandler.ts index 16939d4..1449ee1 100644 --- a/Logic/Actors/StrayClickHandler.ts +++ b/Logic/Actors/StrayClickHandler.ts @@ -17,7 +17,6 @@ export default class StrayClickHandler { selectedElement: UIEventSource, filteredLayers: UIEventSource<{ readonly isDisplayed: UIEventSource }[]>, leafletMap: UIEventSource, - fullscreenMessage: UIEventSource<{content: UIElement, hashText: string}>, uiToShow: (() => UIElement)) { this._uiToShow = uiToShow; const self = this; diff --git a/Logic/Actors/TitleHandler.ts b/Logic/Actors/TitleHandler.ts index 8093b64..c31cff8 100644 --- a/Logic/Actors/TitleHandler.ts +++ b/Logic/Actors/TitleHandler.ts @@ -3,31 +3,70 @@ import LayoutConfig from "../../Customizations/JSON/LayoutConfig"; import Translations from "../../UI/i18n/Translations"; import Locale from "../../UI/i18n/Locale"; 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{ - constructor(layoutToUse: UIEventSource, fullScreenMessage: UIEventSource<{ content: UIElement, hashText: string, titleText?: UIElement }>) { +class TitleElement extends UIElement { + private readonly _layoutToUse: UIEventSource; + private readonly _selectedFeature: UIEventSource; + private readonly _allElementsStorage: ElementStorage; + + constructor(layoutToUse: UIEventSource, + selectedFeature: UIEventSource, + 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) => { - return Translations.WT(layoutToUse?.title)?.txt ?? "MapComplete" - }, [Locale.language] - ).addCallbackAndRun((title) => { - document.title = title - }); + const layout = this._layoutToUse.data; + const properties = this._selectedFeature.data.properties; + for (const layer of layout.layers) { + if(layer.title === undefined){ + continue; + } + if (layer.overpassTags.matchesProperties(properties)) { - fullScreenMessage.addCallbackAndRun(selected => { - const title = Translations.WT(layoutToUse.data?.title)?.txt ?? "MapComplete" - if(selected?.titleText?.data === undefined){ - document.title = title - }else{ - selected.titleText.Update(); - var d = document.createElement('div'); - d.innerHTML = selected.titleText.InnerRender(); - const poi = (d.textContent || d.innerText) - document.title = title + " | " + poi; + const title = new TagRenderingAnswer( + this._allElementsStorage.getEventSourceFor(feature), + layer.title + ) + return new Combine([defaultTitle," | ", title]).Render(); } - }) + } + return defaultTitle; + } + +} +export default class TitleHandler { + constructor(layoutToUse: UIEventSource, + selectedFeature: UIEventSource, + 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); + + }); + } } \ No newline at end of file diff --git a/Logic/MetaTagging.ts b/Logic/MetaTagging.ts index bfad13e..cf9e990 100644 --- a/Logic/MetaTagging.ts +++ b/Logic/MetaTagging.ts @@ -74,7 +74,7 @@ export default class MetaTagging { const tagsSource = State.state.allElements.getEventSourceFor(feature); tagsSource.ping(); } catch (e) { - console.error(e) + console.warn(e) } }); } diff --git a/Logic/Osm/OsmPreferences.ts b/Logic/Osm/OsmPreferences.ts index 8914ae1..29bf456 100644 --- a/Logic/Osm/OsmPreferences.ts +++ b/Logic/Osm/OsmPreferences.ts @@ -4,7 +4,7 @@ import {Utils} from "../../Utils"; export class OsmPreferences { - public preferences = new UIEventSource({}); + public preferences = new UIEventSource({}, "all-osm-preferences"); public preferenceSources: any = {} private auth: any; private userDetails: UIEventSource; @@ -29,7 +29,7 @@ export class OsmPreferences { return this.longPreferences[prefix + key]; } - const source = new UIEventSource(undefined); + const source = new UIEventSource(undefined, "long-osm-preference:"+prefix+key); this.longPreferences[prefix + key] = source; const allStartWith = prefix + key + "-combined"; @@ -106,7 +106,7 @@ export class OsmPreferences { if (this.userDetails.data.loggedIn && this.preferences.data[key] === undefined) { this.UpdatePreferences(); } - const pref = new UIEventSource(this.preferences.data[key]); + const pref = new UIEventSource(this.preferences.data[key],"osm-preference:"+key); pref.addCallback((v) => { this.SetPreference(key, v); }); diff --git a/Logic/UIEventSource.ts b/Logic/UIEventSource.ts index e82d7e8..18d13a5 100644 --- a/Logic/UIEventSource.ts +++ b/Logic/UIEventSource.ts @@ -1,14 +1,64 @@ -export class UIEventSource{ +export class UIEventSource { public data: T; + private readonly tag: string; private _callbacks = []; + + private static allSources : UIEventSource[] = 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) { + constructor(data: T, tag: string = "") { + this.tag = tag; this.data = data; + UIEventSource.allSources.push(this); + } + + public static flatten(source: UIEventSource>, possibleSources: UIEventSource[]): UIEventSource { + const sink = new UIEventSource(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 { + const source = new UIEventSource(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 { - if(callback === console.log){ + if (callback === console.log) { // 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." } @@ -36,25 +86,9 @@ export class UIEventSource{ } } - public static flatten(source: UIEventSource>, possibleSources: UIEventSource[]): UIEventSource { - const sink = new UIEventSource(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(f: ((T) => J), extraSources: UIEventSource[] = [], - g: ((J) => T) = undefined ): UIEventSource { + g: ((J) => T) = undefined): UIEventSource { const self = this; const newSource = new UIEventSource( @@ -70,7 +104,7 @@ export class UIEventSource{ extraSource?.addCallback(update); } - if(g !== undefined) { + if (g !== undefined) { newSource.addCallback((latest) => { self.setData(g(latest)); }) @@ -79,7 +113,6 @@ export class UIEventSource{ return newSource; } - public syncWith(otherSource: UIEventSource, reverseOverride = false): UIEventSource { this.addCallback((latest) => otherSource.setData(latest)); const self = this; @@ -94,7 +127,7 @@ export class UIEventSource{ return this; } - public stabilized(millisToStabilize) : UIEventSource{ + public stabilized(millisToStabilize): UIEventSource { const newSource = new UIEventSource(this.data); @@ -103,7 +136,7 @@ export class UIEventSource{ currentCallback++; const thisCallback = currentCallback; window.setTimeout(() => { - if(thisCallback === currentCallback){ + if (thisCallback === currentCallback) { newSource.setData(latestData); } }, millisToStabilize) @@ -112,19 +145,4 @@ export class UIEventSource{ return newSource; } - public static Chronic(millis: number, asLong: () => boolean = undefined): UIEventSource { - const source = new UIEventSource(undefined); - - function run() { - source.setData(new Date()); - if (asLong === undefined || asLong()) { - window.setTimeout(run, millis); - } - } - - run(); - return source; - - } - } \ No newline at end of file diff --git a/Logic/Web/LocalStorageSource.ts b/Logic/Web/LocalStorageSource.ts index 210a43d..0252ddf 100644 --- a/Logic/Web/LocalStorageSource.ts +++ b/Logic/Web/LocalStorageSource.ts @@ -5,7 +5,7 @@ export class LocalStorageSource { static Get(key: string, defaultValue: string = undefined): UIEventSource { try { const saved = localStorage.getItem(key); - const source = new UIEventSource(saved ?? defaultValue); + const source = new UIEventSource(saved ?? defaultValue, "localstorage:"+key); source.addCallback((data) => { localStorage.setItem(key, data); diff --git a/State.ts b/State.ts index 3b79259..036413f 100644 --- a/State.ts +++ b/State.ts @@ -72,10 +72,6 @@ export default class State { * The message that should be shown at the center of the screen */ public readonly centerMessage = new UIEventSource(""); - /** - 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 @@ -244,7 +240,7 @@ export default class State { } }).ping() - new TitleHandler(this.layoutToUse, this.fullScreenMessage); + new TitleHandler(this.layoutToUse, this.selectedElement, this.allElements); this.allElements = new ElementStorage(); diff --git a/UI/Base/LazyElement.ts b/UI/Base/LazyElement.ts index 2f1bb92..005ce7a 100644 --- a/UI/Base/LazyElement.ts +++ b/UI/Base/LazyElement.ts @@ -1,22 +1,25 @@ import {UIElement} from "../UIElement"; -export default class LazyElement extends UIElement { +export default class LazyElement extends UIElement { - private _content: UIElement = undefined; + public Activate: (onElement?: (element: T) => void) => void; + private _content: T = undefined; + private readonly _loadingContent: string; - public Activate: () => void; - private _loadingContent: string; - - constructor(content: (() => UIElement), loadingContent = "Rendering...") { + constructor(content: (() => T), loadingContent = "Rendering...") { super(); this._loadingContent = loadingContent; this.dumbMode = false; const self = this; - this.Activate = () => { + this.Activate = (onElement?: (element: T) => void) => { + console.log("ACTIVATED") if (this._content === undefined) { self._content = content(); } + if (onElement) { + onElement(self._content) + } self.Update(); } } @@ -27,5 +30,7 @@ export default class LazyElement extends UIElement { } return this._content.InnerRender(); } + + } \ No newline at end of file diff --git a/UI/Base/ScrollableFullScreen.ts b/UI/Base/ScrollableFullScreen.ts index 63248de..7879da9 100644 --- a/UI/Base/ScrollableFullScreen.ts +++ b/UI/Base/ScrollableFullScreen.ts @@ -1,6 +1,5 @@ import {UIElement} from "../UIElement"; import Svg from "../../Svg"; -import State from "../../State"; import Combine from "./Combine"; 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 */ export default class ScrollableFullScreen extends UIElement { + private static _isInited = false; + private title: UIElement; + private content: UIElement; private _component: UIElement; - private elementsToRestore: Set = new Set(); constructor(title: UIElement, content: UIElement, onClose: (() => void)) { 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; - const returnToTheMap = Svg.back_svg().onClick(() => { - console.log("Clicked back!"); - this.RestoreLeaflet(); - if (onClose() !== undefined) { - console.error("WARNING: onClose is not defined") - onClose(); - } - }).SetClass("block sm:hidden mb-2 bg-blue-50 rounded-full w-12 h-12 p-1.5") + const returnToTheMap = + new Combine([ + Svg.back_svg().SetClass("block sm:hidden"), + Svg.close_svg().SetClass("hidden sm:block") + ]) + .onClick(() => { + console.log("Clicked back!"); + 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;")]) .SetClass("block sm:hidden h-5") this._component = new Combine([ - new Combine([ - 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"), - new Combine(["", content, "", 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"), - // 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("fixed top-0 left-0 right-0 h-screen w-screen sm:max-h-65vh sm:w-auto"); + new Combine([ + new Combine([returnToTheMap, title]) + .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(["", content, "", 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"), + // 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("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(); + 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 { return this._component.Render(); } - protected InnerUpdate(htmlElement: HTMLElement) { - - 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 !== "") { - this.elementsToRestore.add(htmlElement); - htmlElement.classList.add("no-transform") - } - htmlElement = htmlElement.parentElement; - } while (htmlElement != null) - - super.InnerUpdate(htmlElement); + Update() { + console.log("Updating the scrollableFullScreen") + super.Update(); + this._component.Update(); } - private RestoreLeaflet() { - this.elementsToRestore.forEach( - el => el.classList.remove("no-transform") - ); + public PrepFullscreen(htmlElement = undefined) { + htmlElement = htmlElement ?? document.getElementById(this.id); + ScrollableFullScreen.PatchLeaflet(htmlElement); + ScrollableFullScreen.HideClutter(htmlElement); } + protected InnerUpdate(htmlElement: HTMLElement) { + this.PrepFullscreen(htmlElement) + super.InnerUpdate(htmlElement); + } + } \ No newline at end of file diff --git a/UI/Base/SubtleButton.ts b/UI/Base/SubtleButton.ts index 34da986..b89f6bf 100644 --- a/UI/Base/SubtleButton.ts +++ b/UI/Base/SubtleButton.ts @@ -24,9 +24,9 @@ export class SubtleButton extends UIElement{ } else { 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]) - .AddClass("flex-shrink-0"); + .SetClass("flex-shrink-0"); } @@ -53,7 +53,7 @@ export class SubtleButton extends UIElement{ return new Combine([ this.image, 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(); } diff --git a/UI/BigComponents/IndexText.ts b/UI/BigComponents/IndexText.ts index 8a76d27..6e0399b 100644 --- a/UI/BigComponents/IndexText.ts +++ b/UI/BigComponents/IndexText.ts @@ -7,22 +7,22 @@ export default class IndexText extends Combine { constructor() { super([ new FixedUiElement(`MapComplete Logo`) - .AddClass("flex-none m-3"), + .SetClass("flex-none m-3"), new Combine([ 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"), - 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"); } } \ No newline at end of file diff --git a/UI/BigComponents/SearchAndGo.ts b/UI/BigComponents/SearchAndGo.ts index ece0897..6f073d6 100644 --- a/UI/BigComponents/SearchAndGo.ts +++ b/UI/BigComponents/SearchAndGo.ts @@ -22,7 +22,7 @@ export default class SearchAndGo extends UIElement { ); 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() { super(undefined); diff --git a/UI/FullScreenMessageBoxHandler.ts b/UI/FullScreenMessageBoxHandler.ts deleted file mode 100644 index 0533be3..0000000 --- a/UI/FullScreenMessageBoxHandler.ts +++ /dev/null @@ -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); - } - - -} \ No newline at end of file diff --git a/UI/Popup/FeatureInfoBox.ts b/UI/Popup/FeatureInfoBox.ts index 3a69273..202d1db 100644 --- a/UI/Popup/FeatureInfoBox.ts +++ b/UI/Popup/FeatureInfoBox.ts @@ -9,29 +9,41 @@ import State from "../../State"; import TagRenderingConfig from "../../Customizations/JSON/TagRenderingConfig"; import ScrollableFullScreen from "../Base/ScrollableFullScreen"; -export default class FeatureInfoBox extends UIElement { - private _component: UIElement; - - public title: UIElement ; +export default class FeatureInfoBox extends ScrollableFullScreen { constructor( tags: UIEventSource, layerConfig: LayerConfig, - onClose: () => {} + onClose: () => void ) { - super(); + super( + FeatureInfoBox.GenerateTitleBar(tags, layerConfig), + FeatureInfoBox.GenerateContent(tags, layerConfig), + onClose + ); if (layerConfig === undefined) { throw "Undefined layerconfig" } - - + } + + private static GenerateTitleBar( tags: UIEventSource, + layerConfig: LayerConfig): UIElement{ const title = new TagRenderingAnswer(tags, layerConfig.title ?? new TagRenderingConfig("POI", undefined)) - .AddClass("text-2xl break-words font-bold p-2"); - this.title = title; + .SetClass("text-2xl break-words font-bold p-2"); const titleIcons = new Combine( layerConfig.titleIcons.map(icon => new TagRenderingAnswer(tags, icon) - .AddClass("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("block w-8 h-8 align-baseline box-content p-0.5"))) + .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, + layerConfig: LayerConfig): UIElement{ + + let questionBox: UIElement = undefined; if (State.state.featureSwitchUserbadge.data) { @@ -53,20 +65,12 @@ export default class FeatureInfoBox extends UIElement { } const tail = new Combine([]).SetClass("only-on-mobile"); - const content = new Combine([ + return new Combine([ ...renderings, 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(); } } diff --git a/UI/Popup/TagRenderingAnswer.ts b/UI/Popup/TagRenderingAnswer.ts index 6806b2b..6f2922f 100644 --- a/UI/Popup/TagRenderingAnswer.ts +++ b/UI/Popup/TagRenderingAnswer.ts @@ -21,7 +21,7 @@ export default class TagRenderingAnswer extends UIElement { if (configuration === undefined) { 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 { diff --git a/UI/Popup/TagRenderingQuestion.ts b/UI/Popup/TagRenderingQuestion.ts index 104ed4b..9c2e7eb 100644 --- a/UI/Popup/TagRenderingQuestion.ts +++ b/UI/Popup/TagRenderingQuestion.ts @@ -88,7 +88,7 @@ export default class TagRenderingQuestion extends UIElement { return tags.asHumanString(true, true); } ) - ).AddClass("block") + ).SetClass("block") } private GenerateInputElement(): InputElement { diff --git a/UI/ShowDataLayer.ts b/UI/ShowDataLayer.ts index 07aa61f..3130d18 100644 --- a/UI/ShowDataLayer.ts +++ b/UI/ShowDataLayer.ts @@ -44,10 +44,10 @@ export default class ShowDataLayer { let geoLayer = self.CreateGeojsonLayer(feats) 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 cluster = cl.markerClusterGroup({ disableClusteringAtZoom: layoutToUse.clustering.maxZoom }); - cluster.addLayer(geoLayer); - geoLayer = cluster; + 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}); + cluster.addLayer(geoLayer); + geoLayer = cluster; } if (oldGeoLayer) { @@ -60,22 +60,22 @@ export default class ShowDataLayer { features.addCallbackAndRun(() => update()); leafletMap.addCallback(() => update()); State.state.selectedElement.addCallback(feature => { - if(feature === undefined){ + if (feature === undefined) { 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]; - if(action){ + if (action) { action(); } }); 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 - if(State.state.selectedElement !== undefined){ + if (State.state.selectedElement !== undefined) { return; // Something is already selected, we don't have to apply this fix } const action = self._onSelectedTrigger[id]; - if(action){ + if (action) { action(); } }) @@ -126,41 +126,48 @@ export default class ShowDataLayer { const tags = State.state.allElements.getEventSourceFor(feature); - const uiElement: LazyElement = new LazyElement(() => new FeatureInfoBox(tags, layer, () => popup.closePopup()), + const uiElement: LazyElement = new LazyElement(() => new FeatureInfoBox(tags, layer, () => { + console.log("Closing the popup!") + State.state.selectedElement.setData(undefined); + popup.remove(); + + }), "
Rendering
"); popup.setContent(uiElement.Render()); popup.on('remove', () => { - if(!popup.isOpen()){ + if (!popup.isOpen()) { return; } - State.state.selectedElement.setData(undefined); + State.state.selectedElement.setData(undefined); }); leafletLayer.bindPopup(popup); // We first render the UIelement (which'll still need an update later on...) // But at least it'll be visible already - leafletLayer.on("click", (e) => { + leafletLayer.on("click", () => { // We set the element as selected... - uiElement.Activate(); + + uiElement.Activate(e => e.PrepFullscreen()); 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] - = () => { - if(popup.isOpen()){ + = () => { + if (popup.isOpen()) { return; } leafletLayer.openPopup(); - uiElement.Activate(); + uiElement.Activate(e => e.PrepFullscreen()); State.state.selectedElement.setData(feature); } - this._onSelectedTrigger[feature.properties.id.replace("/","_")] = this._onSelectedTrigger[id]; - if (feature.properties.id.replace(/\//g, "_") === Hash.hash.data) { + this._onSelectedTrigger[feature.properties.id.replace("/", "_")] = this._onSelectedTrigger[id]; + 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 // 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()); const center = GeoOperations.centerpoint(feature).geometry.coordinates; diff --git a/UI/UIElement.ts b/UI/UIElement.ts index b5d2eb5..53d362b 100644 --- a/UI/UIElement.ts +++ b/UI/UIElement.ts @@ -149,16 +149,12 @@ export abstract class UIElement extends UIEventSource { return this.InnerRender() === ""; } - public SetClass(clss: string): UIElement { - return this.AddClass(clss); - } - /** * Adds all the relevant classes, space seperated * @param clss * @constructor */ - public AddClass(clss: string) { + public SetClass(clss: string) { this.dumbMode = false; const all = clss.split(" "); let recordedChange = false; diff --git a/css/slideshow.css b/css/slideshow.css index ce4074a..38eebd5 100644 --- a/css/slideshow.css +++ b/css/slideshow.css @@ -78,11 +78,4 @@ max-width: 100%; max-height: 30vh; border-radius: 1em; -} - - -.hidden { - /* This is used by the slideshow, to hide non-active slides*/ - display: none !important; - -} +} \ No newline at end of file diff --git a/index.css b/index.css index bdd1220..b197ab3 100644 --- a/index.css +++ b/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 { - /*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; } + + .clutter-hidden { + /*This is a workaround to let popup contents escape the popup on mobile - see scrollableFullScreen.ts*/ + visibility: hidden !important; + } } + :root { --subtle-detail-color: #e5f5ff; --subtle-detail-color-contrast: black; @@ -57,6 +63,11 @@ --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 { height: 100%; min-height: 100vh; @@ -333,7 +344,6 @@ a { .welcomeMessage { display: block; - margin-left: 4em; max-width: calc(100vw - 5em); width: 40em; max-height: calc(100vh - 15em); diff --git a/index.html b/index.html index ade7bfd..3dd9684 100644 --- a/index.html +++ b/index.html @@ -44,11 +44,7 @@ -
-
-
- -
+
-
+
Loading MapComplete, hang on...
-
+
diff --git a/index.ts b/index.ts index 8f7f8ec..0865518 100644 --- a/index.ts +++ b/index.ts @@ -119,11 +119,10 @@ if (layoutFromBase64.startsWith("wiki:")) { } else { // We fall through: no theme loaded: just show a few buttons State.state = new State(undefined); - document.getElementById("messagesboxmobile").remove(); new Combine([new MoreScreen(true) .SetStyle("pointer-events: all;"), 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"); } window.addEventListener('contextmenu', function (e) { // Not compatible with IE < 9