forked from MapComplete/MapComplete
Refactoring: move all code files into a src directory
This commit is contained in:
parent
de99f56ca8
commit
e75d2789d2
389 changed files with 0 additions and 12 deletions
189
src/UI/BaseUIElement.ts
Normal file
189
src/UI/BaseUIElement.ts
Normal file
|
|
@ -0,0 +1,189 @@
|
|||
/**
|
||||
* A thin wrapper around a html element, which allows to generate a HTML-element.
|
||||
*
|
||||
* Assumes a read-only configuration, so it has no 'ListenTo'
|
||||
*/
|
||||
import { Utils } from "../Utils"
|
||||
|
||||
export default abstract class BaseUIElement {
|
||||
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
|
||||
this.SetClass("cursor-pointer")
|
||||
if (this._constructedHtmlElement !== undefined) {
|
||||
this._constructedHtmlElement.onclick = f
|
||||
}
|
||||
return this
|
||||
}
|
||||
|
||||
AttachTo(divId: string) {
|
||||
let element = document.getElementById(divId)
|
||||
if (element === null) {
|
||||
if (Utils.runningFromConsole) {
|
||||
this.ConstructElement()
|
||||
return
|
||||
}
|
||||
throw "SEVERE: could not attach UIElement to " + divId
|
||||
}
|
||||
|
||||
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)
|
||||
}
|
||||
|
||||
if (elementToAdd !== undefined && !alreadyThere) {
|
||||
element.appendChild(elementToAdd)
|
||||
}
|
||||
|
||||
return this
|
||||
}
|
||||
|
||||
public ScrollIntoView() {
|
||||
if (this._constructedHtmlElement === undefined) {
|
||||
return
|
||||
}
|
||||
this._constructedHtmlElement?.scrollIntoView({
|
||||
behavior: "smooth",
|
||||
block: "start",
|
||||
})
|
||||
}
|
||||
/**
|
||||
* Adds all the relevant classes, space separated
|
||||
*/
|
||||
public SetClass(clss: string) {
|
||||
if (clss == undefined) {
|
||||
return this
|
||||
}
|
||||
const all = clss.split(" ").map((clsName) => clsName.trim())
|
||||
let recordedChange = false
|
||||
for (let c of all) {
|
||||
c = c.trim()
|
||||
if (this.clss.has(clss)) {
|
||||
continue
|
||||
}
|
||||
if (c === undefined || c === "") {
|
||||
continue
|
||||
}
|
||||
this.clss.add(c)
|
||||
recordedChange = true
|
||||
}
|
||||
if (recordedChange) {
|
||||
this._constructedHtmlElement?.classList.add(...Array.from(this.clss))
|
||||
}
|
||||
return this
|
||||
}
|
||||
|
||||
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)
|
||||
}
|
||||
}
|
||||
return this
|
||||
}
|
||||
|
||||
public HasClass(clss: string): boolean {
|
||||
return this.clss.has(clss)
|
||||
}
|
||||
|
||||
public SetStyle(style: string): BaseUIElement {
|
||||
this.style = style
|
||||
if (this._constructedHtmlElement !== undefined) {
|
||||
this._constructedHtmlElement.style.cssText = style
|
||||
}
|
||||
return this
|
||||
}
|
||||
|
||||
/**
|
||||
* The same as 'Render', but creates a HTML element instead of the HTML representation
|
||||
*/
|
||||
public ConstructElement(): HTMLElement {
|
||||
if (typeof window === undefined) {
|
||||
return undefined
|
||||
}
|
||||
|
||||
if (this._constructedHtmlElement !== undefined) {
|
||||
return this._constructedHtmlElement
|
||||
}
|
||||
|
||||
try {
|
||||
const el = this.InnerConstructElement()
|
||||
|
||||
if (el === undefined) {
|
||||
return undefined
|
||||
}
|
||||
|
||||
this._constructedHtmlElement = el
|
||||
const style = this.style
|
||||
if (style !== undefined && style !== "") {
|
||||
el.style.cssText = style
|
||||
}
|
||||
if (this.clss?.size > 0) {
|
||||
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
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
if (this._onClick !== undefined) {
|
||||
const self = this
|
||||
el.onclick = async (e) => {
|
||||
// @ts-ignore
|
||||
if (e.consumed) {
|
||||
return
|
||||
}
|
||||
const v = self._onClick()
|
||||
if (typeof v === "object") {
|
||||
await v
|
||||
}
|
||||
// @ts-ignore
|
||||
e.consumed = true
|
||||
}
|
||||
el.classList.add("cursor-pointer")
|
||||
}
|
||||
|
||||
return el
|
||||
} catch (e) {
|
||||
const domExc = e as DOMException
|
||||
if (domExc) {
|
||||
console.error(
|
||||
"An exception occured",
|
||||
domExc.code,
|
||||
domExc.message,
|
||||
domExc.name,
|
||||
domExc
|
||||
)
|
||||
}
|
||||
console.error(e)
|
||||
}
|
||||
}
|
||||
|
||||
public AsMarkdown(): string {
|
||||
throw "AsMarkdown is not implemented; implement it in the subclass"
|
||||
}
|
||||
|
||||
public Destroy() {
|
||||
this.isDestroyed = true
|
||||
}
|
||||
|
||||
protected abstract InnerConstructElement(): HTMLElement
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue