2021-06-10 01:36:20 +02:00
|
|
|
/**
|
|
|
|
* A thin wrapper around a html element, which allows to generate a HTML-element.
|
2021-06-23 02:14:15 +02:00
|
|
|
*
|
2021-06-10 01:36:20 +02:00
|
|
|
* Assumes a read-only configuration, so it has no 'ListenTo'
|
|
|
|
*/
|
|
|
|
export default abstract class BaseUIElement {
|
|
|
|
|
2021-06-23 02:14:15 +02:00
|
|
|
protected _constructedHtmlElement: HTMLElement;
|
2022-01-26 21:40:38 +01:00
|
|
|
protected isDestroyed = false;
|
2022-08-22 13:34:47 +02:00
|
|
|
protected readonly clss: Set<string> = new Set<string>();
|
|
|
|
protected style: string;
|
2022-08-22 19:16:37 +02:00
|
|
|
private _onClick: () => void | Promise<void>;
|
2021-09-09 00:05:51 +02:00
|
|
|
|
2021-06-10 01:36:20 +02:00
|
|
|
public onClick(f: (() => void)) {
|
|
|
|
this._onClick = f;
|
|
|
|
this.SetClass("clickable")
|
2021-06-23 02:14:15 +02:00
|
|
|
if (this._constructedHtmlElement !== undefined) {
|
2021-06-10 01:36:20 +02:00
|
|
|
this._constructedHtmlElement.onclick = f;
|
|
|
|
}
|
|
|
|
return this;
|
|
|
|
}
|
2021-09-09 00:05:51 +02:00
|
|
|
|
2021-06-10 01:36:20 +02:00
|
|
|
AttachTo(divId: string) {
|
|
|
|
let element = document.getElementById(divId);
|
|
|
|
if (element === null) {
|
|
|
|
throw "SEVERE: could not attach UIElement to " + divId;
|
|
|
|
}
|
|
|
|
|
|
|
|
while (element.firstChild) {
|
|
|
|
//The list is LIVE so it will re-index each call
|
|
|
|
element.removeChild(element.firstChild);
|
|
|
|
}
|
|
|
|
const el = this.ConstructElement();
|
2021-06-23 02:14:15 +02:00
|
|
|
if (el !== undefined) {
|
2021-06-10 01:36:20 +02:00
|
|
|
element.appendChild(el)
|
|
|
|
}
|
2021-09-09 00:05:51 +02:00
|
|
|
|
2021-06-10 01:36:20 +02:00
|
|
|
return this;
|
|
|
|
}
|
2022-07-18 10:21:16 +02:00
|
|
|
|
|
|
|
public ScrollToTop() {
|
|
|
|
this._constructedHtmlElement?.scrollTo(0, 0)
|
2022-02-09 00:35:59 +01:00
|
|
|
}
|
2021-06-23 02:14:15 +02:00
|
|
|
|
2021-06-10 01:36:20 +02:00
|
|
|
/**
|
2021-08-19 18:30:43 +02:00
|
|
|
* Adds all the relevant classes, space separated
|
2021-06-10 01:36:20 +02:00
|
|
|
*/
|
|
|
|
public SetClass(clss: string) {
|
2021-11-07 16:34:51 +01:00
|
|
|
if (clss == undefined) {
|
2022-04-13 02:44:06 +02:00
|
|
|
return this
|
2021-11-07 16:34:51 +01:00
|
|
|
}
|
2021-06-10 01:36:20 +02:00
|
|
|
const all = clss.split(" ").map(clsName => clsName.trim());
|
|
|
|
let recordedChange = false;
|
2021-06-12 02:58:32 +02:00
|
|
|
for (let c of all) {
|
|
|
|
c = c.trim();
|
2021-06-10 01:36:20 +02:00
|
|
|
if (this.clss.has(clss)) {
|
|
|
|
continue;
|
|
|
|
}
|
2021-06-23 02:14:15 +02:00
|
|
|
if (c === undefined || c === "") {
|
2021-06-12 02:58:32 +02:00
|
|
|
continue;
|
|
|
|
}
|
2021-06-10 01:36:20 +02:00
|
|
|
this.clss.add(c);
|
|
|
|
recordedChange = true;
|
|
|
|
}
|
|
|
|
if (recordedChange) {
|
|
|
|
this._constructedHtmlElement?.classList.add(...Array.from(this.clss));
|
|
|
|
}
|
|
|
|
return this;
|
|
|
|
}
|
|
|
|
|
2022-07-18 10:21:16 +02:00
|
|
|
public RemoveClass(classes: string): BaseUIElement {
|
|
|
|
const all = classes.split(" ").map(clsName => clsName.trim());
|
|
|
|
for (let clss of all) {
|
|
|
|
if (this.clss.has(clss)) {
|
|
|
|
this.clss.delete(clss);
|
|
|
|
this._constructedHtmlElement?.classList.remove(clss)
|
|
|
|
}
|
2021-06-10 01:36:20 +02:00
|
|
|
}
|
|
|
|
return this;
|
|
|
|
}
|
2021-06-23 02:14:15 +02:00
|
|
|
|
|
|
|
public HasClass(clss: string): boolean {
|
2021-06-15 00:28:59 +02:00
|
|
|
return this.clss.has(clss)
|
|
|
|
}
|
2021-06-10 01:36:20 +02:00
|
|
|
|
|
|
|
public SetStyle(style: string): BaseUIElement {
|
|
|
|
this.style = style;
|
2021-06-23 02:14:15 +02:00
|
|
|
if (this._constructedHtmlElement !== undefined) {
|
2021-06-10 01:36:20 +02:00
|
|
|
this._constructedHtmlElement.style.cssText = style;
|
|
|
|
}
|
|
|
|
return this;
|
|
|
|
}
|
2021-06-23 02:14:15 +02:00
|
|
|
|
2021-06-10 01:36:20 +02:00
|
|
|
/**
|
|
|
|
* The same as 'Render', but creates a HTML element instead of the HTML representation
|
|
|
|
*/
|
|
|
|
public ConstructElement(): HTMLElement {
|
2022-04-28 00:32:15 +02:00
|
|
|
if (typeof window === undefined) {
|
2021-06-10 01:36:20 +02:00
|
|
|
return undefined;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (this._constructedHtmlElement !== undefined) {
|
|
|
|
return this._constructedHtmlElement
|
|
|
|
}
|
|
|
|
|
2021-09-30 21:41:51 +02:00
|
|
|
try {
|
2021-06-23 02:14:15 +02:00
|
|
|
const el = this.InnerConstructElement();
|
2021-06-10 01:36:20 +02:00
|
|
|
|
2021-06-23 02:14:15 +02:00
|
|
|
if (el === undefined) {
|
|
|
|
return undefined;
|
2021-06-10 01:36:20 +02:00
|
|
|
}
|
|
|
|
|
2021-06-23 02:14:15 +02:00
|
|
|
this._constructedHtmlElement = el;
|
|
|
|
const style = this.style
|
|
|
|
if (style !== undefined && style !== "") {
|
|
|
|
el.style.cssText = style
|
|
|
|
}
|
2022-07-20 12:04:14 +02:00
|
|
|
if (this.clss?.size > 0) {
|
2021-06-23 02:14:15 +02:00
|
|
|
try {
|
|
|
|
el.classList.add(...Array.from(this.clss))
|
|
|
|
} catch (e) {
|
|
|
|
console.error("Invalid class name detected in:", Array.from(this.clss).join(" "), "\nErr msg is ", e)
|
2021-06-10 01:36:20 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2021-06-23 02:14:15 +02:00
|
|
|
if (this._onClick !== undefined) {
|
|
|
|
const self = this;
|
2022-08-22 19:16:37 +02:00
|
|
|
el.onclick = async (e) => {
|
2021-06-23 02:14:15 +02:00
|
|
|
// @ts-ignore
|
|
|
|
if (e.consumed) {
|
|
|
|
return;
|
|
|
|
}
|
2022-08-22 19:16:37 +02:00
|
|
|
const v = self._onClick();
|
|
|
|
if(typeof v === "object"){
|
|
|
|
await v
|
|
|
|
}
|
2021-06-23 02:14:15 +02:00
|
|
|
// @ts-ignore
|
|
|
|
e.consumed = true;
|
|
|
|
}
|
2021-09-30 21:44:55 +02:00
|
|
|
el.classList.add("pointer-events-none", "cursor-pointer");
|
2021-06-23 02:14:15 +02:00
|
|
|
}
|
2021-06-10 01:36:20 +02:00
|
|
|
|
2021-06-23 02:14:15 +02:00
|
|
|
return el
|
|
|
|
} catch (e) {
|
2021-06-16 14:23:53 +02:00
|
|
|
const domExc = e as DOMException;
|
2021-06-23 02:14:15 +02:00
|
|
|
if (domExc) {
|
|
|
|
console.log("An exception occured", domExc.code, domExc.message, domExc.name)
|
2021-06-16 14:23:53 +02:00
|
|
|
}
|
|
|
|
console.error(e)
|
2021-06-23 02:14:15 +02:00
|
|
|
}
|
2021-06-10 01:36:20 +02:00
|
|
|
}
|
2021-06-23 02:14:15 +02:00
|
|
|
|
|
|
|
public AsMarkdown(): string {
|
2022-02-14 02:26:03 +01:00
|
|
|
throw "AsMarkdown is not implemented; implement it in the subclass"
|
2021-06-15 00:28:59 +02:00
|
|
|
}
|
2022-01-26 21:40:38 +01:00
|
|
|
|
|
|
|
public Destroy() {
|
2022-01-06 18:51:52 +01:00
|
|
|
this.isDestroyed = true;
|
|
|
|
}
|
2021-06-23 02:14:15 +02:00
|
|
|
|
|
|
|
protected abstract InnerConstructElement(): HTMLElement;
|
2021-09-30 21:41:51 +02:00
|
|
|
}
|