forked from MapComplete/MapComplete
Butchering the UI framework
This commit is contained in:
parent
8d404b1ba9
commit
6415e195d1
90 changed files with 1012 additions and 3101 deletions
|
@ -1,11 +1,11 @@
|
|||
import {UIElement} from "../UIElement";
|
||||
import {FixedUiElement} from "./FixedUiElement";
|
||||
import {Utils} from "../../Utils";
|
||||
import BaseUIElement from "../BaseUIElement";
|
||||
|
||||
export default class Combine extends UIElement {
|
||||
private readonly uiElements: UIElement[];
|
||||
export default class Combine extends BaseUIElement {
|
||||
private readonly uiElements: BaseUIElement[];
|
||||
|
||||
constructor(uiElements: (string | UIElement)[]) {
|
||||
constructor(uiElements: (string | BaseUIElement)[]) {
|
||||
super();
|
||||
this.uiElements = Utils.NoNull(uiElements)
|
||||
.map(el => {
|
||||
|
@ -15,18 +15,21 @@ export default class Combine extends UIElement {
|
|||
return el;
|
||||
});
|
||||
}
|
||||
|
||||
protected InnerConstructElement(): HTMLElement {
|
||||
const el = document.createElement("span")
|
||||
|
||||
InnerRender(): string {
|
||||
return this.uiElements.map(ui => {
|
||||
if(ui === undefined || ui === null){
|
||||
return "";
|
||||
for (const subEl of this.uiElements) {
|
||||
if(subEl === undefined || subEl === null){
|
||||
continue;
|
||||
}
|
||||
if(ui.Render === undefined){
|
||||
console.error("Not a UI-element", ui);
|
||||
return "";
|
||||
const subHtml = subEl.ConstructElement()
|
||||
if(subHtml !== undefined){
|
||||
el.appendChild(subHtml)
|
||||
}
|
||||
return ui.Render();
|
||||
}).join("");
|
||||
}
|
||||
|
||||
return el;
|
||||
}
|
||||
|
||||
|
||||
}
|
|
@ -12,11 +12,11 @@ export default class FeatureSwitched extends UIElement{
|
|||
this._swtch = swtch;
|
||||
}
|
||||
|
||||
InnerRender(): string {
|
||||
InnerRender(): UIElement | string {
|
||||
if(this._swtch.data){
|
||||
return this._upstream.Render();
|
||||
}
|
||||
return "";
|
||||
return undefined;
|
||||
}
|
||||
|
||||
}
|
|
@ -7,7 +7,7 @@ export class FixedUiElement extends UIElement {
|
|||
super(undefined);
|
||||
this._html = html ?? "";
|
||||
}
|
||||
|
||||
|
||||
InnerRender(): string {
|
||||
return this._html;
|
||||
}
|
||||
|
|
|
@ -1,19 +1,29 @@
|
|||
import Constants from "../../Models/Constants";
|
||||
import {Utils} from "../../Utils";
|
||||
import BaseUIElement from "../BaseUIElement";
|
||||
|
||||
export default class Img {
|
||||
export default class Img extends BaseUIElement {
|
||||
private _src: string;
|
||||
|
||||
public static runningFromConsole = false;
|
||||
constructor(src: string) {
|
||||
super();
|
||||
this._src = src;
|
||||
}
|
||||
|
||||
static AsData(source:string){
|
||||
if(Utils.runningFromConsole){
|
||||
return source;
|
||||
}
|
||||
return `data:image/svg+xml;base64,${(btoa(source))}`;
|
||||
}
|
||||
static AsData(source: string) {
|
||||
if (Utils.runningFromConsole) {
|
||||
return source;
|
||||
}
|
||||
return `data:image/svg+xml;base64,${(btoa(source))}`;
|
||||
}
|
||||
|
||||
static AsImageElement(source: string, css_class: string = "", style=""): string{
|
||||
static AsImageElement(source: string, css_class: string = "", style = ""): string {
|
||||
return `<img class="${css_class}" style="${style}" alt="" src="${Img.AsData(source)}">`;
|
||||
}
|
||||
|
||||
protected InnerConstructElement(): HTMLElement {
|
||||
const el = document.createElement("img")
|
||||
el.src = this._src;
|
||||
return el;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -1,24 +1,35 @@
|
|||
import {UIElement} from "../UIElement";
|
||||
import Translations from "../i18n/Translations";
|
||||
import BaseUIElement from "../BaseUIElement";
|
||||
import {UIEventSource} from "../../Logic/UIEventSource";
|
||||
|
||||
|
||||
export default class Link extends UIElement {
|
||||
private readonly _embeddedShow: UIElement;
|
||||
private readonly _target: string;
|
||||
private readonly _newTab: string;
|
||||
export default class Link extends BaseUIElement {
|
||||
private readonly _element: HTMLElement;
|
||||
|
||||
constructor(embeddedShow: UIElement | string, target: string, newTab: boolean = false) {
|
||||
constructor(embeddedShow: BaseUIElement | string, target: string | UIEventSource<string>, newTab: boolean = false) {
|
||||
super();
|
||||
this._embeddedShow = Translations.W(embeddedShow);
|
||||
this._target = target;
|
||||
this._newTab = "";
|
||||
if (newTab) {
|
||||
this._newTab = "target='_blank'"
|
||||
const _embeddedShow = Translations.W(embeddedShow);
|
||||
|
||||
|
||||
const el = document.createElement("a")
|
||||
|
||||
if(typeof target === "string"){
|
||||
el.href = target
|
||||
}else{
|
||||
target.addCallbackAndRun(target => {
|
||||
el.target = target;
|
||||
})
|
||||
}
|
||||
if (newTab) {
|
||||
el.target = "_blank"
|
||||
}
|
||||
el.appendChild(_embeddedShow.ConstructElement())
|
||||
this._element = el
|
||||
}
|
||||
|
||||
InnerRender(): string {
|
||||
return `<a href="${this._target}" ${this._newTab}>${this._embeddedShow.Render()}</a>`;
|
||||
protected InnerConstructElement(): HTMLElement {
|
||||
|
||||
return this._element;
|
||||
}
|
||||
|
||||
}
|
|
@ -7,7 +7,13 @@ import {UIEventSource} from "../../Logic/UIEventSource";
|
|||
import Hash from "../../Logic/Web/Hash";
|
||||
|
||||
/**
|
||||
* Wraps some contents into a panel that scrolls the content _under_ the title
|
||||
*
|
||||
* 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("");
|
||||
|
@ -40,8 +46,8 @@ export default class ScrollableFullScreen extends UIElement {
|
|||
})
|
||||
}
|
||||
|
||||
InnerRender(): string {
|
||||
return this._component.Render();
|
||||
InnerRender(): UIElement {
|
||||
return this._component;
|
||||
}
|
||||
|
||||
Activate(): void {
|
||||
|
|
|
@ -1,55 +1,51 @@
|
|||
import {UIElement} from "../UIElement";
|
||||
import Translations from "../i18n/Translations";
|
||||
import Combine from "./Combine";
|
||||
import {FixedUiElement} from "./FixedUiElement";
|
||||
import BaseUIElement from "../BaseUIElement";
|
||||
import Link from "./Link";
|
||||
import Img from "./Img";
|
||||
import {UIEventSource} from "../../Logic/UIEventSource";
|
||||
|
||||
|
||||
export class SubtleButton extends Combine {
|
||||
|
||||
constructor(imageUrl: string | UIElement, message: string | UIElement, linkTo: { url: string, newTab?: boolean } = undefined) {
|
||||
constructor(imageUrl: string | BaseUIElement, message: string | BaseUIElement, linkTo: { url: string | UIEventSource<string>, newTab?: boolean } = undefined) {
|
||||
super(SubtleButton.generateContent(imageUrl, message, linkTo));
|
||||
|
||||
this.SetClass("block flex p-3 my-2 bg-blue-100 rounded-lg hover:shadow-xl hover:bg-blue-200 link-no-underline")
|
||||
|
||||
}
|
||||
|
||||
private static generateContent(imageUrl: string | UIElement, messageT: string | UIElement, linkTo: { url: string, newTab?: boolean } = undefined): (UIElement | string)[] {
|
||||
private static generateContent(imageUrl: string | BaseUIElement, messageT: string | BaseUIElement, linkTo: { url: string | UIEventSource<string>, newTab?: boolean } = undefined): (BaseUIElement )[] {
|
||||
const message = Translations.W(messageT);
|
||||
if (message !== null) {
|
||||
message.dumbMode = false;
|
||||
}
|
||||
let img;
|
||||
if ((imageUrl ?? "") === "") {
|
||||
img = new FixedUiElement("");
|
||||
img = undefined;
|
||||
} else if (typeof (imageUrl) === "string") {
|
||||
img = new FixedUiElement(`<img style="width: 100%;" src="${imageUrl}" alt="">`);
|
||||
img = new Img(imageUrl).SetClass("w-full")
|
||||
} else {
|
||||
img = imageUrl;
|
||||
}
|
||||
img.SetClass("block flex items-center justify-center h-11 w-11 flex-shrink0")
|
||||
img?.SetClass("block flex items-center justify-center h-11 w-11 flex-shrink0")
|
||||
const image = new Combine([img])
|
||||
.SetClass("flex-shrink-0");
|
||||
|
||||
|
||||
if (message !== null && message.IsEmpty()) {
|
||||
// Message == null: special case to force empty text
|
||||
return [];
|
||||
}
|
||||
|
||||
if (linkTo != undefined) {
|
||||
|
||||
if (linkTo == undefined) {
|
||||
return [
|
||||
`<a class='flex group' href="${linkTo.url}" ${linkTo.newTab ? 'target="_blank"' : ""}>`,
|
||||
image,
|
||||
`<div class='ml-4 overflow-ellipsis'>`,
|
||||
message,
|
||||
`</div>`,
|
||||
`</a>`
|
||||
];
|
||||
}
|
||||
|
||||
|
||||
|
||||
return [
|
||||
image,
|
||||
message,
|
||||
new Link(
|
||||
new Combine([
|
||||
image,
|
||||
message?.SetClass("block ml-4 overflow-ellipsis")
|
||||
]).SetClass("flex group"),
|
||||
linkTo.url,
|
||||
linkTo.newTab ?? false
|
||||
)
|
||||
];
|
||||
|
||||
}
|
||||
|
|
|
@ -1,39 +1,41 @@
|
|||
import {UIElement} from "../UIElement";
|
||||
import Translations from "../i18n/Translations";
|
||||
import {UIEventSource} from "../../Logic/UIEventSource";
|
||||
import Combine from "./Combine";
|
||||
|
||||
export class TabbedComponent extends UIElement {
|
||||
|
||||
private headers: UIElement[] = [];
|
||||
private readonly header: UIElement;
|
||||
private content: UIElement[] = [];
|
||||
|
||||
constructor(elements: { header: UIElement | string, content: UIElement | string }[], openedTab: (UIEventSource<number> | number) = 0) {
|
||||
super(typeof (openedTab) === "number" ? new UIEventSource(openedTab) : (openedTab ?? new UIEventSource<number>(0)));
|
||||
const self = this;
|
||||
const tabs: 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)));
|
||||
const header = Translations.W(element.header).onClick(() => self._source.setData(i))
|
||||
const content = Translations.W(element.content)
|
||||
this.content.push(content);
|
||||
}
|
||||
}
|
||||
|
||||
InnerRender(): string {
|
||||
let headerBar = "";
|
||||
for (let i = 0; i < this.headers.length; i++) {
|
||||
let header = this.headers[i];
|
||||
|
||||
if (!this.content[i].IsEmpty()) {
|
||||
headerBar += `<div class=\'tab-single-header ${i == this._source.data ? 'tab-active' : 'tab-non-active'}\'>` +
|
||||
header.Render() + "</div>"
|
||||
const tab = header.SetClass("block tab-single-header")
|
||||
tabs.push(tab)
|
||||
}
|
||||
}
|
||||
|
||||
this.header = new Combine(tabs).SetClass("block tabs-header-bar")
|
||||
|
||||
headerBar = "<div class='tabs-header-bar'>" + headerBar + "</div>"
|
||||
|
||||
}
|
||||
|
||||
InnerRender(): UIElement {
|
||||
|
||||
const content = this.content[this._source.data];
|
||||
return headerBar + "<div class='tab-content'>" + (content?.Render() ?? "") + "</div>";
|
||||
return new Combine([
|
||||
this.header,
|
||||
content.SetClass("tab-content"),
|
||||
])
|
||||
}
|
||||
|
||||
}
|
|
@ -1,16 +1,35 @@
|
|||
import {UIElement} from "../UIElement";
|
||||
import {UIEventSource} from "../../Logic/UIEventSource";
|
||||
import BaseUIElement from "../BaseUIElement";
|
||||
|
||||
export class VariableUiElement extends UIElement {
|
||||
private _html: UIEventSource<string>;
|
||||
export class VariableUiElement extends BaseUIElement {
|
||||
|
||||
constructor(html: UIEventSource<string>) {
|
||||
super(html);
|
||||
this._html = html;
|
||||
private _element : HTMLElement;
|
||||
|
||||
constructor(contents: UIEventSource<string | BaseUIElement>) {
|
||||
super();
|
||||
|
||||
this._element = document.createElement("span")
|
||||
const el = this._element
|
||||
contents.addCallbackAndRun(contents => {
|
||||
while(el.firstChild){
|
||||
el.removeChild(
|
||||
el.lastChild
|
||||
)
|
||||
}
|
||||
|
||||
if(contents === undefined){
|
||||
return
|
||||
}
|
||||
if(typeof contents === "string"){
|
||||
el.innerHTML = contents
|
||||
}else{
|
||||
el.appendChild(contents.ConstructElement())
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
InnerRender(): string {
|
||||
return this._html.data;
|
||||
protected InnerConstructElement(): HTMLElement {
|
||||
return this._element;
|
||||
}
|
||||
|
||||
}
|
|
@ -1,20 +0,0 @@
|
|||
import {UIElement} from "../UIElement";
|
||||
|
||||
export class VerticalCombine extends UIElement {
|
||||
private readonly _elements: UIElement[];
|
||||
|
||||
constructor(elements: UIElement[]) {
|
||||
super(undefined);
|
||||
this._elements = elements;
|
||||
}
|
||||
|
||||
InnerRender(): string {
|
||||
let html = "";
|
||||
for (const element of this._elements) {
|
||||
if (element !== undefined && !element.IsEmpty()) {
|
||||
html += "<div>" + element.Render() + "</div>";
|
||||
}
|
||||
}
|
||||
return html;
|
||||
}
|
||||
}
|
Loading…
Add table
Add a link
Reference in a new issue