2024-10-17 11:54:28 +08:00
|
|
|
import { $Node, $NodeEventMap } from "./$Node";
|
2024-02-01 23:47:13 +08:00
|
|
|
|
|
|
|
export interface $ElementOptions {
|
|
|
|
id?: string;
|
|
|
|
class?: string[];
|
2024-04-23 18:18:43 +08:00
|
|
|
dom?: HTMLElement | SVGElement;
|
2024-10-03 23:21:55 +08:00
|
|
|
tagname?: string;
|
2024-02-01 23:47:13 +08:00
|
|
|
}
|
|
|
|
|
2024-10-17 11:54:28 +08:00
|
|
|
export class $Element<H extends HTMLElement | SVGElement = HTMLElement, $EM extends $ElementEventMap = $ElementEventMap, EM extends HTMLElementEventMap = HTMLElementEventMap> extends $Node<H, $EM, EM> {
|
2024-02-01 23:47:13 +08:00
|
|
|
readonly dom: H;
|
2024-03-16 13:33:05 +08:00
|
|
|
private static_classes = new Set<string>();
|
2024-02-01 23:47:13 +08:00
|
|
|
constructor(tagname: string, options?: $ElementOptions) {
|
|
|
|
super();
|
2024-04-23 18:18:43 +08:00
|
|
|
this.dom = this.createDom(tagname, options) as H;
|
2024-02-01 23:47:13 +08:00
|
|
|
this.dom.$ = this;
|
2024-04-20 20:46:11 +08:00
|
|
|
this.setOptions(options);
|
2024-02-01 23:47:13 +08:00
|
|
|
}
|
|
|
|
|
2024-04-23 18:18:43 +08:00
|
|
|
private createDom(tagname: string, options?: $ElementOptions) {
|
|
|
|
if (options?.dom) return options.dom;
|
|
|
|
if (tagname === 'svg') return document.createElementNS("http://www.w3.org/2000/svg", "svg");
|
2024-10-03 23:21:55 +08:00
|
|
|
return document.createElement(options?.tagname ?? tagname);
|
2024-04-23 18:18:43 +08:00
|
|
|
|
|
|
|
}
|
|
|
|
|
2024-04-20 20:46:11 +08:00
|
|
|
setOptions(options: $ElementOptions | undefined) {
|
2024-02-01 23:47:13 +08:00
|
|
|
this.id(options?.id)
|
|
|
|
if (options && options.class) this.class(...options.class)
|
|
|
|
return this;
|
|
|
|
}
|
|
|
|
|
|
|
|
/**Replace id of element. @example Element.id('customId');*/
|
|
|
|
id(): string;
|
|
|
|
id(name: string | undefined): this;
|
2024-04-23 18:18:43 +08:00
|
|
|
id(name?: string | undefined): this | string {return $.fluent(this, arguments, () => this.dom.id, () => $.set(this.dom, 'id', name as any))}
|
2024-02-01 23:47:13 +08:00
|
|
|
|
|
|
|
/**Replace list of class name to element. @example Element.class('name1', 'name2') */
|
|
|
|
class(): DOMTokenList;
|
|
|
|
class(...name: (string | undefined)[]): this;
|
2024-03-16 13:33:05 +08:00
|
|
|
class(...name: (string | undefined)[]): this | DOMTokenList {return $.fluent(this, arguments, () => this.dom.classList, () => {this.dom.classList.forEach(n => this.static_classes.has(n) ?? this.dom.classList.remove(n)); this.dom.classList.add(...name.detype())})}
|
2024-02-01 23:47:13 +08:00
|
|
|
/**Add class name to dom. */
|
|
|
|
addClass(...name: (string | undefined)[]): this {return $.fluent(this, arguments, () => this, () => {this.dom.classList.add(...name.detype())})}
|
|
|
|
/**Remove class name from dom */
|
|
|
|
removeClass(...name: (string | undefined)[]): this {return $.fluent(this, arguments, () => this, () => {this.dom.classList.remove(...name.detype())})}
|
|
|
|
|
2024-03-16 13:33:05 +08:00
|
|
|
staticClass(): Set<string>;
|
|
|
|
staticClass(...name: (string | undefined)[]): this;
|
|
|
|
staticClass(...name: (string | undefined)[]) {return $.fluent(this, arguments, () => this.static_classes, () => {this.removeClass(...this.static_classes); this.static_classes.clear(); this.addStaticClass(...name);})}
|
|
|
|
addStaticClass(...name: (string | undefined)[]) {return $.fluent(this, arguments, () => this, () => {name.detype().forEach(n => this.static_classes.add(n)); this.addClass(...name)})}
|
|
|
|
removeStaticClass(...name: (string | undefined)[]) {return $.fluent(this, arguments, () => this, () => {name.detype().forEach(n => this.static_classes.delete(n)); this.removeClass(...name)})}
|
|
|
|
|
2024-02-01 23:47:13 +08:00
|
|
|
/**Modify css of element. */
|
|
|
|
css(): CSSStyleDeclaration
|
|
|
|
css(style: Partial<CSSStyleDeclaration>): this;
|
|
|
|
css(style?: Partial<CSSStyleDeclaration>) { return $.fluent(this, arguments, () => this.dom.style, () => {Object.assign(this.dom.style, style)})}
|
|
|
|
|
2024-04-23 18:18:43 +08:00
|
|
|
/**
|
|
|
|
* Get or set attribute from this element.
|
|
|
|
* @param qualifiedName Attribute name
|
|
|
|
* @param value Attribute value. Set `null` will remove attribute.
|
|
|
|
*/
|
2024-03-16 13:33:05 +08:00
|
|
|
attribute(qualifiedName: string | undefined): string | null;
|
2024-04-23 18:18:43 +08:00
|
|
|
attribute(qualifiedName: string | undefined, value?: string | number | boolean | null): this;
|
|
|
|
attribute(qualifiedName: string | undefined, value?: string | number | boolean | null): this | string | null {
|
2024-03-16 13:33:05 +08:00
|
|
|
if (!arguments.length) return null;
|
|
|
|
if (arguments.length === 1) {
|
|
|
|
if (qualifiedName === undefined) return null;
|
|
|
|
return this.dom.getAttribute(qualifiedName);
|
|
|
|
}
|
|
|
|
if (arguments.length === 2) {
|
2024-04-23 18:18:43 +08:00
|
|
|
if (!qualifiedName) return this;
|
|
|
|
if (value === null) this.dom.removeAttribute(qualifiedName);
|
|
|
|
else if (value !== undefined) this.dom.setAttribute(qualifiedName, `${value}`);
|
2024-03-16 13:33:05 +08:00
|
|
|
return this;
|
|
|
|
}
|
|
|
|
return this;
|
|
|
|
}
|
|
|
|
|
2024-03-28 20:03:03 +08:00
|
|
|
tabIndex(): number;
|
|
|
|
tabIndex(tabIndex: number): this;
|
2024-04-23 18:18:43 +08:00
|
|
|
tabIndex(tabIndex?: number) { return $.fluent(this, arguments, () => this.dom.tabIndex, () => $.set(this.dom, 'tabIndex', tabIndex as any))}
|
2024-03-28 20:03:03 +08:00
|
|
|
|
2024-10-17 11:54:28 +08:00
|
|
|
focus(options?: FocusOptions) { this.dom.focus(options); return this; }
|
2024-03-28 20:03:03 +08:00
|
|
|
blur() { this.dom.blur(); return this; }
|
2024-02-13 19:38:46 +08:00
|
|
|
|
|
|
|
animate(keyframes: Keyframe[] | PropertyIndexedKeyframes | null, options?: number | KeyframeAnimationOptions, callback?: (animation: Animation) => void) {
|
|
|
|
const animation = this.dom.animate(keyframes, options);
|
2024-10-03 23:21:55 +08:00
|
|
|
if (callback) animation.onfinish = () => callback(animation);
|
|
|
|
return animation;
|
2024-02-13 19:38:46 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
getAnimations(options?: GetAnimationsOptions) { return this.dom.getAnimations(options) }
|
|
|
|
|
2024-03-28 20:03:03 +08:00
|
|
|
get dataset() { return this.dom.dataset }
|
2024-04-24 19:02:36 +08:00
|
|
|
|
|
|
|
domRect(target?: $Element | $DOMRect) {
|
|
|
|
const this_rect = this.dom.getBoundingClientRect();
|
|
|
|
if (!target) return this_rect;
|
|
|
|
const target_rect = target instanceof $Element ? target.dom.getBoundingClientRect() : target;
|
|
|
|
const rect: $DOMRect = {
|
|
|
|
...this_rect,
|
|
|
|
top: this_rect.top - target_rect.top,
|
|
|
|
left: this_rect.left - target_rect.left,
|
|
|
|
right: this_rect.right - target_rect.left,
|
|
|
|
bottom: this_rect.bottom - target_rect.top,
|
|
|
|
x: this_rect.x - target_rect.x,
|
|
|
|
y: this_rect.y - target_rect.y,
|
|
|
|
}
|
|
|
|
return rect;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2024-10-17 11:54:28 +08:00
|
|
|
export type $DOMRect = Omit<DOMRect, 'toJSON'>;
|
|
|
|
|
|
|
|
export interface $ElementEventMap extends $NodeEventMap {}
|