Add binoculars theme, auto reformat everything

This commit is contained in:
Pieter Vander Vennet 2021-09-09 00:05:51 +02:00
parent 38dea806c5
commit 78d6482c88
586 changed files with 115573 additions and 111842 deletions

View file

@ -13,7 +13,7 @@ export class Button extends BaseUIElement {
protected InnerConstructElement(): HTMLElement {
const el = this._text.ConstructElement();
if(el === undefined){
if (el === undefined) {
return undefined;
}
const form = document.createElement("form")

View file

@ -1,32 +1,32 @@
import BaseUIElement from "../BaseUIElement";
export class CenterFlexedElement extends BaseUIElement {
private _html: string;
private _html: string;
constructor(html: string) {
super();
this._html = html ?? "";
}
constructor(html: string) {
super();
this._html = html ?? "";
}
InnerRender(): string {
return this._html;
}
InnerRender(): string {
return this._html;
}
protected InnerConstructElement(): HTMLElement {
const e = document.createElement("div");
e.innerHTML = this._html;
e.style.display = "flex";
e.style.height = "100%";
e.style.width = "100%";
e.style.flexDirection = "column";
e.style.flexWrap = "nowrap";
e.style.alignContent = "center";
e.style.justifyContent = "center";
e.style.alignItems = "center";
return e;
}
AsMarkdown(): string {
return this._html;
}
AsMarkdown(): string {
return this._html;
}
protected InnerConstructElement(): HTMLElement {
const e = document.createElement("div");
e.innerHTML = this._html;
e.style.display = "flex";
e.style.height = "100%";
e.style.width = "100%";
e.style.flexDirection = "column";
e.style.flexWrap = "nowrap";
e.style.alignContent = "center";
e.style.justifyContent = "center";
e.style.alignItems = "center";
return e;
}
}

View file

@ -15,33 +15,33 @@ export default class Combine extends BaseUIElement {
return el;
});
}
protected InnerConstructElement(): HTMLElement {
const el = document.createElement("span")
try{
for (const subEl of this.uiElements) {
if(subEl === undefined || subEl === null){
continue;
}
const subHtml = subEl.ConstructElement()
if(subHtml !== undefined){
el.appendChild(subHtml)
}
}
}catch(e){
const domExc = e as DOMException
console.error("DOMException: ", domExc.name)
el.appendChild(new FixedUiElement("Could not generate this combine!").SetClass("alert").ConstructElement())
}
return el;
}
AsMarkdown(): string {
return this.uiElements.map(el => el.AsMarkdown()).join(this.HasClass("flex-col") ? "\n\n" : " ");
}
protected InnerConstructElement(): HTMLElement {
const el = document.createElement("span")
try {
for (const subEl of this.uiElements) {
if (subEl === undefined || subEl === null) {
continue;
}
const subHtml = subEl.ConstructElement()
if (subHtml !== undefined) {
el.appendChild(subHtml)
}
}
} catch (e) {
const domExc = e as DOMException
console.error("DOMException: ", domExc.name)
el.appendChild(new FixedUiElement("Could not generate this combine!").SetClass("alert").ConstructElement())
}
return el;
}
}

View file

@ -7,19 +7,19 @@ export class FixedUiElement extends BaseUIElement {
super();
this._html = html ?? "";
}
InnerRender(): string {
return this._html;
}
AsMarkdown(): string {
return this._html;
}
protected InnerConstructElement(): HTMLElement {
const e = document.createElement("span")
e.innerHTML = this._html
return e;
}
AsMarkdown(): string {
return this._html;
}
}

View file

