forked from MapComplete/MapComplete
More refactoring and fixes
This commit is contained in:
parent
d7004cd3dc
commit
9cc721abad
37 changed files with 519 additions and 632 deletions
|
@ -325,7 +325,7 @@ export default class LayerConfig {
|
|||
|
||||
function render(tr: TagRenderingConfig, deflt?: string) {
|
||||
const str = (tr?.GetRenderValue(tags.data)?.txt ?? deflt);
|
||||
return SubstitutedTranslation.SubstituteKeys(str, tags.data).replace(/{.*}/g, "");
|
||||
return Utils.SubstituteKeys(str, tags.data).replace(/{.*}/g, "");
|
||||
}
|
||||
|
||||
const iconSize = render(this.iconSize, "40,40,center").split(",");
|
||||
|
|
|
@ -7,6 +7,8 @@ import {Utils} from "../../Utils";
|
|||
import {TagUtils} from "../../Logic/Tags/TagUtils";
|
||||
import {And} from "../../Logic/Tags/And";
|
||||
import {TagsFilter} from "../../Logic/Tags/TagsFilter";
|
||||
import {UIElement} from "../../UI/UIElement";
|
||||
import {SubstitutedTranslation} from "../../UI/SubstitutedTranslation";
|
||||
|
||||
/***
|
||||
* The parsed version of TagRenderingConfigJSON
|
||||
|
@ -240,6 +242,46 @@ export default class TagRenderingConfig {
|
|||
return this.question === null && this.condition === null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets all the render values. Will return multiple render values if 'multianswer' is enabled.
|
||||
* The result will equal [GetRenderValue] if not 'multiAnswer'
|
||||
* @param tags
|
||||
* @constructor
|
||||
*/
|
||||
public GetRenderValues(tags: any): Translation[]{
|
||||
if(!this.multiAnswer){
|
||||
return [this.GetRenderValue(tags)]
|
||||
}
|
||||
|
||||
// A flag to check that the freeform key isn't matched multiple times
|
||||
// If it is undefined, it is "used" already, or at least we don't have to check for it anymore
|
||||
let freeformKeyUsed = this.freeform?.key === undefined;
|
||||
// We run over all the mappings first, to check if the mapping matches
|
||||
const applicableMappings: Translation[] = Utils.NoNull((this.mappings ?? [])?.map(mapping => {
|
||||
if (mapping.if === undefined) {
|
||||
return mapping.then;
|
||||
}
|
||||
if (TagUtils.MatchesMultiAnswer(mapping.if, tags)) {
|
||||
if(!freeformKeyUsed){
|
||||
if(mapping.if.usedKeys().indexOf(this.freeform.key) >= 0){
|
||||
// This mapping matches the freeform key - we mark the freeform key to be ignored!
|
||||
freeformKeyUsed = true;
|
||||
}
|
||||
}
|
||||
return mapping.then;
|
||||
}
|
||||
return undefined;
|
||||
}))
|
||||
|
||||
|
||||
|
||||
if (!freeformKeyUsed
|
||||
&& tags[this.freeform.key] !== undefined) {
|
||||
applicableMappings.push(this.render)
|
||||
}
|
||||
return applicableMappings
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the correct rendering value (or undefined if not known)
|
||||
* @constructor
|
||||
|
|
|
@ -269,11 +269,10 @@ export class InitUiElements {
|
|||
|
||||
// ?-Button on Desktop, opens panel with close-X.
|
||||
const help = new MapControlButton(Svg.help_svg());
|
||||
help.onClick(() => isOpened.setData(true))
|
||||
new Toggle(
|
||||
fullOptions
|
||||
.SetClass("welcomeMessage")
|
||||
.onClick(() => {/*Catch the click*/
|
||||
}),
|
||||
.SetClass("welcomeMessage"),
|
||||
help
|
||||
, isOpened
|
||||
).AttachTo("messagesbox");
|
||||
|
@ -308,7 +307,8 @@ export class InitUiElements {
|
|||
copyrightNotice,
|
||||
new MapControlButton(Svg.osm_copyright_svg()),
|
||||
copyrightNotice.isShown
|
||||
).SetClass("p-0.5")
|
||||
).ToggleOnClick()
|
||||
.SetClass("p-0.5")
|
||||
|
||||
const layerControlPanel = new LayerControlPanel(
|
||||
State.state.layerControlIsOpened)
|
||||
|
@ -317,7 +317,7 @@ export class InitUiElements {
|
|||
layerControlPanel,
|
||||
new MapControlButton(Svg.layers_svg()),
|
||||
State.state.layerControlIsOpened
|
||||
)
|
||||
).ToggleOnClick()
|
||||
|
||||
const layerControl = new Toggle(
|
||||
layerControlButton,
|
||||
|
|
|
@ -53,7 +53,6 @@ export default class OverpassFeatureSource implements FeatureSource {
|
|||
return false;
|
||||
}
|
||||
let minzoom = Math.min(...layoutToUse.data.layers.map(layer => layer.minzoom ?? 18));
|
||||
console.debug("overpass source: minzoom is ", minzoom)
|
||||
return location.zoom >= minzoom;
|
||||
}, [layoutToUse]
|
||||
);
|
||||
|
|
|
@ -47,13 +47,13 @@ export default class StrayClickHandler {
|
|||
popupAnchor: [0, -45]
|
||||
})
|
||||
});
|
||||
const popup = L.popup().setContent(uiToShow.Render());
|
||||
const popup = L.popup().setContent("<div id='strayclick'></div>");
|
||||
self._lastMarker.addTo(leafletMap.data);
|
||||
self._lastMarker.bindPopup(popup);
|
||||
|
||||
self._lastMarker.on("click", () => {
|
||||
uiToShow.AttachTo("strayclick")
|
||||
uiToShow.Activate();
|
||||
uiToShow.Update();
|
||||
});
|
||||
});
|
||||
|
||||
|
|
|
@ -65,7 +65,14 @@ export class OsmConnection {
|
|||
|
||||
this.userDetails = new UIEventSource<UserDetails>(new UserDetails(), "userDetails");
|
||||
this.userDetails.data.dryRun = dryRun;
|
||||
this.isLoggedIn = this.userDetails.map(user => user.loggedIn)
|
||||
const self =this;
|
||||
this.isLoggedIn = this.userDetails.map(user => user.loggedIn).addCallback(isLoggedIn => {
|
||||
if(self.userDetails.data.loggedIn == false){
|
||||
// We have an inconsistency: the userdetails say we _didn't_ log in, but this actor says we do
|
||||
// This means someone attempted to toggle this; so we attempt to login!
|
||||
self.AttemptLogin()
|
||||
}
|
||||
});
|
||||
this._dryRun = dryRun;
|
||||
|
||||
this.updateAuthObject();
|
||||
|
@ -217,14 +224,15 @@ export class OsmConnection {
|
|||
});
|
||||
}
|
||||
|
||||
private CheckForMessagesContinuously() {
|
||||
const self = this;
|
||||
window.setTimeout(() => {
|
||||
if (self.userDetails.data.loggedIn) {
|
||||
console.log("Checking for messages")
|
||||
this.AttemptLogin();
|
||||
}
|
||||
}, 5 * 60 * 1000);
|
||||
private CheckForMessagesContinuously(){
|
||||
const self =this;
|
||||
UIEventSource.Chronic(5 * 60 * 1000).addCallback(_ => {
|
||||
if (self.isLoggedIn .data) {
|
||||
console.log("Checking for messages")
|
||||
self.AttemptLogin();
|
||||
}
|
||||
});
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
|
|
@ -6,7 +6,6 @@ export default class Constants {
|
|||
|
||||
// The user journey states thresholds when a new feature gets unlocked
|
||||
public static userJourney = {
|
||||
addNewPointsUnlock: 0,
|
||||
moreScreenUnlock: 1,
|
||||
personalLayoutUnlock: 15,
|
||||
historyLinkVisible: 20,
|
||||
|
|
|
@ -1,4 +1,3 @@
|
|||
import {UIElement} from "../UIElement";
|
||||
import BaseUIElement from "../BaseUIElement";
|
||||
|
||||
export class FixedUiElement extends BaseUIElement {
|
||||
|
|
34
UI/Base/List.ts
Normal file
34
UI/Base/List.ts
Normal file
|
@ -0,0 +1,34 @@
|
|||
import {Utils} from "../../Utils";
|
||||
import BaseUIElement from "../BaseUIElement";
|
||||
import Translations from "../i18n/Translations";
|
||||
|
||||
export default class List extends BaseUIElement {
|
||||
private readonly uiElements: BaseUIElement[];
|
||||
private readonly _ordered: boolean;
|
||||
|
||||
constructor(uiElements: (string | BaseUIElement)[], ordered = false) {
|
||||
super();
|
||||
this._ordered = ordered;
|
||||
this.uiElements = Utils.NoNull(uiElements)
|
||||
.map(Translations.W);
|
||||
}
|
||||
|
||||
protected InnerConstructElement(): HTMLElement {
|
||||
const el = document.createElement(this._ordered ? "ol" : "ul")
|
||||
|
||||
for (const subEl of this.uiElements) {
|
||||
if(subEl === undefined || subEl === null){
|
||||
continue;
|
||||
}
|
||||
const subHtml = subEl.ConstructElement()
|
||||
if(subHtml !== undefined){
|
||||
const item = document.createElement("li")
|
||||
item.appendChild(subHtml)
|
||||
el.appendChild(item)
|
||||
}
|
||||
}
|
||||
|
||||
return el;
|
||||
}
|
||||
|
||||
}
|
|
@ -35,8 +35,8 @@ export class SubtleButton extends UIElement {
|
|||
if (linkTo == undefined) {
|
||||
return new Combine([
|
||||
image,
|
||||
message,
|
||||
]);
|
||||
message?.SetClass("blcok ml-4 overflow-ellipsis"),
|
||||
]).SetClass("flex group");
|
||||
}
|
||||
|
||||
|
||||
|
|
|
@ -24,10 +24,17 @@ export class VariableUiElement extends BaseUIElement {
|
|||
el.innerHTML = contents
|
||||
} else if (contents instanceof Array) {
|
||||
for (const content of contents) {
|
||||
el.appendChild(content.ConstructElement())
|
||||
const c = content.ConstructElement();
|
||||
if (c !== undefined && c !== null) {
|
||||
el.appendChild(c)
|
||||
}
|
||||
|
||||
}
|
||||
} else {
|
||||
const c = contents.ConstructElement();
|
||||
if (c !== undefined && c !== null) {
|
||||
el.appendChild(c)
|
||||
}
|
||||
}else{
|
||||
el.appendChild(contents.ConstructElement())
|
||||
}
|
||||
})
|
||||
}
|
||||
|
|
|
@ -104,6 +104,9 @@ export default abstract class BaseUIElement {
|
|||
return this._constructedHtmlElement
|
||||
}
|
||||
|
||||
if(this.InnerConstructElement === undefined){
|
||||
throw "ERROR! This is not a correct baseUIElement: "+this.constructor.name
|
||||
}
|
||||
|
||||
const el = this.InnerConstructElement();
|
||||
|
||||
|
|
|
@ -65,8 +65,8 @@ export default class FullWelcomePaneWithTabs extends ScrollableFullScreen {
|
|||
);
|
||||
|
||||
return new Toggle(
|
||||
new TabbedComponent(tabs, State.state.welcomeMessageOpenedTab),
|
||||
new TabbedComponent(tabsWithAboutMc, State.state.welcomeMessageOpenedTab),
|
||||
new TabbedComponent(tabs, State.state.welcomeMessageOpenedTab),
|
||||
userDetails.map(userdetails =>
|
||||
userdetails.csCount < Constants.userJourney.mapCompleteHelpUnlock)
|
||||
)
|
||||
|
|
|
@ -56,7 +56,7 @@ export default class LayerSelection extends Combine {
|
|||
.SetStyle(styleWhole),
|
||||
new Combine([new Combine([iconUnselected, "<del>", name, "</del>"]).SetStyle(style), zoomStatus])
|
||||
.SetStyle(styleWhole),
|
||||
layer.isDisplayed)
|
||||
layer.isDisplayed).ToggleOnClick()
|
||||
.SetStyle("margin:0.3em;")
|
||||
);
|
||||
}
|
||||
|
|
|
@ -91,7 +91,7 @@ export default class PersonalLayersPanel extends UIElement {
|
|||
"</del>"
|
||||
])),
|
||||
controls[layer.id] ?? (favs.indexOf(layer.id) >= 0)
|
||||
);
|
||||
).ToggleOnClick();
|
||||
cb.SetClass("custom-layer-checkbox");
|
||||
controls[layer.id] = cb.isEnabled;
|
||||
|
||||
|
|
|
@ -36,7 +36,7 @@ export default class ShareScreen extends Combine {
|
|||
new Combine([check(), tr.fsIncludeCurrentLocation.Clone()]),
|
||||
new Combine([nocheck(), tr.fsIncludeCurrentLocation.Clone()]),
|
||||
new UIEventSource<boolean>(true)
|
||||
)
|
||||
).ToggleOnClick()
|
||||
optionCheckboxes.push(includeLocation);
|
||||
|
||||
const currentLocation = State.state?.locationControl;
|
||||
|
@ -71,7 +71,7 @@ export default class ShareScreen extends Combine {
|
|||
new Combine([check(), currentBackground]),
|
||||
new Combine([nocheck(), currentBackground]),
|
||||
new UIEventSource<boolean>(true)
|
||||
)
|
||||
).ToggleOnClick()
|
||||
optionCheckboxes.push(includeCurrentBackground);
|
||||
optionParts.push(includeCurrentBackground.isEnabled.map((includeBG) => {
|
||||
if (includeBG) {
|
||||
|
@ -86,7 +86,7 @@ export default class ShareScreen extends Combine {
|
|||
new Combine([check(), tr.fsIncludeCurrentLayers.Clone()]),
|
||||
new Combine([nocheck(), tr.fsIncludeCurrentLayers.Clone()]),
|
||||
new UIEventSource<boolean>(true)
|
||||
)
|
||||
).ToggleOnClick()
|
||||
optionCheckboxes.push(includeLayerChoices);
|
||||
|
||||
optionParts.push(includeLayerChoices.isEnabled.map((includeLayerSelection) => {
|
||||
|
@ -116,7 +116,7 @@ export default class ShareScreen extends Combine {
|
|||
new Combine([check(), Translations.W(swtch.human.Clone())]),
|
||||
new Combine([nocheck(), Translations.W(swtch.human.Clone())]),
|
||||
new UIEventSource<boolean>(!swtch.reverse)
|
||||
);
|
||||
).ToggleOnClick();
|
||||
optionCheckboxes.push(checkbox);
|
||||
optionParts.push(checkbox.isEnabled.map((isEn) => {
|
||||
if (isEn) {
|
||||
|
|
|
@ -1,9 +1,7 @@
|
|||
/**
|
||||
* Asks to add a feature at the last clicked location, at least if zoom is sufficient
|
||||
*/
|
||||
import Locale from "../i18n/Locale";
|
||||
import {UIEventSource} from "../../Logic/UIEventSource";
|
||||
import {UIElement} from "../UIElement";
|
||||
import Svg from "../../Svg";
|
||||
import {SubtleButton} from "../Base/SubtleButton";
|
||||
import State from "../../State";
|
||||
|
@ -14,118 +12,163 @@ import LayerConfig from "../../Customizations/JSON/LayerConfig";
|
|||
import {Tag} from "../../Logic/Tags/Tag";
|
||||
import {TagUtils} from "../../Logic/Tags/TagUtils";
|
||||
import BaseUIElement from "../BaseUIElement";
|
||||
import {VariableUiElement} from "../Base/VariableUIElement";
|
||||
import Toggle from "../Input/Toggle";
|
||||
import UserDetails from "../../Logic/Osm/OsmConnection";
|
||||
import {Translation} from "../i18n/Translation";
|
||||
|
||||
export default class SimpleAddUI extends UIElement {
|
||||
private readonly _loginButton: BaseUIElement;
|
||||
/*
|
||||
* The SimpleAddUI is a single panel, which can have multiple states:
|
||||
* - A list of presets which can be added by the user
|
||||
* - A 'confirm-selection' button (or alternatively: please enable the layer)
|
||||
* - A 'something is wrong - please soom in further'
|
||||
* - A 'read your unread messages before adding a point'
|
||||
*/
|
||||
|
||||
private readonly _confirmPreset: UIEventSource<{
|
||||
description: string | BaseUIElement,
|
||||
name: string | BaseUIElement,
|
||||
icon: BaseUIElement,
|
||||
tags: Tag[],
|
||||
layerToAddTo: {
|
||||
layerDef: LayerConfig,
|
||||
isDisplayed: UIEventSource<boolean>
|
||||
}
|
||||
}>
|
||||
= new UIEventSource(undefined);
|
||||
interface PresetInfo {
|
||||
description: string | Translation,
|
||||
name: string | BaseUIElement,
|
||||
icon: BaseUIElement,
|
||||
tags: Tag[],
|
||||
layerToAddTo: {
|
||||
layerDef: LayerConfig,
|
||||
isDisplayed: UIEventSource<boolean>
|
||||
}
|
||||
}
|
||||
|
||||
private _component:BaseUIElement;
|
||||
|
||||
private readonly openLayerControl: BaseUIElement;
|
||||
private readonly cancelButton: BaseUIElement;
|
||||
private readonly goToInboxButton: BaseUIElement = new SubtleButton(Svg.envelope_ui(),
|
||||
Translations.t.general.goToInbox, {url: "https://www.openstreetmap.org/messages/inbox", newTab: false});
|
||||
export default class SimpleAddUI extends Toggle {
|
||||
|
||||
constructor(isShown: UIEventSource<boolean>) {
|
||||
super(State.state.locationControl.map(loc => loc.zoom));
|
||||
const self = this;
|
||||
this.ListenTo(Locale.language);
|
||||
this.ListenTo(State.state.osmConnection.userDetails);
|
||||
this.ListenTo(State.state.layerUpdater.runningQuery);
|
||||
this.ListenTo(this._confirmPreset);
|
||||
this.ListenTo(State.state.locationControl);
|
||||
State.state.filteredLayers.data?.map(layer => {
|
||||
self.ListenTo(layer.isDisplayed)
|
||||
})
|
||||
|
||||
this._loginButton = Translations.t.general.add.pleaseLogin.Clone().onClick(() => State.state.osmConnection.AttemptLogin());
|
||||
|
||||
const loginButton = Translations.t.general.add.pleaseLogin.Clone().onClick(State.state.osmConnection.AttemptLogin);
|
||||
const readYourMessages = new Combine([
|
||||
Translations.t.general.readYourMessages.Clone().SetClass("alert"),
|
||||
new SubtleButton(Svg.envelope_ui(),
|
||||
Translations.t.general.goToInbox, {url: "https://www.openstreetmap.org/messages/inbox", newTab: false})
|
||||
]);
|
||||
|
||||
|
||||
|
||||
const selectedPreset = new UIEventSource<PresetInfo>(undefined);
|
||||
isShown.addCallback(_ => selectedPreset.setData(undefined)) // Clear preset selection when the UI is closed/opened
|
||||
|
||||
function createNewPoint(tags: any[]){
|
||||
const loc = State.state.LastClickLocation.data;
|
||||
let feature = State.state.changes.createElement(tags, loc.lat, loc.lon);
|
||||
State.state.selectedElement.setData(feature);
|
||||
}
|
||||
|
||||
const presetsOverview = SimpleAddUI.CreateAllPresetsPanel(selectedPreset)
|
||||
|
||||
const addUi = new VariableUiElement(
|
||||
selectedPreset.map(preset => {
|
||||
if (preset === undefined) {
|
||||
return presetsOverview
|
||||
}
|
||||
return SimpleAddUI.CreateConfirmButton(preset,
|
||||
tags => {
|
||||
createNewPoint(tags)
|
||||
selectedPreset.setData(undefined)
|
||||
}, () => {
|
||||
selectedPreset.setData(undefined)
|
||||
})
|
||||
}
|
||||
))
|
||||
|
||||
|
||||
super(
|
||||
new Toggle(
|
||||
new Toggle(
|
||||
new Toggle(
|
||||
Translations.t.general.add.stillLoading.Clone().SetClass("alert"),
|
||||
addUi,
|
||||
State.state.layerUpdater.runningQuery
|
||||
),
|
||||
Translations.t.general.add.zoomInFurther.Clone().SetClass("alert") ,
|
||||
State.state.locationControl.map(loc => loc.zoom >= Constants.userJourney.minZoomLevelToAddNewPoints)
|
||||
),
|
||||
readYourMessages,
|
||||
State.state.osmConnection.userDetails.map((userdetails: UserDetails) =>
|
||||
userdetails.csCount >= Constants.userJourney.addNewPointWithUnreadMessagesUnlock ||
|
||||
userdetails.unreadMessages == 0)
|
||||
),
|
||||
loginButton,
|
||||
State.state.osmConnection.isLoggedIn
|
||||
)
|
||||
|
||||
|
||||
this.SetStyle("font-size:large");
|
||||
this.cancelButton = new SubtleButton(Svg.close_ui(),
|
||||
Translations.t.general.cancel
|
||||
).onClick(() => {
|
||||
self._confirmPreset.setData(undefined);
|
||||
})
|
||||
}
|
||||
|
||||
this.openLayerControl = new SubtleButton(Svg.layers_ui(),
|
||||
Translations.t.general.add.openLayerControl
|
||||
).onClick(() => {
|
||||
State.state.layerControlIsOpened.setData(true);
|
||||
})
|
||||
|
||||
|
||||
private static CreateConfirmButton(preset: PresetInfo,
|
||||
confirm: (tags: any[]) => void,
|
||||
cancel: () => void): BaseUIElement {
|
||||
|
||||
|
||||
const confirmButton = new SubtleButton(preset.icon,
|
||||
new Combine([
|
||||
Translations.t.general.add.addNew.Subs({category: preset.name}),
|
||||
Translations.t.general.add.warnVisibleForEveryone.Clone().SetClass("alert")
|
||||
]).SetClass("flex flex-col")
|
||||
).SetClass("font-bold break-words")
|
||||
.onClick(() => confirm(preset.tags));
|
||||
|
||||
|
||||
const openLayerControl =
|
||||
new SubtleButton(
|
||||
Svg.layers_ui(),
|
||||
new Combine([
|
||||
Translations.t.general.add.layerNotEnabled
|
||||
.Subs({layer: preset.layerToAddTo.layerDef.name})
|
||||
.SetClass("alert"),
|
||||
Translations.t.general.add.openLayerControl
|
||||
])
|
||||
)
|
||||
|
||||
.onClick(() => State.state.layerControlIsOpened.setData(true))
|
||||
|
||||
// IS shown is the state of the dialog - we reset the choice if the dialog dissappears
|
||||
isShown.addCallback(isShown =>
|
||||
{
|
||||
if(!isShown){
|
||||
self._confirmPreset.setData(undefined)
|
||||
}
|
||||
})
|
||||
// If the click location changes, we reset the dialog as well
|
||||
State.state.LastClickLocation.addCallback(() => {
|
||||
self._confirmPreset.setData(undefined)
|
||||
})
|
||||
this._component = this.CreateContent();
|
||||
}
|
||||
const openLayerOrConfirm = new Toggle(
|
||||
confirmButton,
|
||||
openLayerControl,
|
||||
preset.layerToAddTo.isDisplayed
|
||||
)
|
||||
const tagInfo = SimpleAddUI.CreateTagInfoFor(preset);
|
||||
|
||||
InnerRender(): BaseUIElement {
|
||||
return this._component;
|
||||
const cancelButton = new SubtleButton(Svg.close_ui(),
|
||||
Translations.t.general.cancel
|
||||
).onClick(cancel )
|
||||
|
||||
return new Combine([
|
||||
Translations.t.general.add.confirmIntro.Subs({title: preset.name}),
|
||||
State.state.osmConnection.userDetails.data.dryRun ?
|
||||
Translations.t.general.testing.Clone().SetClass("alert") : undefined ,
|
||||
openLayerOrConfirm,
|
||||
cancelButton,
|
||||
preset.description,
|
||||
tagInfo
|
||||
|
||||
]).SetClass("flex flex-col")
|
||||
|
||||
}
|
||||
|
||||
private CreatePresetsPanel(): BaseUIElement {
|
||||
const userDetails = State.state.osmConnection.userDetails;
|
||||
if (userDetails === undefined) {
|
||||
return undefined;
|
||||
}
|
||||
private static CreateTagInfoFor(preset: PresetInfo, optionallyLinkToWiki = true) {
|
||||
const csCount = State.state.osmConnection.userDetails.data.csCount;
|
||||
return new Toggle(
|
||||
Translations.t.general.presetInfo.Subs({
|
||||
tags: preset.tags.map(t => t.asHumanString(optionallyLinkToWiki && csCount > Constants.userJourney.tagsVisibleAndWikiLinked, true)).join("&"),
|
||||
|
||||
if (!userDetails.data.loggedIn) {
|
||||
return this._loginButton;
|
||||
}
|
||||
}),
|
||||
|
||||
if (userDetails.data.unreadMessages > 0 && userDetails.data.csCount < Constants.userJourney.addNewPointWithUnreadMessagesUnlock) {
|
||||
return new Combine([
|
||||
Translations.t.general.readYourMessages.Clone().SetClass("alert"),
|
||||
this.goToInboxButton
|
||||
]);
|
||||
|
||||
}
|
||||
|
||||
if (userDetails.data.csCount < Constants.userJourney.addNewPointsUnlock) {
|
||||
return new Combine(["<span class='alert'>",
|
||||
Translations.t.general.fewChangesBefore,
|
||||
"</span>"]);
|
||||
}
|
||||
|
||||
if (State.state.locationControl.data.zoom < Constants.userJourney.minZoomLevelToAddNewPoints) {
|
||||
return Translations.t.general.add.zoomInFurther.SetClass("alert")
|
||||
}
|
||||
|
||||
if (State.state.layerUpdater.runningQuery.data) {
|
||||
return Translations.t.general.add.stillLoading
|
||||
}
|
||||
|
||||
const presetButtons = this.CreatePresetButtons()
|
||||
return new Combine(presetButtons)
|
||||
undefined,
|
||||
State.state.osmConnection.userDetails.map(userdetails => userdetails.csCount >= Constants.userJourney.tagsVisibleAt)
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
private CreateContent(): BaseUIElement {
|
||||
const confirmPanel = this.CreateConfirmPanel();
|
||||
if (confirmPanel !== undefined) {
|
||||
return confirmPanel;
|
||||
}
|
||||
|
||||
private static CreateAllPresetsPanel(selectedPreset: UIEventSource<PresetInfo>): BaseUIElement {
|
||||
const presetButtons = SimpleAddUI.CreatePresetButtons(selectedPreset)
|
||||
let intro: BaseUIElement = Translations.t.general.add.intro;
|
||||
|
||||
let testMode: BaseUIElement = undefined;
|
||||
|
@ -133,113 +176,58 @@ export default class SimpleAddUI extends UIElement {
|
|||
testMode = Translations.t.general.testing.Clone().SetClass("alert")
|
||||
}
|
||||
|
||||
let presets = this.CreatePresetsPanel();
|
||||
return new Combine([intro, testMode, presets])
|
||||
|
||||
return new Combine([intro, testMode, presetButtons]).SetClass("flex flex-col")
|
||||
|
||||
}
|
||||
|
||||
private CreateConfirmPanel(): BaseUIElement {
|
||||
const preset = this._confirmPreset.data;
|
||||
if (preset === undefined) {
|
||||
return undefined;
|
||||
}
|
||||
private static CreatePresetSelectButton(preset: PresetInfo){
|
||||
|
||||
const confirmButton = new SubtleButton(preset.icon,
|
||||
const tagInfo =SimpleAddUI.CreateTagInfoFor(preset, false);
|
||||
return new SubtleButton(
|
||||
preset.icon,
|
||||
new Combine([
|
||||
"<b>",
|
||||
Translations.t.general.add.confirmButton.Subs({category: preset.name}),
|
||||
"</b>"])).SetClass("break-words");
|
||||
confirmButton.onClick(
|
||||
this.CreatePoint(preset.tags)
|
||||
);
|
||||
|
||||
if (!this._confirmPreset.data.layerToAddTo.isDisplayed.data) {
|
||||
return new Combine([
|
||||
Translations.t.general.add.layerNotEnabled.Subs({layer: this._confirmPreset.data.layerToAddTo.layerDef.name})
|
||||
.SetClass("alert"),
|
||||
this.openLayerControl,
|
||||
|
||||
this.cancelButton
|
||||
]);
|
||||
}
|
||||
|
||||
let tagInfo = "";
|
||||
const csCount = State.state.osmConnection.userDetails.data.csCount;
|
||||
if (csCount > Constants.userJourney.tagsVisibleAt) {
|
||||
tagInfo = this._confirmPreset.data.tags.map(t => t.asHumanString(csCount > Constants.userJourney.tagsVisibleAndWikiLinked, true)).join("&");
|
||||
tagInfo = `<br/>More information about the preset: ${tagInfo}`
|
||||
}
|
||||
|
||||
return new Combine([
|
||||
Translations.t.general.add.confirmIntro.Subs({title: this._confirmPreset.data.name}),
|
||||
State.state.osmConnection.userDetails.data.dryRun ? "<span class='alert'>TESTING - changes won't be saved</span>" : "",
|
||||
confirmButton,
|
||||
this.cancelButton,
|
||||
preset.description,
|
||||
tagInfo
|
||||
|
||||
])
|
||||
|
||||
Translations.t.general.add.addNew.Subs({
|
||||
category: preset.name
|
||||
}).SetClass("font-bold"),
|
||||
Translations.WT(preset.description)?.FirstSentence(),
|
||||
tagInfo?.SetClass("subtle")
|
||||
]).SetClass("flex flex-col")
|
||||
)
|
||||
}
|
||||
|
||||
private CreatePresetButtons() {
|
||||
|
||||
/*
|
||||
* Generates the list with all the buttons.*/
|
||||
private static CreatePresetButtons(selectedPreset: UIEventSource<PresetInfo>): BaseUIElement {
|
||||
const allButtons = [];
|
||||
const self = this;
|
||||
for (const layer of State.state.filteredLayers.data) {
|
||||
|
||||
if(layer.isDisplayed.data === false && State.state.featureSwitchLayers){
|
||||
continue;
|
||||
}
|
||||
|
||||
const presets = layer.layerDef.presets;
|
||||
for (const preset of presets) {
|
||||
const tags = TagUtils.KVtoProperties(preset.tags ?? []);
|
||||
let icon: BaseUIElement = layer.layerDef.GenerateLeafletStyle(new UIEventSource<any>(tags), false).icon.html.SetClass("simple-add-ui-icon");
|
||||
|
||||
const csCount = State.state.osmConnection.userDetails.data.csCount;
|
||||
let tagInfo = undefined;
|
||||
if (csCount > Constants.userJourney.tagsVisibleAt) {
|
||||
const presets = preset.tags.map(t => new Combine([t.asHumanString(false, true), " "]).SetClass("subtle break-words"))
|
||||
tagInfo = new Combine(presets)
|
||||
const tags = TagUtils.KVtoProperties(preset.tags ?? []);
|
||||
let icon: BaseUIElement = layer.layerDef.GenerateLeafletStyle(new UIEventSource<any>(tags), false).icon.html
|
||||
.SetClass("w-12 h-12 block relative");
|
||||
const presetInfo: PresetInfo = {
|
||||
tags: preset.tags,
|
||||
layerToAddTo: layer,
|
||||
name: preset.title,
|
||||
description: preset.description,
|
||||
icon: icon
|
||||
}
|
||||
const button: UIElement =
|
||||
new SubtleButton(
|
||||
icon,
|
||||
new Combine([
|
||||
"<b>",
|
||||
preset.title,
|
||||
"</b>",
|
||||
preset.description !== undefined ? new Combine(["<br/>", preset.description.FirstSentence()]) : "",
|
||||
"<br/>",
|
||||
tagInfo
|
||||
])
|
||||
).onClick(
|
||||
() => {
|
||||
self._confirmPreset.setData({
|
||||
tags: preset.tags,
|
||||
layerToAddTo: layer,
|
||||
name: preset.title,
|
||||
description: preset.description,
|
||||
icon: icon
|
||||
});
|
||||
self.Update();
|
||||
}
|
||||
)
|
||||
|
||||
const button = SimpleAddUI.CreatePresetSelectButton(presetInfo);
|
||||
button.onClick(() => {
|
||||
selectedPreset.setData(presetInfo)
|
||||
})
|
||||
allButtons.push(button);
|
||||
}
|
||||
}
|
||||
return allButtons;
|
||||
return new Combine(allButtons).SetClass("flex flex-col");
|
||||
}
|
||||
|
||||
private CreatePoint(tags: Tag[]) {
|
||||
return () => {
|
||||
console.log("Create Point Triggered")
|
||||
const loc = State.state.LastClickLocation.data;
|
||||
let feature = State.state.changes.createElement(tags, loc.lat, loc.lon);
|
||||
State.state.selectedElement.setData(feature);
|
||||
this._confirmPreset.setData(undefined);
|
||||
}
|
||||
}
|
||||
|
||||
public OnClose(){
|
||||
console.log("On close triggered")
|
||||
this._confirmPreset.setData(undefined)
|
||||
}
|
||||
|
||||
}
|
|
@ -37,7 +37,7 @@ export default class DeleteImage extends UIElement {
|
|||
cancelButton
|
||||
]).SetClass("flex flex-col background-black"),
|
||||
Svg.delete_icon_svg().SetStyle("width: 2em; height: 2em; display:block;")
|
||||
)
|
||||
).ToggleOnClick()
|
||||
|
||||
}
|
||||
|
||||
|
|
|
@ -1,44 +1,46 @@
|
|||
import {InputElement} from "./InputElement";
|
||||
import {UIElement} from "../UIElement";
|
||||
import {FixedUiElement} from "../Base/FixedUiElement";
|
||||
import {UIEventSource} from "../../Logic/UIEventSource";
|
||||
import Translations from "../i18n/Translations";
|
||||
import BaseUIElement from "../BaseUIElement";
|
||||
|
||||
export class FixedInputElement<T> extends InputElement<T> {
|
||||
private readonly rendering: UIElement;
|
||||
private readonly value: UIEventSource<T>;
|
||||
public readonly IsSelected : UIEventSource<boolean> = new UIEventSource<boolean>(false);
|
||||
private readonly _comparator: (t0: T, t1: T) => boolean;
|
||||
|
||||
constructor(rendering: UIElement | string,
|
||||
private readonly _el : HTMLElement;
|
||||
|
||||
constructor(rendering: BaseUIElement | string,
|
||||
value: T,
|
||||
comparator: ((t0: T, t1: T) => boolean ) = undefined) {
|
||||
super(undefined);
|
||||
super();
|
||||
this._comparator = comparator ?? ((t0, t1) => t0 == t1);
|
||||
this.value = new UIEventSource<T>(value);
|
||||
this.rendering = typeof (rendering) === 'string' ? new FixedUiElement(rendering) : rendering;
|
||||
const self = this;
|
||||
|
||||
const selected = this.IsSelected;
|
||||
this._el = document.createElement("span")
|
||||
this._el.addEventListener("mouseout", () => selected.setData(false))
|
||||
const e = Translations.W(rendering)?.ConstructElement()
|
||||
if(e){
|
||||
this._el.appendChild( e)
|
||||
}
|
||||
|
||||
this.onClick(() => {
|
||||
self.IsSelected.setData(true)
|
||||
selected.setData(true)
|
||||
})
|
||||
}
|
||||
|
||||
protected InnerConstructElement(): HTMLElement {
|
||||
return undefined;
|
||||
}
|
||||
|
||||
GetValue(): UIEventSource<T> {
|
||||
return this.value;
|
||||
}
|
||||
InnerRender(): string {
|
||||
return this.rendering.Render();
|
||||
}
|
||||
|
||||
IsValid(t: T): boolean {
|
||||
return this._comparator(t, this.value.data);
|
||||
}
|
||||
|
||||
protected InnerUpdate(htmlElement: HTMLElement) {
|
||||
super.InnerUpdate(htmlElement);
|
||||
const self = this;
|
||||
htmlElement.addEventListener("mouseout", () => self.IsSelected.setData(false))
|
||||
|
||||
}
|
||||
|
||||
|
||||
}
|
|
@ -3,24 +3,19 @@ import {UIEventSource} from "../../Logic/UIEventSource";
|
|||
import {Utils} from "../../Utils";
|
||||
|
||||
export class RadioButton<T> extends InputElement<T> {
|
||||
private static _nextId = 0;
|
||||
IsSelected: UIEventSource<boolean> = new UIEventSource<boolean>(false);
|
||||
|
||||
private readonly _selectedElementIndex: UIEventSource<number>
|
||||
= new UIEventSource<number>(null);
|
||||
|
||||
private readonly value: UIEventSource<T>;
|
||||
private readonly _elements: InputElement<T>[]
|
||||
private readonly _selectFirstAsDefault: boolean;
|
||||
private _elements: InputElement<T>[];
|
||||
private readonly _element: HTMLElement;
|
||||
|
||||
constructor(elements: InputElement<T>[],
|
||||
selectFirstAsDefault = true) {
|
||||
super(undefined);
|
||||
this._elements = Utils.NoNull(elements);
|
||||
this._selectFirstAsDefault = selectFirstAsDefault;
|
||||
const self = this;
|
||||
|
||||
this.value =
|
||||
UIEventSource.flatten(this._selectedElementIndex.map(
|
||||
super()
|
||||
elements = Utils.NoNull(elements);
|
||||
const selectedElementIndex: UIEventSource<number> = new UIEventSource<number>(null);
|
||||
const value =
|
||||
UIEventSource.flatten(selectedElementIndex.map(
|
||||
(selectedIndex) => {
|
||||
if (selectedIndex !== undefined && selectedIndex !== null) {
|
||||
return elements[selectedIndex].GetValue()
|
||||
|
@ -28,26 +23,63 @@ export class RadioButton<T> extends InputElement<T> {
|
|||
}
|
||||
), elements.map(e => e?.GetValue()));
|
||||
|
||||
this.value.addCallback((t) => {
|
||||
self?.ShowValue(t);
|
||||
})
|
||||
|
||||
/*
|
||||
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
|
||||
elements[i]?.onClick(() => {
|
||||
self._selectedElementIndex.setData(i);
|
||||
selectedElementIndex.setData(i);
|
||||
});
|
||||
elements[i].IsSelected.addCallback(isSelected => {
|
||||
if (isSelected) {
|
||||
self._selectedElementIndex.setData(i);
|
||||
selectedElementIndex.setData(i);
|
||||
}
|
||||
})
|
||||
elements[i].GetValue().addCallback(() => {
|
||||
self._selectedElementIndex.setData(i);
|
||||
selectedElementIndex.setData(i);
|
||||
})
|
||||
}
|
||||
this.dumbMode = false;
|
||||
|
||||
|
||||
const groupId = "radiogroup" + RadioButton._nextId
|
||||
RadioButton._nextId++
|
||||
|
||||
const form = document.createElement("form")
|
||||
this._element = form;
|
||||
for (let i1 = 0; i1 < elements.length; i1++) {
|
||||
let element = elements[i1];
|
||||
const labelHtml = element.ConstructElement();
|
||||
if (labelHtml === undefined) {
|
||||
continue;
|
||||
}
|
||||
|
||||
const input = document.createElement("input")
|
||||
input.id = "radio" + groupId + "-" + i1;
|
||||
input.name = groupId;
|
||||
input.type = "radio"
|
||||
|
||||
|
||||
const label = document.createElement("label")
|
||||
label.appendChild(labelHtml)
|
||||
label.htmlFor = input.id;
|
||||
input.appendChild(label)
|
||||
|
||||
form.appendChild(input)
|
||||
|
||||
form.addEventListener("change", () => {
|
||||
// TODO FIXME
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
this.value = value;
|
||||
this._elements = elements;
|
||||
|
||||
}
|
||||
|
||||
|
@ -65,25 +97,11 @@ export class RadioButton<T> extends InputElement<T> {
|
|||
}
|
||||
|
||||
|
||||
private IdFor(i) {
|
||||
return 'radio-' + this.id + '-' + i;
|
||||
}
|
||||
|
||||
InnerRender(): string {
|
||||
let body = "";
|
||||
for (let i = 0; i < this._elements.length; i++){
|
||||
const el = this._elements[i];
|
||||
const htmlElement =
|
||||
`<label for="${this.IdFor(i)}" class="question-option-with-border">` +
|
||||
`<input type="radio" id="${this.IdFor(i)}" name="radiogroup-${this.id}">` +
|
||||
el.Render() +
|
||||
`</label>`;
|
||||
body += htmlElement;
|
||||
}
|
||||
|
||||
return `<form id='${this.id}-form'>${body}</form>`;
|
||||
protected InnerConstructElement(): HTMLElement {
|
||||
return this._element;
|
||||
}
|
||||
|
||||
/*
|
||||
public ShowValue(t: T): boolean {
|
||||
if (t === undefined) {
|
||||
return false;
|
||||
|
@ -104,48 +122,7 @@ export class RadioButton<T> extends InputElement<T> {
|
|||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
InnerUpdate(htmlElement: HTMLElement) {
|
||||
const self = this;
|
||||
|
||||
function checkButtons() {
|
||||
for (let i = 0; i < self._elements.length; i++) {
|
||||
const el = document.getElementById(self.IdFor(i));
|
||||
// @ts-ignore
|
||||
if (el.checked) {
|
||||
self._selectedElementIndex.setData(i);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const el = document.getElementById(this.id);
|
||||
el.addEventListener("change",
|
||||
function () {
|
||||
checkButtons();
|
||||
}
|
||||
);
|
||||
if (this._selectedElementIndex.data !== null) {
|
||||
const el = document.getElementById(this.IdFor(this._selectedElementIndex.data));
|
||||
if (el) {
|
||||
// @ts-ignore
|
||||
el.checked = true;
|
||||
checkButtons();
|
||||
}
|
||||
} else if (this._selectFirstAsDefault) {
|
||||
this.ShowValue(this.value.data);
|
||||
if (this._selectedElementIndex.data === null || this._selectedElementIndex.data === undefined) {
|
||||
const el = document.getElementById(this.IdFor(0));
|
||||
if (el) {
|
||||
// @ts-ignore
|
||||
el.checked = true;
|
||||
checkButtons();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
};
|
||||
}*/
|
||||
|
||||
|
||||
}
|
|
@ -15,9 +15,13 @@ export default class Toggle extends VariableUiElement{
|
|||
isEnabled.map(isEnabled => isEnabled ? showEnabled : showDisabled)
|
||||
);
|
||||
this.isEnabled = isEnabled
|
||||
}
|
||||
|
||||
public ToggleOnClick(): Toggle{
|
||||
const self = this;
|
||||
this.onClick(() => {
|
||||
isEnabled.setData(!isEnabled.data);
|
||||
self. isEnabled.setData(!self.isEnabled.data);
|
||||
})
|
||||
|
||||
return this;
|
||||
}
|
||||
}
|
|
@ -1,4 +1,3 @@
|
|||
import {UIElement} from "../UIElement";
|
||||
import {UIEventSource} from "../../Logic/UIEventSource";
|
||||
import TagRenderingConfig from "../../Customizations/JSON/TagRenderingConfig";
|
||||
import TagRenderingQuestion from "./TagRenderingQuestion";
|
||||
|
@ -7,80 +6,65 @@ import Combine from "../Base/Combine";
|
|||
import TagRenderingAnswer from "./TagRenderingAnswer";
|
||||
import State from "../../State";
|
||||
import Svg from "../../Svg";
|
||||
import Toggle from "../Input/Toggle";
|
||||
import BaseUIElement from "../BaseUIElement";
|
||||
|
||||
export default class EditableTagRendering extends UIElement {
|
||||
private readonly _tags: UIEventSource<any>;
|
||||
private readonly _configuration: TagRenderingConfig;
|
||||
|
||||
private _editMode: UIEventSource<boolean> = new UIEventSource<boolean>(false);
|
||||
private _editButton: UIElement;
|
||||
|
||||
private _question: UIElement;
|
||||
private _answer: UIElement;
|
||||
export default class EditableTagRendering extends Toggle {
|
||||
|
||||
constructor(tags: UIEventSource<any>,
|
||||
configuration: TagRenderingConfig) {
|
||||
super(tags);
|
||||
this._tags = tags;
|
||||
this._configuration = configuration;
|
||||
|
||||
this.ListenTo(this._editMode);
|
||||
this.ListenTo(State.state?.osmConnection?.userDetails)
|
||||
const editMode = new UIEventSource<boolean>(false);
|
||||
|
||||
this._answer = new TagRenderingAnswer(tags, configuration);
|
||||
this._answer.SetClass("w-full")
|
||||
this._question = this.GenerateQuestion();
|
||||
this.dumbMode = false;
|
||||
const answer: BaseUIElement = new TagRenderingAnswer(tags, configuration)
|
||||
let rendering = answer;
|
||||
|
||||
if (this._configuration.question !== undefined) {
|
||||
if (State.state?.featureSwitchUserbadge?.data) {
|
||||
// 2.3em total width
|
||||
const self = this;
|
||||
this._editButton =
|
||||
Svg.pencil_svg().SetClass("edit-button")
|
||||
.onClick(() => {
|
||||
self._editMode.setData(true);
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
if (configuration.question !== undefined && State.state?.featureSwitchUserbadge?.data) {
|
||||
// We have a question and editing is enabled
|
||||
const editButton =
|
||||
new Combine([Svg.pencil_ui()]).SetClass("block relative h-10 w-10 p-2 float-right").SetStyle("border: 1px solid black; border-radius: 0.7em")
|
||||
.onClick(() => {
|
||||
editMode.setData(true);
|
||||
});
|
||||
|
||||
InnerRender(): string {
|
||||
if (!this._configuration?.condition?.matchesProperties(this._tags.data)) {
|
||||
return "";
|
||||
}
|
||||
if (this._editMode.data) {
|
||||
return this._question.Render();
|
||||
}
|
||||
if(!this._configuration.IsKnown(this._tags.data)){
|
||||
// Even though it is not known, we hide the question here
|
||||
// It is the questionbox's task to show the question in edit mode
|
||||
return "";
|
||||
}
|
||||
|
||||
return new Combine([this._answer,
|
||||
(State.state?.osmConnection?.userDetails?.data?.loggedIn ?? true) ? this._editButton : undefined
|
||||
]).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")
|
||||
.Render();
|
||||
}
|
||||
const answerWithEditButton = new Combine([answer,
|
||||
new Toggle(editButton, undefined, State.state.osmConnection.isLoggedIn)]).SetClass("w-full")
|
||||
|
||||
|
||||
private GenerateQuestion() {
|
||||
const self = this;
|
||||
if (this._configuration.question !== undefined) {
|
||||
// And at last, set up the skip button
|
||||
const cancelbutton =
|
||||
Translations.t.general.cancel.Clone()
|
||||
.SetClass("btn btn-secondary mr-3")
|
||||
.onClick(() => {
|
||||
self._editMode.setData(false)
|
||||
editMode.setData(false)
|
||||
});
|
||||
|
||||
return new TagRenderingQuestion(this._tags, this._configuration,
|
||||
const question = new TagRenderingQuestion(tags, configuration,
|
||||
() => {
|
||||
self._editMode.setData(false)
|
||||
editMode.setData(false)
|
||||
},
|
||||
cancelbutton)
|
||||
|
||||
|
||||
rendering = new Toggle(
|
||||
question,
|
||||
answerWithEditButton,
|
||||
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")
|
||||
// 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?.condition?.matchesProperties(tags) ?? true))
|
||||
super(
|
||||
rendering,
|
||||
undefined,
|
||||
renderingIsShown
|
||||
)
|
||||
}
|
||||
|
||||
}
|
|
@ -11,10 +11,11 @@ import ScrollableFullScreen from "../Base/ScrollableFullScreen";
|
|||
import {Tag} from "../../Logic/Tags/Tag";
|
||||
import Constants from "../../Models/Constants";
|
||||
import SharedTagRenderings from "../../Customizations/SharedTagRenderings";
|
||||
import BaseUIElement from "../BaseUIElement";
|
||||
|
||||
export default class FeatureInfoBox extends ScrollableFullScreen {
|
||||
|
||||
private constructor(
|
||||
public constructor(
|
||||
tags: UIEventSource<any>,
|
||||
layerConfig: LayerConfig
|
||||
) {
|
||||
|
@ -28,12 +29,8 @@ export default class FeatureInfoBox extends ScrollableFullScreen {
|
|||
|
||||
}
|
||||
|
||||
static construct(tags: UIEventSource<any>, layer: LayerConfig): FeatureInfoBox {
|
||||
return new FeatureInfoBox(tags, layer)
|
||||
}
|
||||
|
||||
private static GenerateTitleBar(tags: UIEventSource<any>,
|
||||
layerConfig: LayerConfig): UIElement {
|
||||
layerConfig: LayerConfig): BaseUIElement {
|
||||
const title = new TagRenderingAnswer(tags, layerConfig.title ?? new TagRenderingConfig("POI", undefined))
|
||||
.SetClass("break-words font-bold sm:p-0.5 md:p-1 sm:p-1.5 md:p-2");
|
||||
const titleIcons = new Combine(
|
||||
|
@ -48,7 +45,7 @@ export default class FeatureInfoBox extends ScrollableFullScreen {
|
|||
}
|
||||
|
||||
private static GenerateContent(tags: UIEventSource<any>,
|
||||
layerConfig: LayerConfig): UIElement {
|
||||
layerConfig: LayerConfig): BaseUIElement {
|
||||
let questionBox: UIElement = undefined;
|
||||
|
||||
if (State.state.featureSwitchUserbadge.data) {
|
||||
|
|
|
@ -1,17 +1,11 @@
|
|||
import {UIEventSource} from "../../Logic/UIEventSource";
|
||||
import {UIElement} from "../UIElement";
|
||||
import Translations from "../i18n/Translations";
|
||||
import {OsmConnection} from "../../Logic/Osm/OsmConnection";
|
||||
import BaseUIElement from "../BaseUIElement";
|
||||
import Toggle from "../Input/Toggle";
|
||||
|
||||
export class SaveButton extends UIElement {
|
||||
|
||||
|
||||
private readonly _element: BaseUIElement;
|
||||
export class SaveButton extends Toggle {
|
||||
|
||||
constructor(value: UIEventSource<any>, osmConnection: OsmConnection) {
|
||||
super(value);
|
||||
if (value === undefined) {
|
||||
throw "No event source for savebutton, something is wrong"
|
||||
}
|
||||
|
@ -31,7 +25,7 @@ export class SaveButton extends UIElement {
|
|||
saveDisabled,
|
||||
isSaveable
|
||||
)
|
||||
this._element = new Toggle(
|
||||
super(
|
||||
save
|
||||
, pleaseLogin,
|
||||
osmConnection?.userDetails?.map(userDetails => userDetails.loggedIn) ?? new UIEventSource<any>(false)
|
||||
|
@ -39,9 +33,4 @@ export class SaveButton extends UIElement {
|
|||
|
||||
}
|
||||
|
||||
InnerRender(): BaseUIElement {
|
||||
return this._element
|
||||
|
||||
}
|
||||
|
||||
}
|
|
@ -1,98 +1,43 @@
|
|||
import {UIEventSource} from "../../Logic/UIEventSource";
|
||||
import TagRenderingConfig from "../../Customizations/JSON/TagRenderingConfig";
|
||||
import {UIElement} from "../UIElement";
|
||||
import {Utils} from "../../Utils";
|
||||
import Combine from "../Base/Combine";
|
||||
import {SubstitutedTranslation} from "../SubstitutedTranslation";
|
||||
import {Translation} from "../i18n/Translation";
|
||||
import {TagUtils} from "../../Logic/Tags/TagUtils";
|
||||
import BaseUIElement from "../BaseUIElement";
|
||||
import {VariableUiElement} from "../Base/VariableUIElement";
|
||||
import List from "../Base/List";
|
||||
import {FixedUiElement} from "../Base/FixedUiElement";
|
||||
|
||||
/***
|
||||
* Displays the correct value for a known tagrendering
|
||||
*/
|
||||
export default class TagRenderingAnswer extends UIElement {
|
||||
private readonly _tags: UIEventSource<any>;
|
||||
private _configuration: TagRenderingConfig;
|
||||
private _content: BaseUIElement;
|
||||
private readonly _contentClass: string;
|
||||
private _contentStyle: string;
|
||||
export default class TagRenderingAnswer extends VariableUiElement {
|
||||
|
||||
constructor(tags: UIEventSource<any>, configuration: TagRenderingConfig, contentClasses: string = "", contentStyle: string = "") {
|
||||
super();
|
||||
this._tags = tags;
|
||||
this.ListenTo(tags)
|
||||
this._configuration = configuration;
|
||||
this._contentClass = contentClasses;
|
||||
this._contentStyle = contentStyle;
|
||||
constructor(tagsSource: UIEventSource<any>, configuration: TagRenderingConfig, contentClasses: string = "", contentStyle: string = "") {
|
||||
if (configuration === undefined) {
|
||||
throw "Trying to generate a tagRenderingAnswer without configuration..."
|
||||
}
|
||||
super(tagsSource.map(tags => {
|
||||
if(tags === undefined){
|
||||
return undefined;
|
||||
}
|
||||
const trs = Utils.NoNull(configuration.GetRenderValues(tags));
|
||||
if(trs.length === 0){
|
||||
return undefined;
|
||||
}
|
||||
trs.forEach(tr => console.log("Rendering ", tr))
|
||||
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(innerComponent => innerComponent?.SetClass(contentClasses)?.SetStyle(contentStyle))
|
||||
)
|
||||
|
||||
this.SetClass("flex items-center flex-row text-lg link-underline")
|
||||
this.SetStyle("word-wrap: anywhere;");
|
||||
}
|
||||
|
||||
InnerRender(): string | BaseUIElement{
|
||||
if (this._configuration.condition !== undefined) {
|
||||
if (!this._configuration.condition.matchesProperties(this._tags.data)) {
|
||||
return "";
|
||||
}
|
||||
}
|
||||
|
||||
const tags = this._tags.data;
|
||||
if (tags === undefined) {
|
||||
return "";
|
||||
}
|
||||
|
||||
// The render value doesn't work well with multi-answers (checkboxes), so we have to check for them manually
|
||||
if (this._configuration.multiAnswer) {
|
||||
|
||||
let freeformKeyUsed = this._configuration.freeform?.key === undefined; // If it is undefined, it is "used" already, or at least we don't have to check for it anymore
|
||||
const applicableThens: Translation[] = Utils.NoNull(this._configuration.mappings?.map(mapping => {
|
||||
if (mapping.if === undefined) {
|
||||
return mapping.then;
|
||||
}
|
||||
if (TagUtils.MatchesMultiAnswer(mapping.if, tags)) {
|
||||
if(!freeformKeyUsed){
|
||||
if(mapping.if.usedKeys().indexOf(this._configuration.freeform.key) >= 0){
|
||||
freeformKeyUsed = true;
|
||||
}
|
||||
}
|
||||
return mapping.then;
|
||||
}
|
||||
return undefined;
|
||||
}) ?? [])
|
||||
|
||||
if (!freeformKeyUsed
|
||||
&& tags[this._configuration.freeform.key] !== undefined) {
|
||||
applicableThens.push(this._configuration.render)
|
||||
}
|
||||
|
||||
const self = this
|
||||
const valuesToRender: UIElement[] = applicableThens.map(tr => SubstitutedTranslation.construct(tr, self._tags))
|
||||
|
||||
if (valuesToRender.length >= 0) {
|
||||
if (valuesToRender.length === 1) {
|
||||
this._content = valuesToRender[0];
|
||||
} else {
|
||||
this._content = new Combine(["<ul>",
|
||||
...valuesToRender.map(tr => new Combine(["<li>", tr, "</li>"])) ,
|
||||
"</ul>"
|
||||
])
|
||||
|
||||
}
|
||||
return this._content.SetClass(this._contentClass).SetStyle(this._contentStyle);
|
||||
}
|
||||
}
|
||||
|
||||
const tr = this._configuration.GetRenderValue(tags);
|
||||
if (tr !== undefined) {
|
||||
this._content = SubstitutedTranslation.construct(tr, this._tags);
|
||||
return this._content.SetClass(this._contentClass).SetStyle(this._contentStyle);
|
||||
}
|
||||
|
||||
return "";
|
||||
|
||||
}
|
||||
|
||||
}
|
|
@ -32,23 +32,23 @@ export default class TagRenderingQuestion extends UIElement {
|
|||
private readonly _tags: UIEventSource<any>;
|
||||
private _configuration: TagRenderingConfig;
|
||||
|
||||
private _saveButton: UIElement;
|
||||
private _saveButton: BaseUIElement;
|
||||
|
||||
private _inputElement: InputElement<TagsFilter>;
|
||||
private _cancelButton: UIElement;
|
||||
private _cancelButton: BaseUIElement;
|
||||
private _appliedTags: BaseUIElement;
|
||||
private _question: UIElement;
|
||||
private _question: BaseUIElement;
|
||||
|
||||
constructor(tags: UIEventSource<any>,
|
||||
configuration: TagRenderingConfig,
|
||||
afterSave?: () => void,
|
||||
cancelButton?: UIElement
|
||||
cancelButton?: BaseUIElement
|
||||
) {
|
||||
super(tags);
|
||||
this._tags = tags;
|
||||
this._configuration = configuration;
|
||||
this._cancelButton = cancelButton;
|
||||
this._question = SubstitutedTranslation.construct(this._configuration.question, tags)
|
||||
this._question = new SubstitutedTranslation(this._configuration.question, tags)
|
||||
.SetClass("question-text");
|
||||
if (configuration === undefined) {
|
||||
throw "A question is needed for a question visualization"
|
||||
|
@ -242,7 +242,7 @@ export default class TagRenderingQuestion extends UIElement {
|
|||
return undefined;
|
||||
}
|
||||
return new FixedInputElement(
|
||||
SubstitutedTranslation.construct(mapping.then, this._tags),
|
||||
new SubstitutedTranslation(mapping.then, this._tags),
|
||||
mapping.if,
|
||||
(t0, t1) => t1.isEquivalent(t0));
|
||||
}
|
||||
|
|
|
@ -102,8 +102,8 @@ export default class ReviewForm extends InputElement<Review> {
|
|||
.SetClass("review-form")
|
||||
|
||||
|
||||
return new Toggle(form, Translations.t.reviews.plz_login,
|
||||
this.userDetails.map(userdetails => userdetails.loggedIn))
|
||||
return new Toggle(form, Translations.t.reviews.plz_login.Clone(),
|
||||
this.userDetails.map(userdetails => userdetails.loggedIn)).ToggleOnClick()
|
||||
.ConstructElement()
|
||||
}
|
||||
|
||||
|
|
|
@ -87,10 +87,10 @@ export default class ShowDataLayer {
|
|||
}
|
||||
marker.openPopup();
|
||||
|
||||
const popup = marker.getPopup();
|
||||
|
||||
const tags = State.state.allElements.getEventSourceById(selected.properties.id);
|
||||
const layer: LayerConfig = this._layerDict[selected._matching_layer_id];
|
||||
const infoBox = FeatureInfoBox.construct(tags, layer);
|
||||
const infoBox = new FeatureInfoBox(tags, layer);
|
||||
|
||||
infoBox.isShown.addCallback(isShown => {
|
||||
if (!isShown) {
|
||||
|
@ -98,9 +98,8 @@ export default class ShowDataLayer {
|
|||
}
|
||||
});
|
||||
|
||||
popup.setContent(infoBox.Render());
|
||||
infoBox.AttachTo(`popup-${selected.properties.id}`)
|
||||
infoBox.Activate();
|
||||
infoBox.Update();
|
||||
})
|
||||
|
||||
}
|
||||
|
@ -156,11 +155,13 @@ export default class ShowDataLayer {
|
|||
}, leafletLayer);
|
||||
|
||||
// By setting 50vh, leaflet will attempt to fit the entire screen and move the feature down
|
||||
popup.setContent("<div style='height: 50vh'>Rendering</div>");
|
||||
popup.setContent(`<div style='height: 50vh' id='popup-${feature.properties.id}'>Rendering</div>`);
|
||||
|
||||
leafletLayer.bindPopup(popup);
|
||||
|
||||
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°
|
||||
});
|
||||
|
||||
this._popups.set(feature, leafletLayer);
|
||||
|
|
|
@ -1,71 +1,37 @@
|
|||
import {UIElement} from "./UIElement";
|
||||
import {UIEventSource} from "../Logic/UIEventSource";
|
||||
import {Translation} from "./i18n/Translation";
|
||||
import Locale from "./i18n/Locale";
|
||||
import Combine from "./Base/Combine";
|
||||
import State from "../State";
|
||||
import {FixedUiElement} from "./Base/FixedUiElement";
|
||||
import SpecialVisualizations from "./SpecialVisualizations";
|
||||
import BaseUIElement from "./BaseUIElement";
|
||||
import {Utils} from "../Utils";
|
||||
import {VariableUiElement} from "./Base/VariableUIElement";
|
||||
import Combine from "./Base/Combine";
|
||||
|
||||
export class SubstitutedTranslation extends UIElement {
|
||||
private readonly tags: UIEventSource<any>;
|
||||
private readonly translation: Translation;
|
||||
private content: BaseUIElement[];
|
||||
export class SubstitutedTranslation extends VariableUiElement {
|
||||
|
||||
private constructor(
|
||||
public constructor(
|
||||
translation: Translation,
|
||||
tags: UIEventSource<any>) {
|
||||
super(tags);
|
||||
this.translation = translation;
|
||||
this.tags = tags;
|
||||
const self = this;
|
||||
tags.addCallbackAndRun(() => {
|
||||
self.content = self.CreateContent();
|
||||
self.Update();
|
||||
});
|
||||
|
||||
Locale.language.addCallback(() => {
|
||||
self.content = self.CreateContent();
|
||||
self.Update();
|
||||
});
|
||||
super(
|
||||
tags.map(tags => {
|
||||
const txt = Utils.SubstituteKeys(translation.txt, tags)
|
||||
if (txt === undefined) {
|
||||
return "no tags subs tr"
|
||||
}
|
||||
const contents = SubstitutedTranslation.EvaluateSpecialComponents(txt, tags)
|
||||
console.log("Substr has contents", contents)
|
||||
return new Combine(contents)
|
||||
}, [Locale.language])
|
||||
)
|
||||
|
||||
|
||||
this.SetClass("w-full")
|
||||
}
|
||||
|
||||
public static construct(
|
||||
translation: Translation,
|
||||
tags: UIEventSource<any>): SubstitutedTranslation {
|
||||
return new SubstitutedTranslation(translation, tags);
|
||||
}
|
||||
|
||||
public static SubstituteKeys(txt: string, tags: any) {
|
||||
for (const key in tags) {
|
||||
if(!tags.hasOwnProperty(key)) {
|
||||
continue
|
||||
}
|
||||
txt = txt.replace(new RegExp("{" + key + "}", "g"), tags[key])
|
||||
}
|
||||
return txt;
|
||||
}
|
||||
|
||||
InnerRender() {
|
||||
if (this.content.length == 1) {
|
||||
return this.content[0];
|
||||
}
|
||||
return new Combine(this.content);
|
||||
}
|
||||
|
||||
private CreateContent(): BaseUIElement[] {
|
||||
let txt = this.translation?.txt;
|
||||
if (txt === undefined) {
|
||||
return []
|
||||
}
|
||||
const tags = this.tags.data;
|
||||
txt = SubstitutedTranslation.SubstituteKeys(txt, tags);
|
||||
return this.EvaluateSpecialComponents(txt);
|
||||
}
|
||||
|
||||
private EvaluateSpecialComponents(template: string): BaseUIElement[] {
|
||||
private static EvaluateSpecialComponents(template: string, tags: UIEventSource<any>): BaseUIElement[] {
|
||||
|
||||
for (const knownSpecial of SpecialVisualizations.specialVisualizations) {
|
||||
|
||||
|
@ -74,9 +40,9 @@ export class SubstitutedTranslation extends UIElement {
|
|||
if (matched != null) {
|
||||
|
||||
// We found a special component that should be brought to live
|
||||
const partBefore = this.EvaluateSpecialComponents(matched[1]);
|
||||
const partBefore = SubstitutedTranslation.EvaluateSpecialComponents(matched[1], tags);
|
||||
const argument = matched[2].trim();
|
||||
const partAfter = this.EvaluateSpecialComponents(matched[3]);
|
||||
const partAfter = SubstitutedTranslation.EvaluateSpecialComponents(matched[3], tags);
|
||||
try {
|
||||
const args = knownSpecial.args.map(arg => arg.defaultValue ?? "");
|
||||
if (argument.length > 0) {
|
||||
|
@ -91,7 +57,13 @@ export class SubstitutedTranslation extends UIElement {
|
|||
}
|
||||
|
||||
|
||||
const element = knownSpecial.constr(State.state, this.tags, args);
|
||||
let element: BaseUIElement = new FixedUiElement(`Constructing ${knownSpecial}(${args.join(", ")})`)
|
||||
try{
|
||||
element = knownSpecial.constr(State.state, tags, args);
|
||||
}catch(e){
|
||||
element = new FixedUiElement(`Could not generate special renering for ${knownSpecial}(${args.join(", ")}) ${e}`).SetClass("alert")
|
||||
}
|
||||
|
||||
return [...partBefore, element, ...partAfter]
|
||||
} catch (e) {
|
||||
console.error(e);
|
||||
|
|
|
@ -21,7 +21,7 @@ export abstract class UIElement extends BaseUIElement{
|
|||
if (source === undefined) {
|
||||
return this;
|
||||
}
|
||||
console.trace("Got a listenTo in ", this.constructor.name)
|
||||
//console.trace("Got a listenTo in ", this.constructor.name)
|
||||
const self = this;
|
||||
source.addCallback(() => {
|
||||
self.lastInnerRender = undefined;
|
||||
|
|
|
@ -32,10 +32,14 @@ export class Translation extends BaseUIElement {
|
|||
}
|
||||
|
||||
get txt(): string {
|
||||
return this.textFor(Translation.forcedLanguage ?? Locale.language.data)
|
||||
}
|
||||
|
||||
public textFor(language: string): string{
|
||||
if (this.translations["*"]) {
|
||||
return this.translations["*"];
|
||||
}
|
||||
const txt = this.translations[Translation.forcedLanguage ?? Locale.language.data];
|
||||
const txt = this.translations[language];
|
||||
if (txt !== undefined) {
|
||||
return txt;
|
||||
}
|
||||
|
@ -52,7 +56,7 @@ export class Translation extends BaseUIElement {
|
|||
console.error("Missing language ", Locale.language.data, "for", this.translations)
|
||||
return "";
|
||||
}
|
||||
|
||||
|
||||
InnerConstructElement(): HTMLElement {
|
||||
const el = document.createElement("span")
|
||||
Locale.language.addCallbackAndRun(_ => {
|
||||
|
@ -106,12 +110,12 @@ export class Translation extends BaseUIElement {
|
|||
// @ts-ignore
|
||||
const date: Date = el;
|
||||
rtext = date.toLocaleString();
|
||||
} else if (el.InnerRenderAsString === undefined) {
|
||||
} else if (el.ConstructElement() === undefined) {
|
||||
console.error("InnerREnder is not defined", el);
|
||||
throw "Hmmm, el.InnerRender is not defined?"
|
||||
} else {
|
||||
Translation.forcedLanguage = lang; // This is a very dirty hack - it'll bite me one day
|
||||
rtext = el.InnerRenderAsString();
|
||||
rtext = el.ConstructElement().innerHTML;
|
||||
|
||||
}
|
||||
for (let i = 0; i < parts.length - 1; i++) {
|
||||
|
|
14
Utils.ts
14
Utils.ts
|
@ -149,6 +149,16 @@ export class Utils {
|
|||
return [a.substr(0, index), a.substr(index + sep.length)];
|
||||
}
|
||||
|
||||
public static SubstituteKeys(txt: string, tags: any) {
|
||||
for (const key in tags) {
|
||||
if(!tags.hasOwnProperty(key)) {
|
||||
continue
|
||||
}
|
||||
txt = txt.replace(new RegExp("{" + key + "}", "g"), tags[key])
|
||||
}
|
||||
return txt;
|
||||
}
|
||||
|
||||
// Date will be undefined on failure
|
||||
public static LoadCustomCss(location: string) {
|
||||
const head = document.getElementsByTagName('head')[0];
|
||||
|
@ -251,6 +261,10 @@ export class Utils {
|
|||
|
||||
public static UnMinify(minified: string): string {
|
||||
|
||||
if(minified === undefined || minified === null){
|
||||
return undefined;
|
||||
}
|
||||
|
||||
const parts = minified.split("|");
|
||||
let result = parts.shift();
|
||||
const keys = Utils.knownKeys.concat(Utils.extraKeys);
|
||||
|
|
|
@ -68,57 +68,4 @@ input:checked + label .question-option-with-border {
|
|||
width: 100%;
|
||||
}
|
||||
|
||||
.edit-button img {
|
||||
width: 1.3em;
|
||||
height: 1.3em;
|
||||
padding: 0.5em;
|
||||
border-radius: 0.65em;
|
||||
border: solid var(--popup-border) 1px;
|
||||
font-size: medium;
|
||||
float: right;
|
||||
}
|
||||
|
||||
.edit-button svg {
|
||||
width: 1.3em;
|
||||
height: 1.3em;
|
||||
padding: 0.5em;
|
||||
border-radius: 0.65em;
|
||||
border: solid var(--foreground-color) 1px;
|
||||
stroke: var(--foreground-color) !important;
|
||||
fill: var(--foreground-color) !important;
|
||||
font-size: medium;
|
||||
float: right;
|
||||
}
|
||||
|
||||
.edit-button svg path {
|
||||
stroke: var(--foreground-color) !important;
|
||||
fill: var(--foreground-color) !important;
|
||||
}
|
||||
|
||||
|
||||
|
||||
.to-the-map span {
|
||||
font-size: xx-large;
|
||||
}
|
||||
|
||||
.to-the-map {
|
||||
background: var(--catch-detail-color);
|
||||
height: var(--return-to-the-map-height);
|
||||
color: var(--catch-detail-color-contrast);
|
||||
font-weight: bold;
|
||||
pointer-events: all;
|
||||
cursor: pointer;
|
||||
padding-top: 0.4em;
|
||||
text-align: center;
|
||||
box-sizing: border-box;
|
||||
display: block;
|
||||
max-height: var(--return-to-the-map-height);
|
||||
position: fixed;
|
||||
width: 100vw;
|
||||
bottom: 0;
|
||||
z-index: 100000;
|
||||
}
|
||||
|
||||
.to-the-map-inner{
|
||||
font-size: xx-large;
|
||||
}
|
||||
|
|
26
index.css
26
index.css
|
@ -222,23 +222,6 @@ li::marker {
|
|||
max-width: 2em !important;
|
||||
}
|
||||
|
||||
.simple-add-ui-icon {
|
||||
position: relative;
|
||||
display: block;
|
||||
width: 4em;
|
||||
height: 3.5em;
|
||||
}
|
||||
|
||||
.simple-add-ui-icon img {
|
||||
max-height: 3.5em !important;
|
||||
max-width: 3.5em !important;
|
||||
}
|
||||
|
||||
.simple-add-ui-icon svg {
|
||||
max-height: 3.5em !important;
|
||||
max-width: 3.5em !important;
|
||||
}
|
||||
|
||||
|
||||
/**************** GENERIC ****************/
|
||||
|
||||
|
@ -292,14 +275,10 @@ li::marker {
|
|||
}
|
||||
|
||||
.link-underline .subtle a {
|
||||
color: var(--foreground-color);
|
||||
text-decoration: underline 1px #7193bb88;
|
||||
color: #7193bb;
|
||||
}
|
||||
|
||||
.bold {
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
.thanks {
|
||||
background-color: #43d904;
|
||||
|
@ -318,11 +297,6 @@ li::marker {
|
|||
pointer-events: none !important;
|
||||
}
|
||||
|
||||
.page-split {
|
||||
display: flex;
|
||||
height: 100%;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/**************************************/
|
||||
|
|
|
@ -54,7 +54,7 @@
|
|||
"zoomInFurther": "Zoom in further to add a point.",
|
||||
"stillLoading": "The data is still loading. Please wait a bit before you add a new point.",
|
||||
"confirmIntro": "<h3>Add a {title} here?</h3>The point you create here will be <b>visible for everyone</b>. Please, only add things on to the map if they truly exist. A lot of applications use this data.",
|
||||
"confirmButton": "Add a {category} here.<br/><div class='alert'>Your addition is visible for everyone</div>",
|
||||
"warnVisibleForEveryone": "Your addition will be visible for everyone",
|
||||
"openLayerControl": "Open the layer control box",
|
||||
"layerNotEnabled": "The layer {layer} is not enabled. Enable this layer to add a point"
|
||||
},
|
||||
|
@ -108,6 +108,7 @@
|
|||
"createYourOwnTheme": "Create your own MapComplete theme from scratch"
|
||||
},
|
||||
"readYourMessages": "Please, read all your OpenStreetMap-messages before adding a new point.",
|
||||
"presetInfo": "The new POI will have {tags}",
|
||||
"fewChangesBefore": "Please, answer a few questions of existing points before adding a new point.",
|
||||
"goToInbox": "Open inbox",
|
||||
"getStartedLogin": "Login with OpenStreetMap to get started",
|
||||
|
|
|
@ -144,8 +144,6 @@ export default class TagSpec extends T{
|
|||
equal(undefined, tr.GetRenderValue({"foo": "bar"}));
|
||||
equal("Has no name", tr.GetRenderValue({"noname": "yes"})?.txt);
|
||||
equal("Ook een {name}", tr.GetRenderValue({"name": "xyz"})?.txt);
|
||||
equal("Ook een xyz", SubstitutedTranslation.construct(tr.GetRenderValue({"name": "xyz"}),
|
||||
new UIEventSource<any>({"name": "xyz"})).InnerRenderAsString());
|
||||
equal(undefined, tr.GetRenderValue({"foo": "bar"}));
|
||||
|
||||
})],
|
||||
|
@ -196,7 +194,7 @@ export default class TagSpec extends T{
|
|||
const uiEl = new EditableTagRendering(new UIEventSource<any>(
|
||||
{leisure: "park", "access": "no"}), constr
|
||||
);
|
||||
const rendered = uiEl.InnerRenderAsString();
|
||||
const rendered = uiEl.ConstructElement().innerHTML;
|
||||
equal(true, rendered.indexOf("Niet toegankelijk") > 0)
|
||||
|
||||
}
|
||||
|
|
|
@ -5,7 +5,6 @@ Utils.runningFromConsole = true;
|
|||
import TagRenderingQuestion from "../UI/Popup/TagRenderingQuestion";
|
||||
import {UIEventSource} from "../Logic/UIEventSource";
|
||||
import TagRenderingConfig from "../Customizations/JSON/TagRenderingConfig";
|
||||
import EditableTagRendering from "../UI/Popup/EditableTagRendering";
|
||||
|
||||
export default class TagQuestionSpec extends T {
|
||||
constructor() {
|
||||
|
|
Loading…
Reference in a new issue