forked from MapComplete/MapComplete
Add binoculars theme, auto reformat everything
This commit is contained in:
parent
38dea806c5
commit
78d6482c88
586 changed files with 115573 additions and 111842 deletions
|
@ -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")
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
||||
}
|
|
@ -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;
|
||||
}
|
||||
|
||||
}
|
|
@ -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})`;
|
||||
}
|
||||
|
||||
}
|
|
@ -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"
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
}
|
|
@ -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>");
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
||||
}
|
|
@ -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")
|
||||
}
|
||||
|
|
|
@ -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";
|
||||
}
|
||||
}
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
||||
|
|
|
@ -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");
|
||||
}
|
||||
|
||||
}
|
|
@ -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 {
|
||||
|
|
|
@ -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();
|
||||
|
|
|
@ -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")
|
||||
}
|
||||
|
|
|
@ -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];
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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")
|
||||
|
|
|
@ -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")
|
||||
|
||||
}
|
||||
|
|
|
@ -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");
|
||||
}
|
||||
|
||||
|
||||
}
|
|
@ -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,
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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])
|
||||
);
|
||||
|
||||
|
||||
|
||||
|
||||
}
|
||||
|
||||
}
|
|
@ -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")
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
|
|
@ -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")
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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');
|
||||
|
|
|
@ -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;"),
|
||||
|
||||
|
|
|
@ -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)
|
||||
)
|
||||
});
|
||||
|
||||
|
|
|
@ -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)
|
||||
|
||||
|
||||
}
|
||||
}
|
|
@ -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
|
||||
)
|
||||
|
||||
|
|
|
@ -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())
|
||||
}
|
||||
|
|
|
@ -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";
|
||||
|
||||
/**
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
||||
}
|
|
@ -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%;`)
|
||||
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
||||
|
||||
}
|
|
@ -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;
|
||||
|
||||
}
|
||||
|
||||
|
|
|
@ -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"
|
||||
|
|
|
@ -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"
|
||||
|
||||
}));
|
||||
|
||||
}
|
|
@ -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) {
|
||||
|
|
|
@ -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
|
||||
}
|
||||
|
||||
}
|
|
@ -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")
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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],
|
||||
|
|
|
@ -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}
|
||||
}
|
||||
|
|
|
@ -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);");
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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();
|
||||
|
|
|
@ -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()
|
||||
}
|
||||
|
||||
}
|
|
@ -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 ***/
|
||||
|
||||
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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 => {
|
||||
|
|
|
@ -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)
|
||||
|
||||
|
|
|
@ -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)
|
||||
}
|
||||
})
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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])
|
||||
|
||||
|
||||
|
|
|
@ -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;
|
||||
|
||||
}
|
||||
|
|
|
@ -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(
|
||||
|
|
|
@ -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 =
|
||||
|
|
|
@ -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;
|
||||
|
||||
}
|
||||
|
|
|
@ -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
|
||||
)
|
||||
}
|
||||
|
|
|
@ -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)
|
||||
}
|
||||
|
||||
|
||||
}
|
|
@ -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"
|
||||
}
|
||||
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue