Butchering the UI framework

This commit is contained in:
Pieter Vander Vennet 2021-06-10 01:36:20 +02:00
parent 8d404b1ba9
commit 6415e195d1
90 changed files with 1012 additions and 3101 deletions

View file

@ -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;
}
}

View file

@ -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;
}
}

View file

@ -7,7 +7,7 @@ export class FixedUiElement extends UIElement {
super(undefined);
this._html = html ?? "";
}
InnerRender(): string {
return this._html;
}

View file

@ -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;
}
}

View file

@ -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;
}
}

View file

@ -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 {

View file

@ -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
)
];
}

View file

@ -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"),
])
}
}

View file

@ -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;
}
}

View file

@ -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;
}
}