Fixes and simplification of the CSS

This commit is contained in:
Pieter Vander Vennet 2020-09-12 23:15:17 +02:00
parent c7f33a9490
commit 6d5f4ade25
24 changed files with 191 additions and 344 deletions

View file

@ -1,45 +1,23 @@
import {UIElement} from "../UIElement";
import Translations from "../i18n/Translations";
import {FixedUiElement} from "./FixedUiElement";
import {Utils} from "../../Utils";
export default class Combine extends UIElement {
private readonly uiElements: (string | UIElement)[];
private readonly className: string = undefined;
private readonly uiElements: UIElement[];
constructor(uiElements: (string | UIElement)[], className: string = undefined) {
super(undefined);
this.dumbMode = false;
this.className = className;
this.uiElements = uiElements;
if (className) {
console.error("Deprecated used of className")
}
constructor(uiElements: (string | UIElement)[]) {
super();
this.uiElements = Utils.NoNull(uiElements)
.map(el => {
if (typeof el === "string") {
return new FixedUiElement(el);
}
return el;
});
}
InnerRender(): string {
let elements = "";
for (const element of this.uiElements) {
if(element === undefined){
continue;
}
if (element instanceof UIElement) {
elements += element.Render();
} else {
elements += element;
}
}
if(this.className !== undefined){
elements = `<span class='${this.className}'>${elements}</span>`;
}
return elements;
return this.uiElements.map(ui => ui.Render()).join("");
}
InnerUpdate(htmlElement: HTMLElement) {
for (const element of this.uiElements) {
if (element instanceof UIElement) {
element.Update();
}
}
}
}

View file

@ -13,7 +13,9 @@ export class TabbedComponent extends UIElement {
for (let i = 0; i < elements.length; i++) {
let element = elements[i];
this.headers.push(Translations.W(element.header).onClick(() => self._source.setData(i)));
this.content.push(Translations.W(element.content));
const content = Translations.W(element.content)
this.ListenTo(content)
this.content.push(content);
}
}
@ -35,10 +37,4 @@ export class TabbedComponent extends UIElement {
return headerBar + "<div class='tab-content'>" + (content?.Render() ?? "") + "</div>";
}
protected InnerUpdate(htmlElement: HTMLElement) {
super.InnerUpdate(htmlElement);
this.content[this._source.data].Update();
}
}

View file

