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
222
UI/UIElement.ts
222
UI/UIElement.ts
|
@ -1,25 +1,19 @@
|
|||
import {UIEventSource} from "../Logic/UIEventSource";
|
||||
import {Utils} from "../Utils";
|
||||
import BaseUIElement from "./BaseUIElement";
|
||||
|
||||
export abstract class UIElement extends UIEventSource<string> {
|
||||
export abstract class UIElement extends BaseUIElement{
|
||||
|
||||
private static nextId: number = 0;
|
||||
public readonly id: string;
|
||||
public readonly _source: UIEventSource<any>;
|
||||
public dumbMode = false;
|
||||
private clss: Set<string> = new Set<string>();
|
||||
private style: string;
|
||||
private _hideIfEmpty = false;
|
||||
|
||||
private lastInnerRender: string;
|
||||
private _onClick: () => void;
|
||||
private _onHover: UIEventSource<boolean>;
|
||||
|
||||
protected constructor(source: UIEventSource<any> = undefined) {
|
||||
super("");
|
||||
this.id = "ui-element-" + UIElement.nextId;
|
||||
super()
|
||||
this.id = `ui-${this.constructor.name}-${UIElement.nextId}`;
|
||||
this._source = source;
|
||||
UIElement.nextId++;
|
||||
this.dumbMode = true;
|
||||
this.ListenTo(source);
|
||||
}
|
||||
|
||||
|
@ -27,183 +21,97 @@ export abstract class UIElement extends UIEventSource<string> {
|
|||
if (source === undefined) {
|
||||
return this;
|
||||
}
|
||||
this.dumbMode = false;
|
||||
const self = this;
|
||||
source.addCallback(() => {
|
||||
self.lastInnerRender = undefined;
|
||||
self.Update();
|
||||
if(self._constructedHtmlElement !== undefined){
|
||||
self.UpdateElement(self._constructedHtmlElement);
|
||||
}
|
||||
|
||||
})
|
||||
return this;
|
||||
}
|
||||
|
||||
public onClick(f: (() => void)) {
|
||||
this.dumbMode = false;
|
||||
this._onClick = f;
|
||||
this.SetClass("clickable")
|
||||
this.Update();
|
||||
return this;
|
||||
}
|
||||
|
||||
public IsHovered(): UIEventSource<boolean> {
|
||||
this.dumbMode = false;
|
||||
if (this._onHover !== undefined) {
|
||||
return this._onHover;
|
||||
}
|
||||
// Note: we just save it. 'Update' will register that an eventsource exist and install the necessary hooks
|
||||
this._onHover = new UIEventSource<boolean>(false);
|
||||
return this._onHover;
|
||||
}
|
||||
|
||||
Update(): void {
|
||||
if (Utils.runningFromConsole) {
|
||||
return;
|
||||
}
|
||||
|
||||
let element = document.getElementById(this.id);
|
||||
if (element === undefined || element === null) {
|
||||
// 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
|
||||
this.UpdateAllChildren();
|
||||
}
|
||||
return;
|
||||
}
|
||||
const newRender = this.InnerRender();
|
||||
if (newRender !== this.lastInnerRender) {
|
||||
this.lastInnerRender = newRender;
|
||||
this.setData(this.InnerRender());
|
||||
element.innerHTML = this.data;
|
||||
}
|
||||
|
||||
if (this._hideIfEmpty) {
|
||||
if (element.innerHTML === "") {
|
||||
element.parentElement.style.display = "none";
|
||||
} else {
|
||||
element.parentElement.style.display = "";
|
||||
}
|
||||
}
|
||||
|
||||
if (this._onClick !== undefined) {
|
||||
const self = this;
|
||||
element.onclick = (e) => {
|
||||
// @ts-ignore
|
||||
if (e.consumed) {
|
||||
return;
|
||||
}
|
||||
self._onClick();
|
||||
// @ts-ignore
|
||||
e.consumed = true;
|
||||
}
|
||||
element.style.pointerEvents = "all";
|
||||
element.style.cursor = "pointer";
|
||||
}
|
||||
|
||||
if (this._onHover !== undefined) {
|
||||
const self = this;
|
||||
element.addEventListener('mouseover', () => self._onHover.setData(true));
|
||||
element.addEventListener('mouseout', () => self._onHover.setData(false));
|
||||
}
|
||||
|
||||
this.InnerUpdate(element);
|
||||
this.UpdateAllChildren();
|
||||
|
||||
}
|
||||
|
||||
HideOnEmpty(hide: boolean): UIElement {
|
||||
this._hideIfEmpty = hide;
|
||||
this.Update();
|
||||
return this;
|
||||
}
|
||||
|
||||
Render(): string {
|
||||
this.lastInnerRender = this.InnerRender();
|
||||
if (this.dumbMode) {
|
||||
return this.lastInnerRender;
|
||||
}
|
||||
|
||||
let style = "";
|
||||
if (this.style !== undefined && this.style !== "") {
|
||||
style = `style="${this.style}" `;
|
||||
}
|
||||
let clss = "";
|
||||
if (this.clss.size > 0) {
|
||||
clss = `class='${Array.from(this.clss).join(" ")}' `;
|
||||
}
|
||||
return `<span ${clss}${style}id='${this.id}' gen="${this.constructor.name}">${this.lastInnerRender}</span>`
|
||||
return "Don't use Render!"
|
||||
}
|
||||
|
||||
AttachTo(divId: string) {
|
||||
this.dumbMode = false;
|
||||
let element = document.getElementById(divId);
|
||||
if (element === null) {
|
||||
throw "SEVERE: could not attach UIElement to " + divId;
|
||||
}
|
||||
element.innerHTML = this.Render();
|
||||
this.Update();
|
||||
return this;
|
||||
}
|
||||
|
||||
public abstract InnerRender(): string;
|
||||
public InnerRenderAsString(): string {
|
||||
let rendered = this.InnerRender();
|
||||
if (typeof rendered !== "string") {
|
||||
let html = rendered.ConstructElement()
|
||||
return html.innerHTML
|
||||
}
|
||||
return rendered
|
||||
}
|
||||
|
||||
public IsEmpty(): boolean {
|
||||
return this.InnerRender() === "";
|
||||
return this.InnerRender() === undefined || this.InnerRender() === "";
|
||||
}
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* Adds all the relevant classes, space seperated
|
||||
* @param clss
|
||||
* @constructor
|
||||
* Should be overridden for specific HTML functionality
|
||||
*/
|
||||
public SetClass(clss: string) {
|
||||
this.dumbMode = false;
|
||||
const all = clss.split(" ");
|
||||
let recordedChange = false;
|
||||
for (const c of all) {
|
||||
if (this.clss.has(clss)) {
|
||||
continue;
|
||||
protected InnerConstructElement(): HTMLElement {
|
||||
// Uses the old fashioned way to construct an element using 'InnerRender'
|
||||
const innerRender = this.InnerRender();
|
||||
if (innerRender === undefined || innerRender === "") {
|
||||
return undefined;
|
||||
}
|
||||
const el = document.createElement("span")
|
||||
if (typeof innerRender === "string") {
|
||||
el.innerHTML = innerRender
|
||||
} else {
|
||||
const subElement = innerRender.ConstructElement();
|
||||
if (subElement === undefined) {
|
||||
return undefined;
|
||||
}
|
||||
this.clss.add(c);
|
||||
recordedChange = true;
|
||||
el.appendChild(subElement)
|
||||
}
|
||||
if (recordedChange) {
|
||||
this.Update();
|
||||
}
|
||||
return this;
|
||||
return el;
|
||||
}
|
||||
|
||||
public RemoveClass(clss: string): UIElement {
|
||||
if (this.clss.has(clss)) {
|
||||
this.clss.delete(clss);
|
||||
this.Update();
|
||||
}
|
||||
return this;
|
||||
}
|
||||
protected UpdateElement(el: HTMLElement) : void{
|
||||
const innerRender = this.InnerRender();
|
||||
|
||||
public SetStyle(style: string): UIElement {
|
||||
this.dumbMode = false;
|
||||
this.style = style;
|
||||
this.Update();
|
||||
return this;
|
||||
}
|
||||
|
||||
// Called after the HTML has been replaced. Can be used for css tricks
|
||||
protected InnerUpdate(htmlElement: HTMLElement) {
|
||||
}
|
||||
|
||||
private UpdateAllChildren() {
|
||||
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();
|
||||
}
|
||||
}
|
||||
if (typeof innerRender === "string") {
|
||||
if(el.innerHTML !== innerRender){
|
||||
el.innerHTML = innerRender
|
||||
}
|
||||
} else {
|
||||
const subElement = innerRender.ConstructElement();
|
||||
if(el.children.length === 1 && el.children[0] === subElement){
|
||||
return; // Nothing changed
|
||||
}
|
||||
|
||||
while (el.firstChild) {
|
||||
el.removeChild(el.firstChild);
|
||||
}
|
||||
|
||||
if (subElement === undefined) {
|
||||
return;
|
||||
}
|
||||
el.appendChild(subElement)
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* @deprecated The method should not be used
|
||||
*/
|
||||
protected abstract InnerRender(): string | BaseUIElement;
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue