From 8ad9b816acdd37cfee3acba70075b50723a61d6f Mon Sep 17 00:00:00 2001 From: pietervdvn Date: Mon, 14 Jun 2021 17:28:11 +0200 Subject: [PATCH] Fix popups and core functionality --- UI/Base/VariableUIElement.ts | 2 +- UI/BaseUIElement.ts | 1 + UI/BigComponents/ShareScreen.ts | 7 ++-- UI/Input/FixedInputElement.ts | 2 +- UI/Input/RadioButton.ts | 39 ++++++++++++++---- UI/Popup/EditableTagRendering.ts | 10 +++-- UI/Popup/TagRenderingAnswer.ts | 25 +++++------- UI/ShowDataLayer.ts | 70 ++++++++++++++++---------------- UI/SubstitutedTranslation.ts | 4 +- test.ts | 28 ++++++++++++- test/Tag.spec.ts | 6 --- test/TagQuestion.spec.ts | 62 ---------------------------- test/TestAll.ts | 4 -- 13 files changed, 116 insertions(+), 144 deletions(-) delete mode 100644 test/TagQuestion.spec.ts diff --git a/UI/Base/VariableUIElement.ts b/UI/Base/VariableUIElement.ts index 8d82858733..c064f9ce6e 100644 --- a/UI/Base/VariableUIElement.ts +++ b/UI/Base/VariableUIElement.ts @@ -26,7 +26,7 @@ export class VariableUiElement extends BaseUIElement { for (const content of contents) { const c = content.ConstructElement(); if (c !== undefined && c !== null) { - el.appendChild(c) + el.appendChild(c) } } diff --git a/UI/BaseUIElement.ts b/UI/BaseUIElement.ts index 840814530e..125d1ffc41 100644 --- a/UI/BaseUIElement.ts +++ b/UI/BaseUIElement.ts @@ -43,6 +43,7 @@ export default abstract class BaseUIElement { throw "SEVERE: could not attach UIElement to " + divId; } + console.log("Attaching to ", element) while (element.firstChild) { //The list is LIVE so it will re-index each call element.removeChild(element.firstChild); diff --git a/UI/BigComponents/ShareScreen.ts b/UI/BigComponents/ShareScreen.ts index 7f32802f53..7396c15682 100644 --- a/UI/BigComponents/ShareScreen.ts +++ b/UI/BigComponents/ShareScreen.ts @@ -46,7 +46,10 @@ export default class ShareScreen extends Combine { return null; } if (includeL) { - return `z=${currentLocation.data.zoom}&lat=${currentLocation.data.lat}&lon=${currentLocation.data.lon}` + return [["z", currentLocation.data?.zoom], ["lat", currentLocation.data?.lat], ["lon", currentLocation.data?.lon]] + .filter(p => p[1] !== undefined) + .map(p => p[0]+"="+p[1]) + .join("&") } else { return null; } @@ -166,8 +169,6 @@ export default class ShareScreen extends Combine { }, optionParts); - const iframe = url.map(url => `<iframe src="${url}" width="100%" height="100%" title="${layout?.title?.txt ?? "MapComplete"} with MapComplete"></iframe>`); - const iframeCode = new VariableUiElement( url.map((url) => { return ` diff --git a/UI/Input/FixedInputElement.ts b/UI/Input/FixedInputElement.ts index de0f2b593e..6e46ae5fc5 100644 --- a/UI/Input/FixedInputElement.ts +++ b/UI/Input/FixedInputElement.ts @@ -31,7 +31,7 @@ export class FixedInputElement extends InputElement { } protected InnerConstructElement(): HTMLElement { - return undefined; + return this._el; } GetValue(): UIEventSource { diff --git a/UI/Input/RadioButton.ts b/UI/Input/RadioButton.ts index ac0828f75b..760c371940 100644 --- a/UI/Input/RadioButton.ts +++ b/UI/Input/RadioButton.ts @@ -22,13 +22,24 @@ export class RadioButton extends InputElement { } } ), elements.map(e => e?.GetValue())); + + if(selectFirstAsDefault){ + + value.addCallbackAndRun(selected =>{ + if(selected === undefined){ + for (const element of elements) { + const v = element.GetValue().data; + if(v !== undefined){ + value.setData(v) + break; + } + } + + + } + }) - - /* - value.addCallback((t) => { - self?.ShowValue(t); - })*/ - + } for (let i = 0; i < elements.length; i++) { // If an element is clicked, the radio button corresponding with it should be selected as well @@ -63,14 +74,25 @@ export class RadioButton extends InputElement { input.name = groupId; input.type = "radio" + input.onchange = () => { + if(input.checked){ + selectedElementIndex.setData(i1) + } + } + + value.addCallbackAndRun( + selected => input.checked = element.IsValid(selected) + ) const label = document.createElement("label") label.appendChild(labelHtml) label.htmlFor = input.id; - input.appendChild(label) - form.appendChild(input) + const block = document.createElement("div") + block.appendChild(input) + block.appendChild(label) + form.appendChild(block) form.addEventListener("change", () => { // TODO FIXME } @@ -81,6 +103,7 @@ export class RadioButton extends InputElement { this.value = value; this._elements = elements; + this.SetClass("flex flex-col") } IsValid(t: T): boolean { diff --git a/UI/Popup/EditableTagRendering.ts b/UI/Popup/EditableTagRendering.ts index db89a4fd5b..373f29ef67 100644 --- a/UI/Popup/EditableTagRendering.ts +++ b/UI/Popup/EditableTagRendering.ts @@ -8,6 +8,7 @@ import State from "../../State"; import Svg from "../../Svg"; import Toggle from "../Input/Toggle"; import BaseUIElement from "../BaseUIElement"; +import {FixedUiElement} from "../Base/FixedUiElement"; export default class EditableTagRendering extends Toggle { @@ -28,8 +29,10 @@ export default class EditableTagRendering extends Toggle { }); + const answerWithEditButton = new Combine([answer, - new Toggle(editButton, undefined, State.state.osmConnection.isLoggedIn)]).SetClass("w-full") + new Toggle(editButton, undefined, State.state.osmConnection.isLoggedIn)]) + .SetClass("flex justify-between w-full") const cancelbutton = @@ -52,13 +55,12 @@ export default class EditableTagRendering extends Toggle { editMode ) } - answer.SetClass("flex w-full break-word justify-between text-default landscape:w-1/2 landscape:p-2 pb-2 border-b border-gray-300 mb-2") - rendering.SetClass("flex m-1 p-1 border-b border-gray-300 mb-2 pb-2") + rendering.SetClass("block w-full break-word text-default m-1 p-1 border-b border-gray-200 mb-2 pb-2") // The tagrendering is hidden if: // The answer is unknown. The questionbox will then show the question // There is a condition hiding the answer const renderingIsShown = tags.map(tags => - !configuration.IsKnown(tags) && + configuration.IsKnown(tags) && (configuration?.condition?.matchesProperties(tags) ?? true)) super( rendering, diff --git a/UI/Popup/TagRenderingAnswer.ts b/UI/Popup/TagRenderingAnswer.ts index 8000361515..86db6437ef 100644 --- a/UI/Popup/TagRenderingAnswer.ts +++ b/UI/Popup/TagRenderingAnswer.ts @@ -1,11 +1,10 @@ import {UIEventSource} from "../../Logic/UIEventSource"; import TagRenderingConfig from "../../Customizations/JSON/TagRenderingConfig"; import {Utils} from "../../Utils"; -import {SubstitutedTranslation} from "../SubstitutedTranslation"; import BaseUIElement from "../BaseUIElement"; import {VariableUiElement} from "../Base/VariableUIElement"; import List from "../Base/List"; -import {FixedUiElement} from "../Base/FixedUiElement"; +import {SubstitutedTranslation} from "../SubstitutedTranslation"; /*** * Displays the correct value for a known tagrendering @@ -24,19 +23,17 @@ export default class TagRenderingAnswer extends VariableUiElement { if(trs.length === 0){ return undefined; } - trs.forEach(tr => console.log("Rendering ", tr)) - const valuesToRender: BaseUIElement[] = trs.map(tr => new SubstitutedTranslation(tr, tagsSource)) + + const valuesToRender: BaseUIElement[] = trs.map(tr => new SubstitutedTranslation(tr, tagsSource)) + if(valuesToRender.length === 1){ + return valuesToRender[0]; + }else if(valuesToRender.length > 1){ + return new List(valuesToRender) + } + return undefined; + }).map((element : BaseUIElement) => element?.SetClass(contentClasses)?.SetStyle(contentStyle))) - if(valuesToRender.length === 1){ - return valuesToRender[0]; - }else if(valuesToRender.length > 1){ - return new List(valuesToRender) - } - return undefined; - }).map(innerComponent => innerComponent?.SetClass(contentClasses)?.SetStyle(contentStyle)) - ) - - this.SetClass("flex items-center flex-row text-lg link-underline") + this.SetClass("flex items-center flex-row text-lg link-underline tag-renering-answer") this.SetStyle("word-wrap: anywhere;"); } diff --git a/UI/ShowDataLayer.ts b/UI/ShowDataLayer.ts index 70560ad7e8..6a2a8953dd 100644 --- a/UI/ShowDataLayer.ts +++ b/UI/ShowDataLayer.ts @@ -14,8 +14,7 @@ export default class ShowDataLayer { private _layerDict; private readonly _leafletMap: UIEventSource; - - private readonly _popups = new Map(); + private _cleanCount = 0; constructor(features: UIEventSource<{ feature: any, freshness: Date }[]>, leafletMap: UIEventSource, @@ -44,6 +43,7 @@ export default class ShowDataLayer { return; } + self._cleanCount++ // clean all the old stuff away, if any if (geoLayer !== undefined) { mp.removeLayer(geoLayer); @@ -74,34 +74,6 @@ export default class ShowDataLayer { features.addCallback(() => update()); leafletMap.addCallback(() => update()); update(); - - - State.state.selectedElement.addCallbackAndRun(selected => { - if (selected === undefined) { - mp.closePopup(); - return; - } - const marker = self._popups.get(selected); - if (marker === undefined) { - return; - } - marker.openPopup(); - - - const tags = State.state.allElements.getEventSourceById(selected.properties.id); - const layer: LayerConfig = this._layerDict[selected._matching_layer_id]; - const infoBox = new FeatureInfoBox(tags, layer); - - infoBox.isShown.addCallback(isShown => { - if (!isShown) { - State.state.selectedElement.setData(undefined); - } - }); - - infoBox.AttachTo(`popup-${selected.properties.id}`) - infoBox.Activate(); - }) - } @@ -154,17 +126,43 @@ export default class ShowDataLayer { closeButton: false }, leafletLayer); - // By setting 50vh, leaflet will attempt to fit the entire screen and move the feature down - popup.setContent(``); - - leafletLayer.bindPopup(popup); + leafletLayer.bindPopup(popup); + let infobox : FeatureInfoBox = undefined; + + const id = `popup-${feature.properties.id}-${this._cleanCount}` + popup.setContent(`
Rendering
`) + leafletLayer.on("popupopen", () => { State.state.selectedElement.setData(feature) - // The feature info box is bound via the selected element callback, as there are multiple ways to open the popup (e.g. a trigger via the URL° + if (infobox === undefined) { + const tags = State.state.allElements.getEventSourceById(feature.properties.id); + infobox = new FeatureInfoBox(tags, layer); + + infobox.isShown.addCallback(isShown => { + if (!isShown) { + State.state.selectedElement.setData(undefined); + } + }); + } + + + infobox.AttachTo(id) + infobox.Activate(); }); + const self = this; + State.state.selectedElement.addCallbackAndRun(selected => { + if (selected === undefined || self._leafletMap.data === undefined) { + return; + } + if (popup.isOpen()) { + return; + } + if (selected.properties.id === feature.properties.id) { + leafletLayer.openPopup() + } + }) - this._popups.set(feature, leafletLayer); } private CreateGeojsonLayer(): L.Layer { diff --git a/UI/SubstitutedTranslation.ts b/UI/SubstitutedTranslation.ts index bd43e127f4..d48e56cb99 100644 --- a/UI/SubstitutedTranslation.ts +++ b/UI/SubstitutedTranslation.ts @@ -20,9 +20,7 @@ export class SubstitutedTranslation extends VariableUiElement { if (txt === undefined) { return "no tags subs tr" } - const contents = SubstitutedTranslation.EvaluateSpecialComponents(txt, tags) - console.log("Substr has contents", contents) - return new Combine(contents) + return new Combine(SubstitutedTranslation.EvaluateSpecialComponents(txt, tags)) }, [Locale.language]) ) diff --git a/test.ts b/test.ts index d4a6ab22e2..1ee32d4091 100644 --- a/test.ts +++ b/test.ts @@ -1,3 +1,27 @@ -import TestAll from "./test/TestAll"; +import {RadioButton} from "./UI/Input/RadioButton"; +import {FixedInputElement} from "./UI/Input/FixedInputElement"; +import {SubstitutedTranslation} from "./UI/SubstitutedTranslation"; +import {UIEventSource} from "./Logic/UIEventSource"; +import {Translation} from "./UI/i18n/Translation"; +import TagRenderingAnswer from "./UI/Popup/TagRenderingAnswer"; +import TagRenderingConfig from "./Customizations/JSON/TagRenderingConfig"; +import EditableTagRendering from "./UI/Popup/EditableTagRendering"; -new TestAll().testAll(); + +const tagsSource = new UIEventSource({ + id:'id', + name:'name', + surface:'asphalt' +}) + +const config = new TagRenderingConfig({ + render: "Rendering {name} {id} {surface}" +}, null, "test") + +new EditableTagRendering( + tagsSource, + config +).AttachTo("extradiv") + + +window.v = tagsSource \ No newline at end of file diff --git a/test/Tag.spec.ts b/test/Tag.spec.ts index b4d07702c9..b1bc0ae103 100644 --- a/test/Tag.spec.ts +++ b/test/Tag.spec.ts @@ -190,12 +190,6 @@ export default class TagSpec extends T{ ] }; - const constr = new TagRenderingConfig(def, undefined, "test"); - const uiEl = new EditableTagRendering(new UIEventSource( - {leisure: "park", "access": "no"}), constr - ); - const rendered = uiEl.ConstructElement().innerHTML; - equal(true, rendered.indexOf("Niet toegankelijk") > 0) } ], [ diff --git a/test/TagQuestion.spec.ts b/test/TagQuestion.spec.ts deleted file mode 100644 index b094986df5..0000000000 --- a/test/TagQuestion.spec.ts +++ /dev/null @@ -1,62 +0,0 @@ -import T from "./TestHelper"; -import {Utils} from "../Utils"; - -Utils.runningFromConsole = true; -import TagRenderingQuestion from "../UI/Popup/TagRenderingQuestion"; -import {UIEventSource} from "../Logic/UIEventSource"; -import TagRenderingConfig from "../Customizations/JSON/TagRenderingConfig"; - -export default class TagQuestionSpec extends T { - constructor() { - super("TagQuestionElement", - [ - ["Freeform has textfield", () => { - const tags = new UIEventSource({ - id: "way/123", - amenity: 'public_bookcases' - }); - const config = new TagRenderingConfig( - { - render: "The name is {name}", - question: "What is the name of this bookcase?", - freeform: { - key: "name", - type: "string" - } - }, undefined, "Testing tag" - ); - const questionElement = new TagRenderingQuestion(tags, config); - const html = questionElement.InnerRenderAsString(); - T.assertContains("What is the name of this bookcase?", html); - T.assertContains(" { - const tags = new UIEventSource({ - id: "way/123", - amenity: 'public_bookcases' - }); - const config = new TagRenderingConfig( - { - render: "The name is {name}", - question: "What is the name of this bookcase?", - freeform: { - key: "name", - type: "string" - }, - mappings: [ - { - "if": "noname=yes", - "then": "This bookcase has no name" - } - ] - }, undefined, "Testing tag" - ); - const questionElement = new TagRenderingQuestion(tags, config); - const html = questionElement.InnerRenderAsString(); - T.assertContains("What is the name of this bookcase?", html); - T.assertContains("This bookcase has no name", html); - T.assertContains("