@ -3,13 +3,10 @@ import {UIEventSource} from "../../Logic/UIEventSource";
export class VariableUiElement extends UIElement {
private _html: UIEventSource<string>;
private _innerUpdate: (htmlElement: HTMLElement) => void;
constructor(html: UIEventSource<string>, innerUpdate : ((htmlElement : HTMLElement) => void) = undefined) {
constructor(html: UIEventSource<string>) {
super(html);
this._html = html;
this._innerUpdate = innerUpdate;
}
InnerRender(): string {

View file

@ -8,25 +8,40 @@ import Combine from "./Base/Combine";
*/
export class FullScreenMessageBox extends UIElement {
private static readonly _toTheMap_height : string = "5em";
private _uielement: UIElement;
private returnToTheMap: UIElement;
private readonly returnToTheMap: UIElement;
constructor(onClear: (() => void)) {
super(undefined);
const self = this;
State.state.fullScreenMessage.addCallback(uielement => {
return self._uielement = uielement?.SetClass("messagesboxmobile-scroll")?.Activate();
State.state.fullScreenMessage.addCallbackAndRun(uiElement => {
this._uielement = new Combine([State.state.fullScreenMessage.data]).SetStyle(
"display:block;"+
"padding: 1em;"+
"padding-bottom:5em;"+
`margin-bottom:${FullScreenMessageBox._toTheMap_height};`+
"box-sizing:border-box;"+
`height:calc(100vh - ${FullScreenMessageBox._toTheMap_height});`+
"overflow-y: auto;" +
"background:white;"
);
});
this._uielement = State.state.fullScreenMessage.data;
this.ListenTo(State.state.fullScreenMessage);
this.HideOnEmpty(true);
State.state.fullScreenMessage.addCallback(latestData => {
if (latestData === undefined) {
location.hash = "";
} else {
// The 'hash' makes sure a new piece of history is added. This makes the 'back-button' on android remove the popup
location.hash = "#element";
}
this.Update();
@ -42,8 +57,23 @@ export class FullScreenMessageBox extends UIElement {
}
}
this.returnToTheMap = Translations.t.general.returnToTheMap.Clone()
.SetClass("to-the-map")
this.returnToTheMap =
new Combine([Translations.t.general.returnToTheMap.Clone().SetStyle("font-size:xx-large")])
.SetStyle("background:#7ebc6f;" +
"position: fixed;" +
"z-index: 10000;" +
"bottom: 0;" +
"left: 0;" +
`height: ${FullScreenMessageBox._toTheMap_height};` +
"width: 100vw;" +
"color: white;" +
"font-weight: bold;" +
"pointer-events: all;" +
"cursor: pointer;" +
"padding-top: 1.2em;" +
"text-align: center;" +
"padding-bottom: 1.2em;" +
"box-sizing:border-box")
.onClick(() => {
console.log("Returning...")
State.state.fullScreenMessage.setData(undefined);
@ -55,10 +85,11 @@ export class FullScreenMessageBox extends UIElement {
InnerRender(): string {
if (this._uielement === undefined) {
if (State.state.fullScreenMessage.data === undefined) {
return "";
}
return new Combine([this._uielement, this.returnToTheMap]).Render();
return new Combine([this._uielement, this.returnToTheMap])
.Render();
}

View file

@ -49,6 +49,7 @@ export class ImageCarousel extends TagDependantUIElement {
private readonly _uiElements: UIEventSource<UIElement[]>;
private readonly _deleteButton: UIElement;
private readonly _confirmation: UIElement;
constructor(tags: UIEventSource<any>, osmConnection: OsmConnection = undefined) {
super(tags);
@ -76,74 +77,78 @@ export class ImageCarousel extends TagDependantUIElement {
return false;
}
return self.searcher.IsDeletable(self.searcher.data[i]);
}, [this.searcher, osmConnection?.userDetails]);
}, [this.searcher, osmConnection?.userDetails, this.slideshow._currentSlide]);
const isDeleted: UIEventSource<boolean> = this.slideshow._currentSlide.map((i: number) => {
return self.searcher._deletedImages.data.indexOf(self.searcher.data[i]) >= 0;
}, [this.searcher, this.searcher._deletedImages]);
this.slideshow._currentSlide.addCallback(() => {
showDeleteButton.ping(); // This pings the showDeleteButton, which indicates that it has to hide it's subbuttons
})
const deleteCurrent = () => {
self.searcher.Delete(self.searcher.data[self.slideshow._currentSlide.data]);
}
const isDeleted = self.searcher._deletedImages.data.indexOf(self.searcher.data[i]) >= 0;
console.log("Now deleted: ", i, isDeleted);
return isDeleted;
}, [this.searcher, this.searcher._deletedImages, this.slideshow._currentSlide]);
const style = ";padding:0.4em;height:2em;padding: 0.4em; font-weight:bold;";
const backButton = Translations.t.image.dontDelete
.SetStyle("background:black;border-radius:0.4em 0.4em 0 0" + style)
const deleteButton = Translations.t.image.doDelete
.SetStyle("background:#ff8c8c;border-radius:0 0 0.4em 0.4em" + style)
.onClick(deleteCurrent);
const deleteButton =
Translations.t.image.doDelete
.SetStyle("background:#ff8c8c;border-radius:0 0 0.4em 0.4em" + style);
this._confirmation = deleteButton;
const isDeletedBadge = Translations.t.image.isDeleted
.SetStyle("display:block;" +
"background-color: black;color:white;padding:0.4em;border-radius:0.4em");
const confirmDialog = new Combine([
backButton,
deleteButton]
).SetStyle("display:flex;" +
"flex-direction:column;" +
"background:black;" +
"color:white;" +
"border-radius:0.5em;" +
"width:max-content;" +
"height:min-content;");
const smallDeleteButton = new FixedUiElement("<img style='width:1.5em' src='./assets/delete.svg'>")
.SetStyle("display:block;" +
"width: 1.5em;" +
"height: 1.5em;" +
"padding: 0.5em;" +
"border-radius: 3em;" +
"background-color: black;")
const deleteButtonCheckbox = new CheckBox(
new Combine([
backButton,
deleteButton]
).SetStyle("display:flex;" +
"flex-direction:column;" +
"background:black;" +
"color:white;" +
"border-radius:0.5em;" +
"width:max-content;" +
"height:min-content;"),
confirmDialog,
new VariableUiElement(
showDeleteButton.map(showDelete => {
if (isDeleted.data) {
return Translations.t.image.isDeleted
.SetStyle("display:block;" +
"background-color: black;color:white;padding:0.4em;border-radius:0.4em").Render()
return isDeletedBadge.Render()
}
if (!showDelete) {
return "";
}
return new FixedUiElement("<img style='width:1.5em' src='./assets/delete.svg'>")
.SetStyle("display:block;" +
"width: 1.5em;" +
"height: 1.5em;" +
"padding: 0.5em;" +
"border-radius: 3em;" +
"background-color: black;").Render();
return smallDeleteButton.Render();
}, [this.searcher._deletedImages, isDeleted]
)));
deleteButton.onClick(() => {
console.log("Deleting image...");
deleteButtonCheckbox.isEnabled.setData(false);
deleteButtonCheckbox.Update();
self.searcher.Delete(self.searcher.data[self.slideshow._currentSlide.data]);
});
isDeleted.addCallback(isD => {
if(isD){
deleteButtonCheckbox.isEnabled.setData(false);
}
})
this._deleteButton = deleteButtonCheckbox;
this._deleteButton.SetStyle(
"position:absolute;display:block;top:1em;left:5em;z-index: 7000;width:min-content;height:min-content;"
)
this.slideshow._currentSlide.addCallback(() => {
deleteButtonCheckbox.isEnabled.setData(false)
})
this.searcher._deletedImages.addCallback(() => {
this.slideshow._currentSlide.ping();
})
}
@ -171,10 +176,5 @@ export class ImageCarousel extends TagDependantUIElement {
}
Activate() {
super.Activate();
this.searcher.Activate();
return this;
}
}

View file

@ -50,19 +50,6 @@ class ImageCarouselWithUpload extends TagDependantUIElement {
this._pictureUploader.Render();
}
Activate() {
super.Activate();
this._imageElement.Activate();
this._pictureUploader.Activate();
return this;
}
Update() {
super.Update();
this._imageElement.Update();
this._pictureUploader.Update();
}
IsKnown(): boolean {
return true;
}

View file

@ -1,16 +1,11 @@
import {UIElement} from "./UIElement";
import $ from "jquery"
import {UserDetails} from "../Logic/Osm/OsmConnection";
import {DropDown} from "./Input/DropDown";
import {VariableUiElement} from "./Base/VariableUIElement";
import Translations from "./i18n/Translations";
import {fail} from "assert";
import Combine from "./Base/Combine";
import {VerticalCombine} from "./Base/VerticalCombine";
import {State} from "../State";
import {UIEventSource} from "../Logic/UIEventSource";
import {Imgur} from "../Logic/Web/Imgur";
import {SubtleButton} from "./Base/SubtleButton";
export class ImageUploadFlow extends UIElement {
private _licensePicker: UIElement;
@ -107,9 +102,20 @@ export class ImageUploadFlow extends UIElement {
const label = new Combine([
"<img style='width: 36px;height: 36px;padding: 0.1em;margin-top: 5px;border-radius: 0;float: left;' src='./assets/camera-plus.svg'/> ",
Translations.t.image.addPicture
.SetStyle("width:max-content;font-size: 28px;font-weight: bold;float: left;margin-top: 4px;padding-top: 4px;padding-bottom: 4px;padding-left: 13px;"),
.SetStyle("width:max-content;font-size: 28px;" +
"font-weight: bold;" +
"float: left;" +
"margin-top: 4px;" +
"padding-top: 4px;" +
"padding-bottom: 4px;" +
"padding-left: 13px;"),
]).SetStyle(" display: flex;cursor:pointer;padding: 0.5em;border-radius: 1em;border: 3px solid black;box-sizing:border-box;")
]).SetStyle(" display: flex;" +
"cursor:pointer;" +
"padding: 0.5em;" +
"border-radius: 1em;" +
"border: 3px solid black;" +
"box-sizing:border-box;")
const actualInputElement =
`<input style='display: none' id='fileselector-${this.id}' type='file' accept='image/*' name='picField' multiple='multiple' alt=''/>`;

View file

@ -1,20 +1,19 @@
import {UIElement} from "../UIElement";
import { FilteredLayer } from "../../Logic/FilteredLayer";
import Translations from "../../UI/i18n/Translations";
import {UIEventSource} from "../../Logic/UIEventSource";
export class CheckBox extends UIElement{
public readonly isEnabled: UIEventSource<boolean>;
private readonly _showEnabled: string|UIElement;
private readonly _showDisabled: string|UIElement;
private readonly _showEnabled: UIElement;
private readonly _showDisabled: UIElement;
constructor(showEnabled: string | UIElement, showDisabled: string | UIElement, data: UIEventSource<boolean> | boolean = false) {
super(undefined);
this.isEnabled =
data instanceof UIEventSource ? data : new UIEventSource(data ?? false);
this.ListenTo(this.isEnabled);
this._showEnabled = showEnabled;
this._showDisabled = showDisabled;
this._showEnabled = Translations.W(showEnabled);
this._showDisabled =Translations.W(showDisabled);
const self = this;
this.onClick(() => {
self.isEnabled.setData(!self.isEnabled.data);

View file

@ -178,8 +178,10 @@ export class TextField<T> extends InputElement<T> {
return `<textarea id="text-${this.id}" class="form-text-field" rows="${this._textAreaRows}" cols="50" style="max-width: 100%; width: 100%; box-sizing: border-box"></textarea>`
}
const placeholder = this._placeholder.InnerRender().replace("'", "&#39");
return `<form onSubmit='return false' class='form-text-field'>` +
`<input type='text' placeholder='${this._placeholder.InnerRender()}' id='text-${this.id}'>` +
`<input type='text' placeholder='${placeholder}' id='text-${this.id}'>` +
`</form>`;
}
@ -246,11 +248,5 @@ export class TextField<T> extends InputElement<T> {
return result !== undefined && result !== null;
}
Clear() {
const field = document.getElementById('text-' + this.id);
if (field !== undefined) {
// @ts-ignore
field.value = "";
}
}
}
}

View file

@ -44,13 +44,13 @@ export class SearchAndGo extends UIElement {
// Triggered by 'enter' or onclick
private RunSearch() {
const searchString = this._searchField.GetValue().data;
this._searchField.Clear();
this._searchField.GetValue().setData("");
this._placeholder.setData(Translations.t.general.search.searching);
const self = this;
Geocoding.Search(searchString, (result) => {
if (result.length == 0) {
this._placeholder.setData(Translations.t.general.search.nothing);
self._placeholder.setData(Translations.t.general.search.nothing);
return;
}
@ -60,10 +60,11 @@ export class SearchAndGo extends UIElement {
[bb[1], bb[3]]
]
State.state.bm.map.fitBounds(bounds);
this._placeholder.setData(Translations.t.general.search.search);
self._placeholder.setData(Translations.t.general.search.search);
},
() => {
this._placeholder.setData(Translations.t.general.search.error);
self._searchField.GetValue().setData("");
self._placeholder.setData(Translations.t.general.search.error);
});
}
@ -74,15 +75,5 @@ export class SearchAndGo extends UIElement {
}
Update() {
super.Update();
this._searchField.Update();
this._goButton.Update();
}
Activate() {
super.Activate();
this._searchField.Activate();
this._goButton.Activate();
}
}

View file

@ -8,8 +8,8 @@ export class SlideShow extends UIElement {
public readonly _currentSlide: UIEventSource<number> = new UIEventSource<number>(0);
private readonly _noimages: UIElement;
private _prev: FixedUiElement;
private _next: FixedUiElement;
private _prev: UIElement;
private _next: UIElement;
constructor(
embeddedElements: UIEventSource<UIElement[]>,
@ -75,18 +75,4 @@ export class SlideShow extends UIElement {
this._currentSlide.setData(index);
}
InnerUpdate(htmlElement) {
this._next.Update();
this._prev.Update();
}
Activate() {
for (const embeddedElement of this._embeddedElements.data) {
embeddedElement.Activate();
}
this._next.Update();
this._prev.Update();
return this;
}
}
}

View file

@ -179,11 +179,11 @@ export class TagRendering extends UIElement implements TagDependantUIElement {
}
const cancelContents = this._editMode.map((isEditing) => {
if (isEditing) {
return "<span class='skip-button'>" + Translations.t.general.cancel.R() + "</span>";
} else {
return "<span class='skip-button'>" + Translations.t.general.skip.R() + "</span>";
}
const tr = Translations.t.general;
const text = isEditing ? tr.cancel : tr.skip;
return text
.SetStyle("display: inline-block;border: solid black 0.5px;padding: 0.2em 0.3em;border-radius: 1.5em;")
.Render();
}, [Locale.language]);
// And at last, set up the skip button
this._skipButton = new VariableUiElement(cancelContents).onClick(cancel);

View file

@ -28,6 +28,7 @@ export abstract class UIElement extends UIEventSource<string> {
this.id = "ui-element-" + UIElement.nextId;
this._source = source;
UIElement.nextId++;
this.dumbMode = true;
this.ListenTo(source);
}
@ -74,32 +75,18 @@ export abstract class UIElement extends UIEventSource<string> {
let element = document.getElementById(this.id);
if (element === undefined || element === null) {
// The element is not painted
// The element is not painted or, in the case of 'dumbmode' this UI-element is not explicitely present
if (this.dumbMode) {
// We update all the children anyway
for (const i in this) {
const child = this[i];
if (child instanceof UIElement) {
child.Update();
} else if (child instanceof Array) {
for (const ch of child) {
if (ch instanceof UIElement) {
ch.Update();
}
}
}
}
this.UpdateAllChildren();
}
return;
}
const newRender = this.InnerRender();
if (newRender !== this.lastInnerRender) {
this.lastInnerRender = newRender;
this.setData(this.InnerRender());
element.innerHTML = this.data;
this.lastInnerRender = newRender;
}
if (this._hideIfEmpty) {
@ -132,7 +119,11 @@ export abstract class UIElement extends UIEventSource<string> {
}
this.InnerUpdate(element);
this.UpdateAllChildren();
}
private UpdateAllChildren() {
for (const i in this) {
const child = this[i];
if (child instanceof UIElement) {
@ -146,27 +137,32 @@ export abstract class UIElement extends UIEventSource<string> {
}
}
}
HideOnEmpty(hide : boolean){
HideOnEmpty(hide: boolean) {
this._hideIfEmpty = hide;
this.Update();
return this;
}
// Called after the HTML has been replaced. Can be used for css tricks
protected InnerUpdate(htmlElement: HTMLElement) {
}
protected InnerUpdate(htmlElement: HTMLElement) {
}
Render(): string {
this.lastInnerRender = this.lastInnerRender ?? this.InnerRender();
this.lastInnerRender = this.InnerRender();
if (this.dumbMode) {
return this.lastInnerRender;
}
let style = "";
if (this.style !== undefined && this.style !== "") {
style = `style="${this.style}"`;
style = `style="${this.style}" `;
}
return `<span class='uielement ${this.clss.join(" ")}' ${style} id='${this.id}'>${this.lastInnerRender}</span>`
const clss = "";
if (this.clss.length > 0) {
`class='${this.clss.join(" ")}' `;
}
return `<span ${clss}${style}id='${this.id}'>${this.lastInnerRender}</span>`
}
AttachTo(divId: string) {
@ -182,32 +178,20 @@ export abstract class UIElement extends UIEventSource<string> {
public abstract InnerRender(): string;
public Activate(): UIElement {
for (const i in this) {
const child = this[i];
if (child instanceof UIElement) {
child.Activate();
} else if (child instanceof Array) {
for (const ch of child) {
if (ch instanceof UIElement) {
ch.Activate();
}
}
}
}
return this;
};
public IsEmpty(): boolean {
return this.InnerRender() === "";
}
public SetClass(clss: string): UIElement {
this.dumbMode = false;
if(clss === "" && this.clss.length > 0){
this.clss = [];
this.Update();
}
if (this.clss.indexOf(clss) < 0) {
this.clss.push(clss);
this.Update();
}
this.Update();
return this;
}

View file

@ -41,6 +41,9 @@ export default class Translation extends UIElement {
get txt(): string {
if(this.translations["*"]){
return this.translations["*"];
}
const txt = this.translations[Translation.forcedLanguage ?? Locale.language.data];
if (txt !== undefined) {
return txt;
@ -79,10 +82,6 @@ export default class Translation extends UIElement {
return result;
}
public R(): string {
return new Translation(this.translations).Render();
}
public Clone() {
return new Translation(this.translations)
}