Derping around, bit of a dead end due to transform3d in a parent

This commit is contained in:
Pieter Vander Vennet 2021-01-22 01:03:50 +01:00
commit 81f0a21076
52 changed files with 1717 additions and 14115 deletions

View file

@ -25,7 +25,11 @@ export default class Combine extends UIElement {
console.error("Not a UI-element", ui);
return "";
}
return ui.Render();
let rendered = ui.Render();
if(ui.IsEmpty()){
return "";
}
return rendered;
}).join("");
}

View file

@ -12,5 +12,4 @@ export class FixedUiElement extends UIElement {
return this._html;
}
}

View file

@ -4,16 +4,16 @@ import {Utils} from "../../Utils";
export default class Img {
public static runningFromConsole = false;
static AsData(source:string){
if(Utils.runningFromConsole){
return source;
}
return `data:image/svg+xml;base64,${(btoa(source))}`;
}
static AsImageElement(source: string): string{
return `<img src="${Img.AsData(source)}">`;
static AsImageElement(source: string, css_class: string = ""): string{
return `<img class="${css_class}" alt="" src="${Img.AsData(source)}">`;
}
}

View file

@ -8,28 +8,30 @@ import Ornament from "./Ornament";
* Wraps some contents into a panel that scrolls the content _under_ the title
*/
export default class ScrollableFullScreen extends UIElement {
private _component: Combine;
private _component: UIElement;
constructor(title: UIElement, content: UIElement, onClose: (() => void)) {
super();
const returnToTheMap = Svg.back_svg().onClick(() => {
onClose();
}).SetClass("sm:hidden")
.SetClass("featureinfobox-back-to-the-map")
title.SetStyle("width: 100%; display: block;")
const ornament = new Combine([new Ornament().SetStyle("height:5em;")])
.SetClass("block sm:hidden")
}) .SetClass("block sm:hidden mb-2 bg-blue-50 rounded-full w-12 h-12 p-1.5")
this._component = new Combine([
title.SetClass("block w-full")
const ornament = new Combine([new Ornament().SetStyle("height:5em;")])
.SetClass("block sm:hidden h-5")
this._component = new Combine([
new Combine([returnToTheMap, title])
.SetClass("text-xl break-words"),
new Combine([content, ornament])
])
this.SetClass("fixed h-screen w-screen fixed sm:relative");
.AddClass("border-b-2 border-black shadow sm:shadow-none z-50 bg-white p-2 pb-0 sm:p-0 flex overflow-x-hidden flex-shrink-0 max-h-20vh"),
new Combine(["<span>", content, "</span>", ornament])
.SetClass("block p-2 sm:pt-4 w-full max-h-screen landscape:max-h-screen overflow-y-auto overflow-x-hidden"),
// We add an ornament which takes around 5em. This is in order to make sure the Web UI doesn't hide
]).SetClass("block flex flex-col fixed max-h-screen sm:max-h-65vh sm:relative top-0 left-0 right-0");
}
InnerRender(): string {
return this._component.Render();

View file

@ -14,20 +14,25 @@ export class SubtleButton extends UIElement{
this.linkTo = linkTo;
this.message = Translations.W(message);
if(this.message !== null){
this.message.dumbMode = false;
this.message.dumbMode = false;
}
let img;
if ((imageUrl ?? "") === "") {
this.image = new FixedUiElement("");
img = new FixedUiElement("");
} else if (typeof (imageUrl) === "string") {
this.image = new FixedUiElement(`<img style="height:3em" src="${imageUrl}">`);
img = new FixedUiElement(`<img style="width: 100%;" src="${imageUrl}" alt="">`);
} else {
this.image = imageUrl;
img = imageUrl;
}
img.AddClass("block flex items-center justify-center h-11 w-11 flex-shrink0")
this.image = new Combine([img])
.AddClass("flex-shrink-0");
}
InnerRender(): string {
if(this.message !== null && this.message.IsEmpty()){
// Message == null: special case to force empty text
return "";
@ -35,19 +40,21 @@ export class SubtleButton extends UIElement{
if(this.linkTo != undefined){
return new Combine([
`<a class="subtle-button" href="${this.linkTo.url}" ${this.linkTo.newTab ? 'target="_blank"' : ""}>`,
`<a class='block flex group p-3 my-2 bg-blue-100 rounded-lg hover:shadow-xl hover:bg-blue-200' href="${this.linkTo.url}" ${this.linkTo.newTab ? 'target="_blank"' : ""}>`,
this.image,
`<div class='ml-4'>`,
this.message,
'</a>'
`</div>`,
`</a>`
]).Render();
}
// Styling todo
return new Combine([
'<span class="subtle-button">',
this.image,
this.message,
'</span>'
]).Render();
]).AddClass("block flex p-3 my-2 bg-blue-100 rounded-lg hover:shadow-xl hover:bg-blue-200")
.Render();
}

View file

@ -70,7 +70,6 @@ export default class FullWelcomePaneWithTabs extends UIElement {
const tabbedPart = new TabbedComponent(tabs, State.state.welcomeMessageOpenedTab)
.ListenTo(this._userDetails);
this._component = new ScrollableFullScreen(
layoutToUse.title,

View file

@ -0,0 +1,28 @@
import {UIElement} from "../UIElement";
import Combine from "../Base/Combine";
import Translations from "../i18n/Translations";
import {FixedUiElement} from "../Base/FixedUiElement";
export default class IndexText extends Combine {
constructor() {
super([
new FixedUiElement(`<img class="w-12 h-12 sm:h-24 sm:w-24" src="./assets/svg/logo.svg" alt="MapComplete Logo">`)
.AddClass("flex-none m-3"),
new Combine([
Translations.t.index.title
.AddClass("text-2xl tracking-tight font-extrabold text-gray-900 sm:text-5xl md:text-6xl block text-gray-800 xl:inline"),
Translations.t.index.intro.AddClass(
"mt-3 text-base font-semibold text-gray-500 sm:mt-5 sm:text-lg sm:max-w-xl sm:mx-auto md:mt-5 md:text-xl lg:mx-0"),
Translations.t.index.pickTheme.AddClass("mt-3 text-base text-green-600 sm:mt-5 sm:text-lg sm:max-w-xl sm:mx-auto md:mt-5 md:text-xl lg:mx-0")
]).AddClass("flex flex-col sm:text-center lg:text-left m-1 mt-2 md:m-2 md:mt-4")
]);
this.AddClass("flex flex-row");
}
}

View file

@ -29,7 +29,7 @@ export default class LayerControlPanel extends UIElement {
}
const title =Translations.t.general.layerSelection.title.SetClass("featureinfobox-title")
const title = Translations.t.general.layerSelection.title.SetClass("text-2xl break-words font-bold p-2")
this._panel = new ScrollableFullScreen(title, layerControlPanel, () => {
onClose

View file

@ -11,13 +11,14 @@ import Translations from "../i18n/Translations";
import * as personal from "../../assets/themes/personalLayout/personalLayout.json"
import Constants from "../../Models/Constants";
import LanguagePicker from "../LanguagePicker";
import IndexText from "./IndexText";
export default class MoreScreen extends UIElement {
private readonly _onMainScreen: boolean;
private _component: UIElement;
constructor(onMainScreen: boolean = false) {
super(State.state.locationControl);
this._onMainScreen = onMainScreen;
@ -65,15 +66,14 @@ export default class MoreScreen extends UIElement {
}
let description = Translations.W(layout.shortDescription);
if (description !== undefined) {
description = new Combine(["<br/>", description]);
}
return new SubtleButton(layout.icon,
new Combine([
"<b>",
`<dt class='text-lg leading-6 font-medium text-gray-900 group-hover:text-blue-800'>`,
Translations.W(layout.title),
"</b>",
`</dt>`,
`<dd class='mt-1 text-base text-gray-500 group-hover:text-blue-900'>`,
description ?? "",
`</dd>`,
]), {url: linkText, newTab: false});
}
@ -83,21 +83,10 @@ export default class MoreScreen extends UIElement {
const els: UIElement[] = []
els.push(new VariableUiElement(
State.state.osmConnection.userDetails.map(userDetails => {
if (userDetails.csCount < Constants.userJourney.themeGeneratorReadOnlyUnlock) {
return tr.requestATheme.Render();
}
return new SubtleButton(Svg.pencil_ui(), tr.createYourOwnTheme, {
url: "./customGenerator.html",
newTab: false
}).Render();
})
));
const linkButton: UIElement[] = []
for (const k in AllKnownLayouts.allSets) {
const layout : LayoutConfig = AllKnownLayouts.allSets[k];
const layout: LayoutConfig = AllKnownLayouts.allSets[k];
if (k === personal.id) {
if (State.state.osmConnection.userDetails.data.csCount < Constants.userJourney.personalLayoutUnlock) {
continue;
@ -106,12 +95,25 @@ export default class MoreScreen extends UIElement {
if (layout.id !== k) {
continue; // This layout was added multiple time due to an uppercase
}
els.push(this.createLinkButton(layout));
linkButton.push(this.createLinkButton(layout));
}
els.push(new Combine(linkButton))
els.push(new VariableUiElement(
State.state.osmConnection.userDetails.map(userDetails => {
if (userDetails.csCount < Constants.userJourney.themeGeneratorReadOnlyUnlock) {
return tr.requestATheme.SetClass("block text-base mx-10 my-3").Render();
}
return new SubtleButton(Svg.pencil_ui(), tr.createYourOwnTheme, {
url: "./customGenerator.html",
newTab: false
}).Render();
})
));
const customThemesNames = State.state.installedThemes.data ?? [];
if (customThemesNames.length > 0) {
els.push(Translations.t.general.customThemeIntro)
@ -123,18 +125,19 @@ export default class MoreScreen extends UIElement {
let intro : UIElement= tr.intro;
if(this._onMainScreen){
intro = new Combine([
LanguagePicker.CreateLanguagePicker(Translations.t.general.index.SupportedLanguages())
.SetStyle("position: absolute; right: 1.5em; top: 1.5em;"),
Translations.t.general.index.SetStyle("margin-top: 2em;display:block; margin-bottom: 1em;")
])
}
this._component = new VerticalCombine([
intro,
new VerticalCombine(els),
tr.streetcomplete
LanguagePicker.CreateLanguagePicker(Translations.t.index.title.SupportedLanguages())
.SetClass("absolute top-2 right-3 dropdown-ui-element-2226"),
new IndexText()
])
}
this._component = new Combine([
intro,
new Combine(els),
tr.streetcomplete.SetClass("block text-base mx-10 my-3 mb-10")
]);
return this._component.Render();
}

View file

@ -22,7 +22,7 @@ export default class SearchAndGo extends UIElement {
);
private _foundEntries = new UIEventSource([]);
private _goButton = Svg.search_ui().SetClass('search-go');
private _goButton = Svg.search_ui().AddClass('w-8 h-8 full-rounded border-black float-right');
constructor() {
super(undefined);

View file

@ -29,7 +29,7 @@ export default class UserBadge extends UIElement {
this._loginButton = Translations.t.general.loginWithOpenStreetMap
.Clone()
.SetClass("userbadge-login")
.SetClass("userbadge-login pt-3 w-full")
.onClick(() => State.state.osmConnection.AttemptLogin());
this._logout =
Svg.logout_svg()
@ -50,7 +50,7 @@ export default class UserBadge extends UIElement {
this._homeButton = new VariableUiElement(
this._userDetails.map((userinfo) => {
if (userinfo.home) {
return Svg.home;
return Svg.home_svg().Render();
}
return "";
})

View file

@ -40,7 +40,6 @@ export default class CenterMessageBox extends UIElement {
return CenterMessageBox.prep().innerHtml;
}
InnerUpdate(htmlElement: HTMLElement) {
const pstyle = htmlElement.parentElement.style;
if (State.state.centerMessage.data != "") {

View file

@ -20,14 +20,12 @@ export default class FullScreenMessageBox extends UIElement {
return "";
}
this._content = State.state.fullScreenMessage.data.content;
return new Combine([this._content]).SetClass("fullscreenmessage-content").Render();
return new Combine([this._content])
.SetClass("block max-h-screen h-screen overflow-x-hidden overflow-y-auto bg-white p-0").Render();
}
protected InnerUpdate(htmlElement: HTMLElement) {
super.InnerUpdate(htmlElement);
// This is a bit out of place, and it is a fix specifically for the featureinfobox-titlebar
const height = htmlElement.getElementsByClassName("featureinfobox-titlebar")[0]?.clientHeight ?? 0;
htmlElement.style.setProperty("--variable-title-height", height + "px")
}

View file

@ -28,5 +28,5 @@ export default class CheckBox extends UIElement{
return Translations.W(this._showDisabled).Render();
}
}
}

View file

@ -11,13 +11,19 @@ export class DropDown<T> extends InputElement<T> {
private readonly _value: UIEventSource<T>;
public IsSelected: UIEventSource<boolean> = new UIEventSource<boolean>(false);
private readonly _label_class: string;
private readonly _select_class: string;
constructor(label: string | UIElement,
values: { value: T, shown: string | UIElement }[],
value: UIEventSource<T> = undefined) {
value: UIEventSource<T> = undefined,
label_class: string = "",
select_class: string = "") {
super(undefined);
this._value = value ?? new UIEventSource<T>(undefined);
this._label = Translations.W(label);
this._label_class = label_class || '';
this._select_class = select_class || '';
this._values = values.map(v => {
return {
value: v.value,
@ -29,10 +35,8 @@ export class DropDown<T> extends InputElement<T> {
this.ListenTo(v.shown._source);
}
this.ListenTo(this._value);
this.onClick(() => {}) // by registering a click, the click event is consumed and doesn't bubble furter to other elements, e.g. checkboxes
this.onClick(() => {}) // by registering a click, the click event is consumed and doesn't bubble furter to other elements, e.g. checkboxes
}
GetValue(): UIEventSource<T> {
@ -52,18 +56,18 @@ export class DropDown<T> extends InputElement<T> {
if(this._values.length <=1){
return "";
}
let options = "";
for (let i = 0; i < this._values.length; i++) {
options += "<option value='" + i + "'>" + this._values[i].shown.InnerRender() + "</option>"
}
return "<form>" +
"<label for='dropdown-" + this.id + "'>" + this._label.Render() + " </label>" +
"<select name='dropdown-" + this.id + "' id='dropdown-" + this.id + "'>" +
return `<form>` +
`<label class='${this._label_class}' for='dropdown-${this.id}'>${this._label.Render()}</label>` +
`<select class='${this._select_class}' name='dropdown-${this.id}' id='dropdown-${this.id}'>` +
options +
"</select>" +
"</form>";
`</select>` +
`</form>`;
}
protected InnerUpdate(element) {

View file

@ -11,7 +11,7 @@ export class RadioButton<T> extends InputElement<T> {
private readonly value: UIEventSource<T>;
private readonly _elements: InputElement<T>[]
private readonly _selectFirstAsDefault: boolean;
constructor(elements: InputElement<T>[],
selectFirstAsDefault = true) {
super(undefined);
@ -74,14 +74,14 @@ export class RadioButton<T> extends InputElement<T> {
for (let i = 0; i < this._elements.length; i++){
const el = this._elements[i];
const htmlElement =
'<input type="radio" id="' + this.IdFor(i) + '" name="radiogroup-' + this.id + '">' +
'<label for="' + this.IdFor(i) + '">' + el.Render() + '</label>' +
'<br>';
`<label for="${this.IdFor(i)}" class="question-option-with-border">` +
`<input type="radio" id="${this.IdFor(i)}" name="radiogroup-${this.id}">` +
el.Render() +
`</label>`;
body += htmlElement;
}
return "<form id='" + this.id + "-form'>" + body + "</form>";
return `<form id='${this.id}-form'>${body}</form>`;
}
public ShowValue(t: T): boolean {

View file

@ -13,10 +13,10 @@ export default class LanguagePicker {
return undefined;
}
return new DropDown(label, languages.map(lang => {
return new DropDown("Change Language", languages.map(lang => {
return {value: lang, shown: lang}
}
), Locale.language);
), Locale.language, 'sr-only', 'bg-indigo-100 p-1 rounded hover:bg-indigo-200');
}

View file

@ -64,7 +64,7 @@ export default class EditableTagRendering extends UIElement {
return new Combine([this._answer,
(State.state?.osmConnection?.userDetails?.data?.loggedIn ?? true) ? this._editButton : undefined
]).SetClass("answer")
]).SetClass("flex w-full break-word justify-between text-default landscape:w-1/2 landscape:p-2 pb-2 border-b border-gray-300 mb-2")
.Render();
}

View file

@ -13,7 +13,7 @@ export default class FeatureInfoBox extends UIElement {
private _component: UIElement;
public title: UIElement ;
constructor(
tags: UIEventSource<any>,
layerConfig: LayerConfig,
@ -26,11 +26,12 @@ export default class FeatureInfoBox extends UIElement {
const title = new TagRenderingAnswer(tags, layerConfig.title ?? new TagRenderingConfig("POI", undefined))
.SetClass("featureinfobox-title");
.AddClass("text-2xl break-words font-bold p-2");
this.title = title;
const titleIcons = new Combine(
layerConfig.titleIcons.map(icon => new TagRenderingAnswer(tags, icon)))
.SetClass("featureinfobox-icons");
layerConfig.titleIcons.map(icon => new TagRenderingAnswer(tags, icon)
.AddClass("block w-8 h-8 align-baseline box-content p-0.5")))
.AddClass("flex flex-row flex-wrap pt-1 items-center mr-2");
let questionBox: UIElement = undefined;
if (State.state.featureSwitchUserbadge.data) {
@ -58,7 +59,7 @@ export default class FeatureInfoBox extends UIElement {
]
)
const titleBar = new Combine([
new Combine([title, titleIcons]).SetClass("featureinfobox-titlebar-title")
new Combine([title, titleIcons]).SetClass("flex flex-grow justify-between")
])
this._component = new ScrollableFullScreen(titleBar, content, onClose)

View file

@ -40,7 +40,6 @@ export default class QuestionBox extends UIElement {
})
));
this._skippedQuestionsButton = Translations.t.general.skippedQuestions.Clone()
.onClick(() => {
self._skippedQuestions.setData([]);
@ -64,19 +63,19 @@ export default class QuestionBox extends UIElement {
}
}
}
if (tagRendering.GetRenderValue(this._tags.data) !== undefined) {
// This value is known and can be rendered
return true;
}
return false;
}
InnerRender(): string {
for (let i = 0; i < this._tagRenderingQuestions.length; i++) {
let tagRendering = this._tagRenderings[i];
if(this.IsKnown(tagRendering)){
continue;
}

View file

@ -21,6 +21,7 @@ export default class TagRenderingAnswer extends UIElement {
if (configuration === undefined) {
throw "Trying to generate a tagRenderingAnswer without configuration..."
}
this.AddClass("flex items-center flex-row text-lg")
}
InnerRender(): string {
@ -34,11 +35,6 @@ export default class TagRenderingAnswer extends UIElement {
if (tags === undefined) {
return "";
}
const tr = this._configuration.GetRenderValue(tags);
if (tr !== undefined) {
this._content = new SubstitutedTranslation(tr, this._tags);
return this._content.Render();
}
// The render value doesn't work well with multi-answers (checkboxes), so we have to check for them manually
if (this._configuration.multiAnswer) {
@ -65,6 +61,14 @@ export default class TagRenderingAnswer extends UIElement {
return this._content.Render();
}
}
const tr = this._configuration.GetRenderValue(tags);
if (tr !== undefined) {
this._content = new SubstitutedTranslation(tr, this._tags);
return this._content.Render();
}
return "";
}

View file

@ -78,7 +78,6 @@ export default class TagRenderingQuestion extends UIElement {
if (csCount < Constants.userJourney.tagsVisibleAt) {
return "";
}
if (tags === undefined) {
return Translations.t.general.noTagsSelected.SetClass("subtle").Render();
}
@ -89,8 +88,7 @@ export default class TagRenderingQuestion extends UIElement {
return tags.asHumanString(true, true);
}
)
)
).AddClass("block")
}
private GenerateInputElement(): InputElement<TagsFilter> {
@ -104,9 +102,6 @@ export default class TagRenderingQuestion extends UIElement {
return ff;
}
mappings = Utils.NoNull([...mappings, ff]);
mappings.forEach(el => el.SetClass("question-option-with-border"))
if (this._configuration.multiAnswer) {
return this.GenerateMultiAnswer(mappings, ff)
} else {
@ -268,16 +263,16 @@ export default class TagRenderingQuestion extends UIElement {
}
InnerRender(): string {
return new Combine([
this._question,
this._inputElement, "<br/>",
this._inputElement,
this._cancelButton,
this._saveButton, "<br/>",
this._appliedTags])
.SetClass("question")
.Render()
this._saveButton,
this._appliedTags]
)
.SetClass("question")
.Render()
}
}

View file

@ -87,11 +87,11 @@ export default class ReviewForm extends InputElement<Review> {
}
InnerRender(): string {
if(!this.userDetails.data.loggedIn){
return Translations.t.reviews.plz_login.Render();
}
return new Combine([
new Combine([this._stars, this._postingAs]).SetClass("review-form-top"),
this._comment,

View file

@ -69,7 +69,7 @@ export default class ShowDataLayer {
action();
}
});
Hash.hash.addCallback(id => {
Hash.hash.addCallbackAndRun(id => {
// This is a bit of an edge case: if the hash becomes an id to search, we have to show the corresponding popup
if(State.state.selectedElement !== undefined){
return; // Something is already selected, we don't have to apply this fix
@ -130,6 +130,9 @@ export default class ShowDataLayer {
"<div style='height: 90vh'>Rendering</div>");
popup.setContent(uiElement.Render());
popup.on('remove', () => {
if(!popup.isOpen()){
return;
}
State.state.selectedElement.setData(undefined);
});
leafletLayer.bindPopup(popup);

View file

@ -1,5 +1,4 @@
import {UIEventSource} from "../Logic/UIEventSource";
import Constants from "../Models/Constants";
import {Utils} from "../Utils";
export abstract class UIElement extends UIEventSource<string> {
@ -8,7 +7,7 @@ export abstract class UIElement extends UIEventSource<string> {
public readonly id: string;
public readonly _source: UIEventSource<any>;
public dumbMode = false;
private clss: string[] = []
private clss: Set<string> = new Set<string>();
private style: string;
private _hideIfEmpty = false;
private lastInnerRender: string;
@ -110,7 +109,7 @@ export abstract class UIElement extends UIEventSource<string> {
}
HideOnEmpty(hide: boolean) {
HideOnEmpty(hide: boolean): UIElement {
this._hideIfEmpty = hide;
this.Update();
return this;
@ -127,8 +126,8 @@ export abstract class UIElement extends UIEventSource<string> {
style = `style="${this.style}" `;
}
let clss = "";
if (this.clss.length > 0) {
clss = `class='${this.clss.join(" ")}' `;
if (this.clss.size > 0) {
clss = `class='${Array.from(this.clss).join(" ")}' `;
}
return `<span ${clss}${style}id='${this.id}'>${this.lastInnerRender}</span>`
}
@ -151,20 +150,34 @@ export abstract class UIElement extends UIEventSource<string> {
}
public SetClass(clss: string): UIElement {
return this.AddClass(clss);
}
/**
* Adds all the relevant classes, space seperated
* @param clss
* @constructor
*/
public AddClass(clss: string) {
this.dumbMode = false;
if (clss === "" && this.clss.length > 0) {
throw "Use RemoveClass instead";
} else if (this.clss.indexOf(clss) < 0) {
this.clss.push(clss);
const all = clss.split(" ");
let recordedChange = false;
for (const c of all) {
if (this.clss.has(clss)) {
continue;
}
this.clss.add(c);
recordedChange = true;
}
if (recordedChange) {
this.Update();
}
return this;
}
public RemoveClass(clss: string): UIElement {
const i = this.clss.indexOf(clss);
if (i >= 0) {
this.clss.splice(i, 1);
if (this.clss.has(clss)) {
this.clss.delete(clss);
this.Update();
}
return this;