v0.3.0
- remove: class $EventMethod, $Event. - change: $EventManager rewrite logic. - change: most $Node base node element generic type have new EM(EventMap) parameter. - new: $EventTarget. - change: $Node extends $EventTarget. - change: mix dom events and $EventManager events into $EventTarget.on().off().once(). - fix: $Container.insert() process synchronous when passing an not async function - new: $Window element. - fix: $(document.documentElement) throw error. - new: $KeyboardManager, $FocusManager, $PointerManager. - new: $ global methods: - $.events() return new $EventManager. - $.pointers() return new $PointerManager. - $.keys() return new $KeyboardManager. - $.focus() return new $FocusManager. - $.call() - change: $Media extends $HTMLElement - change: $Anchor.href() support $State parameter. - new: $State.convert()
This commit is contained in:
parent
a57246e6e1
commit
4c078c26b6
11
$index.ts
11
$index.ts
@ -1,4 +1,4 @@
|
||||
import { $EventManager, $State, $StateArgument, $StateOption } from "./index";
|
||||
import { $EventManager, $EventMap, $EventTarget, $FocusManager, $PointerManager, $State, $StateArgument, $StateOption } from "./index";
|
||||
import { $Node } from "./lib/node/$Node"
|
||||
import { $Document } from "./lib/node/$Document"
|
||||
import { $Anchor } from "./lib/node/$Anchor";
|
||||
@ -19,6 +19,8 @@ import { $Util } from "./lib/$Util";
|
||||
import { $HTMLElement } from "./lib/node/$HTMLElement";
|
||||
import { $Async } from "./lib/node/$Async";
|
||||
import { $Video } from "./lib/node/$Video";
|
||||
import { $Window } from "./lib/$Window";
|
||||
import { $KeyboardManager } from "./lib/$KeyboardManager";
|
||||
|
||||
export type $ = typeof $;
|
||||
export function $<E extends $Element = $Element>(query: `::${string}`): E[];
|
||||
@ -26,6 +28,7 @@ export function $<E extends $Element = $Element>(query: `:${string}`): E | null;
|
||||
export function $(element: null): null;
|
||||
export function $<K extends keyof $.TagNameTypeMap>(resolver: K): $.TagNameTypeMap[K];
|
||||
export function $<K extends string>(resolver: K): $Container;
|
||||
export function $(window: Window): $Window;
|
||||
export function $<H extends HTMLElement>(htmlElement: H): $.$HTMLElementMap<H>;
|
||||
export function $<H extends Element>(element: H): $Element;
|
||||
export function $<N extends $Node>(node: N): N;
|
||||
@ -51,6 +54,7 @@ export function $(resolver: any) {
|
||||
if (resolver.$) return resolver.$;
|
||||
else return $Util.from(resolver);
|
||||
}
|
||||
if (resolver instanceof Window) { return $Window.$ }
|
||||
throw `$: NOT SUPPORT TARGET ELEMENT TYPE ('${resolver}')`
|
||||
}
|
||||
export namespace $ {
|
||||
@ -238,7 +242,10 @@ export namespace $ {
|
||||
return $.TagNameElementMap;
|
||||
}
|
||||
|
||||
export function events<N extends string>(...eventname: N[]) { return new $EventManager<{[keys in N]: any[]}>().register(...eventname) }
|
||||
export function events<EM extends $EventMap>() { return new $EventManager<EM> }
|
||||
export function pointers($node: $Node) { return new $PointerManager($node) }
|
||||
export function keys($target: $EventTarget) { return new $KeyboardManager($target) }
|
||||
export function focus() { return new $FocusManager() }
|
||||
|
||||
export function call<T>(fn: () => T): T { return fn() }
|
||||
}
|
||||
|
@ -86,5 +86,6 @@ value$.set(0)
|
||||
```
|
||||
|
||||
## Extensions
|
||||
1. [@elexis/router](https://github.com/elexisjs/router): Router for Single Page App.
|
||||
2. [@elexis/layout](https://github.com/elexisjs/layout): Build waterfall/justified layout with automatic compute content size and position.
|
||||
1. [@elexis/router](https://git.defaultkavy.com/elexis/router): Router for Single Page App.
|
||||
2. [@elexis/layout](https://git.defaultkavy.com/elexis/layout): Build waterfall/justified layout with automatic compute content size and position.
|
||||
3. [@elexis/view](https://git.defaultkavy.com/elexis/view): Multiple content switch handler.
|
20
index.ts
20
index.ts
@ -3,6 +3,10 @@ declare global {
|
||||
interface Array<T> {
|
||||
detype<F extends any, O>(...types: F[]): Array<Exclude<T, F | undefined | void>>
|
||||
}
|
||||
interface Set<T> {
|
||||
get array(): T[]
|
||||
sort(handler: ((a: T, b: T) => number) | undefined): T[];
|
||||
}
|
||||
type OrMatrix<T> = T | OrMatrix<T>[];
|
||||
type OrArray<T> = T | T[];
|
||||
type OrPromise<T> = T | Promise<T>;
|
||||
@ -29,17 +33,27 @@ Array.prototype.detype = function <T extends any, O>(this: O[], ...types: T[]) {
|
||||
else for (const type of types) if (typeof item !== typeof type) return true; else return false;
|
||||
}) as Exclude<O, T | undefined | void>[];
|
||||
}
|
||||
Object.defineProperties(Set.prototype, {
|
||||
array: { get: function <T>(this: Set<T>) { return Array.from(this)} }
|
||||
})
|
||||
Set.prototype.sort = function <T>(this: Set<T>, handler: ((a: T, b: T) => number) | undefined) { return this.array.sort(handler)}
|
||||
export * from "./$index";
|
||||
export * from "./lib/$NodeManager";
|
||||
export * from "./lib/$EventManager";
|
||||
export * from "./lib/$EventTarget";
|
||||
export * from "./lib/$KeyboardManager";
|
||||
export * from "./lib/$FocusManager";
|
||||
export * from "./lib/$PointerManager";
|
||||
export * from "./lib/$Window";
|
||||
export * from "./lib/$State";
|
||||
export * from "./lib/node/$Node";
|
||||
export * from "./lib/node/$Anchor";
|
||||
export * from "./lib/node/$Element";
|
||||
export * from "./lib/$NodeManager";
|
||||
export * from "./lib/node/$HTMLElement";
|
||||
export * from "./lib/node/$Text";
|
||||
export * from "./lib/node/$Container";
|
||||
export * from "./lib/node/$Button";
|
||||
export * from "./lib/node/$Form";
|
||||
export * from "./lib/$EventManager";
|
||||
export * from "./lib/$State";
|
||||
export * from "./lib/node/$Select";
|
||||
export * from "./lib/node/$Option";
|
||||
export * from "./lib/node/$OptGroup";
|
||||
|
@ -1,65 +1,32 @@
|
||||
export abstract class $EventMethod<EM> {
|
||||
abstract events: $EventManager<EM>;
|
||||
//@ts-expect-error
|
||||
on<K extends keyof EM>(type: K, callback: (...args: EM[K]) => any) { this.events.on(type, callback); return this }
|
||||
//@ts-expect-error
|
||||
off<K extends keyof EM>(type: K, callback: (...args: EM[K]) => any) { this.events.off(type, callback); return this }
|
||||
//@ts-expect-error
|
||||
once<K extends keyof EM>(type: K, callback: (...args: EM[K]) => any) { this.events.once(type, callback); return this }
|
||||
}
|
||||
export class $EventManager<EM> {
|
||||
eventMap = new Map<string, $Event>();
|
||||
register(...names: string[]) {
|
||||
names.forEach(name => {
|
||||
const event = new $Event(name);
|
||||
this.eventMap.set(event.name, event);
|
||||
})
|
||||
return this;
|
||||
}
|
||||
delete(name: string) { this.eventMap.delete(name); return this }
|
||||
export class $EventManager<EM extends $EventMap> {
|
||||
private eventMap = new Map<string, Set<Function>>();
|
||||
//@ts-expect-error
|
||||
fire<K extends keyof EM>(type: K, ...args: EM[K]) {
|
||||
const event = this.get(type)
|
||||
//@ts-expect-error
|
||||
if (event instanceof $Event) event.fire(...args);
|
||||
this.eventMap.get(type as string)?.forEach(fn => fn(...args as []));
|
||||
return this
|
||||
}
|
||||
//@ts-expect-error
|
||||
on<K extends keyof EM>(type: K, callback: (...args: EM[K]) => any) {
|
||||
this.get(type).add(callback);
|
||||
const set = this.eventMap.get(type as string) ?? this.eventMap.set(type as string, new Set()).get(type as string);
|
||||
set?.add(callback);
|
||||
return this
|
||||
}
|
||||
//@ts-expect-error
|
||||
off<K extends keyof EM>(type: K, callback: (...args: EM[K]) => any) {
|
||||
this.get(type).delete(callback);
|
||||
this.eventMap.get(type as string)?.delete(callback);
|
||||
return this
|
||||
}
|
||||
//@ts-expect-error
|
||||
once<K extends keyof EM>(type: K, callback: (...args: EM[K]) => any) {
|
||||
//@ts-expect-error
|
||||
const onceFn = (...args: EM[K]) => {
|
||||
this.get(type).delete(onceFn);
|
||||
const onceFn = (...args: []) => {
|
||||
this.eventMap.get(type as string)?.delete(onceFn);
|
||||
//@ts-expect-error
|
||||
callback(...args);
|
||||
}
|
||||
this.get(type).add(onceFn);
|
||||
const set = this.eventMap.get(type as string) ?? this.eventMap.set(type as string, new Set()).get(type as string)
|
||||
set?.add(onceFn);
|
||||
return this;
|
||||
}
|
||||
}
|
||||
|
||||
get<K extends keyof EM>(type: K) {
|
||||
//@ts-expect-error
|
||||
const event = this.eventMap.get(type);
|
||||
if (!event) throw new Error('EVENT NOT EXIST')
|
||||
return event;
|
||||
}
|
||||
}
|
||||
export class $Event {
|
||||
name: string;
|
||||
private callbackList = new Set<Function>()
|
||||
constructor(name: string) {
|
||||
this.name = name;
|
||||
}
|
||||
fire(...args: any[]) { this.callbackList.forEach(callback => callback(...args)) }
|
||||
add(callback: Function) { this.callbackList.add(callback) }
|
||||
delete(callback: Function) { this.callbackList.delete(callback) }
|
||||
}
|
||||
export interface $EventMap {}
|
50
lib/$EventTarget.ts
Normal file
50
lib/$EventTarget.ts
Normal file
@ -0,0 +1,50 @@
|
||||
import { $EventManager, $EventMap } from "./$EventManager";
|
||||
|
||||
export abstract class $EventTarget<$EM extends $EventMap = $EventMap, EM extends GlobalEventHandlersEventMap = GlobalEventHandlersEventMap> {
|
||||
private domEvents: Partial<{[key in keyof EM]: Map<Function, Function>}> = {};
|
||||
readonly events = new $EventManager<$EM>();
|
||||
abstract dom: EventTarget
|
||||
|
||||
//@ts-expect-error
|
||||
on<K extends keyof $EM>(type: K | K[], callback: (...args: $EM[K]) => any): this;
|
||||
on<K extends keyof EM>(type: K | K[], callback: (event: EM[K], $node: this) => any, options?: AddEventListenerOptions | boolean): this;
|
||||
on<K extends string>(type: K | K[], callback: (event: Event, $node: this) => any, options?: AddEventListenerOptions | boolean): this;
|
||||
on<K extends keyof EM>(types: K | K[], callback: (event: EM[K], $node: this) => any, options?: AddEventListenerOptions | boolean) {
|
||||
types = $.orArrayResolve(types);
|
||||
for (const type of types) {
|
||||
if (!this.domEvents[type]) this.domEvents[type] = new Map()
|
||||
const handler = (e: Event) => { callback(e as EM[K], this); }
|
||||
this.domEvents[type].set(callback, handler);
|
||||
this.events.on(type as any, callback);
|
||||
this.dom.addEventListener(type as string, handler, options);
|
||||
}
|
||||
return this;
|
||||
}
|
||||
|
||||
//@ts-expect-error
|
||||
off<K extends keyof $EM>(type: K, callback: (...args: $EM[K]) => any): this;
|
||||
off<K extends keyof EM>(type: K, callback: (event: EM[K], $node: this) => any, options?: AddEventListenerOptions | boolean): this;
|
||||
off<K extends string>(type: K, callback: (event: Event, $node: this) => any, options?: AddEventListenerOptions | boolean): this;
|
||||
off<K extends keyof EM>(type: K, callback: (event: EM[K], $node: this) => any, options?: AddEventListenerOptions | boolean) {
|
||||
const middleCallback = this.domEvents[type]?.get(callback);
|
||||
if (middleCallback) this.dom.removeEventListener(type as string, middleCallback as EventListener, options);
|
||||
this.events.off(type as any, callback);
|
||||
return this;
|
||||
}
|
||||
|
||||
//@ts-expect-error
|
||||
once<K extends keyof $EM>(type: K, callback: (...args: $EM[K]) => any): this;
|
||||
once<K extends keyof EM>(type: K, callback: (event: EM[K], $node: this) => any, options?: AddEventListenerOptions | boolean): this;
|
||||
once<K extends string>(type: K, callback: (event: Event, $node: this) => any, options?: AddEventListenerOptions | boolean): this;
|
||||
once<K extends keyof EM>(type: K, callback: (event: EM[K], $node: this) => any, options?: AddEventListenerOptions | boolean) {
|
||||
const onceFn = (event: Event) => {
|
||||
this.dom.removeEventListener(type as string, onceFn, options)
|
||||
callback(event as EM[K], this);
|
||||
};
|
||||
this.dom.addEventListener(type as string, onceFn, options);
|
||||
this.events.once(type as any, callback);
|
||||
return this;
|
||||
}
|
||||
|
||||
trigger(event: string) { this.dom.dispatchEvent(new Event(event)); return }
|
||||
}
|
175
lib/$FocusManager.ts
Normal file
175
lib/$FocusManager.ts
Normal file
@ -0,0 +1,175 @@
|
||||
import { $Element } from "..";
|
||||
|
||||
export class $FocusManager {
|
||||
layerMap = new Map<number, $FocusLayer>();
|
||||
currentLayer?: $FocusLayer;
|
||||
historyList: $FocusLayer[] = [];
|
||||
constructor() {}
|
||||
|
||||
layer(id: number) {
|
||||
const layer = this.layerMap.get(id) ?? new $FocusLayer(id);
|
||||
this.layerMap.set(layer.id, layer);
|
||||
return layer;
|
||||
}
|
||||
next() { return this.select($FocusNavigation.Next) }
|
||||
prev() { return this.select($FocusNavigation.Prev) }
|
||||
up() { return this.select($FocusNavigation.Up) }
|
||||
down() { return this.select($FocusNavigation.Down) }
|
||||
right() { return this.select($FocusNavigation.Right) }
|
||||
left() { return this.select($FocusNavigation.Left) }
|
||||
|
||||
blur() {
|
||||
this.currentLayer?.blur();
|
||||
return this;
|
||||
}
|
||||
|
||||
select(navigation: $FocusNavigation) {
|
||||
this.currentLayer = this.currentLayer ?? [...this.layerMap.values()].at(0);
|
||||
if (!this.currentLayer) return this;
|
||||
const $focused = this.currentLayer.currentFocus;
|
||||
const eleList = this.currentLayer.elementSet.array;
|
||||
if (!$focused) { this.currentLayer.focus(this.currentLayer.beforeBlur ?? eleList.at(0)); return this; }
|
||||
const eleIndex = eleList.indexOf($focused)
|
||||
switch (navigation) {
|
||||
case $FocusNavigation.Next:
|
||||
case $FocusNavigation.Prev: {
|
||||
let targetIndex = navigation === 0 ? eleIndex + 1 : eleIndex - 1;
|
||||
if (targetIndex === eleList.length && this.currentLayer.loop()) targetIndex = 0;
|
||||
else if (targetIndex === -1 && !this.currentLayer.loop()) targetIndex = 0;
|
||||
this.currentLayer.focus(eleList.at(targetIndex));
|
||||
break;
|
||||
}
|
||||
case $FocusNavigation.Down:
|
||||
case $FocusNavigation.Left:
|
||||
case $FocusNavigation.Right:
|
||||
case $FocusNavigation.Up: {
|
||||
const focusedPosition = $focused.coordinate();
|
||||
if (!focusedPosition) break;
|
||||
const focusedCoordinate = $.call(() => {
|
||||
switch (navigation) {
|
||||
case $FocusNavigation.Up: return {y: focusedPosition.y, x: focusedPosition.x / 2}
|
||||
case $FocusNavigation.Down: return {y: focusedPosition.y + focusedPosition.height, x: focusedPosition.x / 2}
|
||||
case $FocusNavigation.Left: return {y: focusedPosition.y / 2, x: focusedPosition.x}
|
||||
case $FocusNavigation.Right: return {y: focusedPosition.y / 2, x: focusedPosition.x + focusedPosition.width}
|
||||
}
|
||||
})
|
||||
const eleInfoList = eleList.map($ele => {
|
||||
if ($ele === $focused) return;
|
||||
const elePosition = $ele.coordinate();
|
||||
if (!elePosition) return;
|
||||
const eleCoordinate = $.call(() => {
|
||||
switch (navigation) {
|
||||
case $FocusNavigation.Up: return {y: elePosition.y + elePosition.height, x: elePosition.x / 2};
|
||||
case $FocusNavigation.Down: return {y: elePosition.y, x: elePosition.x / 2};
|
||||
case $FocusNavigation.Left: return {y: elePosition.y / 2, x: elePosition.x + elePosition.width};
|
||||
case $FocusNavigation.Right: return {y: elePosition.y / 2, x: elePosition.x};
|
||||
}
|
||||
})
|
||||
return {
|
||||
$ele, elePosition,
|
||||
distance: Math.sqrt((eleCoordinate.x - focusedCoordinate.x) ** 2 + (eleCoordinate.y - focusedCoordinate.y) ** 2)
|
||||
}
|
||||
}).detype(undefined).filter(({elePosition}) => {
|
||||
switch (navigation) {
|
||||
case $FocusNavigation.Up: if (elePosition.y + elePosition.height >= focusedPosition.y) return false; break;
|
||||
case $FocusNavigation.Down: if (elePosition.y <= focusedPosition.y + focusedPosition.height) return false; break;
|
||||
case $FocusNavigation.Left: if (elePosition.x + elePosition.width >= focusedPosition.x) return false; break;
|
||||
case $FocusNavigation.Right: if (elePosition.x <= focusedPosition.x + focusedPosition.width) return false; break;
|
||||
}
|
||||
return true;
|
||||
})
|
||||
const $target = eleInfoList.sort((a, b) => a.distance - b.distance).at(0)?.$ele;
|
||||
this.currentLayer.focus($target);
|
||||
}
|
||||
}
|
||||
return this;
|
||||
}
|
||||
}
|
||||
|
||||
export enum $FocusNavigation { Next, Prev, Up, Down, Right, Left }
|
||||
|
||||
export class $FocusLayer {
|
||||
id: number;
|
||||
elementSet = new Set<$Element>();
|
||||
entrySet = new Set<$Element>();
|
||||
beforeBlur?: $Element;
|
||||
currentFocus?: $Element;
|
||||
private __$property__ = {
|
||||
loop: true,
|
||||
scrollThreshold: 0
|
||||
}
|
||||
constructor(id: number) {
|
||||
this.id = id
|
||||
this.add = this.add.bind(this);
|
||||
this.entry = this.entry.bind(this);
|
||||
}
|
||||
|
||||
add($elements: OrArray<$Element>) {
|
||||
$.orArrayResolve($elements).forEach($element => {
|
||||
this.elementSet.add($element);
|
||||
$element.tabIndex(0);
|
||||
});
|
||||
return this;
|
||||
}
|
||||
|
||||
remove($element: $Element) {
|
||||
this.elementSet.delete($element);
|
||||
return this;
|
||||
}
|
||||
|
||||
entry($elements: OrArray<$Element>) {
|
||||
$.orArrayResolve($elements).forEach(this.entrySet.add.bind(this.entrySet))
|
||||
return this;
|
||||
}
|
||||
|
||||
focus($element: $Element | undefined) {
|
||||
if (!$element) return this;
|
||||
$element.hide(false);
|
||||
const {scrollTop, scrollLeft} = document.documentElement;
|
||||
const position = $.call(() => {
|
||||
const rect = $element.domRect()
|
||||
return {
|
||||
left: rect.left + scrollLeft,
|
||||
top: rect.top + scrollTop,
|
||||
right: rect.right + scrollLeft,
|
||||
bottom: rect.bottom + scrollTop,
|
||||
height: rect.height,
|
||||
width: rect.width
|
||||
}
|
||||
})
|
||||
const {scrollThreshold} = this.__$property__;
|
||||
this.blur();
|
||||
this.currentFocus = $element;
|
||||
if (scrollTop > position.top - scrollThreshold // scroll after item threshold
|
||||
|| scrollTop > position.bottom + scrollThreshold
|
||||
) document.documentElement.scrollTo({left: position.left - scrollThreshold, top: position.top - scrollThreshold});
|
||||
if (scrollTop + innerHeight < position.top + scrollThreshold // scroll before item
|
||||
|| scrollTop + innerHeight < position.bottom + scrollThreshold
|
||||
) document.documentElement.scrollTo({left: position.left - scrollThreshold, top: (position.bottom - innerHeight) + scrollThreshold});
|
||||
$element.attribute('focus', '')
|
||||
$element.focus({preventScroll: true});
|
||||
return this;
|
||||
}
|
||||
|
||||
blur() {
|
||||
if (!this.currentFocus) return this;
|
||||
this.beforeBlur = this.currentFocus;
|
||||
this.currentFocus.attribute('focus', null);
|
||||
this.currentFocus?.blur();
|
||||
this.currentFocus = undefined;
|
||||
return this;
|
||||
}
|
||||
|
||||
removeAll() {
|
||||
this.elementSet.clear();
|
||||
return this;
|
||||
}
|
||||
|
||||
loop(): boolean;
|
||||
loop(boolean: boolean): this;
|
||||
loop(boolean?: boolean) { return $.fluent(this, arguments, () => this.__$property__.loop, () => $.set(this.__$property__, 'loop', boolean)) }
|
||||
|
||||
scrollThreshold(): number;
|
||||
scrollThreshold(number: number): this;
|
||||
scrollThreshold(number?: number) { return $.fluent(this, arguments, () => this.__$property__.scrollThreshold, () => $.set(this.__$property__, 'scrollThreshold', number)) }
|
||||
}
|
52
lib/$KeyboardManager.ts
Normal file
52
lib/$KeyboardManager.ts
Normal file
@ -0,0 +1,52 @@
|
||||
import { $EventTarget } from "./$EventTarget";
|
||||
import { $Util } from "./$Util";
|
||||
|
||||
export class $KeyboardManager {
|
||||
keyMap = new Map<string, $KeyboardEventMap>();
|
||||
protected conditional?: ((event: KeyboardEvent) => boolean | undefined);
|
||||
constructor($element: $EventTarget) {
|
||||
$element.on('keydown', e => { if (this.conditional && !this.conditional(e)) return; this.keyMap.get(e.key)?.keydown.forEach(fn => fn(e)) })
|
||||
$element.on('keyup', e => { if (this.conditional && !this.conditional(e)) return; this.keyMap.get(e.key)?.keyup.forEach(fn => fn(e)) })
|
||||
$element.on('keypress', e => { if (this.conditional && !this.conditional(e)) return; this.keyMap.get(e.key)?.keypress.forEach(fn => fn(e)) })
|
||||
}
|
||||
|
||||
if(callback: (event: KeyboardEvent) => boolean | undefined) {
|
||||
this.conditional = callback;
|
||||
return this;
|
||||
}
|
||||
|
||||
assigns(keys: OrArray<string>, on: OrArray<$KeyboardEventType>, callback: $KeyboardEventHandler) {
|
||||
keys = $Util.orArrayResolve(keys);
|
||||
on = $Util.orArrayResolve(on);
|
||||
for (const key of keys) {
|
||||
const eventData: $KeyboardEventMap = this.keyMap.get(key) ?? {keydown: new Set(), keypress: new Set(), keyup: new Set()};
|
||||
for (const event of on) {
|
||||
eventData[event].add(callback);
|
||||
}
|
||||
this.keyMap.set(key, eventData);
|
||||
}
|
||||
return this;
|
||||
}
|
||||
|
||||
unassign(keys: OrArray<string>, on?: OrArray<$KeyboardEventType>, callback?: (event: KeyboardEvent) => void) {
|
||||
keys = $Util.orArrayResolve(keys);
|
||||
on = on ? $Util.orArrayResolve(on) : ['keydown', 'keypress', 'keyup'];
|
||||
for (const key of keys) {
|
||||
const eventData: $KeyboardEventMap = this.keyMap.get(key) ?? {keydown: new Set(), keypress: new Set(), keyup: new Set()};
|
||||
for (const event of on) {
|
||||
if (callback) eventData[event].delete(callback);
|
||||
else eventData[event].clear();
|
||||
}
|
||||
this.keyMap.set(key, eventData);
|
||||
}
|
||||
return this;
|
||||
}
|
||||
|
||||
keydown(keys: OrArray<string>, callback: $KeyboardEventHandler) { this.assigns(keys, 'keydown', callback); return this; }
|
||||
keyup(keys: OrArray<string>, callback: $KeyboardEventHandler) { this.assigns(keys, 'keyup', callback); return this; }
|
||||
keypress(keys: OrArray<string>, callback: $KeyboardEventHandler) { this.assigns(keys, 'keypress', callback); return this; }
|
||||
}
|
||||
|
||||
export type $KeyboardEventType = 'keydown' | 'keyup' | 'keypress';
|
||||
export type $KeyboardEventHandler = (event: KeyboardEvent) => void;
|
||||
type $KeyboardEventMap = {[key in $KeyboardEventType]: Set<$KeyboardEventHandler>};
|
@ -1,6 +1,5 @@
|
||||
import { $Container } from "./node/$Container";
|
||||
import { $Node } from "./node/$Node";
|
||||
import { $Text } from "./node/$Text";
|
||||
|
||||
export class $NodeManager {
|
||||
readonly $container: $Container;
|
||||
@ -12,13 +11,13 @@ export class $NodeManager {
|
||||
add(element: $Node, position = -1) {
|
||||
if (position === -1 || this.childList.size - 1 === position) {
|
||||
this.childList.add(element);
|
||||
(element as Mutable<$Node>).parent = this.$container;
|
||||
} else {
|
||||
const children = [...this.childList]
|
||||
children.splice(position, 0, element);
|
||||
this.childList.clear();
|
||||
children.forEach(child => this.childList.add(child));
|
||||
}
|
||||
(element as Mutable<$Node>).parent = this.$container;
|
||||
}
|
||||
|
||||
remove(element: $Node) {
|
||||
@ -39,6 +38,7 @@ export class $NodeManager {
|
||||
target.remove();
|
||||
this.childList.clear();
|
||||
array.forEach(node => this.childList.add(node));
|
||||
(replace as Mutable<$Node>).parent = this.$container;
|
||||
return this;
|
||||
}
|
||||
|
||||
@ -49,18 +49,22 @@ export class $NodeManager {
|
||||
while (nodeList.length || domList.length) { // while nodeList or domList has item
|
||||
const [node, dom] = [nodeList.at(0), domList.at(0)];
|
||||
if (!dom) { if (node && !appendedNodeList.includes(node)) node.remove(); nodeList.shift()}
|
||||
else if (!node) { if (!dom.$.__hidden) this.dom.append(dom); domList.shift();}
|
||||
else if (!node) { if (!dom.$.hide()) this.dom.append(dom); domList.shift();}
|
||||
else if (dom !== node) {
|
||||
if (!dom.$.__hidden) { this.dom.insertBefore(dom, node); appendedNodeList.push(dom) }
|
||||
if (!dom.$.hide()) { this.dom.insertBefore(dom, node); appendedNodeList.push(dom) }
|
||||
domList.shift();
|
||||
}
|
||||
else {
|
||||
if (dom.$.__hidden) this.dom.removeChild(dom);
|
||||
if (dom.$.hide()) this.dom.removeChild(dom);
|
||||
domList.shift(); nodeList.shift();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
indexOf(target: $Node) {
|
||||
return this.array.indexOf(target);
|
||||
}
|
||||
|
||||
get array() {return [...this.childList.values()]};
|
||||
|
||||
get dom() {return this.$container.dom}
|
||||
|
105
lib/$PointerManager.ts
Normal file
105
lib/$PointerManager.ts
Normal file
@ -0,0 +1,105 @@
|
||||
import { $EventManager, $EventMap } from "./$EventManager";
|
||||
import { type $Node } from "./node/$Node";
|
||||
|
||||
export class $PointerManager extends $EventManager<$PointerManagerEventMap> {
|
||||
$node: $Node;
|
||||
map = new Map<number, $Pointer>();
|
||||
constructor($node: $Node) {
|
||||
super();
|
||||
this.$node = $node;
|
||||
this.$node.on('pointerdown', (e) => this.down(e))
|
||||
this.$node.on('pointerup', (e) => this.up(e))
|
||||
this.$node.on('pointermove', (e) => this.move(e))
|
||||
this.$node.on('pointercancel', (e) => this.cancel(e))
|
||||
}
|
||||
|
||||
protected down(e: PointerEvent) {
|
||||
const pointer = new $Pointer(this, this.toData(e), $(e.target!))
|
||||
this.map.set(pointer.id, pointer);
|
||||
this.fire('down', pointer, e);
|
||||
}
|
||||
|
||||
protected up(e: PointerEvent) {
|
||||
const pointer = this.map.get(e.pointerId);
|
||||
if (!pointer) return;
|
||||
this.map.delete(e.pointerId);
|
||||
this.fire('up', pointer, e);
|
||||
}
|
||||
|
||||
protected move(e: PointerEvent) {
|
||||
const pointer = this.map.get(e.pointerId);
|
||||
if (!pointer) return;
|
||||
this.map.set(pointer.id, pointer);
|
||||
pointer.update(this.toData(e));
|
||||
this.fire('move', pointer, e);
|
||||
}
|
||||
|
||||
protected cancel(e: PointerEvent) {
|
||||
const pointer = this.map.get(e.pointerId);
|
||||
if (!pointer) return;
|
||||
pointer.update(this.toData(e));
|
||||
this.map.delete(pointer.id);
|
||||
this.fire('cancel', pointer, e);
|
||||
}
|
||||
|
||||
protected toData(e: PointerEvent): $PointerData {
|
||||
return {
|
||||
id: e.pointerId,
|
||||
type: e.pointerType as PointerType,
|
||||
width: e.width,
|
||||
height: e.height,
|
||||
x: e.x,
|
||||
y: e.y,
|
||||
movement_x: e.movementX,
|
||||
movement_y: e.movementY
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
export interface $PointerManagerEventMap extends $EventMap {
|
||||
up: [$Pointer, MouseEvent];
|
||||
down: [$Pointer, MouseEvent];
|
||||
move: [$Pointer, MouseEvent];
|
||||
cancel: [$Pointer, MouseEvent];
|
||||
}
|
||||
|
||||
export interface $Pointer extends $PointerData {}
|
||||
export class $Pointer {
|
||||
initial_x: number;
|
||||
initial_y: number;
|
||||
$target: $Node;
|
||||
protected manager: $PointerManager;
|
||||
constructor(manager: $PointerManager, data: $PointerData, target: $Node) {
|
||||
Object.assign(this, data);
|
||||
this.manager = manager;
|
||||
this.$target = target;
|
||||
this.initial_x = data.x;
|
||||
this.initial_y = data.y;
|
||||
}
|
||||
|
||||
get move_x() { return this.x - this.initial_x }
|
||||
get move_y() { return this.y - this.initial_y }
|
||||
|
||||
update(data: $PointerData) {
|
||||
Object.assign(this, data);
|
||||
return this;
|
||||
}
|
||||
|
||||
delete() {
|
||||
this.manager.map.delete(this.id);
|
||||
return this;
|
||||
}
|
||||
}
|
||||
|
||||
export interface $PointerData {
|
||||
id: number;
|
||||
type: PointerType;
|
||||
width: number;
|
||||
height: number;
|
||||
x: number;
|
||||
y: number;
|
||||
movement_x: number;
|
||||
movement_y: number;
|
||||
}
|
||||
|
||||
export type PointerType = 'mouse' | 'pen' | 'touch'
|
@ -17,6 +17,16 @@ export class $State<T> {
|
||||
this.linkStates.forEach($state => $state.update());
|
||||
}
|
||||
|
||||
static toJSON(object: Object): Object {
|
||||
const data = {};
|
||||
for (let [key, value] of Object.entries(object)) {
|
||||
if (value instanceof $State) value = value.toJSON();
|
||||
else if (value instanceof Object) $State.toJSON(value);
|
||||
Object.assign(data, {[key]: value})
|
||||
}
|
||||
return data;
|
||||
}
|
||||
|
||||
protected update() {
|
||||
// update element content for eatch attributes
|
||||
for (const [node, attrList] of this.attributes.entries()) {
|
||||
@ -36,37 +46,31 @@ export class $State<T> {
|
||||
}
|
||||
}
|
||||
|
||||
toString(): string {
|
||||
if (this.options.format) return this.options.format(this.value);
|
||||
if (this.value instanceof Object) return JSON.stringify(this.toJSON());
|
||||
return `${this.value}`
|
||||
}
|
||||
|
||||
use<O extends Object, K extends keyof O>(object: O, attrName: K) {
|
||||
const attrList = this.attributes.get(object)
|
||||
if (attrList) attrList.add(attrName);
|
||||
else this.attributes.set(object, new Set<string | number | symbol>().add(attrName))
|
||||
}
|
||||
|
||||
convert(fn: (value: T) => string) {
|
||||
return new $State<T>(this as any, {format: fn});
|
||||
}
|
||||
|
||||
get value(): T {
|
||||
return this._value instanceof $State ? this._value.value as T : this._value;
|
||||
}
|
||||
|
||||
toString(): string {
|
||||
if (this.options.format) return this.options.format(this.value);
|
||||
if (this.value instanceof Object) return JSON.stringify(this.toJSON());
|
||||
return `${this.value}`
|
||||
}
|
||||
|
||||
toJSON(): Object {
|
||||
if (this.value instanceof $State) return this.value.toJSON();
|
||||
if (this.value instanceof Object) return $State.toJSON(this.value);
|
||||
else return this.toString();
|
||||
}
|
||||
|
||||
static toJSON(object: Object): Object {
|
||||
const data = {};
|
||||
for (let [key, value] of Object.entries(object)) {
|
||||
if (value instanceof $State) value = value.toJSON();
|
||||
else if (value instanceof Object) $State.toJSON(value);
|
||||
Object.assign(data, {[key]: value})
|
||||
}
|
||||
return data;
|
||||
}
|
||||
|
||||
get value(): T {
|
||||
return this._value instanceof $State ? this._value.value as T : this._value;
|
||||
}
|
||||
};
|
||||
|
||||
export type $StateArgument<T> = $State<T> | undefined | (T extends (infer R)[] ? R : T);
|
13
lib/$Util.ts
13
lib/$Util.ts
@ -42,14 +42,17 @@ export namespace $Util {
|
||||
export function from(element: Node): $Node {
|
||||
if (element.$) return element.$;
|
||||
if (element.nodeName.toLowerCase() === 'body') return new $Container('body', {dom: element as HTMLBodyElement});
|
||||
if (element.nodeName.toLowerCase() === 'head') return new $Container('head', {dom: element as HTMLHeadElement});
|
||||
if (element.nodeName.toLowerCase() === '#document') return $Document.from(element as Document);
|
||||
else if (element instanceof HTMLElement) {
|
||||
const instance = $.TagNameElementMap[element.tagName.toLowerCase() as keyof typeof $.TagNameElementMap];
|
||||
const $node = instance === $Container
|
||||
//@ts-expect-error
|
||||
? new instance(element.tagName, {dom: element})
|
||||
//@ts-expect-error
|
||||
: new instance({dom: element} as any);
|
||||
const $node = !instance
|
||||
? new $Container(element.tagName, {dom: element})
|
||||
: instance === $Container
|
||||
//@ts-expect-error
|
||||
? new instance(element.tagName, {dom: element})
|
||||
//@ts-expect-error
|
||||
: new instance({dom: element} as any);
|
||||
if ($node instanceof $Container) for (const childnode of Array.from($node.dom.childNodes)) {
|
||||
$node.children.add($(childnode as any));
|
||||
}
|
||||
|
9
lib/$Window.ts
Normal file
9
lib/$Window.ts
Normal file
@ -0,0 +1,9 @@
|
||||
import { $EventManager, $EventMap } from "./$EventManager";
|
||||
import { $EventTarget } from "./$EventTarget";
|
||||
|
||||
export class $Window<EM extends $WindowEventMap = $WindowEventMap> extends $EventTarget<EM, WindowEventMap> {
|
||||
static $ = new $Window();
|
||||
readonly dom = window;
|
||||
}
|
||||
|
||||
export interface $WindowEventMap extends $EventMap {}
|
@ -1,3 +1,4 @@
|
||||
import { $StateArgument } from "../$State";
|
||||
import { $Container, $ContainerOptions } from "./$Container";
|
||||
|
||||
export interface AnchorOptions extends $ContainerOptions {}
|
||||
@ -15,8 +16,8 @@ export class $Anchor extends $Container<HTMLAnchorElement> {
|
||||
}
|
||||
/**Set URL of anchor element. */
|
||||
href(): string;
|
||||
href(url: string | undefined): this;
|
||||
href(url?: string | undefined) { return $.fluent(this, arguments, () => this.dom.href, () => {if (url) this.dom.href = url}) }
|
||||
href(url: $StateArgument<string>): this;
|
||||
href(url?: $StateArgument<string>) { return $.fluent(this, arguments, () => this.dom.href, () => $.set(this.dom, 'href', url)) }
|
||||
/**Link open with this window, new tab or other */
|
||||
target(): $AnchorTarget | undefined;
|
||||
target(target: $AnchorTarget | undefined): this;
|
||||
|
@ -1,12 +1,12 @@
|
||||
import { $Element, $ElementOptions } from "./$Element";
|
||||
import { $Element } from "./$Element";
|
||||
import { $NodeManager } from "../$NodeManager";
|
||||
import { $Node } from "./$Node";
|
||||
import { $State, $StateArgument } from "../$State";
|
||||
import { $Text } from "./$Text";
|
||||
import { $HTMLElement, $HTMLElementOptions } from "./$HTMLElement";
|
||||
import { $HTMLElement, $HTMLElementEventMap, $HTMLElementOptions } from "./$HTMLElement";
|
||||
|
||||
export interface $ContainerOptions extends $HTMLElementOptions {}
|
||||
export class $Container<H extends HTMLElement = HTMLElement> extends $HTMLElement<H> {
|
||||
export class $Container<H extends HTMLElement = HTMLElement, EM extends $ContainerEventMap = $ContainerEventMap> extends $HTMLElement<H, EM> {
|
||||
readonly children: $NodeManager = new $NodeManager(this);
|
||||
constructor(tagname: string, options?: $ContainerOptions) {
|
||||
super(tagname, options)
|
||||
@ -23,7 +23,11 @@ export class $Container<H extends HTMLElement = HTMLElement> extends $HTMLElemen
|
||||
private __position_cursor = 0;
|
||||
/**Insert element to this element */
|
||||
insert(children: $ContainerContentBuilder<this>, position = -1): this { return $.fluent(this, arguments, () => this, async () => {
|
||||
if (children instanceof Function) children = await children(this); // resolve function
|
||||
if (children instanceof Function) { // resolve function and promise
|
||||
let cache = children(this);
|
||||
if (cache instanceof Promise) children = await cache;
|
||||
else children = cache;
|
||||
} else if (children instanceof Promise) { children = await children }
|
||||
children = $.orArrayResolve(children);
|
||||
// Set position cursor depend negative or positive number, position will count from last index when position is negative.
|
||||
this.__position_cursor = position < 0 ? this.children.array.length + position : position;
|
||||
@ -53,11 +57,12 @@ export class $Container<H extends HTMLElement = HTMLElement> extends $HTMLElemen
|
||||
return this;
|
||||
}
|
||||
|
||||
//**Query selector one of child element */
|
||||
$<E extends $Element>(query: string): E | null { return $(this.dom.querySelector(query)) as E | null }
|
||||
|
||||
//**Query selector of child elements */
|
||||
$all<E extends $Element>(query: string): E[] { return Array.from(this.dom.querySelectorAll(query)).map($dom => $($dom) as E) }
|
||||
$<E extends $Element = $Element>(query: `::${string}`): E[];
|
||||
$<E extends $Element = $Element>(query: `:${string}`): E | null;
|
||||
$(query: string) {
|
||||
if (query.startsWith('::')) return Array.from(document.querySelectorAll(query.replace(/^::/, ''))).map(dom => $(dom));
|
||||
else if (query.startsWith(':')) return $(document.querySelector(query.replace(/^:/, '')));
|
||||
}
|
||||
|
||||
get scrollHeight() { return this.dom.scrollHeight }
|
||||
get scrollWidth() { return this.dom.scrollWidth }
|
||||
@ -74,3 +79,5 @@ export class $Container<H extends HTMLElement = HTMLElement> extends $HTMLElemen
|
||||
export type $ContainerContentBuilder<P extends $Container> = $ContainerContentGroup | (($node: P) => OrPromise<$ContainerContentGroup>)
|
||||
export type $ContainerContentGroup = OrMatrix<OrPromise<$ContainerContentType>>
|
||||
export type $ContainerContentType = $Node | string | undefined | $State<any> | null
|
||||
|
||||
export interface $ContainerEventMap extends $HTMLElementEventMap {}
|
@ -1,4 +1,4 @@
|
||||
import { $Node } from "./$Node";
|
||||
import { $Node, $NodeEventMap } from "./$Node";
|
||||
|
||||
export interface $ElementOptions {
|
||||
id?: string;
|
||||
@ -7,7 +7,7 @@ export interface $ElementOptions {
|
||||
tagname?: string;
|
||||
}
|
||||
|
||||
export class $Element<H extends HTMLElement | SVGElement = HTMLElement> extends $Node<H> {
|
||||
export class $Element<H extends HTMLElement | SVGElement = HTMLElement, $EM extends $ElementEventMap = $ElementEventMap, EM extends HTMLElementEventMap = HTMLElementEventMap> extends $Node<H, $EM, EM> {
|
||||
readonly dom: H;
|
||||
private static_classes = new Set<string>();
|
||||
constructor(tagname: string, options?: $ElementOptions) {
|
||||
@ -81,7 +81,7 @@ export class $Element<H extends HTMLElement | SVGElement = HTMLElement> extends
|
||||
tabIndex(tabIndex: number): this;
|
||||
tabIndex(tabIndex?: number) { return $.fluent(this, arguments, () => this.dom.tabIndex, () => $.set(this.dom, 'tabIndex', tabIndex as any))}
|
||||
|
||||
focus() { this.dom.focus(); return this; }
|
||||
focus(options?: FocusOptions) { this.dom.focus(options); return this; }
|
||||
blur() { this.dom.blur(); return this; }
|
||||
|
||||
animate(keyframes: Keyframe[] | PropertyIndexedKeyframes | null, options?: number | KeyframeAnimationOptions, callback?: (animation: Animation) => void) {
|
||||
@ -112,3 +112,5 @@ export class $Element<H extends HTMLElement | SVGElement = HTMLElement> extends
|
||||
}
|
||||
|
||||
export type $DOMRect = Omit<DOMRect, 'toJSON'>;
|
||||
|
||||
export interface $ElementEventMap extends $NodeEventMap {}
|
@ -1,7 +1,7 @@
|
||||
import { $Element, $ElementOptions } from "./$Element";
|
||||
import { $Element, $ElementEventMap, $ElementOptions } from "./$Element";
|
||||
|
||||
export interface $HTMLElementOptions extends $ElementOptions {}
|
||||
export class $HTMLElement<H extends HTMLElement = HTMLElement> extends $Element<H> {
|
||||
export class $HTMLElement<H extends HTMLElement = HTMLElement, $EM extends $HTMLElementEventMap = $HTMLElementEventMap> extends $Element<H, $EM> {
|
||||
constructor(tagname: string, options?: $HTMLElementOptions) {
|
||||
super(tagname, options)
|
||||
}
|
||||
@ -63,3 +63,5 @@ export class $HTMLElement<H extends HTMLElement = HTMLElement> extends $Element<
|
||||
get offsetTop() { return this.dom.offsetTop }
|
||||
get offsetWidth() { return this.dom.offsetWidth }
|
||||
}
|
||||
|
||||
export interface $HTMLElementEventMap extends $ElementEventMap {}
|
@ -1,8 +1,9 @@
|
||||
import { $State, $StateArgument } from "../$State";
|
||||
import { $Element, $ElementOptions } from "./$Element";
|
||||
import { $StateArgument } from "../$State";
|
||||
import { $ElementOptions } from "./$Element";
|
||||
import { $HTMLElement } from "./$HTMLElement";
|
||||
|
||||
export interface $MediaOptions extends $ElementOptions {}
|
||||
export class $Media<H extends HTMLMediaElement> extends $Element<H> {
|
||||
export class $Media<H extends HTMLMediaElement> extends $HTMLElement<H> {
|
||||
constructor(tagname: string, options?: $MediaOptions) {
|
||||
super(tagname, options);
|
||||
}
|
||||
|
@ -1,41 +1,20 @@
|
||||
import { $, $Element, $State, $Text } from "../../index";
|
||||
import { $Container } from "./$Container";
|
||||
import { $EventTarget } from "../$EventTarget";
|
||||
import { $, $Element, $EventManager, $State, $HTMLElement, $Container } from "../../index";
|
||||
|
||||
export abstract class $Node<N extends Node = Node> {
|
||||
export abstract class $Node<N extends Node = Node, $EM extends $NodeEventMap = $NodeEventMap, EM extends GlobalEventHandlersEventMap = GlobalEventHandlersEventMap> extends $EventTarget<$EM, EM> {
|
||||
abstract readonly dom: N;
|
||||
readonly __hidden: boolean = false;
|
||||
private domEvents: {[key: string]: Map<Function, Function>} = {};
|
||||
protected __$property__ = {
|
||||
hidden: false,
|
||||
coordinate: undefined as $NodeCoordinate | undefined
|
||||
}
|
||||
readonly parent?: $Container;
|
||||
|
||||
on<K extends keyof HTMLElementEventMap>(type: K, callback: (event: HTMLElementEventMap[K], $node: this) => any, 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) => any, 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) => any, 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>, render?: boolean): this;
|
||||
hide(hide?: boolean | $State<boolean>, render = true) { return $.fluent(this, arguments, () => this.__hidden, () => {
|
||||
hide(hide?: boolean | $State<boolean>, render = true) { return $.fluent(this, arguments, () => this.__$property__.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;
|
||||
if (hide instanceof $State) { this.__$property__.hidden = hide.value; hide.use(this, 'hide')}
|
||||
else this.__$property__.hidden = hide;
|
||||
if (render) this.parent?.children.render();
|
||||
return this;
|
||||
})}
|
||||
@ -59,8 +38,13 @@ export abstract class $Node<N extends Node = Node> {
|
||||
else return this.dom.contains(target)
|
||||
}
|
||||
|
||||
self(callback: ($node: this) => void) { callback(this); return this; }
|
||||
coordinate(): $NodeCoordinate | undefined;
|
||||
coordinate(coordinate: $NodeCoordinate): this;
|
||||
coordinate(coordinate?: $NodeCoordinate) { return $.fluent(this, arguments, () => this.__$property__.coordinate, () => $.set(this.__$property__, 'coordinate', coordinate))}
|
||||
|
||||
self(callback: OrArray<($node: this) => void>) { $.orArrayResolve(callback).forEach(fn => fn(this)); return this; }
|
||||
inDOM() { return document.contains(this.dom); }
|
||||
|
||||
isElement(): this is $Element {
|
||||
if (this instanceof $Element) return true;
|
||||
else return false;
|
||||
@ -69,4 +53,23 @@ export abstract class $Node<N extends Node = Node> {
|
||||
if (this instanceof $Element) return this;
|
||||
else return null;
|
||||
}
|
||||
get htmlElement(): $HTMLElement | null {
|
||||
if (this instanceof $HTMLElement) return this;
|
||||
else return null;
|
||||
}
|
||||
}
|
||||
|
||||
export interface $NodeCoordinate {
|
||||
x: number;
|
||||
y: number;
|
||||
height: number;
|
||||
width: number;
|
||||
}
|
||||
|
||||
export interface $NodeEventMap {
|
||||
|
||||
}
|
||||
|
||||
type $HTMLElementEventMap<$N> = {
|
||||
[keys in keyof HTMLElementEventMap]: [event: HTMLElementEventMap[keys], $this: $N];
|
||||
};
|
@ -1,7 +1,7 @@
|
||||
{
|
||||
"name": "elexis",
|
||||
"description": "Build Web in Native JavaScript Syntax",
|
||||
"version": "0.2.5",
|
||||
"version": "0.3.0",
|
||||
"author": {
|
||||
"name": "defaultkavy",
|
||||
"email": "defaultkavy@gmail.com",
|
||||
|
Loading…
Reference in New Issue
Block a user