@ -10,22 +10,27 @@ export default class Link extends BaseUIElement {
constructor(embeddedShow: BaseUIElement | string, href: string | UIEventSource<string>, newTab: boolean = false) {
super();
this._embeddedShow =Translations.W(embeddedShow);
this._embeddedShow = Translations.W(embeddedShow);
this._href = href;
this._newTab = newTab;
}
AsMarkdown(): string {
// @ts-ignore
return `[${this._embeddedShow.AsMarkdown()}](${this._href.data ?? this._href})`;
}
protected InnerConstructElement(): HTMLElement {
const embeddedShow = this._embeddedShow?.ConstructElement();
if(embeddedShow === undefined){
if (embeddedShow === undefined) {
return undefined;
}
const el = document.createElement("a")
if(typeof this._href === "string"){
if (typeof this._href === "string") {
el.href = this._href
}else{
this._href.addCallbackAndRun(href => {
} else {
this._href.addCallbackAndRun(href => {
el.href = href;
})
}
@ -36,9 +41,4 @@ export default class Link extends BaseUIElement {
return el;
}
AsMarkdown(): string {
// @ts-ignore
return `[${this._embeddedShow.AsMarkdown()}](${this._href.data ?? this._href})`;
}
}

View file

@ -13,15 +13,24 @@ export default class List extends BaseUIElement {
.map(Translations.W);
}
AsMarkdown(): string {
if (this._ordered) {
return "\n\n" + this.uiElements.map((el, i) => " " + i + ". " + el.AsMarkdown().replace(/\n/g, ' \n')).join("\n") + "\n"
} else {
return "\n\n" + this.uiElements.map(el => " - " + el.AsMarkdown().replace(/\n/g, ' \n')).join("\n") + "\n"
}
}
protected InnerConstructElement(): HTMLElement {
const el = document.createElement(this._ordered ? "ol" : "ul")
for (const subEl of this.uiElements) {
if(subEl === undefined || subEl === null){
if (subEl === undefined || subEl === null) {
continue;
}
const subHtml = subEl.ConstructElement()
if(subHtml !== undefined){
if (subHtml !== undefined) {
const item = document.createElement("li")
item.appendChild(subHtml)
el.appendChild(item)
@ -30,14 +39,5 @@ export default class List extends BaseUIElement {
return el;
}
AsMarkdown(): string {
if(this._ordered){
return "\n\n"+this.uiElements.map((el, i) => " "+i+". "+el.AsMarkdown().replace(/\n/g, ' \n') ).join("\n") + "\n"
}else{
return "\n\n"+this.uiElements.map(el => " - "+el.AsMarkdown().replace(/\n/g, ' \n') ).join("\n")+"\n"
}
}
}

View file

@ -122,11 +122,11 @@ export default class Minimap extends BaseUIElement {
);
if (this._attribution !== undefined) {
if(this._attribution === true){
map.attributionControl.setPrefix(false)
}else{
map.attributionControl.setPrefix(
"<span id='leaflet-attribution'></span>");
if (this._attribution === true) {
map.attributionControl.setPrefix(false)
} else {
map.attributionControl.setPrefix(
"<span id='leaflet-attribution'></span>");
}
}

View file

@ -8,27 +8,27 @@ import Hash from "../../Logic/Web/Hash";
import BaseUIElement from "../BaseUIElement";
/**
*
*
* The scrollableFullScreen is a bit of a peculiar component:
* - It shows a title and some contents, constructed from the respective functions passed into the constructor
* - When the element is 'activated', one clone of title+contents is attached to the fullscreen
* - The element itself will - upon rendering - also show the title and contents (allthough it'll be a different clone)
*
*
*
*
*/
export default class ScrollableFullScreen extends UIElement {
private static readonly empty = new FixedUiElement("");
private static readonly _actor = ScrollableFullScreen.InitActor();
private static _currentlyOpen: ScrollableFullScreen;
public isShown: UIEventSource<boolean>;
private _component: BaseUIElement;
private _fullscreencomponent: BaseUIElement;
private static readonly _actor = ScrollableFullScreen.InitActor();
private _hashToSet: string;
private static _currentlyOpen : ScrollableFullScreen;
private _hashToSet: string;
constructor(title: ((mode: string) => BaseUIElement), content: ((mode: string) => BaseUIElement),
hashToSet: string,
isShown: UIEventSource<boolean> = new UIEventSource<boolean>(false)
) {
) {
super();
this.isShown = isShown;
this._hashToSet = hashToSet;
@ -46,6 +46,23 @@ export default class ScrollableFullScreen extends UIElement {
})
}
private static clear() {
ScrollableFullScreen.empty.AttachTo("fullscreen")
const fs = document.getElementById("fullscreen");
ScrollableFullScreen._currentlyOpen?.isShown?.setData(false);
fs.classList.add("hidden")
Hash.hash.setData(undefined);
}
private static InitActor() {
Hash.hash.addCallback(hash => {
if (hash === undefined || hash === "") {
ScrollableFullScreen.clear()
}
});
return true;
}
InnerRender(): BaseUIElement {
return this._component;
}
@ -53,7 +70,7 @@ export default class ScrollableFullScreen extends UIElement {
Activate(): void {
this.isShown.setData(true)
this._fullscreencomponent.AttachTo("fullscreen");
if(this._hashToSet != undefined){
if (this._hashToSet != undefined) {
Hash.hash.setData(this._hashToSet)
}
const fs = document.getElementById("fullscreen");
@ -61,7 +78,7 @@ export default class ScrollableFullScreen extends UIElement {
fs.classList.remove("hidden")
}
private BuildComponent(title: BaseUIElement, content:BaseUIElement, isShown: UIEventSource<boolean>) {
private BuildComponent(title: BaseUIElement, content: BaseUIElement, isShown: UIEventSource<boolean>) {
const returnToTheMap =
new Combine([
Svg.back_svg().SetClass("block md:hidden"),
@ -86,21 +103,4 @@ export default class ScrollableFullScreen extends UIElement {
}
private static clear() {
ScrollableFullScreen.empty.AttachTo("fullscreen")
const fs = document.getElementById("fullscreen");
ScrollableFullScreen._currentlyOpen?.isShown?.setData(false);
fs.classList.add("hidden")
Hash.hash.setData(undefined);
}
private static InitActor(){
Hash.hash.addCallback(hash => {
if(hash === undefined || hash === ""){
ScrollableFullScreen.clear()
}
});
return true;
}
}

View file

@ -9,17 +9,17 @@ export class TabbedComponent extends Combine {
constructor(elements: { header: BaseUIElement | string, content: BaseUIElement | string }[], openedTab: (UIEventSource<number> | number) = 0) {
const openedTabSrc = typeof (openedTab) === "number" ? new UIEventSource(openedTab) : (openedTab ?? new UIEventSource<number>(0))
const tabs: BaseUIElement[] = []
const contentElements: BaseUIElement[] = [];
for (let i = 0; i < elements.length; i++) {
let element = elements[i];
const header = Translations.W(element.header).onClick(() => openedTabSrc.setData(i))
openedTabSrc.addCallbackAndRun(selected => {
if(selected === i){
if (selected === i) {
header.SetClass("tab-active")
header.RemoveClass("tab-non-active")
}else{
} else {
header.SetClass("tab-non-active")
header.RemoveClass("tab-active")
}

View file

@ -1,36 +1,37 @@
import BaseUIElement from "../BaseUIElement";
import Translations from "../i18n/Translations";
export default class Title extends BaseUIElement{
export default class Title extends BaseUIElement {
private readonly _embedded: BaseUIElement;
private readonly _level: number;
constructor(embedded: string | BaseUIElement, level: number =3 ) {
private readonly _level: number;
constructor(embedded: string | BaseUIElement, level: number = 3) {
super()
this._embedded = Translations.W(embedded);
this._level = level;
}
AsMarkdown(): string {
const embedded = " " + this._embedded.AsMarkdown() + " ";
if (this._level == 1) {
return "\n" + embedded + "\n" + "=".repeat(embedded.length) + "\n\n"
}
if (this._level == 2) {
return "\n" + embedded + "\n" + "-".repeat(embedded.length) + "\n\n"
}
return "\n" + "#".repeat(this._level) + embedded + "\n\n";
}
protected InnerConstructElement(): HTMLElement {
const el = this._embedded.ConstructElement()
if(el === undefined){
if (el === undefined) {
return undefined;
}
const h = document.createElement("h"+this._level)
const h = document.createElement("h" + this._level)
h.appendChild(el)
return h;
}
AsMarkdown(): string {
const embedded = " " +this._embedded.AsMarkdown()+" ";
if(this._level == 1){
return "\n"+embedded+"\n"+"=".repeat(embedded.length)+"\n\n"
}
if(this._level == 2){
return "\n"+embedded+"\n"+"-".repeat(embedded.length)+"\n\n"
}
return "\n"+"#".repeat( this._level)+embedded +"\n\n";
}
}

View file

@ -1,43 +1,43 @@
import { UIEventSource } from "../../Logic/UIEventSource";
import {UIEventSource} from "../../Logic/UIEventSource";
import BaseUIElement from "../BaseUIElement";
export class VariableUiElement extends BaseUIElement {
private _element: HTMLElement;
private _element: HTMLElement;
constructor(
contents: UIEventSource<string | BaseUIElement | BaseUIElement[]>
) {
super();
constructor(
contents: UIEventSource<string | BaseUIElement | BaseUIElement[]>
) {
super();
this._element = document.createElement("span");
const el = this._element;
contents.addCallbackAndRun((contents) => {
while (el.firstChild) {
el.removeChild(el.lastChild);
}
this._element = document.createElement("span");
const el = this._element;
contents.addCallbackAndRun((contents) => {
while (el.firstChild) {
el.removeChild(el.lastChild);
}
if (contents === undefined) {
return el;
}
if (typeof contents === "string") {
el.innerHTML = contents;
} else if (contents instanceof Array) {
for (const content of contents) {
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);
}
}
});
}
if (contents === undefined) {
return el;
}
if (typeof contents === "string") {
el.innerHTML = contents;
} else if (contents instanceof Array) {
for (const content of contents) {
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);
}
}
});
}
protected InnerConstructElement(): HTMLElement {
return this._element;
}
protected InnerConstructElement(): HTMLElement {
return this._element;
}
}

View file

@ -13,7 +13,7 @@ export default abstract class BaseUIElement {
private style: string;
private _onClick: () => void;
private _onHover: UIEventSource<boolean>;
public onClick(f: (() => void)) {
this._onClick = f;
this.SetClass("clickable")
@ -22,7 +22,7 @@ export default abstract class BaseUIElement {
}
return this;
}
AttachTo(divId: string) {
let element = document.getElementById(divId);
if (element === null) {
@ -37,7 +37,7 @@ export default abstract class BaseUIElement {
if (el !== undefined) {
element.appendChild(el)
}
return this;
}

View file

@ -27,7 +27,7 @@ export default class AddNewMarker extends Combine {
}
}
}
if(icons.length === 1){
if (icons.length === 1) {
return icons[0]
}
icons.push(last)
@ -46,9 +46,9 @@ export default class AddNewMarker extends Combine {
.SetClass("absolute p-1 rounded-full overflow-hidden"),
Svg.addSmall_svg().SetClass("absolute animate-pulse").SetStyle("width: 30px; left: 30px; top: 35px;")
]).SetClass("absolute"),
new Combine([label]).SetStyle("position: absolute; left: 50%")
new Combine([label]).SetStyle("position: absolute; left: 50%")
])
this.SetClass("block relative");
this.SetClass("block relative");
}
}

View file

@ -9,8 +9,6 @@ import {DownloadPanel} from "./DownloadPanel";
import {SubtleButton} from "../Base/SubtleButton";
import Svg from "../../Svg";
import ExportPDF from "../ExportPDF";
import {Browser} from "leaflet";
import ie = Browser.ie;
import {FixedUiElement} from "../Base/FixedUiElement";
export default class AllDownloads extends ScrollableFullScreen {

View file

@ -18,26 +18,25 @@ export default class Attribution extends Combine {
userDetails: UIEventSource<UserDetails>,
layoutToUse: UIEventSource<LayoutConfig>,
leafletMap: UIEventSource<L.Map>) {
const mapComplete = new Link(`Mapcomplete ${Constants.vNumber}`, 'https://github.com/pietervdvn/MapComplete', true);
const reportBug = new Link(Svg.bug_ui().SetClass("small-image"), "https://github.com/pietervdvn/MapComplete/issues", true);
const layoutId = layoutToUse?.data?.id;
const now = new Date()
// Note: getMonth is zero-index, we want 1-index but with one substracted, so it checks out!
const startDate = now.getFullYear()+"-"+now.getMonth()+"-"+now.getDate()
const startDate = now.getFullYear() + "-" + now.getMonth() + "-" + now.getDate()
const osmChaLink = `https://osmcha.org/?filters=%7B%22comment%22%3A%5B%7B%22label%22%3A%22%23${layoutId}%22%2C%22value%22%3A%22%23${layoutId}%22%7D%5D%2C%22date__gte%22%3A%5B%7B%22label%22%3A%22${startDate}%22%2C%22value%22%3A%222020-07-05%22%7D%5D%2C%22editor%22%3A%5B%7B%22label%22%3A%22MapComplete%22%2C%22value%22%3A%22MapComplete%22%7D%5D%7D`
const stats = new Link(Svg.statistics_ui().SetClass("small-image"), osmChaLink, true)
const idLink = location.map(location => `https://www.openstreetmap.org/edit?editor=id#map=${location?.zoom ?? 0}/${location?.lat ?? 0}/${location?.lon ?? 0}`)
const idLink = location.map(location => `https://www.openstreetmap.org/edit?editor=id#map=${location?.zoom ?? 0}/${location?.lat ?? 0}/${location?.lon ?? 0}`)
const editHere = new Link(Svg.pencil_ui().SetClass("small-image"), idLink, true)
const mapillaryLink = location.map(location => `https://www.mapillary.com/app/?focus=map&lat=${location?.lat ?? 0}&lng=${location?.lon ?? 0}&z=${Math.max((location?.zoom ?? 2) - 1, 1)}`)
const mapillary = new Link(Svg.mapillary_black_ui().SetClass("small-image"), mapillaryLink, true);
let editWithJosm = new VariableUiElement(
userDetails.map(userDetails => {
@ -45,7 +44,7 @@ export default class Attribution extends Combine {
return undefined;
}
const bounds: any = leafletMap?.data?.getBounds();
if(bounds === undefined){
if (bounds === undefined) {
return undefined
}
const top = bounds.getNorth();

View file

@ -121,7 +121,7 @@ export default class AttributionPanel extends Combine {
})
]
).SetClass("block m-2")
]).SetClass("flex flex-col").SetStyle("width: calc(100% - 50px - 0.5em); min-width: 12rem;")
]).SetClass("flex flex-wrap border-b border-gray-300 m-2 border-box")
}

View file

@ -10,7 +10,7 @@ export default class BackgroundSelector extends VariableUiElement {
const available = State.state.availableBackgroundLayers.map(available => {
const baseLayers: { value: BaseLayer, shown: string }[] = [];
for (const i in available) {
if(!available.hasOwnProperty(i)){
if (!available.hasOwnProperty(i)) {
continue;
}
const layer: BaseLayer = available[i];

View file

@ -21,14 +21,14 @@ export default class FullWelcomePaneWithTabs extends ScrollableFullScreen {
constructor(isShown: UIEventSource<boolean>) {
const layoutToUse = State.state.layoutToUse.data;
super (
super(
() => layoutToUse.title.Clone(),
() => FullWelcomePaneWithTabs.GenerateContents(layoutToUse, State.state.osmConnection.userDetails, isShown),
"welcome" ,isShown
"welcome", isShown
)
}
private static ConstructBaseTabs(layoutToUse: LayoutConfig, isShown: UIEventSource<boolean>): { header: string | BaseUIElement; content: BaseUIElement }[]{
private static ConstructBaseTabs(layoutToUse: LayoutConfig, isShown: UIEventSource<boolean>): { header: string | BaseUIElement; content: BaseUIElement }[] {
let welcome: BaseUIElement = new ThemeIntroductionPanel(isShown);
if (layoutToUse.id === personal.id) {
@ -70,8 +70,8 @@ export default class FullWelcomePaneWithTabs extends ScrollableFullScreen {
);
return new Toggle(
new TabbedComponent(tabsWithAboutMc, State.state.welcomeMessageOpenedTab),
new TabbedComponent(tabs, State.state.welcomeMessageOpenedTab),
new TabbedComponent(tabsWithAboutMc, State.state.welcomeMessageOpenedTab),
new TabbedComponent(tabs, State.state.welcomeMessageOpenedTab),
userDetails.map((userdetails: UserDetails) =>
userdetails.loggedIn &&
userdetails.csCount >= Constants.userJourney.mapCompleteHelpUnlock)

View file

@ -40,7 +40,7 @@ export default class Histogram<T> extends VariableUiElement {
const keys = Array.from(counts.keys());
keys.sort()
const max = Math.max(...Array.from(counts.values()))
const fallbackColor = (keyValue: string) => {
@ -50,7 +50,7 @@ export default class Histogram<T> extends VariableUiElement {
let actualAssignColor = undefined;
if (assignColor === undefined) {
actualAssignColor = fallbackColor;
}else{
} else {
actualAssignColor = (keyValue: string) => {
return assignColor(keyValue) ?? fallbackColor(keyValue)
}
@ -61,7 +61,7 @@ export default class Histogram<T> extends VariableUiElement {
keys.map(key => [
key,
new Combine([
new Combine([new FixedUiElement("" + counts.get(key)).SetClass("font-bold rounded-full block")])
new Combine([new FixedUiElement("" + counts.get(key)).SetClass("font-bold rounded-full block")])
.SetClass("flex justify-center rounded border border-black")
.SetStyle(`background: ${actualAssignColor(key)}; width: ${100 * counts.get(key) / max}%`)
]).SetClass("block w-full")

View file

@ -24,7 +24,7 @@ export default class LeftControls extends Combine {
),
undefined
);
const copyrightButton = new Toggle(
toggledCopyright,
new MapControlButton(Svg.copyright_svg())
@ -61,7 +61,7 @@ export default class LeftControls extends Combine {
State.state.filterIsOpened
),
new MapControlButton(Svg.filter_svg())
.onClick(() => State.state.filterIsOpened.setData(true)),
.onClick(() => State.state.filterIsOpened.setData(true)),
State.state.filterIsOpened
)
@ -87,7 +87,7 @@ export default class LeftControls extends Combine {
super([filterButton,
downloadButtonn,
copyrightButton])
this.SetClass("flex flex-col")
}

View file

@ -3,8 +3,8 @@ import Translations from "../i18n/Translations";
import State from "../../State";
import {UIEventSource} from "../../Logic/UIEventSource";
export default class LicensePicker extends DropDown<string>{
export default class LicensePicker extends DropDown<string> {
constructor() {
super(Translations.t.image.willBePublished.Clone(),
[
@ -14,7 +14,7 @@ export default class LicensePicker extends DropDown<string>{
],
State.state?.osmConnection?.GetPreference("pictures-license") ?? new UIEventSource<string>("CC0")
)
this.SetClass("flex flex-col sm:flex-row").SetStyle("float:left");
this.SetClass("flex flex-col sm:flex-row").SetStyle("float:left");
}
}

View file

@ -30,22 +30,22 @@ export default class MoreScreen extends Combine {
.SetClass("absolute top-2 right-3"),
new IndexText()
]);
themeButtonStyle = "h-32 min-h-32 max-h-32 overflow-ellipsis overflow-hidden"
themeListStyle = "md:grid md:grid-flow-row md:grid-cols-2 lg:grid-cols-3 xl:grid-cols-g4 gap-4"
}
return[
return [
intro,
MoreScreen.createOfficialThemesList(state, themeButtonStyle).SetClass(themeListStyle),
MoreScreen.createUnofficialThemeList(themeButtonStyle)?.SetClass(themeListStyle),
tr.streetcomplete.Clone().SetClass("block text-base mx-10 my-3 mb-10")
];
}
private static createUnofficialThemeList(buttonClass: string): BaseUIElement{
private static createUnofficialThemeList(buttonClass: string): BaseUIElement {
return new VariableUiElement(State.state.installedThemes.map(customThemes => {
const els : BaseUIElement[] = []
const els: BaseUIElement[] = []
if (customThemes.length > 0) {
els.push(Translations.t.general.customThemeIntro.Clone())
@ -62,18 +62,18 @@ export default class MoreScreen extends Combine {
let officialThemes = AllKnownLayouts.layoutsList
let buttons = officialThemes.map((layout) => {
if(layout === undefined){
if (layout === undefined) {
console.trace("Layout is undefined")
return undefined
}
const button = MoreScreen.createLinkButton(layout)?.SetClass(buttonClass);
if(layout.id === personal.id){
if (layout.id === personal.id) {
return new VariableUiElement(
State.state.osmConnection.userDetails.map(userdetails => userdetails.csCount)
.map(csCount => {
if(csCount < Constants.userJourney.personalLayoutUnlock){
if (csCount < Constants.userJourney.personalLayoutUnlock) {
return undefined
}else{
} else {
return button
}
})
@ -131,7 +131,7 @@ export default class MoreScreen extends Combine {
}
const currentLocation = State.state.locationControl;
let path = window.location.pathname;
// Path starts with a '/' and contains everything, e.g. '/dir/dir/page.html'
path = path.substr(0, path.lastIndexOf("/"));
@ -155,15 +155,13 @@ export default class MoreScreen extends Combine {
const params = [
["z", currentLocation?.zoom],
["lat", currentLocation?.lat],
["lon",currentLocation?.lon]
["lon", currentLocation?.lon]
].filter(part => part[1] !== undefined)
.map(part => part[0]+"="+part[1])
.map(part => part[0] + "=" + part[1])
.join("&")
return `${linkPrefix}${params}${linkSuffix}`;
})
let description = Translations.WT(layout.shortDescription).Clone();
return new SubtleButton(layout.icon,

View file

@ -7,7 +7,6 @@ import {SubtleButton} from "../Base/SubtleButton";
import Translations from "../i18n/Translations";
import BaseUIElement from "../BaseUIElement";
import {VariableUiElement} from "../Base/VariableUIElement";
import Img from "../Base/Img";
import {UIEventSource} from "../../Logic/UIEventSource";
import LayerConfig from "../../Models/ThemeConfig/LayerConfig";
@ -62,18 +61,18 @@ export default class PersonalLayersPanel extends VariableUiElement {
* @private
*/
private static CreateLayerToggle(layer: LayerConfig): Toggle {
let icon :BaseUIElement =new Combine([ layer.GenerateLeafletStyle(
let icon: BaseUIElement = new Combine([layer.GenerateLeafletStyle(
new UIEventSource<any>({id: "node/-1"}),
false
).icon.html]).SetClass("relative")
let iconUnset =new Combine([ layer.GenerateLeafletStyle(
let iconUnset = new Combine([layer.GenerateLeafletStyle(
new UIEventSource<any>({id: "node/-1"}),
false
).icon.html]).SetClass("relative")
iconUnset.SetStyle("opacity:0.1")
let name = layer.name ;
let name = layer.name;
if (name === undefined) {
return undefined;
}
@ -90,7 +89,7 @@ export default class PersonalLayersPanel extends VariableUiElement {
return new Toggle(
new SubtleButton(
icon,
content ),
content),
new SubtleButton(
iconUnset,
contentUnselected

View file

@ -1,83 +1,82 @@
import Locale from "../i18n/Locale";
import { UIEventSource } from "../../Logic/UIEventSource";
import { Translation } from "../i18n/Translation";
import { VariableUiElement } from "../Base/VariableUIElement";
import {UIEventSource} from "../../Logic/UIEventSource";
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 {TextField} from "../Input/TextField";
import {Geocoding} from "../../Logic/Osm/Geocoding";
import Translations from "../i18n/Translations";
import Hash from "../../Logic/Web/Hash";
import Combine from "../Base/Combine";
export default class SearchAndGo extends Combine {
constructor() {
const goButton = Svg.search_ui().SetClass(
"w-8 h-8 full-rounded border-black float-right"
);
constructor() {
const goButton = Svg.search_ui().SetClass(
"w-8 h-8 full-rounded border-black float-right"
);
const placeholder = new UIEventSource<Translation>(
Translations.t.general.search.search
);
const searchField = new TextField({
placeholder: new VariableUiElement(placeholder),
value: new UIEventSource<string>(""),
inputStyle:
" background: transparent;\n" +
" border: none;\n" +
" font-size: large;\n" +
" width: 100%;\n" +
" height: 100%;\n" +
" box-sizing: border-box;\n" +
" color: var(--foreground-color);",
});
const placeholder = new UIEventSource<Translation>(
Translations.t.general.search.search
);
const searchField = new TextField({
placeholder: new VariableUiElement(placeholder),
value: new UIEventSource<string>(""),
inputStyle:
" background: transparent;\n" +
" border: none;\n" +
" font-size: large;\n" +
" width: 100%;\n" +
" height: 100%;\n" +
" box-sizing: border-box;\n" +
" color: var(--foreground-color);",
});
searchField.SetClass("relative float-left mt-0 ml-2");
searchField.SetStyle("width: calc(100% - 3em);height: 100%");
searchField.SetClass("relative float-left mt-0 ml-2");
searchField.SetStyle("width: calc(100% - 3em);height: 100%");
super([searchField, goButton]);
super([searchField, goButton]);
this.SetClass("block h-8");
this.SetStyle(
"background: var(--background-color); color: var(--foreground-color); pointer-evetns:all;"
);
this.SetClass("block h-8");
this.SetStyle(
"background: var(--background-color); color: var(--foreground-color); pointer-evetns:all;"
);
// Triggered by 'enter' or onclick
function runSearch() {
const searchString = searchField.GetValue().data;
if (searchString === undefined || searchString === "") {
return;
}
searchField.GetValue().setData("");
placeholder.setData(Translations.t.general.search.searching);
Geocoding.Search(
searchString,
(result) => {
console.log("Search result", result);
if (result.length == 0) {
placeholder.setData(Translations.t.general.search.nothing);
return;
}
// Triggered by 'enter' or onclick
function runSearch() {
const searchString = searchField.GetValue().data;
if (searchString === undefined || searchString === "") {
return;
}
searchField.GetValue().setData("");
placeholder.setData(Translations.t.general.search.searching);
Geocoding.Search(
searchString,
(result) => {
console.log("Search result", result);
if (result.length == 0) {
placeholder.setData(Translations.t.general.search.nothing);
return;
}
const poi = result[0];
const bb = poi.boundingbox;
const bounds: [[number, number], [number, number]] = [
[bb[0], bb[2]],
[bb[1], bb[3]],
];
State.state.selectedElement.setData(undefined);
Hash.hash.setData(poi.osm_type + "/" + poi.osm_id);
State.state.leafletMap.data.fitBounds(bounds);
placeholder.setData(Translations.t.general.search.search);
},
() => {
searchField.GetValue().setData("");
placeholder.setData(Translations.t.general.search.error);
const poi = result[0];
const bb = poi.boundingbox;
const bounds: [[number, number], [number, number]] = [
[bb[0], bb[2]],
[bb[1], bb[3]],
];
State.state.selectedElement.setData(undefined);
Hash.hash.setData(poi.osm_type + "/" + poi.osm_id);
State.state.leafletMap.data.fitBounds(bounds);
placeholder.setData(Translations.t.general.search.search);
},
() => {
searchField.GetValue().setData("");
placeholder.setData(Translations.t.general.search.error);
}
);
}
);
}
searchField.enterPressed.addCallback(runSearch);
goButton.onClick(runSearch);
}
searchField.enterPressed.addCallback(runSearch);
goButton.onClick(runSearch);
}
}

View file

@ -1,9 +1,9 @@
import BaseUIElement from "../BaseUIElement";
export default class ShareButton extends BaseUIElement{
export default class ShareButton extends BaseUIElement {
private _embedded: BaseUIElement;
private _shareData: () => { text: string; title: string; url: string };
constructor(embedded: BaseUIElement, generateShareData: () => {
text: string,
title: string,
@ -19,7 +19,7 @@ export default class ShareButton extends BaseUIElement{
const e = document.createElement("button")
e.type = "button"
e.appendChild(this._embedded.ConstructElement())
e.addEventListener('click', () => {
if (navigator.share) {
navigator.share(this._shareData()).then(() => {
@ -32,7 +32,7 @@ export default class ShareButton extends BaseUIElement{
console.log('web share not supported');
}
});
return e;
}

View file

@ -23,7 +23,7 @@ export default class ShareScreen extends Combine {
const optionCheckboxes: BaseUIElement[] = []
const optionParts: (UIEventSource<string>)[] = [];
function check() {
return Svg.checkmark_svg().SetStyle("width: 1.5em; display:inline-block;");
}
@ -48,7 +48,7 @@ export default class ShareScreen extends Combine {
if (includeL) {
return [["z", currentLocation.data?.zoom], ["lat", currentLocation.data?.lat], ["lon", currentLocation.data?.lon]]
.filter(p => p[1] !== undefined)
.map(p => p[0]+"="+p[1])
.map(p => p[0] + "=" + p[1])
.join("&")
} else {
return null;
@ -56,7 +56,7 @@ export default class ShareScreen extends Combine {
}, [currentLocation]));
function fLayerToParam(flayer: {isDisplayed: UIEventSource<boolean>, layerDef: LayerConfig}) {
function fLayerToParam(flayer: { isDisplayed: UIEventSource<boolean>, layerDef: LayerConfig }) {
if (flayer.isDisplayed.data) {
return null; // Being displayed is the default
}
@ -123,12 +123,12 @@ export default class ShareScreen extends Combine {
optionCheckboxes.push(checkbox);
optionParts.push(checkbox.isEnabled.map((isEn) => {
if (isEn) {
if(swtch.reverse){
return `${swtch.urlName}=true`
if (swtch.reverse) {
return `${swtch.urlName}=true`
}
return null;
} else {
if(swtch.reverse){
if (swtch.reverse) {
return null;
}
return `${swtch.urlName}=false`
@ -178,9 +178,7 @@ export default class ShareScreen extends Combine {
);
let editLayout : BaseUIElement= new FixedUiElement("");
let editLayout: BaseUIElement = new FixedUiElement("");
if ((layoutDefinition !== undefined && State.state?.osmConnection !== undefined)) {
editLayout =
new VariableUiElement(
@ -240,11 +238,11 @@ export default class ShareScreen extends Combine {
});
super ([
super([
editLayout,
tr.intro.Clone(),
link,
new VariableUiElement(linkStatus),
new VariableUiElement(linkStatus),
tr.addToHomeScreen.Clone(),
tr.embedIntro.Clone(),
options,

View file

@ -82,7 +82,7 @@ export default class SimpleAddUI extends Toggle {
createNewPoint(tags, location, undefined)
} else {
OsmObject.DownloadObject(snapOntoWayId).addCallbackAndRunD(way => {
createNewPoint(tags, location,<OsmWay> way)
createNewPoint(tags, location, <OsmWay>way)
return true;
})
}
@ -188,17 +188,17 @@ export default class SimpleAddUI extends Toggle {
])
)
.onClick(() => State.state.filterIsOpened.setData(true))
const openLayerOrConfirm = new Toggle(
confirmButton,
openLayerControl,
preset.layerToAddTo.isDisplayed
)
const disableFilter = new SubtleButton(
new Combine([
Svg.filter_ui().SetClass("absolute w-full"),
Svg.filter_ui().SetClass("absolute w-full"),
Svg.cross_bottom_right_svg().SetClass("absolute red-svg")
]).SetClass("relative"),
new Combine(
@ -211,7 +211,7 @@ export default class SimpleAddUI extends Toggle {
preset.layerToAddTo.appliedFilters.setData(new And([]))
cancel()
})
const disableFiltersOrConfirm = new Toggle(
openLayerOrConfirm,
disableFilter,
@ -220,9 +220,8 @@ export default class SimpleAddUI extends Toggle {
return filters === undefined || filters.normalize().and.length === 0;
})
)
const tagInfo = SimpleAddUI.CreateTagInfoFor(preset);
const cancelButton = new SubtleButton(Svg.close_ui(),
@ -230,7 +229,7 @@ export default class SimpleAddUI extends Toggle {
).onClick(cancel)
return new Combine([
// Translations.t.general.add.confirmIntro.Subs({title: preset.name}),
// Translations.t.general.add.confirmIntro.Subs({title: preset.name}),
State.state.osmConnection.userDetails.data.dryRun ?
Translations.t.general.testing.Clone().SetClass("alert") : undefined,
disableFiltersOrConfirm,

View file

@ -17,22 +17,22 @@ export default class ThemeIntroductionPanel extends VariableUiElement {
State.state.layoutToUse.map(layout => LanguagePicker.CreateLanguagePicker(layout.language, Translations.t.general.pickLanguage.Clone()))
)
;
const toTheMap = new SubtleButton(
undefined,
Translations.t.general.openTheMap.Clone().SetClass("text-xl font-bold w-full text-center")
).onClick(() =>{
).onClick(() => {
isShown.setData(false)
}).SetClass("only-on-mobile")
const plzLogIn =
new SubtleButton(
Svg.osm_logo_ui(),
new Combine([Translations.t.general.loginWithOpenStreetMap
.Clone().SetClass("text-xl font-bold"),
Translations.t.general.loginOnlyNeededToEdit.Clone().SetClass("font-bold")]
).SetClass("flex flex-col text-center w-full")
).SetClass("flex flex-col text-center w-full")
)
.onClick(() => {
State.state.osmConnection.AttemptLogin()
@ -40,8 +40,7 @@ export default class ThemeIntroductionPanel extends VariableUiElement {
const welcomeBack = Translations.t.general.welcomeBack.Clone();
const loginStatus =
new Toggle(
@ -55,7 +54,7 @@ export default class ThemeIntroductionPanel extends VariableUiElement {
)
super(State.state.layoutToUse.map (layout => new Combine([
super(State.state.layoutToUse.map(layout => new Combine([
layout.description.Clone(),
"<br/><br/>",
toTheMap,

View file

@ -6,43 +6,40 @@ import Translations from "../i18n/Translations";
/**
* Shows that 'images are uploading', 'all images are uploaded' as relevant...
*/
export default class UploadFlowStateUI extends VariableUiElement{
export default class UploadFlowStateUI extends VariableUiElement {
constructor(queue: UIEventSource<string[]>, failed: UIEventSource<string[]>, success: UIEventSource<string[]>) {
const t = Translations.t.image;
super(
queue.map(queue => {
const failedReasons = failed.data
const successCount = success.data.length
const pendingCount = queue.length - successCount - failedReasons.length;
let stateMessages : BaseUIElement[] = []
if(pendingCount == 1){
stateMessages.push(t.uploadingPicture.Clone().SetClass("alert"))
}
if(pendingCount > 1){
stateMessages.push(t.uploadingMultiple.Subs({count: ""+pendingCount}).SetClass("alert"))
}
if(failedReasons.length > 0){
stateMessages.push(t.uploadFailed.Clone().SetClass("alert"))
}
if(successCount > 0 && pendingCount == 0){
stateMessages.push(t.uploadDone.SetClass("thanks"))
}
stateMessages.forEach(msg => msg.SetStyle("display: block ruby"))
return stateMessages
}, [failed, success])
queue.map(queue => {
const failedReasons = failed.data
const successCount = success.data.length
const pendingCount = queue.length - successCount - failedReasons.length;
let stateMessages: BaseUIElement[] = []
if (pendingCount == 1) {
stateMessages.push(t.uploadingPicture.Clone().SetClass("alert"))
}
if (pendingCount > 1) {
stateMessages.push(t.uploadingMultiple.Subs({count: "" + pendingCount}).SetClass("alert"))
}
if (failedReasons.length > 0) {
stateMessages.push(t.uploadFailed.Clone().SetClass("alert"))
}
if (successCount > 0 && pendingCount == 0) {
stateMessages.push(t.uploadDone.SetClass("thanks"))
}
stateMessages.forEach(msg => msg.SetStyle("display: block ruby"))
return stateMessages
}, [failed, success])
);
}
}

View file

@ -105,12 +105,12 @@ export default class UserBadge extends Toggle {
userStats
]).SetClass("flex flex-col sm:w-auto sm:pl-2 overflow-hidden w-0")
const userIcon =
(user.img === undefined ? Svg.osm_logo_ui() : new Img(user.img)).SetClass("rounded-full opacity-0 m-0 p-0 duration-500 w-16 min-width-16 h16 float-left")
(user.img === undefined ? Svg.osm_logo_ui() : new Img(user.img)).SetClass("rounded-full opacity-0 m-0 p-0 duration-500 w-16 min-width-16 h16 float-left")
.onClick(() => {
if(usertext.HasClass("w-0")){
if (usertext.HasClass("w-0")) {
usertext.RemoveClass("w-0")
usertext.SetClass("w-min pl-2")
}else{
} else {
usertext.RemoveClass("w-min")
usertext.RemoveClass("pl-2")
usertext.SetClass("w-0")
@ -121,7 +121,7 @@ export default class UserBadge extends Toggle {
usertext,
userIcon,
]).SetClass("h-16 flex bg-white")
}
}));
@ -132,9 +132,9 @@ export default class UserBadge extends Toggle {
State.state.osmConnection.isLoggedIn
)
this.SetClass("shadow rounded-full h-min overflow-hidden block w-max")
this.SetClass("shadow rounded-full h-min overflow-hidden block w-max")
}

View file

@ -24,9 +24,9 @@ export default class CenterMessageBox extends VariableUiElement {
},
[updater.timeout, updater.sufficientlyZoomed, state.locationControl]
)
super(message.map(toShow => toShow.el))
this.SetClass("block " +
"rounded-3xl bg-white text-xl font-bold text-center pointer-events-none p-4")
this.SetStyle("transition: opacity 750ms linear")

View file

@ -25,6 +25,7 @@ import LayoutConfig from "../Models/ThemeConfig/LayoutConfig";
export default class ExportPDF {
// dimensions of the map in milimeter
public isRunning = new UIEventSource(true)
// A4: 297 * 210mm
private readonly mapW = 297;
private readonly mapH = 210;
@ -33,8 +34,6 @@ export default class ExportPDF {
private readonly _layout: UIEventSource<LayoutConfig>;
private _screenhotTaken = false;
public isRunning = new UIEventSource(true)
constructor(
options: {
freeDivId: string,
@ -62,8 +61,8 @@ export default class ExportPDF {
location: new UIEventSource<Loc>(loc), // We remove the link between the old and the new UI-event source as moving the map while the export is running fucks up the screenshot
background: options.background,
allowMoving: false,
onFullyLoaded: leaflet => window.setTimeout(() => {
if (self._screenhotTaken) {
return;

View file

@ -9,7 +9,7 @@ export class AttributedImage extends Combine {
constructor(urlSource: string, imgSource: ImageAttributionSource) {
urlSource = imgSource.PrepareUrl(urlSource)
super([
new Img( urlSource),
new Img(urlSource),
new Attribution(imgSource.GetAttributionFor(urlSource), imgSource.SourceIcon())
]);
this.SetClass('block relative h-full');

View file

@ -12,12 +12,12 @@ export default class Attribution extends VariableUiElement {
throw "No license source given in the attribution element"
}
super(
license.map((license : LicenseInfo) => {
license.map((license: LicenseInfo) => {
if (license?.artist === undefined) {
return undefined;
}
return new Combine([
icon?.SetClass("block left").SetStyle("height: 2em; width: 2em; padding-right: 0.5em;"),

View file

@ -16,8 +16,7 @@ export default class DeleteImage extends Toggle {
.SetClass("rounded-full p-1")
.SetStyle("color:white;background:#ff8c8c")
.onClick(() => {
State.state?.changes?.
applyAction(new ChangeTagAction(tags.data.id, new Tag(key, oldValue), tags.data))
State.state?.changes?.applyAction(new ChangeTagAction(tags.data.id, new Tag(key, oldValue), tags.data))
});
const deleteButton = Translations.t.image.doDelete.Clone()
@ -25,7 +24,7 @@ export default class DeleteImage extends Toggle {
.SetStyle("color:white;background:#ff8c8c; border-top-left-radius:30rem; border-top-right-radius: 30rem;")
.onClick(() => {
State.state?.changes?.applyAction(
new ChangeTagAction( tags.data.id, new Tag(key, ""), tags.data)
new ChangeTagAction(tags.data.id, new Tag(key, ""), tags.data)
)
});

View file

@ -46,7 +46,7 @@ export class ImageCarousel extends Toggle {
*/
private static CreateImageElement(url: string): BaseUIElement {
// @ts-ignore
let attrSource : ImageAttributionSource = undefined;
let attrSource: ImageAttributionSource = undefined;
if (url.startsWith("File:")) {
attrSource = Wikimedia.singleton
} else if (url.toLowerCase().startsWith("https://commons.wikimedia.org/wiki/")) {
@ -58,8 +58,8 @@ export class ImageCarousel extends Toggle {
} else {
return new Img(url);
}
return new AttributedImage(url, attrSource)
}
}

View file

@ -43,7 +43,7 @@ export class ImageUploadFlow extends Toggle {
Svg.camera_plus_ui().SetClass("block w-12 h-12 p-1"),
Translations.t.image.addPicture.Clone().SetClass("block align-middle mt-1 ml-3")
]).SetClass("p-2 border-4 border-black rounded-full text-4xl font-bold h-full align-middle w-full flex justify-center")
const fileSelector = new FileSelectorButton(label)
fileSelector.GetValue().addCallback(filelist => {
if (filelist === undefined) {
@ -97,7 +97,7 @@ export class ImageUploadFlow extends Toggle {
/* User not logged in*/ pleaseLoginButton,
State.state?.osmConnection?.isLoggedIn
),
undefined /* Nothing as the user badge is disabled*/,
undefined /* Nothing as the user badge is disabled*/,
State.state.featureSwitchUserbadge
)

View file

@ -10,9 +10,9 @@ export class SlideShow extends BaseUIElement {
constructor(embeddedElements: UIEventSource<BaseUIElement[]>) {
super()
this.embeddedElements =embeddedElements;
this.embeddedElements = embeddedElements;
this.SetStyle("scroll-snap-type: x mandatory; overflow-x: scroll")
}
}
protected InnerConstructElement(): HTMLElement {
const el = document.createElement("div")
@ -20,20 +20,20 @@ export class SlideShow extends BaseUIElement {
el.style.display = "flex"
el.style.justifyContent = "center"
this.embeddedElements.addCallbackAndRun(elements => {
if(elements.length > 1){
if (elements.length > 1) {
el.style.justifyContent = "unset"
}
while (el.firstChild) {
el.removeChild(el.lastChild)
}
elements = Utils.NoNull(elements).map(el => new Combine([el])
elements = Utils.NoNull(elements).map(el => new Combine([el])
.SetClass("block relative ml-1 bg-gray-200 m-1 rounded slideshow-item")
.SetStyle("min-width: 150px; width: max-content; height: var(--image-carousel-height);max-height: var(--image-carousel-height);scroll-snap-align: start;")
)
for (const element of elements ?? []) {
el.appendChild(element.ConstructElement())
}

View file

@ -1,6 +1,6 @@
import { InputElement } from "./InputElement";
import { UIEventSource } from "../../Logic/UIEventSource";
import { Utils } from "../../Utils";
import {InputElement} from "./InputElement";
import {UIEventSource} from "../../Logic/UIEventSource";
import {Utils} from "../../Utils";
import BaseUIElement from "../BaseUIElement";
/**

View file

@ -3,41 +3,41 @@ import {UIEventSource} from "../../Logic/UIEventSource";
export default class ColorPicker extends InputElement<string> {
IsSelected: UIEventSource<boolean> = new UIEventSource<boolean>(false);
private readonly value: UIEventSource<string>
private readonly _element : HTMLElement
private readonly _element: HTMLElement
constructor(
value: UIEventSource<string> = new UIEventSource<string>(undefined)
) {
super();
this.value = value ;
this.value = value;
const el = document.createElement("input")
this._element = el;
el.type = "color"
this.value.addCallbackAndRunD(v => {
el.value =v
el.value = v
});
el.oninput = () => {
const hex = el.value;
value.setData(hex);
}
}
protected InnerConstructElement(): HTMLElement {
return this._element;
}
GetValue(): UIEventSource<string> {
return this.value;
}
IsSelected: UIEventSource<boolean> = new UIEventSource<boolean>(false);
IsValid(t: string): boolean {
return false;
}
protected InnerConstructElement(): HTMLElement {
return this._element;
}
}

View file

@ -13,13 +13,12 @@ import Loc from "../../Models/Loc";
*/
export default class DirectionInput extends InputElement<string> {
public static constructMinimap: ((any) => BaseUIElement);
private readonly _location: UIEventSource<Loc>;
public readonly IsSelected: UIEventSource<boolean> = new UIEventSource<boolean>(false);
private readonly _location: UIEventSource<Loc>;
private readonly value: UIEventSource<string>;
private background;
constructor(mapBackground: UIEventSource<any>,
constructor(mapBackground: UIEventSource<any>,
location: UIEventSource<Loc>,
value?: UIEventSource<string>) {
super();
@ -49,10 +48,10 @@ export default class DirectionInput extends InputElement<string> {
}
const element = new Combine([
Svg.direction_stroke_svg().SetStyle(
Svg.direction_stroke_svg().SetStyle(
`position: absolute;top: 0;left: 0;width: 100%;height: 100%;transform:rotate(${this.value.data ?? 0}deg);`)
.SetClass("direction-svg relative")
.SetStyle("z-index: 1000"),
.SetStyle("z-index: 1000"),
map.SetStyle(
`position: absolute;top: 0;left: 0;width: 100%;height: 100%;`)

View file

@ -4,15 +4,15 @@ import Translations from "../i18n/Translations";
import BaseUIElement from "../BaseUIElement";
export class FixedInputElement<T> extends InputElement<T> {
public readonly IsSelected: UIEventSource<boolean> = new UIEventSource<boolean>(false);
private readonly value: UIEventSource<T>;
public readonly IsSelected : UIEventSource<boolean> = new UIEventSource<boolean>(false);
private readonly _comparator: (t0: T, t1: T) => boolean;
private readonly _el : HTMLElement;
constructor(rendering: BaseUIElement | string,
private readonly _el: HTMLElement;
constructor(rendering: BaseUIElement | string,
value: T,
comparator: ((t0: T, t1: T) => boolean ) = undefined) {
comparator: ((t0: T, t1: T) => boolean) = undefined) {
super();
this._comparator = comparator ?? ((t0, t1) => t0 == t1);
this.value = new UIEventSource<T>(value);
@ -21,19 +21,15 @@ export class FixedInputElement<T> extends InputElement<T> {
this._el = document.createElement("span")
this._el.addEventListener("mouseout", () => selected.setData(false))
const e = Translations.W(rendering)?.ConstructElement()
if(e){
this._el.appendChild( e)
if (e) {
this._el.appendChild(e)
}
this.onClick(() => {
selected.setData(true)
})
}
protected InnerConstructElement(): HTMLElement {
return this._el;
}
GetValue(): UIEventSource<T> {
return this.value;
}
@ -42,5 +38,9 @@ export class FixedInputElement<T> extends InputElement<T> {
return this._comparator(t, this.value.data);
}
protected InnerConstructElement(): HTMLElement {
return this._el;
}
}

View file

@ -1,11 +1,13 @@
import {UIEventSource} from "../../Logic/UIEventSource";
import BaseUIElement from "../BaseUIElement";
export abstract class InputElement<T> extends BaseUIElement{
abstract GetValue() : UIEventSource<T>;
export abstract class InputElement<T> extends BaseUIElement {
abstract IsSelected: UIEventSource<boolean>;
abstract IsValid(t: T) : boolean;
abstract GetValue(): UIEventSource<T>;
abstract IsValid(t: T): boolean;
}

View file

@ -6,17 +6,14 @@ import {Utils} from "../../Utils";
import Loc from "../../Models/Loc";
import {GeoOperations} from "../../Logic/GeoOperations";
import DirectionInput from "./DirectionInput";
import {RadioButton} from "./RadioButton";
import {FixedInputElement} from "./FixedInputElement";
/**
* Selects a length after clicking on the minimap, in meters
*/
export default class LengthInput extends InputElement<string> {
private readonly _location: UIEventSource<Loc>;
public readonly IsSelected: UIEventSource<boolean> = new UIEventSource<boolean>(false);
private readonly _location: UIEventSource<Loc>;
private readonly value: UIEventSource<string>;
private background;
@ -68,8 +65,8 @@ export default class LengthInput extends InputElement<string> {
this.RegisterTriggers(element, map?.leafletMap)
element.style.overflow = "hidden"
element.style.display = "block"
return element
return element
}
private RegisterTriggers(htmlElement: HTMLElement, leafletMap: UIEventSource<L.Map>) {
@ -77,7 +74,7 @@ export default class LengthInput extends InputElement<string> {
let firstClickXY: [number, number] = undefined
let lastClickXY: [number, number] = undefined
const self = this;
function onPosChange(x: number, y: number, isDown: boolean, isUp?: boolean) {
if (x === undefined || y === undefined) {
@ -115,7 +112,7 @@ export default class LengthInput extends InputElement<string> {
const measurementCrosshair = htmlElement.getElementsByClassName("length-crosshair-svg")[0] as HTMLElement
const measurementCrosshairInner: HTMLElement = <HTMLElement>measurementCrosshair.firstChild
if (firstClickXY === undefined) {
measurementCrosshair.style.visibility = "hidden"

View file

@ -14,7 +14,27 @@ import * as L from "leaflet";
export default class LocationInput extends InputElement<Loc> {
private static readonly matchLayout = new UIEventSource(new LayoutConfig({
description: "Matchpoint style",
icon: "./assets/svg/crosshair-empty.svg",
id: "matchpoint",
language: ["en"],
layers: [{
id: "matchpoint", source: {
osmTags: {and: []}
},
icon: "./assets/svg/crosshair-empty.svg"
}],
maintainer: "MapComplete",
startLat: 0,
startLon: 0,
startZoom: 0,
title: "Location input",
version: "0"
}));
IsSelected: UIEventSource<boolean> = new UIEventSource<boolean>(false);
public readonly snappedOnto: UIEventSource<any> = new UIEventSource<any>(undefined)
private _centerLocation: UIEventSource<Loc>;
private readonly mapBackground: UIEventSource<BaseLayer>;
private readonly _snapTo: UIEventSource<{ feature: any }[]>
@ -22,7 +42,6 @@ export default class LocationInput extends InputElement<Loc> {
private readonly _snappedPoint: UIEventSource<any>
private readonly _maxSnapDistance: number
private readonly _snappedPointTags: any;
public readonly snappedOnto: UIEventSource<any> = new UIEventSource<any>(undefined)
constructor(options: {
mapBackground?: UIEventSource<BaseLayer>,
@ -229,24 +248,4 @@ export default class LocationInput extends InputElement<Loc> {
}
}
private static readonly matchLayout = new UIEventSource(new LayoutConfig({
description: "Matchpoint style",
icon: "./assets/svg/crosshair-empty.svg",
id: "matchpoint",
language: ["en"],
layers: [{
id: "matchpoint", source: {
osmTags: {and: []}
},
icon: "./assets/svg/crosshair-empty.svg"
}],
maintainer: "MapComplete",
startLat: 0,
startLon: 0,
startZoom: 0,
title: "Location input",
version: "0"
}));
}

View file

@ -25,6 +25,19 @@ export class RadioButton<T> extends InputElement<T> {
this._dontStyle = options.dontStyle ?? false
}
IsValid(t: T): boolean {
for (const inputElement of this._elements) {
if (inputElement.IsValid(t)) {
return true;
}
}
return false;
}
GetValue(): UIEventSource<T> {
return this.value;
}
protected InnerConstructElement(): HTMLElement {
const elements = this._elements;
const selectFirstAsDefault = this._selectFirstAsDefault;
@ -166,19 +179,6 @@ export class RadioButton<T> extends InputElement<T> {
return form;
}
IsValid(t: T): boolean {
for (const inputElement of this._elements) {
if (inputElement.IsValid(t)) {
return true;
}
}
return false;
}
GetValue(): UIEventSource<T> {
return this.value;
}
/*
public ShowValue(t: T): boolean {
if (t === undefined) {

View file

@ -3,17 +3,17 @@ import {UIEventSource} from "../../Logic/UIEventSource";
export default class SimpleDatePicker extends InputElement<string> {
IsSelected: UIEventSource<boolean> = new UIEventSource<boolean>(false);
private readonly value: UIEventSource<string>
private readonly _element: HTMLElement;
constructor(
value?: UIEventSource<string>
) {
super();
this.value = value ?? new UIEventSource<string>(undefined);
const self = this;
const el = document.createElement("input")
this._element = el;
el.type = "date"
@ -30,17 +30,16 @@ export default class SimpleDatePicker extends InputElement<string> {
}
protected InnerConstructElement(): HTMLElement {
return this._element
}
GetValue(): UIEventSource<string> {
return this.value;
}
IsSelected: UIEventSource<boolean> = new UIEventSource<boolean>(false);
IsValid(t: string): boolean {
return false;
}
protected InnerConstructElement(): HTMLElement {
return this._element
}
}

View file

@ -66,7 +66,7 @@ export class TextField extends InputElement<string> {
this.value.addCallbackAndRunD(value => {
// We leave the textfield as is in the case of undefined or null (handled by addCallbackAndRunD) - make sure we do not erase it!
// We leave the textfield as is in the case of undefined or null (handled by addCallbackAndRunD) - make sure we do not erase it!
field["value"] = value;
if (self.IsValid(value)) {
self.RemoveClass("invalid")

View file

@ -6,7 +6,7 @@ import {VariableUiElement} from "../Base/VariableUIElement";
* The 'Toggle' is a UIElement showing either one of two elements, depending on the state.
* It can be used to implement e.g. checkboxes or collapsible elements
*/
export default class Toggle extends VariableUiElement{
export default class Toggle extends VariableUiElement {
public readonly isEnabled: UIEventSource<boolean>;
@ -16,11 +16,11 @@ export default class Toggle extends VariableUiElement{
);
this.isEnabled = isEnabled
}
public ToggleOnClick(): Toggle{
public ToggleOnClick(): Toggle {
const self = this;
this.onClick(() => {
self. isEnabled.setData(!self.isEnabled.data);
self.isEnabled.setData(!self.isEnabled.data);
})
return this;
}

View file

@ -118,15 +118,15 @@ export default class ValidatedTextField {
throw "Invalid zoom level for argument at 'length'-input"
}
}
// Bit of a hack: we project the centerpoint to the closes point on the road - if available
if(options.feature !== undefined && options.feature.geometry.type !== "Point"){
if (options.feature !== undefined && options.feature.geometry.type !== "Point") {
const lonlat: [number, number] = [...options.location]
lonlat.reverse()
options.location = <[number,number]> GeoOperations.nearestPoint(options.feature, lonlat).geometry.coordinates
options.location = <[number, number]>GeoOperations.nearestPoint(options.feature, lonlat).geometry.coordinates
options.location.reverse()
}
const location = new UIEventSource<Loc>({
lat: options.location[0],
lon: options.location[1],

View file

@ -3,16 +3,16 @@ import Locale from "./i18n/Locale";
import BaseUIElement from "./BaseUIElement";
export default class LanguagePicker {
public static CreateLanguagePicker(
languages : string[] ,
languages: string[],
label: string | BaseUIElement = "") {
if (languages.length <= 1) {
return undefined;
}
return new DropDown(label, languages.map(lang => {
return {value: lang, shown: lang}
}

View file

@ -5,16 +5,16 @@ import Combine from "./Base/Combine";
* A button floating above the map, in a uniform style
*/
export default class MapControlButton extends Combine {
constructor(contents: BaseUIElement, options?:{
dontStyle?: boolean
}) {
super([contents]);
if(!options?.dontStyle){
contents.SetClass("mapcontrol p-1")
constructor(contents: BaseUIElement, options?: {
dontStyle?: boolean
}) {
super([contents]);
if (!options?.dontStyle) {
contents.SetClass("mapcontrol p-1")
}
this.SetClass(
"relative block rounded-full w-10 h-10 p-1 pointer-events-auto z-above-map subtle-background m-0.5 md:m-1"
);
this.SetStyle("box-shadow: 0 0 10px var(--shadow-color);");
}
this.SetClass(
"relative block rounded-full w-10 h-10 p-1 pointer-events-auto z-above-map subtle-background m-0.5 md:m-1"
);
this.SetStyle("box-shadow: 0 0 10px var(--shadow-color);");
}
}

View file

@ -93,7 +93,7 @@ export class OH {
const queue = ohs.map(oh => {
if (oh.endHour === 0 && oh.endMinutes === 0) {
const newOh = {
...oh
...oh
}
newOh.endHour = 24
return newOh
@ -256,7 +256,7 @@ export class OH {
start?: string,
end?: string
} {
if(str === undefined){
if (str === undefined) {
return null
}
str = str.trim();

View file

@ -95,10 +95,6 @@ export default class OpeningHoursInput extends InputElement<string> {
])
}
protected InnerConstructElement(): HTMLElement {
return this._element.ConstructElement()
}
GetValue(): UIEventSource<string> {
return this._value;
}
@ -107,4 +103,8 @@ export default class OpeningHoursInput extends InputElement<string> {
return true;
}
protected InnerConstructElement(): HTMLElement {
return this._element.ConstructElement()
}
}

View file

@ -25,12 +25,11 @@ export default class OpeningHoursPickerTable extends InputElement<OpeningHour[]>
Translations.t.general.weekdays.abbreviations.sunday
]
public readonly IsSelected: UIEventSource<boolean>;
private readonly source: UIEventSource<OpeningHour[]>;
/*
These html-elements are an overlay over the table columns and act as a host for the ranges in the weekdays
*/
public readonly weekdayElements : HTMLElement[] = Utils.TimesT(7, () => document.createElement("div"))
public readonly weekdayElements: HTMLElement[] = Utils.TimesT(7, () => document.createElement("div"))
private readonly source: UIEventSource<OpeningHour[]>;
constructor(source?: UIEventSource<OpeningHour[]>) {
super();
@ -51,7 +50,7 @@ export default class OpeningHoursPickerTable extends InputElement<OpeningHour[]>
const table = document.createElement("table")
table.classList.add("oh-table")
const cellHeightInPx = 14;
const headerRow = document.createElement("tr")
@ -62,28 +61,26 @@ export default class OpeningHoursPickerTable extends InputElement<OpeningHour[]>
const cell = document.createElement("th")
cell.style.width = "14%"
cell.appendChild(weekday.ConstructElement())
const fullColumnSpan = this.weekdayElements[i]
fullColumnSpan.classList.add("w-full","relative")
fullColumnSpan.classList.add("w-full", "relative")
// We need to round! The table height is rounded as following, we use this to calculate the actual number of pixels afterwards
fullColumnSpan.style.height = ( (cellHeightInPx) * 48) + "px"
fullColumnSpan.style.height = ((cellHeightInPx) * 48) + "px"
const ranges = new VariableUiElement(
this.source.map(ohs => ohs.filter((oh : OpeningHour) => oh.weekday === i))
this.source.map(ohs => ohs.filter((oh: OpeningHour) => oh.weekday === i))
.map(ohsForToday => {
return new Combine(ohsForToday.map(oh => new OpeningHoursRange(oh, () =>{
return new Combine(ohsForToday.map(oh => new OpeningHoursRange(oh, () => {
this.source.data.splice(this.source.data.indexOf(oh), 1)
this.source.ping()
})))
})
)
fullColumnSpan.appendChild(ranges.ConstructElement())
const fullColumnSpanWrapper = document.createElement("div")
fullColumnSpanWrapper.classList.add("absolute")
fullColumnSpanWrapper.style.zIndex = "10"
@ -91,7 +88,7 @@ export default class OpeningHoursPickerTable extends InputElement<OpeningHour[]>
fullColumnSpanWrapper.style.pointerEvents = "none"
fullColumnSpanWrapper.appendChild(fullColumnSpan)
cell.appendChild(fullColumnSpanWrapper)
headerRow.appendChild(cell)
}
@ -115,7 +112,7 @@ export default class OpeningHoursPickerTable extends InputElement<OpeningHour[]>
cell.classList.add("oh-timecell", "oh-timecell-full", `oh-timecell-${weekday}`)
evenRow.appendChild(cell)
}
evenRow.style.height = (cellHeightInPx)+"px";
evenRow.style.height = (cellHeightInPx) + "px";
evenRow.style.maxHeight = evenRow.style.height;
evenRow.style.minHeight = evenRow.style.height;
table.appendChild(evenRow)
@ -133,7 +130,7 @@ export default class OpeningHoursPickerTable extends InputElement<OpeningHour[]>
table.appendChild(oddRow)
}
/**** Event handling below ***/

View file

@ -53,7 +53,7 @@ export default class OpeningHoursVisualization extends Toggle {
}
))
super(
super(
ohTable,
Translations.t.general.opening_hours.loadingCountry.Clone(),
tags.map(tgs => tgs._country !== undefined)
@ -141,7 +141,7 @@ export default class OpeningHoursVisualization extends Toggle {
OpeningHoursVisualization.CreateRangeElem(availableArea, earliestOpen, latestclose, range, isWeekstable)
)
const allRanges = new Combine([
...OpeningHoursVisualization.CreateLinesAtChangeHours(changeHours, availableArea, earliestOpen) ,
...OpeningHoursVisualization.CreateLinesAtChangeHours(changeHours, availableArea, earliestOpen),
...rangesForDay]).SetClass("w-full block");
let extraStyle = ""
@ -224,7 +224,7 @@ export default class OpeningHoursVisualization extends Toggle {
continue;
}
if (i > 0 && ((changeMoment - changeHours[i - 1]) / (60*60)) < 2) {
if (i > 0 && ((changeMoment - changeHours[i - 1]) / (60 * 60)) < 2) {
// Quite close to the previous value
// We alternate the heights
showHigherUsed = true;

View file

@ -75,10 +75,10 @@ export default class PublicHolidayInput extends InputElement<string> {
const value = this._value;
value.map(ph => OH.ParsePHRule(ph))
.addCallbackAndRunD(parsed => {
mode.setData(parsed.mode)
startTime.setData(parsed.start)
endTime.setData(parsed.end)
})
mode.setData(parsed.mode)
startTime.setData(parsed.start)
endTime.setData(parsed.end)
})
// We use this as a 'addCallbackAndRun'
mode.map(mode => {

View file

@ -60,7 +60,7 @@ export default class DeleteWizard extends Toggle {
}
(State.state?.changes ?? new Changes())
.applyAction(new ChangeTagAction(
id, new And(tagsToApply.map(kv => new Tag(kv.k, kv.v))), tagsSource.data
id, new And(tagsToApply.map(kv => new Tag(kv.k, kv.v))), tagsSource.data
))
}
@ -115,30 +115,30 @@ export default class DeleteWizard extends Toggle {
}
).SetClass("w-1/2 float-right");
const isShown = new UIEventSource<boolean>(id.indexOf("-")< 0)
const isShown = new UIEventSource<boolean>(id.indexOf("-") < 0)
super(
new Toggle(
new Combine([Svg.delete_icon_svg().SetClass("h-16 w-16 p-2 m-2 block bg-gray-300 rounded-full"),
t.isDeleted.Clone()]).SetClass("flex m-2 rounded-full"),
new Toggle(
new Combine([Svg.delete_icon_svg().SetClass("h-16 w-16 p-2 m-2 block bg-gray-300 rounded-full"),
t.isDeleted.Clone()]).SetClass("flex m-2 rounded-full"),
new Toggle(
new Toggle(
new Toggle(
question,
new SubtleButton(Svg.envelope_ui(), t.readMessages.Clone()),
State.state.osmConnection.userDetails.map(ud => ud.csCount > Constants.userJourney.addNewPointWithUnreadMessagesUnlock || ud.unreadMessages == 0)
),
new Toggle(
question,
new SubtleButton(Svg.envelope_ui(), t.readMessages.Clone()),
State.state.osmConnection.userDetails.map(ud => ud.csCount > Constants.userJourney.addNewPointWithUnreadMessagesUnlock || ud.unreadMessages == 0)
),
deleteButton,
confirm),
new VariableUiElement(deleteAction.canBeDeleted.map(cbd => new Combine([cbd.reason.Clone(), t.useSomethingElse.Clone()]))),
deleteAction.canBeDeleted.map(cbd => allowSoftDeletion || cbd.canBeDeleted !== false)),
deleteButton,
confirm),
new VariableUiElement(deleteAction.canBeDeleted.map(cbd => new Combine([cbd.reason.Clone(), t.useSomethingElse.Clone()]))),
deleteAction.canBeDeleted.map(cbd => allowSoftDeletion || cbd.canBeDeleted !== false)),
t.loginToDelete.Clone().onClick(State.state.osmConnection.AttemptLogin),
State.state.osmConnection.isLoggedIn
),
deleteAction.isDeleted),
t.loginToDelete.Clone().onClick(State.state.osmConnection.AttemptLogin),
State.state.osmConnection.isLoggedIn
),
deleteAction.isDeleted),
undefined,
isShown)

View file

@ -16,7 +16,7 @@ export default class EditableTagRendering extends Toggle {
configuration: TagRenderingConfig,
units: Unit [],
editMode = new UIEventSource<boolean>(false)
) {
) {
const answer: BaseUIElement = new TagRenderingAnswer(tags, configuration)
answer.SetClass("w-full")
let rendering = answer;
@ -30,7 +30,6 @@ export default class EditableTagRendering extends Toggle {
});
const answerWithEditButton = new Combine([answer,
new Toggle(editButton, undefined, State.state.osmConnection.isLoggedIn)])
.SetClass("flex justify-between w-full")
@ -47,7 +46,7 @@ export default class EditableTagRendering extends Toggle {
{
units: units,
cancelButton: cancelbutton,
afterSave: () => {
afterSave: () => {
editMode.setData(false)
}
})

View file

@ -66,8 +66,8 @@ export default class FeatureInfoBox extends ScrollableFullScreen {
}
return new EditableTagRendering(tags, tr, layerConfig.units);
});
let editElements : BaseUIElement[] = []
let editElements: BaseUIElement[] = []
if (!questionBoxIsUsed) {
editElements.push(questionBox);
}
@ -124,10 +124,10 @@ export default class FeatureInfoBox extends ScrollableFullScreen {
})
)
)
const editors = new VariableUiElement(State.state.featureSwitchUserbadge.map(
userbadge => {
if(!userbadge){
if (!userbadge) {
return undefined
}
return new Combine(editElements)

View file

@ -97,10 +97,10 @@ export default class SplitRoadWizard extends Toggle {
let hasRun = false
way.map(way => {
const partOf = partOfSrc.data
if(way === undefined || partOf === undefined){
if (way === undefined || partOf === undefined) {
return;
}
if(hasRun){
if (hasRun) {
return
}
hasRun = true
@ -108,7 +108,7 @@ export default class SplitRoadWizard extends Toggle {
<OsmWay>way, way.asGeoJson(), partOf, splitPoints.data.map(ff => ff.feature)
)
State.state.changes.applyAction(splitAction)
}, [partOfSrc])

View file

@ -54,7 +54,7 @@ export default class TagRenderingQuestion extends Combine {
const inputElement: InputElement<TagsFilter> = TagRenderingQuestion.GenerateInputElement(configuration, applicableUnit, tags)
if(inputElement === undefined){
if (inputElement === undefined) {
console.trace("MultiAnswer failed", configuration)
const inputElement0: InputElement<TagsFilter> = TagRenderingQuestion.GenerateInputElement(configuration, applicableUnit, tags)
@ -128,21 +128,22 @@ export default class TagRenderingQuestion extends Combine {
})
function allIfNotsExcept(excludeIndex: number) : TagsFilter[]{
if(configuration.mappings === undefined){
function allIfNotsExcept(excludeIndex: number): TagsFilter[] {
if (configuration.mappings === undefined) {
return []
}
if(configuration.multiAnswer){
if (configuration.multiAnswer) {
// The multianswer will do the ifnot configuration themself
return []
}
return Utils.NoNull(configuration.mappings?.map((m,i) => excludeIndex === i ? undefined: m.ifnot))
return Utils.NoNull(configuration.mappings?.map((m, i) => excludeIndex === i ? undefined : m.ifnot))
}
const ff = TagRenderingQuestion.GenerateFreeform(configuration, applicableUnit, tagsSource);
const hasImages = mappings.filter(mapping => mapping.then.ExtractImages().length > 0).length > 0
if (mappings.length < 8 || configuration.multiAnswer || hasImages) {
inputEls = (mappings ?? []).map((mapping,i) => TagRenderingQuestion.GenerateMappingElement(tagsSource, mapping, allIfNotsExcept(i)));
inputEls = (mappings ?? []).map((mapping, i) => TagRenderingQuestion.GenerateMappingElement(tagsSource, mapping, allIfNotsExcept(i)));
inputEls = Utils.NoNull(inputEls);
} else {
const dropdown: InputElement<TagsFilter> = new DropDown("",
@ -182,7 +183,7 @@ export default class TagRenderingQuestion extends Combine {
configuration: TagRenderingConfig,
elements: InputElement<TagsFilter>[], freeformField: InputElement<TagsFilter>, ifNotSelected: TagsFilter[]): InputElement<TagsFilter> {
const checkBoxes = new CheckBoxes(elements);
const inputEl = new InputElementMap<number[], TagsFilter>(
checkBoxes,
(t0, t1) => {
@ -350,19 +351,19 @@ export default class TagRenderingQuestion extends Combine {
input.GetValue().setData(tagsData[freeform.key] ?? freeform.default);
let inputTagsFilter : InputElement<TagsFilter> = new InputElementMap(
let inputTagsFilter: InputElement<TagsFilter> = new InputElementMap(
input, (a, b) => a === b || (a?.isEquivalent(b) ?? false),
pickString, toString
);
if(freeform.inline){
if (freeform.inline) {
inputTagsFilter.SetClass("w-16-imp")
inputTagsFilter = new InputElementWrapper(inputTagsFilter, configuration.render, freeform.key, tags)
inputTagsFilter.SetClass("block")
}
return inputTagsFilter;
}

View file

@ -7,7 +7,7 @@ import BaseUIElement from "../BaseUIElement";
import Img from "../Base/Img";
export default class SingleReview extends Combine {
constructor(review: Review) {
const d = review.date;
super(

View file

@ -56,10 +56,10 @@ export default class SpecialVisualizations {
if (!tags.hasOwnProperty(key)) {
continue;
}
parts.push([key , tags[key] ?? "<b>undefined</b>" ]);
parts.push([key, tags[key] ?? "<b>undefined</b>"]);
}
return new Table(
["key","value"],
["key", "value"],
parts
)
})).SetStyle("border: 1px solid black; border-radius: 1em;padding:1em;display:block;")
@ -130,7 +130,7 @@ export default class SpecialVisualizations {
// This is a list of values
idList = JSON.parse(value)
}
for (const id of idList) {
features.push({
freshness: new Date(),
@ -383,8 +383,9 @@ export default class SpecialVisualizations {
}
]
static HelpMessage: BaseUIElement = SpecialVisualizations.GenHelpMessage();
private static GenHelpMessage() {
const helpTexts =

View file

@ -1,6 +1,6 @@
import BaseUIElement from "./BaseUIElement";
export abstract class UIElement extends BaseUIElement{
export abstract class UIElement extends BaseUIElement {
/**
@ -24,7 +24,7 @@ export abstract class UIElement extends BaseUIElement{
}
return el;
}
protected abstract InnerRender(): string | BaseUIElement;
}

View file

@ -16,7 +16,7 @@ export default class Locale {
source.setData(language)
}
source.syncWith(
QueryParameters.GetQueryParameter("language", undefined,"The language to display mapcomplete in. Will be ignored in case a logged-in-user did set their language before. If the specified language does not exist, it will default to the first language in the theme."),
QueryParameters.GetQueryParameter("language", undefined, "The language to display mapcomplete in. Will be ignored in case a logged-in-user did set their language before. If the specified language does not exist, it will default to the first language in the theme."),
true
)
}

View file

@ -31,10 +31,10 @@ export class Translation extends BaseUIElement {
}
get txt(): string {
return this.textFor(Translation.forcedLanguage ?? Locale.language.data)
return this.textFor(Translation.forcedLanguage ?? Locale.language.data)
}
public textFor(language: string): string{
public textFor(language: string): string {
if (this.translations["*"]) {
return this.translations["*"];
}
@ -55,7 +55,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(_ => {
@ -73,7 +73,7 @@ export class Translation extends BaseUIElement {
if (translationsKey === "#") {
continue;
}
if(!this.translations.hasOwnProperty(translationsKey)){
if (!this.translations.hasOwnProperty(translationsKey)) {
continue
}
langs.push(translationsKey)
@ -112,7 +112,7 @@ export class Translation extends BaseUIElement {
} else if (el.ConstructElement === undefined) {
console.error("ConstructElement is not defined", el);
throw "ConstructElement is not defined, you are working with a " + (typeof el) + ":" + (el.constructor.name)
}else if(el["textFor"] !== undefined){
} else if (el["textFor"] !== undefined) {
// @ts-ignore
rtext = el.textFor(lang)
} else {
@ -195,5 +195,5 @@ export class Translation extends BaseUIElement {
}
return allIcons.filter(icon => icon != undefined)
}
}

View file

@ -5,57 +5,57 @@ import BaseUIElement from "../BaseUIElement";
export default class Translations {
static t = AllTranslationAssets.t;
private static wtcache = {}
constructor() {
throw "Translations is static. If you want to intitialize a new translation, use the singular form"
}
static t = AllTranslationAssets.t;
public static W(s: string | BaseUIElement): BaseUIElement {
if (typeof (s) === "string") {
return new FixedUiElement(s);
}
if(typeof s === "number"){
return new FixedUiElement(""+s)
if (typeof s === "number") {
return new FixedUiElement("" + s)
}
return s;
}
static T(t: string | any, context = undefined): Translation {
if(t === undefined || t === null){
if (t === undefined || t === null) {
return undefined;
}
if(typeof t === "string"){
return new Translation({"*":t}, context);
if (typeof t === "string") {
return new Translation({"*": t}, context);
}
if(t.render !== undefined){
if (t.render !== undefined) {
const msg = "Creating a translation, but this object contains a 'render'-field. Use the translation directly"
console.error(msg, t);
throw msg
}
if(t instanceof Translation){
if (t instanceof Translation) {
return t;
}
return new Translation(t, context);
}
private static wtcache = {}
public static WT(s: string | Translation): Translation {
if(s === undefined || s === null){
if (s === undefined || s === null) {
return undefined;
}
if (typeof (s) === "string") {
if(Translations.wtcache[s]){
if (Translations.wtcache[s]) {
return Translations.wtcache[s];
}
const tr = new Translation({en: s});
Translations.wtcache[s]= tr;
Translations.wtcache[s] = tr;
return tr;
}
if (s instanceof Translation) {
return s;
}
console.error("Trying to Translation.WT, but got ",s)
console.error("Trying to Translation.WT, but got ", s)
throw "??? Not a valid translation"
}