You cannot select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
Hydro/packages/ui-default/components/DOMAttachedObject.ts

132 lines
3.9 KiB
TypeScript

import $ from 'jquery';
let removalChecker = null;
const res = [];
function checkResources() {
for (let i = res.length - 1; i >= 0; --i) {
if (res[i].detached || !document.body.contains(res[i].$dom[0])) {
if (!res[i].detached) res[i].detach();
res.splice(i, 1);
}
}
if (res.length === 0) {
clearInterval(removalChecker);
removalChecker = null;
}
}
function monitorResource(resource) {
res.push(resource);
if (removalChecker === null) {
removalChecker = setInterval(checkResources, 500);
}
}
class DOMAttachedObject {
static uniqueIdCounter = 0;
static DOMAttachKey: string;
static DOMAttachSelector: string;
static DOMDetachSelector: string;
// FIXME should be JQuery<HTMLElement>, but typings for $.transition() wasn't found
$dom: any;
id: number;
detached: boolean;
eventNS: string;
static get($obj) {
const key = this.DOMAttachKey;
return $obj.data(key);
}
static getOrConstruct<T = DOMAttachedObject>($obj, ...args): T {
const $singleObj = $obj.eq(0);
const key = this.DOMAttachKey;
const Protoclass = this as any;
const instance = this.get($singleObj);
if (instance !== undefined) return instance;
const newInstance = new Protoclass($singleObj, ...args);
// $dom may be updated in constructor so that we use $dom instead
// of $singleObj here.
if (!newInstance.$dom) return null;
newInstance.$dom.data(key, newInstance);
return newInstance;
}
static attachAll(container: Document | HTMLElement = document.body, ...args) {
if (process.env.NODE_ENV !== 'production') {
if (!this.DOMAttachSelector) {
throw new Error("'DOMAttachSelector' should be specified");
}
}
if (process.env.NODE_ENV !== 'production') {
console.time(`DOMAttachedObject[${this.DOMAttachKey}]: attachAll`);
}
$(container)
.find(this.DOMAttachSelector)
.addBack(this.DOMAttachSelector)
.get()
.forEach((element) => this.getOrConstruct($(element), ...args));
if (process.env.NODE_ENV !== 'production') {
console.timeEnd(`DOMAttachedObject[${this.DOMAttachKey}]: attachAll`);
}
}
static detachAll(container: Document | HTMLElement = document.body) {
const selector = this.DOMDetachSelector || this.DOMAttachSelector;
if (process.env.NODE_ENV !== 'production') {
if (!selector) {
throw new Error("'DOMDetachSelector' or 'DOMAttachSelector' should be specified");
}
}
if (process.env.NODE_ENV !== 'production') {
console.time(`DOMAttachedObject[${this.DOMAttachKey}]: detachAll`);
}
$(container)
.find(selector)
.addBack(selector)
.get()
.forEach((element) => {
const instance = this.get($(element));
if (instance) {
instance.detach();
}
});
if (process.env.NODE_ENV !== 'production') {
console.timeEnd(`DOMAttachedObject[${this.DOMAttachKey}]: detachAll`);
}
}
static registerLifeCycleHooks(attach = true) {
if (process.env.NODE_ENV !== 'production') {
if (!this.DOMAttachSelector) {
throw new Error("'DOMAttachSelector' should be specified");
}
}
$(document).on('vjContentNew', (e) => this.attachAll(e.target));
$(document).on('vjContentRemove', (e) => this.detachAll(e.target));
if (attach) {
this.attachAll();
}
}
detach() {
if ((this.constructor as any).DOMAttachKey) {
this.$dom.removeData((this.constructor as any).DOMAttachKey);
}
this.detached = true;
}
constructor($dom: JQuery<HTMLElement>, monitorDetach = false) {
// eslint-disable-next-line no-constructor-return
if ($dom == null) return null;
this.$dom = $dom;
this.id = ++DOMAttachedObject.uniqueIdCounter;
this.eventNS = `vj4obj_${this.id}`;
this.detached = false;
if (monitorDetach) monitorResource(this);
}
}
export default DOMAttachedObject as typeof DOMAttachedObject & DOMAttachedObject;