elexis/lib/$Node.ts
defaultkavy 1489b1dba5 add: $Node.isElement()
add: Router/Route event - load
enhance: RouteData.data
add: Router.setStateData()
fix: route record open event not fire
rename: $Node.$hidden -> $Node.__hidden
add: $Element.static_classes, .stateClass(), .addStaticClass, .removeStaticClass, .attribute()
2024-03-16 13:33:05 +08:00

85 lines
3.5 KiB
TypeScript

import { $Element, $State, $Text } from "../index";
import { $Container } from "./$Container";
export abstract class $Node<N extends Node = Node> {
readonly parent?: $Container;
abstract readonly dom: N;
readonly __hidden: boolean = false;
private domEvents: {[key: string]: Map<Function, Function>} = {};
on<K extends keyof HTMLElementEventMap>(type: K, callback: (event: HTMLElementEventMap[K], $node: this) => void, options?: AddEventListenerOptions | boolean) {
if (!this.domEvents[type]) this.domEvents[type] = new Map()
const middleCallback = (e: Event) => callback(e as HTMLElementEventMap[K], this);
this.domEvents[type].set(callback, middleCallback)
this.dom.addEventListener(type, middleCallback, options)
return this;
}
off<K extends keyof HTMLElementEventMap>(type: K, callback: (event: HTMLElementEventMap[K], $node: this) => void, options?: AddEventListenerOptions | boolean) {
const middleCallback = this.domEvents[type]?.get(callback);
if (middleCallback) this.dom.removeEventListener(type, middleCallback as EventListener, options)
return this;
}
once<K extends keyof HTMLElementEventMap>(type: K, callback: (event: HTMLElementEventMap[K], $node: this) => void, options?: AddEventListenerOptions | boolean) {
const onceFn = (event: Event) => {
this.dom.removeEventListener(type, onceFn, options)
callback(event as HTMLElementEventMap[K], this);
};
this.dom.addEventListener(type, onceFn, options)
return this;
}
hide(): boolean;
hide(hide?: boolean | $State<boolean>): this;
hide(hide?: boolean | $State<boolean>) { return $.fluent(this, arguments, () => this.__hidden, () => {
if (hide === undefined) return;
if (hide instanceof $State) { (this as Mutable<$Node>).__hidden = hide.value; hide.use(this, 'hide')}
else (this as Mutable<$Node>).__hidden = hide;
this.parent?.children.render();
return this;
})}
/**Remove this element from parent */
remove() {
this.parent?.children.remove(this).render();
return this;
}
/**Replace $Node with this element */
replace($node: $Node) {
this.parent?.children.replace(this, $node).render();
return this;
}
contains(target: $Node | EventTarget | Node | null) {
if (!target) return false;
if (target instanceof $Node) return this.dom.contains(target.dom);
else if (target instanceof EventTarget) return this.dom.contains($(target).dom)
else return this.dom.contains(target)
}
self(callback: ($node: this) => void) { callback(this); return this; }
inDOM() { return document.contains(this.dom); }
isElement() {
if (this instanceof $Element) return this;
else return undefined;
}
static from(element: HTMLElement | Text): $Node {
if (element.$) return element.$;
else if (element instanceof HTMLElement) {
const node = $(element.tagName) as Mutable<$Node>;
node.dom = element;
if (element.parentElement) node.parent = $(element.parentElement) as $Container;
return node as $Node;
}
else if (element instanceof Text) {
const node = new $Text(element.textContent ?? '') as Mutable<$Node>;
node.dom = element;
if (element.parentElement) node.parent = $(element.parentElement) as $Container;
return node as $Node;
}
throw '$NODE.FROM: NOT SUPPORT TARGET ELEMENT TYPE'
}
}