Switch to a panel based UI, fix #552

This commit is contained in:
Pieter Vander Vennet 2022-12-08 02:56:49 +01:00
parent 4d930ae985
commit 494a49bc48
9 changed files with 148 additions and 204 deletions

View file

@ -1,8 +1,7 @@
import { UIElement } from "../UIElement"
import Svg from "../../Svg"
import Combine from "./Combine"
import { FixedUiElement } from "./FixedUiElement"
import { UIEventSource } from "../../Logic/UIEventSource"
import {FixedUiElement} from "./FixedUiElement"
import {UIEventSource} from "../../Logic/UIEventSource"
import Hash from "../../Logic/Web/Hash"
import BaseUIElement from "../BaseUIElement"
import Title from "./Title"
@ -16,12 +15,11 @@ import Title from "./Title"
*
*
*/
export default class ScrollableFullScreen extends UIElement {
export default class ScrollableFullScreen {
private static readonly empty = new FixedUiElement("")
private static _currentlyOpen: ScrollableFullScreen
public isShown: UIEventSource<boolean>
private hashToShow: string
private _component: BaseUIElement
private _fullscreencomponent: BaseUIElement
private _resetScrollSignal: UIEventSource<void> = new UIEventSource<void>(undefined)
@ -37,7 +35,6 @@ export default class ScrollableFullScreen extends UIElement {
setHash?: true | boolean
}
) {
super()
this.hashToShow = hashToShow
this.isShown = isShown
@ -45,20 +42,11 @@ export default class ScrollableFullScreen extends UIElement {
throw "HashToShow should be defined as it is vital for the 'back' key functionality"
}
const desktopOptions = {
mode: "desktop",
resetScrollSignal: this._resetScrollSignal,
}
const mobileOptions = {
mode: "mobile",
resetScrollSignal: this._resetScrollSignal,
}
this._component = this.BuildComponent(
title(desktopOptions),
content(desktopOptions)
).SetClass("hidden md:block")
this._fullscreencomponent = this.BuildComponent(
title(mobileOptions),
content(mobileOptions).SetClass("pb-20")
@ -95,17 +83,15 @@ export default class ScrollableFullScreen extends UIElement {
})
}
InnerRender(): BaseUIElement {
return this._component
}
Destroy() {
super.Destroy()
this._component.Destroy()
this._fullscreencomponent.Destroy()
}
Activate(): void {
/**
* Actually show this in the 'fullscreen'-div
* @constructor
*/
public Activate(): void {
this.isShown.setData(true)
this._fullscreencomponent.AttachTo("fullscreen")
const fs = document.getElementById("fullscreen")

View file

@ -45,30 +45,21 @@ export default class LeftControls extends Combine {
})
).SetClass("inline-block w-full h-full")
const featureBox = new VariableUiElement(
feature.map((feature) => {
if (feature === undefined) {
return undefined
}
return new Lazy(() => {
const tagsSource = state.allElements.getEventSourceById(
feature.properties.id
)
return new FeatureInfoBox(tagsSource, currentViewFL.layerDef, state, {
hashToShow: "currentview",
isShown: guiState.currentViewControlIsOpened,
}).SetClass("md:floating-element-width")
})
})
)
.SetStyle("width: 40rem")
.SetClass("block")
return new Toggle(
featureBox,
new MapControlButton(icon),
guiState.currentViewControlIsOpened
)
feature.map((feature) => {
if (feature === undefined) {
return undefined
}
const tagsSource = state.allElements.getEventSourceById(
feature.properties.id
)
return new FeatureInfoBox(tagsSource, currentViewFL.layerDef, state, {
hashToShow: "currentview",
isShown: guiState.currentViewControlIsOpened,
})
})
return new MapControlButton(icon)
}).onClick(() => {
guiState.currentViewControlIsOpened.setData(true)
}),
@ -79,14 +70,9 @@ export default class LeftControls extends Combine {
)
)
const toggledDownload = new Toggle(
new AllDownloads(guiState.downloadControlIsOpened, state).SetClass(
"block p-1 rounded-full md:floating-element-width"
),
new MapControlButton(Svg.download_svg()).onClick(() =>
guiState.downloadControlIsOpened.setData(true)
),
guiState.downloadControlIsOpened
new AllDownloads(guiState.downloadControlIsOpened, state)
const toggledDownload = new MapControlButton(Svg.download_svg()).onClick(() =>
guiState.downloadControlIsOpened.setData(true)
)
const downloadButtonn = new Toggle(
@ -98,21 +84,19 @@ export default class LeftControls extends Combine {
)
)
const toggledFilter = new Toggle(
new ScrollableFullScreen(
() => Translations.t.general.layerSelection.title.Clone(),
() =>
new FilterView(state.filteredLayers, state.overlayToggles, state).SetClass(
"block p-1"
),
"filters",
guiState.filterViewIsOpened
).SetClass("rounded-lg md:floating-element-width"),
new MapControlButton(Svg.layers_svg()).onClick(() =>
guiState.filterViewIsOpened.setData(true)
),
new ScrollableFullScreen(
() => Translations.t.general.layerSelection.title.Clone(),
() =>
new FilterView(state.filteredLayers, state.overlayToggles, state).SetClass(
"block p-1"
),
"filters",
guiState.filterViewIsOpened
)
const toggledFilter = new MapControlButton(Svg.layers_svg()).onClick(() =>
guiState.filterViewIsOpened.setData(true)
)
const filterButton = new Toggle(toggledFilter, undefined, state.featureSwitchFilter)
@ -127,18 +111,19 @@ export default class LeftControls extends Combine {
undefined,
new Lazy(
() =>
new Toggle(
new ScrollableFullScreen(
() => Translations.t.general.attribution.attributionTitle,
() => new CopyrightPanel(state),
"copyright",
guiState.copyrightViewIsOpened
),
new MapControlButton(Svg.copyright_svg()).onClick(() =>
guiState.copyrightViewIsOpened.setData(true)
),
{
new ScrollableFullScreen(
() => Translations.t.general.attribution.attributionTitle,
() => new CopyrightPanel(state),
"copyright",
guiState.copyrightViewIsOpened
)
);
return new MapControlButton(Svg.copyright_svg()).onClick(() =>
guiState.copyrightViewIsOpened.setData(true)
)
}
),
state.featureSwitchWelcomeMessage
)

View file

@ -1,7 +1,7 @@
import FeaturePipelineState from "../Logic/State/FeaturePipelineState"
import State from "../State"
import { Utils } from "../Utils"
import { UIEventSource } from "../Logic/UIEventSource"
import {Utils} from "../Utils"
import {UIEventSource} from "../Logic/UIEventSource"
import FullWelcomePaneWithTabs from "./BigComponents/FullWelcomePaneWithTabs"
import MapControlButton from "./MapControlButton"
import Svg from "../Svg"
@ -17,7 +17,7 @@ import ScrollableFullScreen from "./Base/ScrollableFullScreen"
import Translations from "./i18n/Translations"
import SimpleAddUI from "./BigComponents/SimpleAddUI"
import StrayClickHandler from "../Logic/Actors/StrayClickHandler"
import { DefaultGuiState } from "./DefaultGuiState"
import {DefaultGuiState} from "./DefaultGuiState"
import LayerConfig from "../Models/ThemeConfig/LayerConfig"
import * as home_location_json from "../assets/layers/home_location/home_location.json"
import NewNoteUi from "./Popup/NewNoteUi"
@ -27,9 +27,9 @@ import FilteredLayer from "../Models/FilteredLayer"
import ExtraLinkButton from "./BigComponents/ExtraLinkButton"
/**
* The default MapComplete GUI initializor
* The default MapComplete GUI initializer
*
* Adds a welcome pane, contorl buttons, ... etc to index.html
* Adds a welcome pane, control buttons, ... etc to index.html
*/
export default class DefaultGUI {
private readonly guiState: DefaultGuiState
@ -53,7 +53,7 @@ export default class DefaultGUI {
Utils.downloadJson("./service-worker-version")
.then((data) => console.log("Service worker", data))
.catch((e) => console.log("Service worker not active"))
.catch((_) => console.log("Service worker not active"))
}
public setupClickDialogOnMap(
@ -218,7 +218,7 @@ export default class DefaultGUI {
private InitWelcomeMessage(): BaseUIElement {
const isOpened = this.guiState.welcomeMessageIsOpened
const fullOptions = new FullWelcomePaneWithTabs(
new FullWelcomePaneWithTabs(
isOpened,
this.guiState.welcomeMessageOpenedTab,
this.state
@ -242,10 +242,6 @@ export default class DefaultGUI {
isOpened.setData(false)
})
return new Toggle(
fullOptions.SetClass("welcomeMessage pointer-events-auto"),
help.SetClass("pointer-events-auto"),
isOpened
)
return help.SetClass("pointer-events-auto")
}
}

View file

@ -1,11 +1,13 @@
import { Store, UIEventSource } from "../../Logic/UIEventSource"
import {Store, UIEventSource} from "../../Logic/UIEventSource"
import LayerConfig from "../../Models/ThemeConfig/LayerConfig"
import { ShowDataLayerOptions } from "./ShowDataLayerOptions"
import { ElementStorage } from "../../Logic/ElementStorage"
import {ShowDataLayerOptions} from "./ShowDataLayerOptions"
import {ElementStorage} from "../../Logic/ElementStorage"
import RenderingMultiPlexerFeatureSource from "../../Logic/FeatureSource/Sources/RenderingMultiPlexerFeatureSource"
import ScrollableFullScreen from "../Base/ScrollableFullScreen"
import {LeafletMouseEvent} from "leaflet";
import Hash from "../../Logic/Web/Hash";
/*
// import 'leaflet-polylineoffset';
// import 'leaflet-polylineoffset';
We don't actually import it here. It is imported in the 'MinimapImplementation'-class, which'll result in a patched 'L' object.
Even though actually importing this here would seem cleaner, we don't do this as this breaks some scripts:
- Scripts are ran in ts-node
@ -41,7 +43,7 @@ export default class ShowDataLayerImplementation {
* Note: the key of this dictionary is 'feature.properties.id+features.geometry.type' as one feature might have multiple presentations
* @private
*/
private readonly leafletLayersPerId = new Map<string, { feature: any; leafletlayer: any }>()
private readonly leafletLayersPerId = new Map<string, { feature: any; activateFunc: (event: LeafletMouseEvent) => void }>()
private readonly showDataLayerid: number
private readonly createPopup: (
tags: UIEventSource<any>,
@ -128,11 +130,7 @@ export default class ShowDataLayerImplementation {
if (v === undefined) {
return
}
const leafletLayer = v.leafletlayer
const feature = v.feature
if (leafletLayer.getPopup().isOpen()) {
return
}
if (selected.properties.id !== feature.properties.id) {
return
}
@ -143,11 +141,7 @@ export default class ShowDataLayerImplementation {
console.log("Not opening the popup for", feature, "as probably renamed")
return
}
if (
selected.geometry.type === feature.geometry.type // If a feature is rendered both as way and as point, opening one popup might trigger the other to open, which might trigger the one to open again
) {
leafletLayer.openPopup()
}
v.activateFunc(null)
}
private update(options: ShowDataLayerOptions): boolean {
@ -323,64 +317,51 @@ export default class ShowDataLayerImplementation {
* @param leafletLayer
* @private
*/
private postProcessFeature(feature, leafletLayer: L.Layer) {
private postProcessFeature(feature, leafletLayer: L.Evented) {
const layer: LayerConfig = this._layerToShow
if (layer.title === undefined || !this._enablePopups) {
// No popup action defined -> Don't do anything
// or probably a map in the popup - no popups needed!
return
}
const popup = L.popup(
{
autoPan: true,
closeOnEscapeKey: true,
closeButton: false,
autoPanPaddingTopLeft: [15, 15],
},
leafletLayer
)
leafletLayer.bindPopup(popup)
const key = feature.properties.id
if(this.leafletLayersPerId.has(key)){
const activate = this.leafletLayersPerId.get(key)
leafletLayer.addEventListener('click', activate.activateFunc)
if(Hash.hash.data === key ){
activate.activateFunc(null)
}
return;
}
let infobox: ScrollableFullScreen = undefined
const id = `popup-${feature.properties.id}-${feature.geometry.type}-${
this.showDataLayerid
}-${this._cleanCount}-${feature.pointRenderingIndex ?? feature.lineRenderingIndex}-${
feature.multiLineStringIndex ?? ""
}`
popup.setContent(
`<div style='height: 65vh' id='${id}'>Popup for ${feature.properties.id} ${feature.geometry.type} ${id} is loading</div>`
)
const createpopup = this.createPopup
leafletLayer.on("popupopen", () => {
const self = this
function activate (event: LeafletMouseEvent) {
console.log("Activating!")
if (infobox === undefined) {
const tags =
this.allElements?.getEventSourceById(feature.properties.id) ??
self.allElements?.getEventSourceById(key) ??
new UIEventSource<any>(feature.properties)
infobox = createpopup(tags, layer)
infobox = self.createPopup(tags, layer)
infobox.isShown.addCallback((isShown) => {
if (!isShown) {
leafletLayer.closePopup()
}
self.unregister.push(() => {
console.log("Destroying infobox")
infobox.Destroy()
})
}
infobox.AttachTo(id)
infobox.Activate()
this.unregister.push(() => {
console.log("Destroying infobox")
infobox.Destroy()
})
if (this._selectedElement?.data?.properties?.id !== feature.properties.id) {
this._selectedElement?.setData(feature)
}
})
}
leafletLayer.addEventListener('click', activate)
// Add the feature to the index to open the popup when needed
this.leafletLayersPerId.set(feature.properties.id + feature.geometry.type, {
this.leafletLayersPerId.set(key, {
feature: feature,
leafletlayer: leafletLayer,
activateFunc: activate,
})
if(Hash.hash.data === key ){
activate(null)
}
}
}