forked from MapComplete/MapComplete
More refactoring, stabilizing rotation and direction_gradient
This commit is contained in:
parent
5fec108ba2
commit
778044d0fb
45 changed files with 656 additions and 640 deletions
22
UI/Base/FeatureSwitched.ts
Normal file
22
UI/Base/FeatureSwitched.ts
Normal file
|
@ -0,0 +1,22 @@
|
|||
import {UIElement} from "../UIElement";
|
||||
import {UIEventSource} from "../../Logic/UIEventSource";
|
||||
|
||||
export default class FeatureSwitched extends UIElement{
|
||||
private readonly _upstream: UIElement;
|
||||
private readonly _swtch: UIEventSource<boolean>;
|
||||
|
||||
constructor(upstream :UIElement,
|
||||
swtch: UIEventSource<boolean>) {
|
||||
super(swtch);
|
||||
this._upstream = upstream;
|
||||
this._swtch = swtch;
|
||||
}
|
||||
|
||||
InnerRender(): string {
|
||||
if(this._swtch.data){
|
||||
return this._upstream.Render();
|
||||
}
|
||||
return "";
|
||||
}
|
||||
|
||||
}
|
|
@ -1,9 +1,9 @@
|
|||
import {UIElement} from "./UIElement";
|
||||
import {DropDown} from "./Input/DropDown";
|
||||
import Translations from "./i18n/Translations";
|
||||
import State from "../State";
|
||||
import {UIEventSource} from "../Logic/UIEventSource";
|
||||
import {BaseLayer} from "../Models/BaseLayer";
|
||||
import {UIElement} from "../UIElement";
|
||||
import {DropDown} from "../Input/DropDown";
|
||||
import Translations from "../i18n/Translations";
|
||||
import State from "../../State";
|
||||
import {UIEventSource} from "../../Logic/UIEventSource";
|
||||
import {BaseLayer} from "../../Models/BaseLayer";
|
||||
|
||||
export default class BackgroundSelector extends UIElement {
|
||||
|
|
@ -1,8 +1,8 @@
|
|||
import * as L from "leaflet"
|
||||
import {UIEventSource} from "../Logic/UIEventSource";
|
||||
import Loc from "../Models/Loc";
|
||||
import {UIElement} from "./UIElement";
|
||||
import {BaseLayer} from "../Models/BaseLayer";
|
||||
import {UIEventSource} from "../../Logic/UIEventSource";
|
||||
import Loc from "../../Models/Loc";
|
||||
import {UIElement} from "../UIElement";
|
||||
import BaseLayer from "../../Models/BaseLayer";
|
||||
|
||||
export class Basemap {
|
||||
|
80
UI/BigComponents/FullWelcomePaneWithTabs.ts
Normal file
80
UI/BigComponents/FullWelcomePaneWithTabs.ts
Normal file
|
@ -0,0 +1,80 @@
|
|||
import {UIElement} from "../UIElement";
|
||||
import State from "../../State";
|
||||
import WelcomeMessage from "./WelcomeMessage";
|
||||
import * as personal from "../../assets/themes/personalLayout/personalLayout.json";
|
||||
import PersonalLayersPanel from "./PersonalLayersPanel";
|
||||
import Svg from "../../Svg";
|
||||
import Translations from "../i18n/Translations";
|
||||
import ShareScreen from "./ShareScreen";
|
||||
import MoreScreen from "./MoreScreen";
|
||||
import {VariableUiElement} from "../Base/VariableUIElement";
|
||||
import Constants from "../../Models/Constants";
|
||||
import Combine from "../Base/Combine";
|
||||
import Locale from "../i18n/Locale";
|
||||
import {TabbedComponent} from "../Base/TabbedComponent";
|
||||
import {UIEventSource} from "../../Logic/UIEventSource";
|
||||
import LayoutConfig from "../../Customizations/JSON/LayoutConfig";
|
||||
import UserDetails from "../../Logic/Osm/OsmConnection";
|
||||
|
||||
export default class FullWelcomePaneWithTabs extends UIElement {
|
||||
private readonly _layoutToUse: UIEventSource<LayoutConfig>;
|
||||
private readonly _userDetails: UIEventSource<UserDetails>;
|
||||
|
||||
private readonly _component: UIElement;
|
||||
|
||||
constructor() {
|
||||
super(State.state.layoutToUse);
|
||||
this._layoutToUse = State.state.layoutToUse;
|
||||
this._userDetails = State.state.osmConnection.userDetails;
|
||||
|
||||
|
||||
const layoutToUse = this._layoutToUse.data;
|
||||
let welcome: UIElement = new WelcomeMessage();
|
||||
if (layoutToUse.id === personal.id) {
|
||||
welcome = new PersonalLayersPanel();
|
||||
}
|
||||
const tabs = [
|
||||
{header: `<img src='${layoutToUse.icon}'>`, content: welcome},
|
||||
{
|
||||
header: Svg.osm_logo_img,
|
||||
content: Translations.t.general.openStreetMapIntro as UIElement
|
||||
},
|
||||
|
||||
]
|
||||
|
||||
if (State.state.featureSwitchShareScreen.data) {
|
||||
tabs.push({header: Svg.share_img, content: new ShareScreen()});
|
||||
}
|
||||
|
||||
if (State.state.featureSwitchMoreQuests.data) {
|
||||
|
||||
tabs.push({
|
||||
header: Svg.add_img,
|
||||
content: new MoreScreen()
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
tabs.push({
|
||||
header: Svg.help,
|
||||
content: new VariableUiElement(this._userDetails.map(userdetails => {
|
||||
if (userdetails.csCount < Constants.userJourney.mapCompleteHelpUnlock) {
|
||||
return ""
|
||||
}
|
||||
return new Combine([Translations.t.general.aboutMapcomplete, "<br/>Version " + Constants.vNumber]).Render();
|
||||
}, [Locale.language]))
|
||||
}
|
||||
);
|
||||
|
||||
this._component = new TabbedComponent(tabs, State.state.welcomeMessageOpenedTab)
|
||||
.ListenTo(this._userDetails);
|
||||
|
||||
|
||||
}
|
||||
|
||||
InnerRender(): string {
|
||||
return this._component.Render();
|
||||
|
||||
}
|
||||
|
||||
}
|
33
UI/BigComponents/LayerControlPanel.ts
Normal file
33
UI/BigComponents/LayerControlPanel.ts
Normal file
|
@ -0,0 +1,33 @@
|
|||
import {UIElement} from "../UIElement";
|
||||
import State from "../../State";
|
||||
import BackgroundSelector from "./BackgroundSelector";
|
||||
import LayerSelection from "./LayerSelection";
|
||||
import Combine from "../Base/Combine";
|
||||
|
||||
export default class LayerControlPanel extends UIElement{
|
||||
private readonly _panel: UIElement;
|
||||
|
||||
|
||||
constructor() {
|
||||
super();
|
||||
let layerControlPanel: UIElement = undefined;
|
||||
if (State.state.layoutToUse.data.enableBackgroundLayerSelection) {
|
||||
layerControlPanel = new BackgroundSelector();
|
||||
layerControlPanel.SetStyle("margin:1em");
|
||||
layerControlPanel.onClick(() => {
|
||||
});
|
||||
}
|
||||
|
||||
if (State.state.filteredLayers.data.length > 1) {
|
||||
const layerSelection = new LayerSelection();
|
||||
layerSelection.onClick(() => { });
|
||||
layerControlPanel = new Combine([layerSelection, "<br/>", layerControlPanel]);
|
||||
}
|
||||
this._panel = layerControlPanel;
|
||||
}
|
||||
|
||||
InnerRender(): string {
|
||||
return this._panel.Render();
|
||||
}
|
||||
|
||||
}
|
|
@ -1,13 +1,13 @@
|
|||
import {UIElement} from "./UIElement";
|
||||
import CheckBox from "./Input/CheckBox";
|
||||
import Combine from "./Base/Combine";
|
||||
import State from "../State";
|
||||
import Translations from "./i18n/Translations";
|
||||
import {FixedUiElement} from "./Base/FixedUiElement";
|
||||
import {VariableUiElement} from "./Base/VariableUIElement";
|
||||
import {UIEventSource} from "../Logic/UIEventSource";
|
||||
import {UIEventSource} from "../../Logic/UIEventSource";
|
||||
import {UIElement} from "../UIElement";
|
||||
import {VariableUiElement} from "../Base/VariableUIElement";
|
||||
import State from "../../State";
|
||||
import CheckBox from "../Input/CheckBox";
|
||||
import Combine from "../Base/Combine";
|
||||
import {FixedUiElement} from "../Base/FixedUiElement";
|
||||
import Translations from "../i18n/Translations";
|
||||
|
||||
export class LayerSelection extends UIElement {
|
||||
export default class LayerSelection extends UIElement {
|
||||
|
||||
private readonly _checkboxes: UIElement[];
|
||||
|
||||
|
@ -16,7 +16,9 @@ export class LayerSelection extends UIElement {
|
|||
this._checkboxes = [];
|
||||
|
||||
for (const layer of State.state.filteredLayers.data) {
|
||||
const leafletStyle = layer.layerDef.GenerateLeafletStyle(new UIEventSource<any>({id: "node/-1"}), true)
|
||||
const leafletStyle = layer.layerDef.GenerateLeafletStyle(
|
||||
new UIEventSource<any>({id: "node/-1"}),
|
||||
false)
|
||||
const leafletHtml = leafletStyle.icon.html;
|
||||
const icon =
|
||||
new FixedUiElement(leafletHtml.Render())
|
|
@ -1,17 +1,17 @@
|
|||
import {UIElement} from "./UIElement";
|
||||
import {VerticalCombine} from "./Base/VerticalCombine";
|
||||
import Translations from "./i18n/Translations";
|
||||
import {AllKnownLayouts} from "../Customizations/AllKnownLayouts";
|
||||
import Combine from "./Base/Combine";
|
||||
import {SubtleButton} from "./Base/SubtleButton";
|
||||
import State from "../State";
|
||||
import {VariableUiElement} from "./Base/VariableUIElement";
|
||||
import Svg from "../Svg";
|
||||
import LayoutConfig from "../Customizations/JSON/LayoutConfig";
|
||||
import * as personal from "../assets/themes/personalLayout/personalLayout.json"
|
||||
import Constants from "../Models/Constants";
|
||||
import {VerticalCombine} from "../Base/VerticalCombine";
|
||||
import {UIElement} from "../UIElement";
|
||||
import {VariableUiElement} from "../Base/VariableUIElement";
|
||||
import LayoutConfig from "../../Customizations/JSON/LayoutConfig";
|
||||
import {AllKnownLayouts} from "../../Customizations/AllKnownLayouts";
|
||||
import Svg from "../../Svg";
|
||||
import State from "../../State";
|
||||
import Combine from "../Base/Combine";
|
||||
import {SubtleButton} from "../Base/SubtleButton";
|
||||
import Translations from "../i18n/Translations";
|
||||
import * as personal from "../../assets/themes/personalLayout/personalLayout.json"
|
||||
import Constants from "../../Models/Constants";
|
||||
|
||||
export class MoreScreen extends UIElement {
|
||||
export default class MoreScreen extends UIElement {
|
||||
|
||||
|
||||
constructor() {
|
|
@ -1,17 +1,16 @@
|
|||
import {UIElement} from "./UIElement";
|
||||
import State from "../State";
|
||||
import Translations from "../UI/i18n/Translations";
|
||||
import {AllKnownLayouts} from "../Customizations/AllKnownLayouts";
|
||||
import Combine from "../UI/Base/Combine";
|
||||
import CheckBox from "../UI/Input/CheckBox";
|
||||
import * as personal from "../assets/themes/personalLayout/personalLayout.json";
|
||||
import {SubtleButton} from "./Base/SubtleButton";
|
||||
import {FixedUiElement} from "./Base/FixedUiElement";
|
||||
import Svg from "../Svg";
|
||||
import LayoutConfig from "../Customizations/JSON/LayoutConfig";
|
||||
import {UIEventSource} from "../Logic/UIEventSource";
|
||||
|
||||
export class PersonalLayersPanel extends UIElement {
|
||||
import {UIEventSource} from "../../Logic/UIEventSource";
|
||||
import {UIElement} from "../UIElement";
|
||||
import LayoutConfig from "../../Customizations/JSON/LayoutConfig";
|
||||
import {AllKnownLayouts} from "../../Customizations/AllKnownLayouts";
|
||||
import Svg from "../../Svg";
|
||||
import State from "../../State";
|
||||
import Combine from "../Base/Combine";
|
||||
import CheckBox from "../Input/CheckBox";
|
||||
import {SubtleButton} from "../Base/SubtleButton";
|
||||
import {FixedUiElement} from "../Base/FixedUiElement";
|
||||
import Translations from "../i18n/Translations";
|
||||
import * as personal from "../../assets/themes/personalLayout/personalLayout.json"
|
||||
export default class PersonalLayersPanel extends UIElement {
|
||||
private checkboxes: UIElement[] = [];
|
||||
|
||||
constructor() {
|
|
@ -1,23 +1,22 @@
|
|||
import Locale from "./i18n/Locale";
|
||||
import {UIElement} from "./UIElement";
|
||||
import {VariableUiElement} from "./Base/VariableUIElement";
|
||||
import {TextField} from "./Input/TextField";
|
||||
import {Geocoding} from "../Logic/Osm/Geocoding";
|
||||
import Translations from "./i18n/Translations";
|
||||
import State from "../State";
|
||||
import Locale from "../i18n/Locale";
|
||||
import {UIEventSource} from "../../Logic/UIEventSource";
|
||||
import {UIElement} from "../UIElement";
|
||||
import {Translation} from "../i18n/Translation";
|
||||
import {VariableUiElement} from "../Base/VariableUIElement";
|
||||
import Svg from "../../Svg";
|
||||
import State from "../../State";
|
||||
import {TextField} from "../Input/TextField";
|
||||
import {Geocoding} from "../../Logic/Osm/Geocoding";
|
||||
import Translations from "../i18n/Translations";
|
||||
|
||||
import {UIEventSource} from "../Logic/UIEventSource";
|
||||
import Svg from "../Svg";
|
||||
import {Translation} from "./i18n/Translation";
|
||||
|
||||
export class SearchAndGo extends UIElement {
|
||||
export default class SearchAndGo extends UIElement {
|
||||
|
||||
private _placeholder = new UIEventSource<Translation>(Translations.t.general.search.search)
|
||||
private _searchField = new TextField({
|
||||
placeholder: new VariableUiElement(
|
||||
this._placeholder.map(uiElement => uiElement.InnerRender(), [Locale.language])
|
||||
),
|
||||
value: new UIEventSource<string>("")
|
||||
value: new UIEventSource<string>("")
|
||||
}
|
||||
);
|
||||
|
||||
|
@ -39,25 +38,31 @@ export class SearchAndGo extends UIElement {
|
|||
|
||||
}
|
||||
|
||||
InnerRender(): string {
|
||||
return this._searchField.Render() +
|
||||
this._goButton.Render();
|
||||
|
||||
}
|
||||
|
||||
// Triggered by 'enter' or onclick
|
||||
private RunSearch() {
|
||||
const searchString = this._searchField.GetValue().data;
|
||||
if(searchString === undefined || searchString === ""){
|
||||
if (searchString === undefined || searchString === "") {
|
||||
return;
|
||||
}
|
||||
this._searchField.GetValue().setData("");
|
||||
this._placeholder.setData(Translations.t.general.search.searching);
|
||||
const self = this;
|
||||
Geocoding.Search(searchString, (result) => {
|
||||
Geocoding.Search(searchString, (result) => {
|
||||
|
||||
console.log("Search result", result)
|
||||
console.log("Search result", result)
|
||||
if (result.length == 0) {
|
||||
self._placeholder.setData(Translations.t.general.search.nothing);
|
||||
return;
|
||||
}
|
||||
|
||||
const bb = result[0].boundingbox;
|
||||
const bounds : [[number, number], [number, number]] = [
|
||||
const bounds: [[number, number], [number, number]] = [
|
||||
[bb[0], bb[2]],
|
||||
[bb[1], bb[3]]
|
||||
]
|
||||
|
@ -71,11 +76,5 @@ export class SearchAndGo extends UIElement {
|
|||
|
||||
}
|
||||
|
||||
InnerRender(): string {
|
||||
return this._searchField.Render() +
|
||||
this._goButton.Render();
|
||||
|
||||
}
|
||||
|
||||
|
||||
}
|
|
@ -1,4 +1,4 @@
|
|||
import {UIElement} from "./UIElement";
|
||||
import {UIElement} from "../UIElement";
|
||||
|
||||
export default class ShareButton extends UIElement{
|
||||
private _embedded: UIElement;
|
|
@ -1,21 +1,21 @@
|
|||
import {UIElement} from "./UIElement";
|
||||
import Translations from "./i18n/Translations";
|
||||
import {FixedUiElement} from "./Base/FixedUiElement";
|
||||
import Combine from "./Base/Combine";
|
||||
import {VariableUiElement} from "./Base/VariableUIElement";
|
||||
import CheckBox from "./Input/CheckBox";
|
||||
import {VerticalCombine} from "./Base/VerticalCombine";
|
||||
import State from "../State";
|
||||
import {FilteredLayer} from "../Logic/FilteredLayer";
|
||||
import {Utils} from "../Utils";
|
||||
import {UIEventSource} from "../Logic/UIEventSource";
|
||||
import {SubtleButton} from "./Base/SubtleButton";
|
||||
import Svg from "../Svg";
|
||||
import {Translation} from "./i18n/Translation";
|
||||
import LayoutConfig from "../Customizations/JSON/LayoutConfig";
|
||||
import Constants from "../Models/Constants";
|
||||
import {VerticalCombine} from "../Base/VerticalCombine";
|
||||
import {UIElement} from "../UIElement";
|
||||
import {VariableUiElement} from "../Base/VariableUIElement";
|
||||
import {Translation} from "../i18n/Translation";
|
||||
import LayoutConfig from "../../Customizations/JSON/LayoutConfig";
|
||||
import Svg from "../../Svg";
|
||||
import Combine from "../Base/Combine";
|
||||
import {SubtleButton} from "../Base/SubtleButton";
|
||||
import {UIEventSource} from "../../Logic/UIEventSource";
|
||||
import {Utils} from "../../Utils";
|
||||
import State from "../../State";
|
||||
import CheckBox from "../Input/CheckBox";
|
||||
import {FixedUiElement} from "../Base/FixedUiElement";
|
||||
import Translations from "../i18n/Translations";
|
||||
import Constants from "../../Models/Constants";
|
||||
import LayerConfig from "../../Customizations/JSON/LayerConfig";
|
||||
|
||||
export class ShareScreen extends UIElement {
|
||||
export default class ShareScreen extends UIElement {
|
||||
private readonly _options: UIElement;
|
||||
private readonly _iframeCode: UIElement;
|
||||
public iframe: UIEventSource<string>;
|
||||
|
@ -61,7 +61,7 @@ export class ShareScreen extends UIElement {
|
|||
}, [currentLocation]));
|
||||
|
||||
|
||||
function fLayerToParam(flayer: FilteredLayer) {
|
||||
function fLayerToParam(flayer: {isDisplayed: UIEventSource<boolean>, layerDef: LayerConfig}) {
|
||||
if (flayer.isDisplayed.data) {
|
||||
return null; // Being displayed is the default
|
||||
}
|
|
@ -1,20 +1,19 @@
|
|||
import {UIElement} from "./UIElement";
|
||||
import {Tag, TagUtils} from "../Logic/Tags";
|
||||
import Translations from "./i18n/Translations";
|
||||
import Combine from "./Base/Combine";
|
||||
import {SubtleButton} from "./Base/SubtleButton";
|
||||
import Locale from "./i18n/Locale";
|
||||
import State from "../State";
|
||||
|
||||
import {UIEventSource} from "../Logic/UIEventSource";
|
||||
import Svg from "../Svg";
|
||||
import {FixedUiElement} from "./Base/FixedUiElement";
|
||||
import Constants from "../Models/Constants";
|
||||
|
||||
/**
|
||||
* Asks to add a feature at the last clicked location, at least if zoom is sufficient
|
||||
*/
|
||||
export class SimpleAddUI extends UIElement {
|
||||
import Locale from "../i18n/Locale";
|
||||
import {UIEventSource} from "../../Logic/UIEventSource";
|
||||
import {Tag, TagUtils} from "../../Logic/Tags";
|
||||
import {UIElement} from "../UIElement";
|
||||
import Svg from "../../Svg";
|
||||
import {SubtleButton} from "../Base/SubtleButton";
|
||||
import State from "../../State";
|
||||
import Combine from "../Base/Combine";
|
||||
import {FixedUiElement} from "../Base/FixedUiElement";
|
||||
import Translations from "../i18n/Translations";
|
||||
import Constants from "../../Models/Constants";
|
||||
|
||||
export default class SimpleAddUI extends UIElement {
|
||||
private readonly _addButtons: UIElement[];
|
||||
|
||||
private _loginButton : UIElement;
|
|
@ -1,25 +1,25 @@
|
|||
import {UIElement} from "./UIElement";
|
||||
import {FixedUiElement} from "./Base/FixedUiElement";
|
||||
import {VariableUiElement} from "./Base/VariableUIElement";
|
||||
import Translations from "./i18n/Translations";
|
||||
import {UserDetails} from "../Logic/Osm/OsmConnection";
|
||||
import State from "../State";
|
||||
import {UIEventSource} from "../Logic/UIEventSource";
|
||||
import Combine from "./Base/Combine";
|
||||
import Svg from "../Svg";
|
||||
import Link from "./Base/Link";
|
||||
import LanguagePicker from "./LanguagePicker";
|
||||
|
||||
/**
|
||||
* Handles and updates the user badge
|
||||
*/
|
||||
export class UserBadge extends UIElement {
|
||||
import {UIEventSource} from "../../Logic/UIEventSource";
|
||||
import {UIElement} from "../UIElement";
|
||||
import {VariableUiElement} from "../Base/VariableUIElement";
|
||||
import {UserDetails} from "../../Logic/Osm/OsmConnection";
|
||||
import Svg from "../../Svg";
|
||||
import State from "../../State";
|
||||
import Combine from "../Base/Combine";
|
||||
import {FixedUiElement} from "../Base/FixedUiElement";
|
||||
import LanguagePicker from "../LanguagePicker";
|
||||
import Translations from "../i18n/Translations";
|
||||
import Link from "../Base/Link";
|
||||
|
||||
export default class UserBadge extends UIElement {
|
||||
private _userDetails: UIEventSource<UserDetails>;
|
||||
private _logout: UIElement;
|
||||
private _homeButton: UIElement;
|
||||
private _languagePicker: UIElement;
|
||||
|
||||
private _loginButton : UIElement;
|
||||
private _loginButton: UIElement;
|
||||
|
||||
constructor() {
|
||||
super(State.state.osmConnection.userDetails);
|
||||
|
@ -94,7 +94,7 @@ export class UserBadge extends UIElement {
|
|||
|
||||
const settings =
|
||||
new Link(Svg.gear_svg(),
|
||||
`https://www.openstreetmap.org/user/${encodeURIComponent(user.name)}/account`,
|
||||
`https://www.openstreetmap.org/user/${encodeURIComponent(user.name)}/account`,
|
||||
true)
|
||||
|
||||
|
|
@ -1,12 +1,12 @@
|
|||
import {UIElement} from "./UIElement";
|
||||
import Locale from "../UI/i18n/Locale";
|
||||
import State from "../State";
|
||||
import Translations from "./i18n/Translations";
|
||||
import Combine from "./Base/Combine";
|
||||
import LanguagePicker from "./LanguagePicker";
|
||||
import Locale from "../i18n/Locale";
|
||||
import {UIElement} from "../UIElement";
|
||||
import State from "../../State";
|
||||
import Combine from "../Base/Combine";
|
||||
import LanguagePicker from "../LanguagePicker";
|
||||
import Translations from "../i18n/Translations";
|
||||
|
||||
|
||||
export class WelcomeMessage extends UIElement {
|
||||
export default class WelcomeMessage extends UIElement {
|
||||
private languagePicker: UIElement;
|
||||
|
||||
private readonly description: UIElement;
|
||||
|
@ -25,11 +25,6 @@ export class WelcomeMessage extends UIElement {
|
|||
"<h3>", layout.title, "</h3>",
|
||||
layout.description
|
||||
])
|
||||
layout.descriptionTail
|
||||
|
||||
|
||||
|
||||
|
||||
this.plzLogIn =
|
||||
Translations.t.general.loginWithOpenStreetMap
|
||||
.onClick(() => {
|
|
@ -2,10 +2,9 @@ import {UIElement} from "./UIElement";
|
|||
import Translations from "./i18n/Translations";
|
||||
import State from "../State";
|
||||
|
||||
export class CenterMessageBox extends UIElement {
|
||||
export default class CenterMessageBox extends UIElement {
|
||||
|
||||
constructor(
|
||||
) {
|
||||
constructor() {
|
||||
super(State.state.centerMessage);
|
||||
|
||||
this.ListenTo(State.state.locationControl);
|
||||
|
@ -19,14 +18,17 @@ export class CenterMessageBox extends UIElement {
|
|||
return {innerHtml: State.state.centerMessage.data, done: false};
|
||||
}
|
||||
const lu = State.state.layerUpdater;
|
||||
if(lu.retries.data > 0) {
|
||||
return {innerHtml: Translations.t.centerMessage.retrying.Subs({count: ""+ lu.retries.data}).Render(), done: false};
|
||||
if (lu.retries.data > 0) {
|
||||
return {
|
||||
innerHtml: Translations.t.centerMessage.retrying.Subs({count: "" + lu.retries.data}).Render(),
|
||||
done: false
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
if (lu.runningQuery.data) {
|
||||
return {innerHtml: Translations.t.centerMessage.loadingData.Render(), done: false};
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
if (!lu.sufficientlyZoomed.data) {
|
||||
return {innerHtml: Translations.t.centerMessage.zoomIn.Render(), done: false};
|
||||
} else {
|
||||
|
|
|
@ -6,7 +6,7 @@ import Combine from "./Base/Combine";
|
|||
/**
|
||||
* Handles the full screen popup on mobile
|
||||
*/
|
||||
export class FullScreenMessageBox extends UIElement {
|
||||
export default class FullScreenMessageBox extends UIElement {
|
||||
|
||||
private readonly returnToTheMap: UIElement;
|
||||
private _content: UIElement;
|
||||
|
|
|
@ -9,8 +9,8 @@ import State from "../../State";
|
|||
import Svg from "../../Svg";
|
||||
|
||||
export default class EditableTagRendering extends UIElement {
|
||||
private _tags: UIEventSource<any>;
|
||||
private _configuration: TagRenderingConfig;
|
||||
private readonly _tags: UIEventSource<any>;
|
||||
private readonly _configuration: TagRenderingConfig;
|
||||
|
||||
private _editMode: UIEventSource<boolean> = new UIEventSource<boolean>(false);
|
||||
private _editButton: UIElement;
|
||||
|
|
|
@ -8,14 +8,14 @@ import TagRenderingAnswer from "./TagRenderingAnswer";
|
|||
import State from "../../State";
|
||||
import {FixedUiElement} from "../Base/FixedUiElement";
|
||||
|
||||
export class FeatureInfoBox extends UIElement {
|
||||
export default class FeatureInfoBox extends UIElement {
|
||||
private _tags: UIEventSource<any>;
|
||||
private _layerConfig: LayerConfig;
|
||||
|
||||
private _title : UIElement;
|
||||
private _title: UIElement;
|
||||
private _titleIcons: UIElement;
|
||||
private _renderings: UIElement[];
|
||||
private _questionBox : UIElement;
|
||||
private _questionBox: UIElement;
|
||||
|
||||
constructor(
|
||||
tags: UIEventSource<any>,
|
||||
|
@ -35,15 +35,15 @@ export class FeatureInfoBox extends UIElement {
|
|||
this._titleIcons = new Combine(
|
||||
layerConfig.titleIcons.map(icon => new TagRenderingAnswer(tags, icon)))
|
||||
.SetClass("featureinfobox-icons");
|
||||
|
||||
let questionBox : UIElement = undefined;
|
||||
|
||||
let questionBox: UIElement = undefined;
|
||||
if (State.state.featureSwitchUserbadge.data) {
|
||||
questionBox = new QuestionBox(tags, layerConfig.tagRenderings);
|
||||
}
|
||||
|
||||
|
||||
let questionBoxIsUsed = false;
|
||||
this._renderings = layerConfig.tagRenderings.map(tr => {
|
||||
if(tr.question === null){
|
||||
if (tr.question === null) {
|
||||
questionBoxIsUsed = true;
|
||||
// This is the question box!
|
||||
return questionBox;
|
||||
|
@ -51,9 +51,9 @@ export class FeatureInfoBox extends UIElement {
|
|||
return new EditableTagRendering(tags, tr);
|
||||
});
|
||||
this._renderings[0]?.SetClass("first-rendering");
|
||||
if(!questionBoxIsUsed){
|
||||
this._renderings.push(questionBox);
|
||||
}
|
||||
if (!questionBoxIsUsed) {
|
||||
this._renderings.push(questionBox);
|
||||
}
|
||||
}
|
||||
|
||||
InnerRender(): string {
|
||||
|
|
|
@ -9,9 +9,9 @@ import Translations from "../i18n/Translations";
|
|||
* Generates all the questions, one by one
|
||||
*/
|
||||
export default class QuestionBox extends UIElement {
|
||||
private _tags: UIEventSource<any>;
|
||||
private readonly _tags: UIEventSource<any>;
|
||||
|
||||
private _tagRenderings: TagRenderingConfig[];
|
||||
private readonly _tagRenderings: TagRenderingConfig[];
|
||||
private _tagRenderingQuestions: UIElement[];
|
||||
|
||||
private _skippedQuestions: UIEventSource<number[]> = new UIEventSource<number[]>([])
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
import {UIEventSource} from "../../Logic/UIEventSource";
|
||||
import {UIElement} from "../UIElement";
|
||||
import Translations from "../i18n/Translations";
|
||||
import {OsmConnection, UserDetails} from "../../Logic/Osm/OsmConnection";
|
||||
import UserDetails, {OsmConnection} from "../../Logic/Osm/OsmConnection";
|
||||
|
||||
export class SaveButton extends UIElement {
|
||||
|
||||
|
|
|
@ -7,7 +7,7 @@ import {SubstitutedTranslation} from "../SpecialVisualizations";
|
|||
* Displays the correct value for a known tagrendering
|
||||
*/
|
||||
export default class TagRenderingAnswer extends UIElement {
|
||||
private _tags: UIEventSource<any>;
|
||||
private readonly _tags: UIEventSource<any>;
|
||||
private _configuration: TagRenderingConfig;
|
||||
private _content: UIElement;
|
||||
|
||||
|
|
|
@ -25,7 +25,7 @@ import Constants from "../../Models/Constants";
|
|||
* Note that the value _migh_ already be known, e.g. when selected or when changing the value
|
||||
*/
|
||||
export default class TagRenderingQuestion extends UIElement {
|
||||
private _tags: UIEventSource<any>;
|
||||
private readonly _tags: UIEventSource<any>;
|
||||
private _configuration: TagRenderingConfig;
|
||||
|
||||
private _saveButton: UIElement;
|
||||
|
|
141
UI/ShowDataLayer.ts
Normal file
141
UI/ShowDataLayer.ts
Normal file
|
@ -0,0 +1,141 @@
|
|||
/**
|
||||
* The data layer shows all the given geojson elements with the appropriate icon etc
|
||||
*/
|
||||
import {UIEventSource} from "../Logic/UIEventSource";
|
||||
import * as L from "leaflet"
|
||||
import LayerConfig from "../Customizations/JSON/LayerConfig";
|
||||
import State from "../State";
|
||||
import LazyElement from "./Base/LazyElement";
|
||||
import Hash from "../Logic/Web/Hash";
|
||||
import {GeoOperations} from "../Logic/GeoOperations";
|
||||
import FeatureInfoBox from "./Popup/FeatureInfoBox";
|
||||
|
||||
export default class ShowDataLayer {
|
||||
|
||||
private readonly _layerDict;
|
||||
private readonly _leafletMap: UIEventSource<L.Map>;
|
||||
|
||||
constructor(features: UIEventSource<{ feature: any, freshness: Date }[]>,
|
||||
leafletMap: UIEventSource<L.Map>,
|
||||
layers: { layerDef: LayerConfig, isDisplayed: UIEventSource<boolean> }[]) {
|
||||
this._leafletMap = leafletMap;
|
||||
const self = this;
|
||||
|
||||
let oldGeoLayer: L.Layer = undefined;
|
||||
|
||||
this._layerDict = {};
|
||||
for (const layer of layers) {
|
||||
this._layerDict[layer.layerDef.id] = layer.layerDef;
|
||||
}
|
||||
|
||||
function update() {
|
||||
if (features.data === undefined) {
|
||||
return;
|
||||
}
|
||||
if (leafletMap.data === undefined) {
|
||||
return;
|
||||
}
|
||||
const mp = leafletMap.data;
|
||||
|
||||
const feats = features.data.map(ff => ff.feature);
|
||||
const geoLayer = self.CreateGeojsonLayer(feats);
|
||||
|
||||
if (oldGeoLayer) {
|
||||
mp.removeLayer(oldGeoLayer);
|
||||
}
|
||||
|
||||
geoLayer.addTo(mp);
|
||||
oldGeoLayer = geoLayer;
|
||||
}
|
||||
|
||||
features.addCallbackAndRun(() => update());
|
||||
leafletMap.addCallback(() => update());
|
||||
|
||||
}
|
||||
|
||||
|
||||
private createStyleFor(feature) {
|
||||
const tagsSource = State.state.allElements.getEventSourceFor(feature);
|
||||
// Every object is tied to exactly one layer
|
||||
const layer = this._layerDict[feature._matching_layer_id];
|
||||
return layer.GenerateLeafletStyle(tagsSource, layer._showOnPopup !== undefined);
|
||||
}
|
||||
|
||||
private pointToLayer(feature, latLng): L.Layer {
|
||||
// Leaflet cannot handle geojson points natively
|
||||
// We have to convert them to the appropriate icon
|
||||
// Click handling is done in the next step
|
||||
|
||||
const tagSource = State.state.allElements.getEventSourceFor(feature);
|
||||
const layer : LayerConfig = this._layerDict[feature._matching_layer_id];
|
||||
|
||||
const style = layer.GenerateLeafletStyle(tagSource, !(layer.title === undefined && (layer.tagRenderings ?? []).length === 0));
|
||||
return L.marker(latLng, {
|
||||
icon: L.divIcon({
|
||||
html: style.icon.html.Render(),
|
||||
className: style.icon.className,
|
||||
iconAnchor: style.icon.iconAnchor,
|
||||
iconUrl: style.icon.iconUrl,
|
||||
popupAnchor: style.icon.popupAnchor,
|
||||
iconSize: style.icon.iconSize
|
||||
})
|
||||
});
|
||||
}
|
||||
|
||||
private postProcessFeature(feature, leafletLayer: L.Layer){
|
||||
const layer : LayerConfig = this._layerDict[feature._matching_layer_id];
|
||||
if (layer.title === undefined && (layer.tagRenderings ?? []).length === 0) {
|
||||
// No popup action defined -> Don't do anything
|
||||
return;
|
||||
}
|
||||
|
||||
const popup = L.popup({
|
||||
autoPan: true,
|
||||
closeOnEscapeKey: true,
|
||||
}, leafletLayer);
|
||||
|
||||
|
||||
const tags = State.state.allElements.getEventSourceFor(feature);
|
||||
let uiElement: LazyElement = new LazyElement(() => new FeatureInfoBox(tags, layer));
|
||||
popup.setContent(uiElement.Render());
|
||||
leafletLayer.bindPopup(popup);
|
||||
// We first render the UIelement (which'll still need an update later on...)
|
||||
// But at least it'll be visible already
|
||||
|
||||
|
||||
leafletLayer.on("click", (e) => {
|
||||
// We set the element as selected...
|
||||
uiElement.Activate();
|
||||
State.state.selectedElement.setData(feature);
|
||||
});
|
||||
|
||||
|
||||
if (feature.properties.id.replace(/\//g, "_") === Hash.Get().data) {
|
||||
// This element is in the URL, so this is a share link
|
||||
// We already open it
|
||||
uiElement.Activate();
|
||||
popup.setContent(uiElement.Render());
|
||||
|
||||
const center = GeoOperations.centerpoint(feature).geometry.coordinates;
|
||||
popup.setLatLng({lat: center[1], lng: center[0]});
|
||||
popup.openOn(State.state.leafletMap.data);
|
||||
State.state.selectedElement.setData(feature);
|
||||
uiElement.Update();
|
||||
}
|
||||
}
|
||||
|
||||
private CreateGeojsonLayer(features: any[]): L.Layer {
|
||||
const self = this;
|
||||
const data = {
|
||||
type: "FeatureCollection",
|
||||
features: features
|
||||
}
|
||||
return L.geoJSON(data, {
|
||||
style: feature => self.createStyleFor(feature),
|
||||
pointToLayer: (feature, latLng) => self.pointToLayer(feature, latLng),
|
||||
onEachFeature: (feature, leafletLayer) => self.postProcessFeature(feature, leafletLayer)
|
||||
});
|
||||
|
||||
}
|
||||
|
||||
}
|
|
@ -9,7 +9,7 @@ import Locale from "../UI/i18n/Locale";
|
|||
import {ImageUploadFlow} from "./Image/ImageUploadFlow";
|
||||
import {Translation} from "./i18n/Translation";
|
||||
|
||||
import ShareButton from "./ShareButton";
|
||||
import ShareButton from "./BigComponents/ShareButton";
|
||||
import Svg from "../Svg";
|
||||
import ReviewElement from "./Reviews/ReviewElement";
|
||||
import MangroveReviews from "../Logic/Web/MangroveReviews";
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue