Some work on stabilizing the popups, WIP
This commit is contained in:
parent
8f8ef690a4
commit
1f4b06ae55
5 changed files with 55 additions and 44 deletions
|
@ -40,6 +40,10 @@ export default class SelectedFeatureHandler {
|
||||||
if(features === undefined){
|
if(features === undefined){
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
if(this._selectedFeature.data?.properties?.id === this._hash.data){
|
||||||
|
// Feature already selected
|
||||||
|
return;
|
||||||
|
}
|
||||||
for (const feature of features) {
|
for (const feature of features) {
|
||||||
const id = feature.feature?.properties?.id;
|
const id = feature.feature?.properties?.id;
|
||||||
if(id === this._hash.data){
|
if(id === this._hash.data){
|
||||||
|
|
|
@ -1,24 +1,21 @@
|
||||||
import {UIElement} from "../UIElement";
|
import {UIElement} from "../UIElement";
|
||||||
|
|
||||||
export default class LazyElement<T extends UIElement> extends UIElement {
|
export default class LazyElement extends UIElement {
|
||||||
|
|
||||||
|
|
||||||
public Activate: (onElement?: (element: T) => void) => void;
|
public Activate: () => void;
|
||||||
private _content: T = undefined;
|
private _content: UIElement = undefined;
|
||||||
private readonly _loadingContent: string;
|
private readonly _loadingContent: string;
|
||||||
|
|
||||||
constructor(content: (() => T), loadingContent = "Rendering...") {
|
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 = (onElement?: (element: T) => void) => {
|
this.Activate = () => {
|
||||||
if (this._content === undefined) {
|
if (this._content === undefined) {
|
||||||
self._content = content();
|
self._content = content();
|
||||||
}
|
}
|
||||||
if (onElement) {
|
|
||||||
onElement(self._content)
|
|
||||||
}
|
|
||||||
self.Update();
|
self.Update();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -12,7 +12,7 @@ import ScrollableFullScreen from "../Base/ScrollableFullScreen";
|
||||||
export default class FeatureInfoBox extends UIElement {
|
export default class FeatureInfoBox extends UIElement {
|
||||||
private _component: ScrollableFullScreen;
|
private _component: ScrollableFullScreen;
|
||||||
|
|
||||||
constructor(
|
private constructor(
|
||||||
tags: UIEventSource<any>,
|
tags: UIEventSource<any>,
|
||||||
layerConfig: LayerConfig,
|
layerConfig: LayerConfig,
|
||||||
onClose: () => void
|
onClose: () => void
|
||||||
|
@ -77,4 +77,19 @@ export default class FeatureInfoBox extends UIElement {
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
private static featureInfoboxCache : Map<LayerConfig, Map<UIEventSource<any>, FeatureInfoBox>> = new Map<LayerConfig, Map<UIEventSource<any>, FeatureInfoBox>>();
|
||||||
|
static construct(tags: UIEventSource<any>, layer: LayerConfig, onClose: () => void) {
|
||||||
|
let innerMap = FeatureInfoBox.featureInfoboxCache.get(layer);
|
||||||
|
if(innerMap === undefined){
|
||||||
|
innerMap = new Map<UIEventSource<any>, FeatureInfoBox>();
|
||||||
|
FeatureInfoBox.featureInfoboxCache.set(layer, innerMap);
|
||||||
|
}
|
||||||
|
|
||||||
|
let featureInfoBox = innerMap.get(tags);
|
||||||
|
if(featureInfoBox === undefined){
|
||||||
|
featureInfoBox = new FeatureInfoBox(tags, layer, onClose);
|
||||||
|
innerMap.set(tags, featureInfoBox);
|
||||||
|
}
|
||||||
|
return featureInfoBox;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -43,7 +43,7 @@ export default class TagRenderingQuestion extends UIElement {
|
||||||
this._tags = tags;
|
this._tags = tags;
|
||||||
this._configuration = configuration;
|
this._configuration = configuration;
|
||||||
this._cancelButton = cancelButton;
|
this._cancelButton = cancelButton;
|
||||||
this._question = new SubstitutedTranslation(this._configuration.question, tags)
|
this._question = SubstitutedTranslation.construct(this._configuration.question, tags)
|
||||||
.SetClass("question-text");
|
.SetClass("question-text");
|
||||||
if (configuration === undefined) {
|
if (configuration === undefined) {
|
||||||
throw "A question is needed for a question visualization"
|
throw "A question is needed for a question visualization"
|
||||||
|
|
|
@ -7,12 +7,8 @@ import "leaflet.markercluster"
|
||||||
import LayerConfig from "../Customizations/JSON/LayerConfig";
|
import LayerConfig from "../Customizations/JSON/LayerConfig";
|
||||||
import State from "../State";
|
import State from "../State";
|
||||||
import LazyElement from "./Base/LazyElement";
|
import LazyElement from "./Base/LazyElement";
|
||||||
import Hash from "../Logic/Web/Hash";
|
|
||||||
import {GeoOperations} from "../Logic/GeoOperations";
|
|
||||||
import FeatureInfoBox from "./Popup/FeatureInfoBox";
|
import FeatureInfoBox from "./Popup/FeatureInfoBox";
|
||||||
import LayoutConfig from "../Customizations/JSON/LayoutConfig";
|
import LayoutConfig from "../Customizations/JSON/LayoutConfig";
|
||||||
import {UIElement} from "./UIElement";
|
|
||||||
import Combine from "./Base/Combine";
|
|
||||||
import ScrollableFullScreen from "./Base/ScrollableFullScreen";
|
import ScrollableFullScreen from "./Base/ScrollableFullScreen";
|
||||||
|
|
||||||
|
|
||||||
|
@ -34,6 +30,18 @@ export default class ShowDataLayer {
|
||||||
this._layerDict[layer.id] = layer;
|
this._layerDict[layer.id] = layer;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
function openSelectedElementFeature(feature: any){
|
||||||
|
if (feature === undefined) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
const id = feature.properties.id + feature.geometry.type + feature._matching_layer_id;
|
||||||
|
const action = self._onSelectedTrigger[id];
|
||||||
|
if (action) {
|
||||||
|
action();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
function update() {
|
function update() {
|
||||||
if (features.data === undefined) {
|
if (features.data === undefined) {
|
||||||
return;
|
return;
|
||||||
|
@ -60,21 +68,10 @@ export default class ShowDataLayer {
|
||||||
oldGeoLayer = geoLayer;
|
oldGeoLayer = geoLayer;
|
||||||
}
|
}
|
||||||
|
|
||||||
features.addCallbackAndRun(() => update());
|
features.addCallback(() => update());
|
||||||
leafletMap.addCallback(() => update());
|
leafletMap.addCallback(() => update());
|
||||||
State.state.selectedElement.addCallback(feature => {
|
|
||||||
if (feature === undefined) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
const id = feature.properties.id + feature.geometry.type + feature._matching_layer_id;
|
|
||||||
const action = self._onSelectedTrigger[id];
|
|
||||||
if (action) {
|
|
||||||
action();
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
update();
|
update();
|
||||||
|
State.state.selectedElement.addCallbackAndRun(openSelectedElementFeature);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@ -121,12 +118,14 @@ export default class ShowDataLayer {
|
||||||
|
|
||||||
|
|
||||||
const tags = State.state.allElements.getEventSourceFor(feature);
|
const tags = State.state.allElements.getEventSourceFor(feature);
|
||||||
const uiElement: LazyElement<UIElement> = new LazyElement(() =>new Combine([ new FeatureInfoBox(tags, layer, () => {
|
const uiElement = new LazyElement(() =>
|
||||||
State.state.selectedElement.setData(undefined);
|
FeatureInfoBox.construct(tags, layer, () => {
|
||||||
popup.remove();
|
State.state.selectedElement.setData(undefined);
|
||||||
|
popup.remove();
|
||||||
})]),
|
leafletLayer.closePopup();
|
||||||
"<div style='height: 90vh'>Rendering</div>");
|
ScrollableFullScreen.RestoreLeaflet();
|
||||||
|
}),
|
||||||
|
"<div style='height: 90vh'>Rendering</div>"); // By setting 90vh, leaflet will attempt to fit the entire screen and move the feature down
|
||||||
popup.setContent(uiElement.Render());
|
popup.setContent(uiElement.Render());
|
||||||
popup.on('remove', () => {
|
popup.on('remove', () => {
|
||||||
ScrollableFullScreen.RestoreLeaflet(); // Just in case...
|
ScrollableFullScreen.RestoreLeaflet(); // Just in case...
|
||||||
|
@ -139,25 +138,21 @@ export default class ShowDataLayer {
|
||||||
// We first render the UIelement (which'll still need an update later on...)
|
// We first render the UIelement (which'll still need an update later on...)
|
||||||
// But at least it'll be visible already
|
// But at least it'll be visible already
|
||||||
|
|
||||||
|
leafletLayer.on("popupopen", () => {
|
||||||
leafletLayer.on("click", () => {
|
|
||||||
// We set the element as selected...
|
|
||||||
|
|
||||||
uiElement.Activate();
|
uiElement.Activate();
|
||||||
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()) {
|
||||||
|
leafletLayer.openPopup();
|
||||||
|
uiElement.Activate();
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
leafletLayer.openPopup();
|
|
||||||
uiElement.Activate();
|
|
||||||
State.state.selectedElement.setData(feature);
|
|
||||||
}
|
}
|
||||||
this._onSelectedTrigger[feature.properties.id.replace("/", "_")] = this._onSelectedTrigger[id];
|
this._onSelectedTrigger[feature.properties.id.replace("/", "_")] = this._onSelectedTrigger[id];
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private CreateGeojsonLayer(features: any[]): L.Layer {
|
private CreateGeojsonLayer(features: any[]): L.Layer {
|
||||||
|
|
Loading…
Reference in a new issue