add: $Container with HTMLElement properties/methods
new: $Document
move: $Node.from() => $Util.from()
This commit is contained in:
defaultkavy 2024-04-24 19:02:36 +08:00
parent 36cca4765b
commit ec62a580b6
9 changed files with 99 additions and 18 deletions

View File

@ -1,4 +1,6 @@
import { $Node, $State, $StateOption } from "./index";
import { $State, $StateArgument, $StateOption } from "./index";
import { $Node } from "./lib/node/$Node"
import { $Document } from "./lib/node/$Document"
import { $Anchor } from "./lib/node/$Anchor";
import { $Button } from "./lib/node/$Button";
import { $Form } from "./lib/node/$Form";
@ -60,7 +62,7 @@ export function $(resolver: any) {
}
} else return new $Container(resolver);
}
if (resolver instanceof HTMLElement || resolver instanceof Text || resolver instanceof SVGElement) {
if (resolver instanceof Node) {
if (resolver.$) return resolver.$;
else return $Util.from(resolver);
}
@ -71,6 +73,7 @@ export namespace $ {
export let anchorPreventDefault: boolean = false;
export const routers = new Set<Router>;
export const TagNameElementMap = {
'document': $Document,
'body': $Container,
'a': $Anchor,
'p': $Container,
@ -158,10 +161,11 @@ export namespace $ {
* @returns
*/
export function set<O, K extends keyof O>(
object: O, key: K,
object: O,
key: K,
value: O[K] extends (...args: any) => any
? (Parameters<O[K]> | $State<Parameters<O[K]> | undefined>)
: (O[K] | undefined | $State<O[K] | undefined>),
? (undefined | $StateArgument<Parameters<O[K]>>)
: (undefined | $StateArgument<O[K]>),
methodKey?: string) {
if (value === undefined) return;
if (value instanceof $State && object instanceof Node) {

View File

@ -49,3 +49,4 @@ export * from "./lib/node/$OptGroup";
export * from "./lib/node/$Textarea";
export * from "./lib/node/$Image";
export * from "./lib/node/$AsyncNode";
export * from "./lib/node/$Document";

View File

@ -1,5 +1,7 @@
import { $State } from "./$State";
import { $AsyncNode } from "./node/$AsyncNode";
import { $Container } from "./node/$Container";
import { $Document } from "./node/$Document";
import { $Node } from "./node/$Node";
import { $SVGElement } from "./node/$SVGElement";
import { $Text } from "./node/$Text";
@ -38,14 +40,18 @@ export namespace $Util {
return new $State<T>(value)
}
export function from(element: HTMLElement | Text | Node): $Node {
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() === '#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 ? new instance(element.tagName, {dom: element}) : new instance({dom: element} as any);
const $node = instance === $Container
? 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));
$node.children.add($(childnode as any));
}
return $node as $Node;
}

View File

@ -1,12 +1,13 @@
import { $Node } from "./$Node";
export interface $AsyncNodeOptions {
dom?: Node;
}
export class $AsyncNode<N extends $Node = $Node> extends $Node {
dom: Node = document.createElement('async');
loaded: boolean = false;
constructor($node?: Promise<N>) {
constructor(options?: $AsyncNodeOptions) {
super()
this.dom.$ = this;
if ($node) $node.then($node => this._loaded($node));
}
await<T extends $Node = $Node>($node: Promise<T>) {

View File

@ -1,7 +1,7 @@
import { $Element, $ElementOptions } from "./$Element";
import { $NodeManager } from "../$NodeManager";
import { $Node } from "./$Node";
import { $State } from "../$State";
import { $State, $StateArgument } from "../$State";
import { $Text } from "./$Text";
import { $HTMLElement, $HTMLElementOptions } from "./$HTMLElement";
@ -47,6 +47,17 @@ export class $Container<H extends HTMLElement = HTMLElement> extends $HTMLElemen
//**Query selector of child elements */
$all<E extends $Element>(query: string) { return Array.from(this.dom.querySelectorAll(query)).map($dom => $($dom) as E) }
get scrollHeight() { return this.dom.scrollHeight }
get scrollWidth() { return this.dom.scrollWidth }
scrollTop(): number;
scrollTop(scrollTop: $StateArgument<number> | undefined): this
scrollTop(scrollTop?: $StateArgument<number> | undefined) { return $.fluent(this, arguments, () => this.dom.scrollTop, () => $.set(this.dom, 'scrollTop', scrollTop as any))}
scrollLeft(): number;
scrollLeft(scrollLeft: $StateArgument<number> | undefined): this
scrollLeft(scrollLeft?: $StateArgument<number> | undefined) { return $.fluent(this, arguments, () => this.dom.scrollLeft, () => $.set(this.dom, 'scrollLeft', scrollLeft as any))}
}
export type $ContainerContentBuilder<P extends $Container> = OrMatrix<$ContainerContentType> | (($node: P) => OrMatrix<$ContainerContentType>)

40
lib/node/$Document.ts Normal file
View File

@ -0,0 +1,40 @@
import { $Element, $DOMRect } from "./$Element";
import { $Node } from "./$Node";
export class $Document extends $Node {
dom: Node;
constructor(document: Document) {
super()
this.dom = document;
this.dom.$ = this;
}
domRect(target?: $Element | $DOMRect) {
const this_rect: $DOMRect = {
bottom: innerHeight,
height: innerHeight,
left: 0,
right: innerWidth,
top: 0,
width: innerWidth,
x: 0,
y: 0
};
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;
}
static from(document: Document) {
if (document.$ instanceof $Document) return document.$
else return new $Document(document);
}
}

View File

@ -92,4 +92,22 @@ export class $Element<H extends HTMLElement | SVGElement = HTMLElement> extends
getAnimations(options?: GetAnimationsOptions) { return this.dom.getAnimations(options) }
get dataset() { return this.dom.dataset }
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;
}
}
export type $DOMRect = Omit<DOMRect, 'toJSON'>;

View File

@ -60,9 +60,9 @@ export abstract class $Node<N extends Node = Node> {
self(callback: ($node: this) => void) { callback(this); return this; }
inDOM() { return document.contains(this.dom); }
isElement(): $Element | undefined {
if (this instanceof $Element) return this;
else return undefined;
isElement(): this is $Element {
if (this instanceof $Element) return true;
else return false;
}
get parent() {

View File

@ -1,7 +1,7 @@
{
"name": "fluentx",
"description": "Fast, fluent, simple web builder",
"version": "0.0.7",
"version": "0.0.9",
"type": "module",
"module": "index.ts",
"author": {