Fix popups and core functionality
This commit is contained in:
parent
9cc721abad
commit
8ad9b816ac
13 changed files with 116 additions and 144 deletions
|
@ -43,6 +43,7 @@ export default abstract class BaseUIElement {
|
||||||
throw "SEVERE: could not attach UIElement to " + divId;
|
throw "SEVERE: could not attach UIElement to " + divId;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
console.log("Attaching to ", element)
|
||||||
while (element.firstChild) {
|
while (element.firstChild) {
|
||||||
//The list is LIVE so it will re-index each call
|
//The list is LIVE so it will re-index each call
|
||||||
element.removeChild(element.firstChild);
|
element.removeChild(element.firstChild);
|
||||||
|
|
|
@ -46,7 +46,10 @@ export default class ShareScreen extends Combine {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
if (includeL) {
|
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 {
|
} else {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
@ -166,8 +169,6 @@ export default class ShareScreen extends Combine {
|
||||||
}, optionParts);
|
}, 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(
|
const iframeCode = new VariableUiElement(
|
||||||
url.map((url) => {
|
url.map((url) => {
|
||||||
return `<span class='literal-code iframe-code-block'>
|
return `<span class='literal-code iframe-code-block'>
|
||||||
|
|
|
@ -31,7 +31,7 @@ export class FixedInputElement<T> extends InputElement<T> {
|
||||||
}
|
}
|
||||||
|
|
||||||
protected InnerConstructElement(): HTMLElement {
|
protected InnerConstructElement(): HTMLElement {
|
||||||
return undefined;
|
return this._el;
|
||||||
}
|
}
|
||||||
|
|
||||||
GetValue(): UIEventSource<T> {
|
GetValue(): UIEventSource<T> {
|
||||||
|
|
|
@ -23,13 +23,24 @@ export class RadioButton<T> extends InputElement<T> {
|
||||||
}
|
}
|
||||||
), elements.map(e => e?.GetValue()));
|
), elements.map(e => e?.GetValue()));
|
||||||
|
|
||||||
|
if(selectFirstAsDefault){
|
||||||
|
|
||||||
/*
|
value.addCallbackAndRun(selected =>{
|
||||||
value.addCallback((t) => {
|
if(selected === undefined){
|
||||||
self?.ShowValue(t);
|
for (const element of elements) {
|
||||||
})*/
|
const v = element.GetValue().data;
|
||||||
|
if(v !== undefined){
|
||||||
|
value.setData(v)
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
for (let i = 0; i < elements.length; i++) {
|
for (let i = 0; i < elements.length; i++) {
|
||||||
// If an element is clicked, the radio button corresponding with it should be selected as well
|
// If an element is clicked, the radio button corresponding with it should be selected as well
|
||||||
elements[i]?.onClick(() => {
|
elements[i]?.onClick(() => {
|
||||||
|
@ -63,14 +74,25 @@ export class RadioButton<T> extends InputElement<T> {
|
||||||
input.name = groupId;
|
input.name = groupId;
|
||||||
input.type = "radio"
|
input.type = "radio"
|
||||||
|
|
||||||
|
input.onchange = () => {
|
||||||
|
if(input.checked){
|
||||||
|
selectedElementIndex.setData(i1)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
value.addCallbackAndRun(
|
||||||
|
selected => input.checked = element.IsValid(selected)
|
||||||
|
)
|
||||||
|
|
||||||
const label = document.createElement("label")
|
const label = document.createElement("label")
|
||||||
label.appendChild(labelHtml)
|
label.appendChild(labelHtml)
|
||||||
label.htmlFor = input.id;
|
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", () => {
|
form.addEventListener("change", () => {
|
||||||
// TODO FIXME
|
// TODO FIXME
|
||||||
}
|
}
|
||||||
|
@ -81,6 +103,7 @@ export class RadioButton<T> extends InputElement<T> {
|
||||||
this.value = value;
|
this.value = value;
|
||||||
this._elements = elements;
|
this._elements = elements;
|
||||||
|
|
||||||
|
this.SetClass("flex flex-col")
|
||||||
}
|
}
|
||||||
|
|
||||||
IsValid(t: T): boolean {
|
IsValid(t: T): boolean {
|
||||||
|
|
|
@ -8,6 +8,7 @@ import State from "../../State";
|
||||||
import Svg from "../../Svg";
|
import Svg from "../../Svg";
|
||||||
import Toggle from "../Input/Toggle";
|
import Toggle from "../Input/Toggle";
|
||||||
import BaseUIElement from "../BaseUIElement";
|
import BaseUIElement from "../BaseUIElement";
|
||||||
|
import {FixedUiElement} from "../Base/FixedUiElement";
|
||||||
|
|
||||||
export default class EditableTagRendering extends Toggle {
|
export default class EditableTagRendering extends Toggle {
|
||||||
|
|
||||||
|
@ -28,8 +29,10 @@ export default class EditableTagRendering extends Toggle {
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
const answerWithEditButton = new Combine([answer,
|
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 =
|
const cancelbutton =
|
||||||
|
@ -52,13 +55,12 @@ export default class EditableTagRendering extends Toggle {
|
||||||
editMode
|
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("block w-full break-word text-default m-1 p-1 border-b border-gray-200 mb-2 pb-2")
|
||||||
rendering.SetClass("flex m-1 p-1 border-b border-gray-300 mb-2 pb-2")
|
|
||||||
// The tagrendering is hidden if:
|
// The tagrendering is hidden if:
|
||||||
// The answer is unknown. The questionbox will then show the question
|
// The answer is unknown. The questionbox will then show the question
|
||||||
// There is a condition hiding the answer
|
// There is a condition hiding the answer
|
||||||
const renderingIsShown = tags.map(tags =>
|
const renderingIsShown = tags.map(tags =>
|
||||||
!configuration.IsKnown(tags) &&
|
configuration.IsKnown(tags) &&
|
||||||
(configuration?.condition?.matchesProperties(tags) ?? true))
|
(configuration?.condition?.matchesProperties(tags) ?? true))
|
||||||
super(
|
super(
|
||||||
rendering,
|
rendering,
|
||||||
|
|
|
@ -1,11 +1,10 @@
|
||||||
import {UIEventSource} from "../../Logic/UIEventSource";
|
import {UIEventSource} from "../../Logic/UIEventSource";
|
||||||
import TagRenderingConfig from "../../Customizations/JSON/TagRenderingConfig";
|
import TagRenderingConfig from "../../Customizations/JSON/TagRenderingConfig";
|
||||||
import {Utils} from "../../Utils";
|
import {Utils} from "../../Utils";
|
||||||
import {SubstitutedTranslation} from "../SubstitutedTranslation";
|
|
||||||
import BaseUIElement from "../BaseUIElement";
|
import BaseUIElement from "../BaseUIElement";
|
||||||
import {VariableUiElement} from "../Base/VariableUIElement";
|
import {VariableUiElement} from "../Base/VariableUIElement";
|
||||||
import List from "../Base/List";
|
import List from "../Base/List";
|
||||||
import {FixedUiElement} from "../Base/FixedUiElement";
|
import {SubstitutedTranslation} from "../SubstitutedTranslation";
|
||||||
|
|
||||||
/***
|
/***
|
||||||
* Displays the correct value for a known tagrendering
|
* Displays the correct value for a known tagrendering
|
||||||
|
@ -24,19 +23,17 @@ export default class TagRenderingAnswer extends VariableUiElement {
|
||||||
if(trs.length === 0){
|
if(trs.length === 0){
|
||||||
return undefined;
|
return undefined;
|
||||||
}
|
}
|
||||||
trs.forEach(tr => console.log("Rendering ", tr))
|
|
||||||
const valuesToRender: BaseUIElement[] = trs.map(tr => new SubstitutedTranslation(tr, tagsSource))
|
|
||||||
|
|
||||||
if(valuesToRender.length === 1){
|
const valuesToRender: BaseUIElement[] = trs.map(tr => new SubstitutedTranslation(tr, tagsSource))
|
||||||
return valuesToRender[0];
|
if(valuesToRender.length === 1){
|
||||||
}else if(valuesToRender.length > 1){
|
return valuesToRender[0];
|
||||||
return new List(valuesToRender)
|
}else if(valuesToRender.length > 1){
|
||||||
}
|
return new List(valuesToRender)
|
||||||
return undefined;
|
}
|
||||||
}).map(innerComponent => innerComponent?.SetClass(contentClasses)?.SetStyle(contentStyle))
|
return undefined;
|
||||||
)
|
}).map((element : BaseUIElement) => element?.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;");
|
this.SetStyle("word-wrap: anywhere;");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -14,8 +14,7 @@ export default class ShowDataLayer {
|
||||||
|
|
||||||
private _layerDict;
|
private _layerDict;
|
||||||
private readonly _leafletMap: UIEventSource<L.Map>;
|
private readonly _leafletMap: UIEventSource<L.Map>;
|
||||||
|
private _cleanCount = 0;
|
||||||
private readonly _popups = new Map<any, L.Layer>();
|
|
||||||
|
|
||||||
constructor(features: UIEventSource<{ feature: any, freshness: Date }[]>,
|
constructor(features: UIEventSource<{ feature: any, freshness: Date }[]>,
|
||||||
leafletMap: UIEventSource<L.Map>,
|
leafletMap: UIEventSource<L.Map>,
|
||||||
|
@ -44,6 +43,7 @@ export default class ShowDataLayer {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
self._cleanCount++
|
||||||
// clean all the old stuff away, if any
|
// clean all the old stuff away, if any
|
||||||
if (geoLayer !== undefined) {
|
if (geoLayer !== undefined) {
|
||||||
mp.removeLayer(geoLayer);
|
mp.removeLayer(geoLayer);
|
||||||
|
@ -74,34 +74,6 @@ export default class ShowDataLayer {
|
||||||
features.addCallback(() => update());
|
features.addCallback(() => update());
|
||||||
leafletMap.addCallback(() => update());
|
leafletMap.addCallback(() => update());
|
||||||
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
|
closeButton: false
|
||||||
}, leafletLayer);
|
}, leafletLayer);
|
||||||
|
|
||||||
// By setting 50vh, leaflet will attempt to fit the entire screen and move the feature down
|
leafletLayer.bindPopup(popup);
|
||||||
popup.setContent(`<div style='height: 50vh' id='popup-${feature.properties.id}'>Rendering</div>`);
|
|
||||||
|
|
||||||
leafletLayer.bindPopup(popup);
|
let infobox : FeatureInfoBox = undefined;
|
||||||
|
|
||||||
|
const id = `popup-${feature.properties.id}-${this._cleanCount}`
|
||||||
|
popup.setContent(`<div style='height: 50vh' id='${id}'>Rendering</div>`)
|
||||||
|
|
||||||
leafletLayer.on("popupopen", () => {
|
leafletLayer.on("popupopen", () => {
|
||||||
State.state.selectedElement.setData(feature)
|
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 {
|
private CreateGeojsonLayer(): L.Layer {
|
||||||
|
|
|
@ -20,9 +20,7 @@ export class SubstitutedTranslation extends VariableUiElement {
|
||||||
if (txt === undefined) {
|
if (txt === undefined) {
|
||||||
return "no tags subs tr"
|
return "no tags subs tr"
|
||||||
}
|
}
|
||||||
const contents = SubstitutedTranslation.EvaluateSpecialComponents(txt, tags)
|
return new Combine(SubstitutedTranslation.EvaluateSpecialComponents(txt, tags))
|
||||||
console.log("Substr has contents", contents)
|
|
||||||
return new Combine(contents)
|
|
||||||
}, [Locale.language])
|
}, [Locale.language])
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
28
test.ts
28
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
|
|
@ -190,12 +190,6 @@ export default class TagSpec extends T{
|
||||||
]
|
]
|
||||||
};
|
};
|
||||||
|
|
||||||
const constr = new TagRenderingConfig(def, undefined, "test");
|
|
||||||
const uiEl = new EditableTagRendering(new UIEventSource<any>(
|
|
||||||
{leisure: "park", "access": "no"}), constr
|
|
||||||
);
|
|
||||||
const rendered = uiEl.ConstructElement().innerHTML;
|
|
||||||
equal(true, rendered.indexOf("Niet toegankelijk") > 0)
|
|
||||||
|
|
||||||
}
|
}
|
||||||
], [
|
], [
|
||||||
|
|
|
@ -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("<input type='text'", html);
|
|
||||||
}],
|
|
||||||
["TagsQuestion with Freeform and mappings 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"
|
|
||||||
},
|
|
||||||
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("<input type='text'", html);
|
|
||||||
}]
|
|
||||||
]);
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,11 +1,8 @@
|
||||||
import {Utils} from "../Utils";
|
import {Utils} from "../Utils";
|
||||||
|
|
||||||
Utils.runningFromConsole = true;
|
Utils.runningFromConsole = true;
|
||||||
|
|
||||||
import TagSpec from "./Tag.spec";
|
import TagSpec from "./Tag.spec";
|
||||||
import ImageAttributionSpec from "./ImageAttribution.spec";
|
import ImageAttributionSpec from "./ImageAttribution.spec";
|
||||||
import GeoOperationsSpec from "./GeoOperations.spec";
|
import GeoOperationsSpec from "./GeoOperations.spec";
|
||||||
import TagQuestionSpec from "./TagQuestion.spec";
|
|
||||||
import ImageSearcherSpec from "./ImageSearcher.spec";
|
import ImageSearcherSpec from "./ImageSearcher.spec";
|
||||||
import ThemeSpec from "./Theme.spec";
|
import ThemeSpec from "./Theme.spec";
|
||||||
import UtilsSpec from "./Utils.spec";
|
import UtilsSpec from "./Utils.spec";
|
||||||
|
@ -34,7 +31,6 @@ const allTests = [
|
||||||
new TagSpec(),
|
new TagSpec(),
|
||||||
new ImageAttributionSpec(),
|
new ImageAttributionSpec(),
|
||||||
new GeoOperationsSpec(),
|
new GeoOperationsSpec(),
|
||||||
new TagQuestionSpec(),
|
|
||||||
new ImageSearcherSpec(),
|
new ImageSearcherSpec(),
|
||||||
new ThemeSpec(),
|
new ThemeSpec(),
|
||||||
new UtilsSpec()]
|
new UtilsSpec()]
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue