| 
									
										
										
										
											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' | 
					
						
							|  |  |  |  */ | 
					
						
							| 
									
										
										
										
											2022-12-16 13:44:25 +01:00
										 |  |  | import { Utils } from "../Utils" | 
					
						
							| 
									
										
										
										
											2022-12-06 03:43:54 +01:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-06-10 01:36:20 +02:00
										 |  |  | export default abstract class BaseUIElement { | 
					
						
							| 
									
										
										
										
											2022-09-08 21:40:48 +02:00
										 |  |  |     protected _constructedHtmlElement: HTMLElement | 
					
						
							|  |  |  |     protected isDestroyed = false | 
					
						
							|  |  |  |     protected readonly clss: Set<string> = new Set<string>() | 
					
						
							|  |  |  |     protected style: string | 
					
						
							|  |  |  |     private _onClick: () => void | Promise<void> | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     public onClick(f: () => void) { | 
					
						
							|  |  |  |         this._onClick = f | 
					
						
							| 
									
										
										
										
											2021-06-10 01:36:20 +02:00
										 |  |  |         this.SetClass("clickable") | 
					
						
							| 
									
										
										
										
											2021-06-23 02:14:15 +02:00
										 |  |  |         if (this._constructedHtmlElement !== undefined) { | 
					
						
							| 
									
										
										
										
											2022-09-08 21:40:48 +02:00
										 |  |  |             this._constructedHtmlElement.onclick = f | 
					
						
							| 
									
										
										
										
											2021-06-10 01:36:20 +02:00
										 |  |  |         } | 
					
						
							| 
									
										
										
										
											2022-09-08 21:40:48 +02:00
										 |  |  |         return this | 
					
						
							| 
									
										
										
										
											2021-06-10 01:36:20 +02:00
										 |  |  |     } | 
					
						
							| 
									
										
										
										
											2021-09-09 00:05:51 +02:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-06-10 01:36:20 +02:00
										 |  |  |     AttachTo(divId: string) { | 
					
						
							| 
									
										
										
										
											2022-09-08 21:40:48 +02:00
										 |  |  |         let element = document.getElementById(divId) | 
					
						
							| 
									
										
										
										
											2021-06-10 01:36:20 +02:00
										 |  |  |         if (element === null) { | 
					
						
							| 
									
										
										
										
											2022-12-24 03:44:21 +01:00
										 |  |  |             if (Utils.runningFromConsole) { | 
					
						
							|  |  |  |                 this.ConstructElement() | 
					
						
							|  |  |  |                 return | 
					
						
							|  |  |  |             } | 
					
						
							| 
									
										
										
										
											2022-09-08 21:40:48 +02:00
										 |  |  |             throw "SEVERE: could not attach UIElement to " + divId | 
					
						
							| 
									
										
										
										
											2021-06-10 01:36:20 +02:00
										 |  |  |         } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-01-06 04:16:32 +01:00
										 |  |  |         let alreadyThere = false | 
					
						
							|  |  |  |         const elementToAdd = this.ConstructElement() | 
					
						
							|  |  |  |         const childs = Array.from(element.childNodes) | 
					
						
							|  |  |  |         for (const child of childs) { | 
					
						
							|  |  |  |             if (child === elementToAdd) { | 
					
						
							|  |  |  |                 alreadyThere = true | 
					
						
							|  |  |  |                 continue | 
					
						
							|  |  |  |             } | 
					
						
							|  |  |  |             element.removeChild(child) | 
					
						
							| 
									
										
										
										
											2021-06-10 01:36:20 +02:00
										 |  |  |         } | 
					
						
							| 
									
										
										
										
											2023-01-06 04:16:32 +01:00
										 |  |  | 
 | 
					
						
							|  |  |  |         if (elementToAdd !== undefined && !alreadyThere) { | 
					
						
							|  |  |  |             element.appendChild(elementToAdd) | 
					
						
							| 
									
										
										
										
											2021-06-10 01:36:20 +02:00
										 |  |  |         } | 
					
						
							| 
									
										
										
										
											2021-09-09 00:05:51 +02:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-09-08 21:40:48 +02:00
										 |  |  |         return this | 
					
						
							| 
									
										
										
										
											2021-06-10 01:36:20 +02:00
										 |  |  |     } | 
					
						
							| 
									
										
										
										
											2022-07-18 08:21:16 +00:00
										 |  |  | 
 | 
					
						
							|  |  |  |     public ScrollToTop() { | 
					
						
							|  |  |  |         this._constructedHtmlElement?.scrollTo(0, 0) | 
					
						
							| 
									
										
										
										
											2022-02-09 00:35:59 +01:00
										 |  |  |     } | 
					
						
							| 
									
										
										
										
											2021-06-23 02:14:15 +02:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-12-16 13:44:25 +01:00
										 |  |  |     public ScrollIntoView(options?: { onlyIfPartiallyHidden?: boolean }) { | 
					
						
							|  |  |  |         if (this._constructedHtmlElement === undefined) { | 
					
						
							| 
									
										
										
										
											2022-12-06 03:43:54 +01:00
										 |  |  |             return | 
					
						
							|  |  |  |         } | 
					
						
							| 
									
										
										
										
											2022-12-16 13:44:25 +01:00
										 |  |  |         let alignToTop = true | 
					
						
							|  |  |  |         if (options?.onlyIfPartiallyHidden) { | 
					
						
							| 
									
										
										
										
											2022-12-06 03:43:54 +01:00
										 |  |  |             // Is the element completely in the view?
 | 
					
						
							| 
									
										
										
										
											2022-12-16 13:44:25 +01:00
										 |  |  |             const parentRect = Utils.findParentWithScrolling( | 
					
						
							|  |  |  |                 this._constructedHtmlElement.parentElement | 
					
						
							|  |  |  |             ).getBoundingClientRect() | 
					
						
							|  |  |  |             const elementRect = this._constructedHtmlElement.getBoundingClientRect() | 
					
						
							| 
									
										
										
										
											2022-12-06 03:43:54 +01:00
										 |  |  | 
 | 
					
						
							|  |  |  |             // Check if the element is within the vertical bounds of the parent element
 | 
					
						
							|  |  |  |             const topIsVisible = elementRect.top >= parentRect.top | 
					
						
							|  |  |  |             const bottomIsVisible = elementRect.bottom <= parentRect.bottom | 
					
						
							| 
									
										
										
										
											2022-12-16 13:44:25 +01:00
										 |  |  |             const inView = topIsVisible && bottomIsVisible | 
					
						
							|  |  |  |             if (inView) { | 
					
						
							| 
									
										
										
										
											2022-12-06 03:43:54 +01:00
										 |  |  |                 return | 
					
						
							|  |  |  |             } | 
					
						
							| 
									
										
										
										
											2022-12-16 13:44:25 +01:00
										 |  |  |             if (topIsVisible) { | 
					
						
							| 
									
										
										
										
											2022-12-06 03:43:54 +01:00
										 |  |  |                 alignToTop = false | 
					
						
							|  |  |  |             } | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  |         this._constructedHtmlElement?.scrollIntoView({ | 
					
						
							|  |  |  |             behavior: "smooth", | 
					
						
							| 
									
										
										
										
											2022-12-16 13:44:25 +01:00
										 |  |  |             block: "start", | 
					
						
							| 
									
										
										
										
											2022-12-06 03:43:54 +01: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
										 |  |  |         } | 
					
						
							| 
									
										
										
										
											2022-09-08 21:40:48 +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) { | 
					
						
							| 
									
										
										
										
											2022-09-08 21:40:48 +02:00
										 |  |  |             c = c.trim() | 
					
						
							| 
									
										
										
										
											2021-06-10 01:36:20 +02:00
										 |  |  |             if (this.clss.has(clss)) { | 
					
						
							| 
									
										
										
										
											2022-09-08 21:40:48 +02:00
										 |  |  |                 continue | 
					
						
							| 
									
										
										
										
											2021-06-10 01:36:20 +02:00
										 |  |  |             } | 
					
						
							| 
									
										
										
										
											2021-06-23 02:14:15 +02:00
										 |  |  |             if (c === undefined || c === "") { | 
					
						
							| 
									
										
										
										
											2022-09-08 21:40:48 +02:00
										 |  |  |                 continue | 
					
						
							| 
									
										
										
										
											2021-06-12 02:58:32 +02:00
										 |  |  |             } | 
					
						
							| 
									
										
										
										
											2022-09-08 21:40:48 +02:00
										 |  |  |             this.clss.add(c) | 
					
						
							|  |  |  |             recordedChange = true | 
					
						
							| 
									
										
										
										
											2021-06-10 01:36:20 +02:00
										 |  |  |         } | 
					
						
							|  |  |  |         if (recordedChange) { | 
					
						
							| 
									
										
										
										
											2022-09-08 21:40:48 +02:00
										 |  |  |             this._constructedHtmlElement?.classList.add(...Array.from(this.clss)) | 
					
						
							| 
									
										
										
										
											2021-06-10 01:36:20 +02:00
										 |  |  |         } | 
					
						
							| 
									
										
										
										
											2022-09-08 21:40:48 +02:00
										 |  |  |         return this | 
					
						
							| 
									
										
										
										
											2021-06-10 01:36:20 +02:00
										 |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-07-18 08:21:16 +00:00
										 |  |  |     public RemoveClass(classes: string): BaseUIElement { | 
					
						
							| 
									
										
										
										
											2022-09-08 21:40:48 +02:00
										 |  |  |         const all = classes.split(" ").map((clsName) => clsName.trim()) | 
					
						
							| 
									
										
										
										
											2022-07-18 08:21:16 +00:00
										 |  |  |         for (let clss of all) { | 
					
						
							|  |  |  |             if (this.clss.has(clss)) { | 
					
						
							| 
									
										
										
										
											2022-09-08 21:40:48 +02:00
										 |  |  |                 this.clss.delete(clss) | 
					
						
							| 
									
										
										
										
											2022-07-18 08:21:16 +00:00
										 |  |  |                 this._constructedHtmlElement?.classList.remove(clss) | 
					
						
							|  |  |  |             } | 
					
						
							| 
									
										
										
										
											2021-06-10 01:36:20 +02:00
										 |  |  |         } | 
					
						
							| 
									
										
										
										
											2022-09-08 21:40:48 +02:00
										 |  |  |         return this | 
					
						
							| 
									
										
										
										
											2021-06-10 01:36:20 +02:00
										 |  |  |     } | 
					
						
							| 
									
										
										
										
											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 { | 
					
						
							| 
									
										
										
										
											2022-09-08 21:40:48 +02:00
										 |  |  |         this.style = style | 
					
						
							| 
									
										
										
										
											2021-06-23 02:14:15 +02:00
										 |  |  |         if (this._constructedHtmlElement !== undefined) { | 
					
						
							| 
									
										
										
										
											2022-09-08 21:40:48 +02:00
										 |  |  |             this._constructedHtmlElement.style.cssText = style | 
					
						
							| 
									
										
										
										
											2021-06-10 01:36:20 +02:00
										 |  |  |         } | 
					
						
							| 
									
										
										
										
											2022-09-08 21:40:48 +02:00
										 |  |  |         return this | 
					
						
							| 
									
										
										
										
											2021-06-10 01:36:20 +02:00
										 |  |  |     } | 
					
						
							| 
									
										
										
										
											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) { | 
					
						
							| 
									
										
										
										
											2022-09-08 21:40:48 +02:00
										 |  |  |             return undefined | 
					
						
							| 
									
										
										
										
											2021-06-10 01:36:20 +02:00
										 |  |  |         } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         if (this._constructedHtmlElement !== undefined) { | 
					
						
							|  |  |  |             return this._constructedHtmlElement | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-09-30 21:41:51 +02:00
										 |  |  |         try { | 
					
						
							| 
									
										
										
										
											2022-09-08 21:40:48 +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) { | 
					
						
							| 
									
										
										
										
											2022-09-08 21:40:48 +02:00
										 |  |  |                 return undefined | 
					
						
							| 
									
										
										
										
											2021-06-10 01:36:20 +02:00
										 |  |  |             } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-09-08 21:40:48 +02:00
										 |  |  |             this._constructedHtmlElement = el | 
					
						
							| 
									
										
										
										
											2021-06-23 02:14:15 +02:00
										 |  |  |             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) { | 
					
						
							| 
									
										
										
										
											2022-09-08 21:40:48 +02:00
										 |  |  |                     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) { | 
					
						
							| 
									
										
										
										
											2022-09-08 21:40:48 +02:00
										 |  |  |                 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) { | 
					
						
							| 
									
										
										
										
											2022-09-08 21:40:48 +02:00
										 |  |  |                         return | 
					
						
							| 
									
										
										
										
											2021-06-23 02:14:15 +02:00
										 |  |  |                     } | 
					
						
							| 
									
										
										
										
											2022-09-08 21:40:48 +02:00
										 |  |  |                     const v = self._onClick() | 
					
						
							|  |  |  |                     if (typeof v === "object") { | 
					
						
							| 
									
										
										
										
											2022-08-22 19:16:37 +02:00
										 |  |  |                         await v | 
					
						
							|  |  |  |                     } | 
					
						
							| 
									
										
										
										
											2021-06-23 02:14:15 +02:00
										 |  |  |                     // @ts-ignore
 | 
					
						
							| 
									
										
										
										
											2022-09-08 21:40:48 +02:00
										 |  |  |                     e.consumed = true | 
					
						
							| 
									
										
										
										
											2021-06-23 02:14:15 +02:00
										 |  |  |                 } | 
					
						
							| 
									
										
										
										
											2022-09-08 21:40:48 +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) { | 
					
						
							| 
									
										
										
										
											2022-09-08 21:40:48 +02:00
										 |  |  |             const domExc = e as DOMException | 
					
						
							| 
									
										
										
										
											2021-06-23 02:14:15 +02:00
										 |  |  |             if (domExc) { | 
					
						
							| 
									
										
										
										
											2022-10-27 01:50:01 +02:00
										 |  |  |                 console.error( | 
					
						
							|  |  |  |                     "An exception occured", | 
					
						
							|  |  |  |                     domExc.code, | 
					
						
							|  |  |  |                     domExc.message, | 
					
						
							|  |  |  |                     domExc.name, | 
					
						
							|  |  |  |                     domExc | 
					
						
							|  |  |  |                 ) | 
					
						
							| 
									
										
										
										
											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-09-08 21:40:48 +02:00
										 |  |  |         this.isDestroyed = true | 
					
						
							| 
									
										
										
										
											2022-01-06 18:51:52 +01:00
										 |  |  |     } | 
					
						
							| 
									
										
										
										
											2021-06-23 02:14:15 +02:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-09-08 21:40:48 +02:00
										 |  |  |     protected abstract InnerConstructElement(): HTMLElement | 
					
						
							| 
									
										
										
										
											2021-09-30 21:41:51 +02:00
										 |  |  | } |