generated from garrettmills/template-npm-typescript
You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
140 lines
4.1 KiB
140 lines
4.1 KiB
|
|
export abstract class ExComponent extends HTMLElement {
|
|
protected static html = ``
|
|
|
|
static get observedAttributes() {
|
|
const map: any = (this as any).exPropertyAttributeMapping
|
|
if ( Array.isArray(map) ) {
|
|
return map.map(x => x.attribute)
|
|
}
|
|
|
|
return []
|
|
}
|
|
|
|
protected readonly shadow = this.attachShadow({ mode: 'open' })
|
|
|
|
private hadFirstRender = false
|
|
|
|
private preFirstRenderDefaultAttributes: any = {}
|
|
|
|
private rendersMap: any = {}
|
|
|
|
private mountListeners: ((el: this) => unknown)[] = []
|
|
|
|
private didMount = false
|
|
|
|
constructor() {
|
|
super()
|
|
this.setPropertyAttributeMappings()
|
|
this.setPropertyRendersMappings()
|
|
this.attachTemplate()
|
|
this.setPropertyQueryMappings()
|
|
}
|
|
|
|
dispatchCustom(name: string, detail: any): boolean {
|
|
return this.dispatchEvent(
|
|
new CustomEvent(name, { detail }),
|
|
)
|
|
}
|
|
|
|
connectedCallback() {
|
|
this.mount()
|
|
this.didMount = true
|
|
this.mountListeners.map(x => x(this))
|
|
this.render()
|
|
}
|
|
|
|
attachTemplate() {
|
|
const template = document.createElement('template')
|
|
template.innerHTML = ((this as any).constructor as typeof ExComponent).html
|
|
this.shadow.appendChild(template.content.cloneNode(true))
|
|
}
|
|
|
|
setPropertyQueryMappings(): void {
|
|
const map: any = (this as any).constructor.exPropertyElementMapping
|
|
if ( Array.isArray(map) ) {
|
|
for ( const mapping of map ) {
|
|
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
|
|
// @ts-ignore
|
|
this[mapping.property] = this.shadow.querySelector(mapping.selector)
|
|
}
|
|
}
|
|
}
|
|
|
|
setPropertyRendersMappings(): void {
|
|
const map: any = (this as any).constructor.exPropertyRendersMapping
|
|
if ( Array.isArray(map) ) {
|
|
for ( const mapping of map ) {
|
|
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
|
|
// @ts-ignore
|
|
delete this[mapping.property]
|
|
|
|
Object.defineProperty(this, mapping.property, {
|
|
get: () => this.rendersMap[mapping.property],
|
|
set: value => {
|
|
this.rendersMap[mapping.property] = value
|
|
|
|
if ( this.hadFirstRender ) {
|
|
this.render()
|
|
}
|
|
},
|
|
})
|
|
}
|
|
}
|
|
}
|
|
|
|
setPropertyAttributeMappings(): void {
|
|
const map: any = (this as any).constructor.exPropertyAttributeMapping
|
|
if ( Array.isArray(map) ) {
|
|
for ( const mapping of map ) {
|
|
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
|
|
// @ts-ignore
|
|
delete this[mapping.property]
|
|
|
|
Object.defineProperty(this, mapping.property, {
|
|
get: () => this.getAttribute(mapping.attribute),
|
|
set: value => {
|
|
if ( !this.hadFirstRender ) {
|
|
this.preFirstRenderDefaultAttributes[mapping.attribute] = value
|
|
return
|
|
}
|
|
|
|
this.setAttribute(mapping.attribute, value)
|
|
},
|
|
})
|
|
}
|
|
}
|
|
}
|
|
|
|
attributeChangedCallback(): void {
|
|
this.render()
|
|
}
|
|
|
|
onMount(callback: (el: this) => unknown) {
|
|
if ( this.didMount ) {
|
|
callback(this)
|
|
} else {
|
|
this.mountListeners.push(callback)
|
|
}
|
|
}
|
|
|
|
tap<T>(callback: (el: this) => T): T {
|
|
return callback(this)
|
|
}
|
|
|
|
// eslint-disable-next-line @typescript-eslint/no-empty-function
|
|
mount(): void {}
|
|
|
|
render(): void {
|
|
if ( !this.hadFirstRender ) {
|
|
for ( const prop of ((this as any).constructor as typeof ExComponent).observedAttributes ) {
|
|
if ( !this.getAttribute(prop) ) {
|
|
this.setAttribute(prop, this.preFirstRenderDefaultAttributes[prop])
|
|
}
|
|
}
|
|
}
|
|
|
|
this.hadFirstRender = true
|
|
}
|
|
}
|