v0.0.2
new: $Image enhance: $ parameter accept null and undefined new: $Dialog new: $Canvas add: $.rem() fix: Router resolve path error with param resolve fix: $Container.insert() render error with undefined child change: Route builder function parameter with one object enhance: $Node event listener with event type add: $Node.inDOM()
This commit is contained in:
parent
8e37f65f4f
commit
658df2d8e6
58
$index.ts
58
$index.ts
@ -7,13 +7,21 @@ import { $Container } from "./lib/$Container";
|
|||||||
import { $Element } from "./lib/$Element";
|
import { $Element } from "./lib/$Element";
|
||||||
import { $Label } from "./lib/$Label";
|
import { $Label } from "./lib/$Label";
|
||||||
import { Router } from "./lib/Router/Router";
|
import { Router } from "./lib/Router/Router";
|
||||||
|
import { $Image } from "./lib/$Image";
|
||||||
|
import { $Canvas } from "./lib/$Canvas";
|
||||||
|
import { $Dialog } from "./lib/$Dialog";
|
||||||
export type $ = typeof $;
|
export type $ = typeof $;
|
||||||
|
export function $(element: null): null;
|
||||||
export function $<K extends keyof $.TagNameTypeMap>(resolver: K): $.TagNameTypeMap[K];
|
export function $<K extends keyof $.TagNameTypeMap>(resolver: K): $.TagNameTypeMap[K];
|
||||||
export function $<K extends string>(resolver: K): $Container;
|
export function $<K extends string>(resolver: K): $Container;
|
||||||
export function $<H extends HTMLElement>(htmlElement: H): $.HTMLElementTo$ElementMap<H>;
|
export function $<H extends HTMLElement>(htmlElement: H): $.HTMLElementTo$ElementMap<H>;
|
||||||
export function $<H extends Element>(element: H): $Element;
|
export function $<H extends Element>(element: H): $Element;
|
||||||
export function $<H extends EventTarget>(element: H): $Element;
|
export function $<H extends EventTarget>(element: H): $Element;
|
||||||
|
export function $(element: null | HTMLElement | EventTarget): $Element | null;
|
||||||
|
export function $(element: undefined): undefined;
|
||||||
export function $(resolver: any) {
|
export function $(resolver: any) {
|
||||||
|
if (typeof resolver === 'undefined') return resolver;
|
||||||
|
if (resolver === null) return resolver;
|
||||||
if (typeof resolver === 'string') {
|
if (typeof resolver === 'string') {
|
||||||
if (resolver in $.TagNameElementMap) {
|
if (resolver in $.TagNameElementMap) {
|
||||||
const instance = $.TagNameElementMap[resolver as keyof typeof $.TagNameElementMap]
|
const instance = $.TagNameElementMap[resolver as keyof typeof $.TagNameElementMap]
|
||||||
@ -25,6 +33,9 @@ export function $(resolver: any) {
|
|||||||
case $Label: return new $Label();
|
case $Label: return new $Label();
|
||||||
case $Form: return new $Form();
|
case $Form: return new $Form();
|
||||||
case $Button: return new $Button();
|
case $Button: return new $Button();
|
||||||
|
case $Image: return new $Image();
|
||||||
|
case $Canvas: return new $Canvas();
|
||||||
|
case $Dialog: return new $Dialog();
|
||||||
}
|
}
|
||||||
} else return new $Container(resolver);
|
} else return new $Container(resolver);
|
||||||
}
|
}
|
||||||
@ -60,7 +71,10 @@ export namespace $ {
|
|||||||
'input': $Input,
|
'input': $Input,
|
||||||
'label': $Label,
|
'label': $Label,
|
||||||
'button': $Button,
|
'button': $Button,
|
||||||
'form': $Form
|
'form': $Form,
|
||||||
|
'img': $Image,
|
||||||
|
'dialog': $Dialog,
|
||||||
|
'canvas': $Canvas
|
||||||
}
|
}
|
||||||
export type TagNameTypeMap = {
|
export type TagNameTypeMap = {
|
||||||
[key in keyof typeof $.TagNameElementMap]: InstanceType<typeof $.TagNameElementMap[key]>;
|
[key in keyof typeof $.TagNameElementMap]: InstanceType<typeof $.TagNameElementMap[key]>;
|
||||||
@ -74,6 +88,10 @@ export namespace $ {
|
|||||||
: H extends HTMLAnchorElement ? $Anchor
|
: H extends HTMLAnchorElement ? $Anchor
|
||||||
: H extends HTMLButtonElement ? $Button
|
: H extends HTMLButtonElement ? $Button
|
||||||
: H extends HTMLFormElement ? $Form
|
: H extends HTMLFormElement ? $Form
|
||||||
|
: H extends HTMLImageElement ? $Image
|
||||||
|
: H extends HTMLFormElement ? $Form
|
||||||
|
: H extends HTMLCanvasElement ? $Canvas
|
||||||
|
: H extends HTMLDialogElement ? $Dialog
|
||||||
: $Element<H>;
|
: $Element<H>;
|
||||||
|
|
||||||
export function fluent<T, A, V>(instance: T, args: IArguments, value: () => V, action: (...args: any[]) => void) {
|
export function fluent<T, A, V>(instance: T, args: IArguments, value: () => V, action: (...args: any[]) => void) {
|
||||||
@ -101,14 +119,48 @@ export namespace $ {
|
|||||||
return target;
|
return target;
|
||||||
}
|
}
|
||||||
|
|
||||||
export function set<O, K extends keyof O>(object: O, key: K, value: any) {
|
export function set<O, K extends keyof O>(object: O, key: K, value: any, methodKey?: string) {
|
||||||
if (value !== undefined) object[key] = value;
|
if (value === undefined) return;
|
||||||
|
if (value instanceof $State && object instanceof Node) {
|
||||||
|
value.use(object.$, methodKey ?? key as any);
|
||||||
|
object[key] = value.value;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
object[key] = value;
|
||||||
}
|
}
|
||||||
|
|
||||||
export function state<T>(value: T) {
|
export function state<T>(value: T) {
|
||||||
return new $State<T>(value)
|
return new $State<T>(value)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export async function resize(object: Blob, size: number): Promise<string> {
|
||||||
|
return new Promise(resolve => {
|
||||||
|
const reader = new FileReader();
|
||||||
|
reader.onload = (e) => {
|
||||||
|
const $img = $('img');
|
||||||
|
$img.once('load', e => {
|
||||||
|
const $canvas = $('canvas');
|
||||||
|
const context = $canvas.getContext('2d');
|
||||||
|
const ratio = $img.height() / $img.width();
|
||||||
|
const [w, h] = [
|
||||||
|
ratio > 1 ? size / ratio : size,
|
||||||
|
ratio > 1 ? size : size * ratio,
|
||||||
|
]
|
||||||
|
$canvas.height(h).width(w);
|
||||||
|
context?.drawImage($img.dom, 0, 0, w, h);
|
||||||
|
resolve($canvas.toDataURL(object.type))
|
||||||
|
})
|
||||||
|
if (!e.target) throw "$.resize(): e.target is null";
|
||||||
|
$img.src(e.target.result as string);
|
||||||
|
}
|
||||||
|
reader.readAsDataURL(object);
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
export function rem(amount: number = 1) {
|
||||||
|
return parseInt(getComputedStyle(document.documentElement).fontSize) * amount
|
||||||
|
}
|
||||||
|
|
||||||
/**Build multiple element in once. */
|
/**Build multiple element in once. */
|
||||||
export function builder<F extends BuildNodeFunction, R extends ReturnType<F>>(bulder: F, params: [...Parameters<F>][], callback?: BuilderSelfFunction<R>): R[]
|
export function builder<F extends BuildNodeFunction, R extends ReturnType<F>>(bulder: F, params: [...Parameters<F>][], callback?: BuilderSelfFunction<R>): R[]
|
||||||
export function builder<F extends BuildNodeFunction, R extends ReturnType<F>>(bulder: [F, ...Parameters<F>], size: number, callback?: BuilderSelfFunction<R>): R[]
|
export function builder<F extends BuildNodeFunction, R extends ReturnType<F>>(bulder: [F, ...Parameters<F>], size: number, callback?: BuilderSelfFunction<R>): R[]
|
||||||
|
4
index.ts
4
index.ts
@ -15,7 +15,9 @@ declare global {
|
|||||||
type InputType = "button" | "checkbox" | "color" | "date" | "datetime-local" | "email" | "file" | "hidden" | "image" | "month" | "number" | "password" | "radio" | "range" | "reset" | "search" | "submit" | "tel" | "text" | "time" | "url" | "week";
|
type InputType = "button" | "checkbox" | "color" | "date" | "datetime-local" | "email" | "file" | "hidden" | "image" | "month" | "number" | "password" | "radio" | "range" | "reset" | "search" | "submit" | "tel" | "text" | "time" | "url" | "week";
|
||||||
type ButtonType = "submit" | "reset" | "button" | "menu";
|
type ButtonType = "submit" | "reset" | "button" | "menu";
|
||||||
type TextDirection = 'ltr' | 'rtl' | 'auto' | '';
|
type TextDirection = 'ltr' | 'rtl' | 'auto' | '';
|
||||||
|
type ImageDecoding = "async" | "sync" | "auto";
|
||||||
|
type ImageLoading = "eager" | "lazy";
|
||||||
|
type ContructorType<T> = { new (...args: any[]): T }
|
||||||
interface Node {
|
interface Node {
|
||||||
$: import('./lib/$Node').$Node;
|
$: import('./lib/$Node').$Node;
|
||||||
}
|
}
|
||||||
|
@ -1,5 +1,6 @@
|
|||||||
import { $Container, $ContainerOptions } from "./$Container";
|
import { $Container, $ContainerOptions } from "./$Container";
|
||||||
import { FormElementMethod, $FormElementMethod } from "./$Form";
|
import { FormElementMethod, $FormElementMethod } from "./$Form";
|
||||||
|
import { $State } from "./$State";
|
||||||
export interface $ButtonOptions extends $ContainerOptions {}
|
export interface $ButtonOptions extends $ContainerOptions {}
|
||||||
//@ts-expect-error
|
//@ts-expect-error
|
||||||
export interface $Button extends $FormElementMethod {}
|
export interface $Button extends $FormElementMethod {}
|
||||||
@ -10,8 +11,8 @@ export class $Button extends $Container<HTMLButtonElement> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
disabled(): boolean;
|
disabled(): boolean;
|
||||||
disabled(disabled: boolean): this;
|
disabled(disabled: boolean | $State<boolean>): this;
|
||||||
disabled(disabled?: boolean) { return $.fluent(this, arguments, () => this.dom.disabled, () => $.set(this.dom, 'disabled', disabled))}
|
disabled(disabled?: boolean | $State<boolean>) { return $.fluent(this, arguments, () => this.dom.disabled, () => $.set(this.dom, 'disabled', disabled))}
|
||||||
|
|
||||||
type(): ButtonType;
|
type(): ButtonType;
|
||||||
type(type: ButtonType): this;
|
type(type: ButtonType): this;
|
||||||
|
29
lib/$Canvas.ts
Normal file
29
lib/$Canvas.ts
Normal file
@ -0,0 +1,29 @@
|
|||||||
|
import { $Container, $ContainerOptions } from "./$Container";
|
||||||
|
export interface $CanvasOptions extends $ContainerOptions {}
|
||||||
|
export class $Canvas extends $Container<HTMLCanvasElement> {
|
||||||
|
constructor(options?: $CanvasOptions) {
|
||||||
|
super('canvas', options);
|
||||||
|
}
|
||||||
|
|
||||||
|
height(): number;
|
||||||
|
height(height?: number): this;
|
||||||
|
height(height?: number) { return $.fluent(this, arguments, () => this.dom.height, () => { $.set(this.dom, 'height', height)}) }
|
||||||
|
|
||||||
|
width(): number;
|
||||||
|
width(width?: number): this;
|
||||||
|
width(width?: number) { return $.fluent(this, arguments, () => this.dom.width, () => { $.set(this.dom, 'width', width)}) }
|
||||||
|
|
||||||
|
captureStream(frameRequestRate?: number) { return this.dom.captureStream(frameRequestRate) }
|
||||||
|
|
||||||
|
getContext(contextId: "2d", options?: CanvasRenderingContext2DSettings): CanvasRenderingContext2D | null;
|
||||||
|
getContext(contextId: "bitmaprenderer", options?: ImageBitmapRenderingContextSettings): ImageBitmapRenderingContext | null;
|
||||||
|
getContext(contextId: "webgl", options?: WebGLContextAttributes): WebGLRenderingContext | null;
|
||||||
|
getContext(contextId: "webgl2", options?: WebGLContextAttributes): WebGL2RenderingContext | null;
|
||||||
|
getContext(contextId: string, options?: any): RenderingContext | null { return this.dom.getContext(contextId); }
|
||||||
|
|
||||||
|
toBlob(callback: BlobCallback, type?: string, quality?: any) { this.dom.toBlob(callback, type, quality); return this;}
|
||||||
|
|
||||||
|
toDataURL(type?: string, quality?: any) { return this.dom.toDataURL(type, quality) }
|
||||||
|
|
||||||
|
transferControlToOffscreen() { return this.dom.transferControlToOffscreen() }
|
||||||
|
}
|
@ -2,6 +2,7 @@ import { $Element, $ElementOptions } from "./$Element";
|
|||||||
import { $NodeManager } from "./$NodeManager";
|
import { $NodeManager } from "./$NodeManager";
|
||||||
import { $Node } from "./$Node";
|
import { $Node } from "./$Node";
|
||||||
import { $State } from "./$State";
|
import { $State } from "./$State";
|
||||||
|
import { $Text } from "./$Text";
|
||||||
|
|
||||||
export interface $ContainerOptions extends $ElementOptions {}
|
export interface $ContainerOptions extends $ElementOptions {}
|
||||||
|
|
||||||
@ -24,9 +25,13 @@ export class $Container<H extends HTMLElement = HTMLElement> extends $Element<H>
|
|||||||
if (children instanceof Function) children = children(this);
|
if (children instanceof Function) children = children(this);
|
||||||
children = $.multableResolve(children);
|
children = $.multableResolve(children);
|
||||||
for (const child of children) {
|
for (const child of children) {
|
||||||
if (child === undefined) return;
|
if (child === undefined) continue;
|
||||||
if (child instanceof Array) this.insert(child)
|
if (child instanceof Array) this.insert(child)
|
||||||
else this.children.add(child);
|
else if (child instanceof $State) {
|
||||||
|
const ele = new $Text(child.toString());
|
||||||
|
child.use(ele, 'content');
|
||||||
|
this.children.add(ele);
|
||||||
|
} else this.children.add(child);
|
||||||
}
|
}
|
||||||
this.children.render();
|
this.children.render();
|
||||||
})}
|
})}
|
||||||
|
19
lib/$Dialog.ts
Normal file
19
lib/$Dialog.ts
Normal file
@ -0,0 +1,19 @@
|
|||||||
|
import { $Container, $ContainerOptions } from "./$Container";
|
||||||
|
export interface $DialogOptions extends $ContainerOptions {}
|
||||||
|
export class $Dialog extends $Container<HTMLDialogElement> {
|
||||||
|
constructor(options?: $DialogOptions) {
|
||||||
|
super('dialog', options);
|
||||||
|
}
|
||||||
|
|
||||||
|
open(): boolean;
|
||||||
|
open(open?: boolean): this;
|
||||||
|
open(open?: boolean) { return $.fluent(this, arguments, () => this.dom.open, () => $.set(this.dom, 'open', open)) }
|
||||||
|
|
||||||
|
returnValue(): string;
|
||||||
|
returnValue(returnValue?: string): this;
|
||||||
|
returnValue(returnValue?: string) { return $.fluent(this, arguments, () => this.dom.returnValue, () => $.set(this.dom, 'returnValue', returnValue)) }
|
||||||
|
|
||||||
|
close() { this.dom.close(); return this; }
|
||||||
|
show() { this.dom.show(); return this; }
|
||||||
|
showModal() { this.dom.showModal(); return this; }
|
||||||
|
}
|
@ -38,32 +38,69 @@ export class $Element<H extends HTMLElement = HTMLElement> extends $Node<H> {
|
|||||||
css(): CSSStyleDeclaration
|
css(): CSSStyleDeclaration
|
||||||
css(style: Partial<CSSStyleDeclaration>): this;
|
css(style: Partial<CSSStyleDeclaration>): this;
|
||||||
css(style?: Partial<CSSStyleDeclaration>) { return $.fluent(this, arguments, () => this.dom.style, () => {Object.assign(this.dom.style, style)})}
|
css(style?: Partial<CSSStyleDeclaration>) { return $.fluent(this, arguments, () => this.dom.style, () => {Object.assign(this.dom.style, style)})}
|
||||||
|
|
||||||
/**Remove this element from parent */
|
|
||||||
remove() {
|
|
||||||
this.parent?.children.remove(this);
|
|
||||||
(this as Mutable<this>).parent = undefined;
|
|
||||||
this.dom.remove();
|
|
||||||
return this;
|
|
||||||
}
|
|
||||||
|
|
||||||
autocapitalize(): Autocapitalize;
|
autocapitalize(): Autocapitalize;
|
||||||
autocapitalize(autocapitalize: Autocapitalize): this;
|
autocapitalize(autocapitalize?: Autocapitalize): this;
|
||||||
autocapitalize(autocapitalize?: Autocapitalize) { return $.fluent(this, arguments, () => this.dom.autocapitalize, () => $.set(this.dom, 'autocapitalize', autocapitalize))}
|
autocapitalize(autocapitalize?: Autocapitalize) { return $.fluent(this, arguments, () => this.dom.autocapitalize, () => $.set(this.dom, 'autocapitalize', autocapitalize))}
|
||||||
|
|
||||||
dir(): TextDirection;
|
dir(): TextDirection;
|
||||||
dir(dir: TextDirection): this;
|
dir(dir?: TextDirection): this;
|
||||||
dir(dir?: TextDirection) { return $.fluent(this, arguments, () => this.dom.dir, () => $.set(this.dom, 'dir', dir))}
|
dir(dir?: TextDirection) { return $.fluent(this, arguments, () => this.dom.dir, () => $.set(this.dom, 'dir', dir))}
|
||||||
|
|
||||||
innerText(): string;
|
innerText(): string;
|
||||||
innerText(text: string): this;
|
innerText(text?: string): this;
|
||||||
innerText(text?: string) { return $.fluent(this, arguments, () => this.dom.innerText, () => $.set(this.dom, 'innerText', text))}
|
innerText(text?: string) { return $.fluent(this, arguments, () => this.dom.innerText, () => $.set(this.dom, 'innerText', text))}
|
||||||
|
|
||||||
title(): string;
|
title(): string;
|
||||||
title(title: string): this;
|
title(title?: string): this;
|
||||||
title(title?: string) { return $.fluent(this, arguments, () => this.dom.title, () => $.set(this.dom, 'title', title))}
|
title(title?: string) { return $.fluent(this, arguments, () => this.dom.title, () => $.set(this.dom, 'title', title))}
|
||||||
|
|
||||||
translate(): boolean;
|
translate(): boolean;
|
||||||
translate(translate: boolean): this;
|
translate(translate?: boolean): this;
|
||||||
translate(translate?: boolean) { return $.fluent(this, arguments, () => this.dom.translate, () => $.set(this.dom, 'translate', translate))}
|
translate(translate?: boolean) { return $.fluent(this, arguments, () => this.dom.translate, () => $.set(this.dom, 'translate', translate))}
|
||||||
|
|
||||||
|
popover(): string | null;
|
||||||
|
popover(popover?: string | null): this;
|
||||||
|
popover(popover?: string | null) { return $.fluent(this, arguments, () => this.dom.popover, () => $.set(this.dom, 'popover', popover))}
|
||||||
|
|
||||||
|
spellcheck(): boolean;
|
||||||
|
spellcheck(spellcheck?: boolean): this;
|
||||||
|
spellcheck(spellcheck?: boolean) { return $.fluent(this, arguments, () => this.dom.spellcheck, () => $.set(this.dom, 'spellcheck', spellcheck))}
|
||||||
|
|
||||||
|
inert(): boolean;
|
||||||
|
inert(inert?: boolean): this;
|
||||||
|
inert(inert?: boolean) { return $.fluent(this, arguments, () => this.dom.inert, () => $.set(this.dom, 'inert', inert))}
|
||||||
|
|
||||||
|
lang(): string;
|
||||||
|
lang(lang?: string): this;
|
||||||
|
lang(lang?: string) { return $.fluent(this, arguments, () => this.dom.lang, () => $.set(this.dom, 'lang', lang))}
|
||||||
|
|
||||||
|
draggable(): boolean;
|
||||||
|
draggable(draggable?: boolean): this;
|
||||||
|
draggable(draggable?: boolean) { return $.fluent(this, arguments, () => this.dom.draggable, () => $.set(this.dom, 'draggable', draggable))}
|
||||||
|
|
||||||
|
hidden(): boolean;
|
||||||
|
hidden(hidden?: boolean): this;
|
||||||
|
hidden(hidden?: boolean) { return $.fluent(this, arguments, () => this.dom.hidden, () => $.set(this.dom, 'hidden', hidden))}
|
||||||
|
|
||||||
|
click() { this.dom.click(); return this; }
|
||||||
|
attachInternals() { return this.dom.attachInternals(); }
|
||||||
|
hidePopover() { this.dom.hidePopover(); return this; }
|
||||||
|
showPopover() { this.dom.showPopover(); return this; }
|
||||||
|
togglePopover() { this.dom.togglePopover(); return this; }
|
||||||
|
|
||||||
|
animate(keyframes: Keyframe[] | PropertyIndexedKeyframes | null, options?: number | KeyframeAnimationOptions, callback?: (animation: Animation) => void) {
|
||||||
|
const animation = this.dom.animate(keyframes, options);
|
||||||
|
if (callback) callback(animation);
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
getAnimations(options?: GetAnimationsOptions) { return this.dom.getAnimations(options) }
|
||||||
|
|
||||||
|
get accessKeyLabel() { return this.dom.accessKeyLabel }
|
||||||
|
get offsetHeight() { return this.dom.offsetHeight }
|
||||||
|
get offsetLeft() { return this.dom.offsetLeft }
|
||||||
|
get offsetParent() { return $(this.dom.offsetParent) }
|
||||||
|
get offsetTop() { return this.dom.offsetTop }
|
||||||
|
get offsetWidth() { return this.dom.offsetWidth }
|
||||||
}
|
}
|
33
lib/$Form.ts
33
lib/$Form.ts
@ -1,4 +1,5 @@
|
|||||||
import { $Container, $ContainerOptions } from "./$Container";
|
import { $Container, $ContainerOptions } from "./$Container";
|
||||||
|
import { $State } from "./$State";
|
||||||
import { $Util } from "./$Util";
|
import { $Util } from "./$Util";
|
||||||
export interface $FormOptions extends $ContainerOptions {}
|
export interface $FormOptions extends $ContainerOptions {}
|
||||||
export class $Form extends $Container<HTMLFormElement> {
|
export class $Form extends $Container<HTMLFormElement> {
|
||||||
@ -7,31 +8,31 @@ export class $Form extends $Container<HTMLFormElement> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
autocomplete(): AutoFillBase;
|
autocomplete(): AutoFillBase;
|
||||||
autocomplete(autocomplete: AutoFill): this;
|
autocomplete(autocomplete: AutoFill | undefined): this;
|
||||||
autocomplete(autocomplete?: AutoFill) { return $.fluent(this, arguments, () => this.dom.autocomplete, () => $.set(this.dom, 'autocomplete', autocomplete))}
|
autocomplete(autocomplete?: AutoFill) { return $.fluent(this, arguments, () => this.dom.autocomplete, () => $.set(this.dom, 'autocomplete', autocomplete))}
|
||||||
|
|
||||||
action(): string;
|
action(): string;
|
||||||
action(action: string): this;
|
action(action: string | undefined): this;
|
||||||
action(action?: string) { return $.fluent(this, arguments, () => this.dom.formAction, () => $.set(this.dom, 'formAction', action))}
|
action(action?: string) { return $.fluent(this, arguments, () => this.dom.formAction, () => $.set(this.dom, 'formAction', action))}
|
||||||
|
|
||||||
enctype(): string;
|
enctype(): string;
|
||||||
enctype(enctype: string): this;
|
enctype(enctype: string | undefined): this;
|
||||||
enctype(enctype?: string) { return $.fluent(this, arguments, () => this.dom.formEnctype, () => $.set(this.dom, 'formEnctype', enctype))}
|
enctype(enctype?: string) { return $.fluent(this, arguments, () => this.dom.formEnctype, () => $.set(this.dom, 'formEnctype', enctype))}
|
||||||
|
|
||||||
method(): string;
|
method(): string;
|
||||||
method(method: string): this;
|
method(method: string | undefined): this;
|
||||||
method(method?: string) { return $.fluent(this, arguments, () => this.dom.formMethod, () => $.set(this.dom, 'formMethod', method))}
|
method(method?: string) { return $.fluent(this, arguments, () => this.dom.formMethod, () => $.set(this.dom, 'formMethod', method))}
|
||||||
|
|
||||||
noValidate(): boolean;
|
noValidate(): boolean;
|
||||||
noValidate(boolean: boolean): this;
|
noValidate(boolean: boolean | undefined): this;
|
||||||
noValidate(boolean?: boolean) { return $.fluent(this, arguments, () => this.dom.formNoValidate, () => $.set(this.dom, 'formNoValidate', boolean))}
|
noValidate(boolean?: boolean) { return $.fluent(this, arguments, () => this.dom.formNoValidate, () => $.set(this.dom, 'formNoValidate', boolean))}
|
||||||
|
|
||||||
acceptCharset(): string;
|
acceptCharset(): string;
|
||||||
acceptCharset(acceptCharset: string): this;
|
acceptCharset(acceptCharset: string | undefined): this;
|
||||||
acceptCharset(acceptCharset?: string) { return $.fluent(this, arguments, () => this.dom.acceptCharset, () => $.set(this.dom, 'acceptCharset', acceptCharset))}
|
acceptCharset(acceptCharset?: string) { return $.fluent(this, arguments, () => this.dom.acceptCharset, () => $.set(this.dom, 'acceptCharset', acceptCharset))}
|
||||||
|
|
||||||
target(): string;
|
target(): string;
|
||||||
target(target: string): this;
|
target(target: string | undefined): this;
|
||||||
target(target?: string) { return $.fluent(this, arguments, () => this.dom.formTarget, () => $.set(this.dom, 'formTarget', target))}
|
target(target?: string) { return $.fluent(this, arguments, () => this.dom.formTarget, () => $.set(this.dom, 'formTarget', target))}
|
||||||
|
|
||||||
requestSubmit() { this.dom.requestSubmit(); return this }
|
requestSubmit() { this.dom.requestSubmit(); return this }
|
||||||
@ -49,32 +50,32 @@ export abstract class $FormElementMethod {
|
|||||||
abstract dom: HTMLButtonElement | HTMLInputElement;
|
abstract dom: HTMLButtonElement | HTMLInputElement;
|
||||||
|
|
||||||
formAction(): string;
|
formAction(): string;
|
||||||
formAction(action: string): this;
|
formAction(action: string | undefined): this;
|
||||||
formAction(action?: string) { return $.fluent(this, arguments, () => this.dom.formAction, () => $.set(this.dom, 'formAction', action))}
|
formAction(action?: string) { return $.fluent(this, arguments, () => this.dom.formAction, () => $.set(this.dom, 'formAction', action))}
|
||||||
|
|
||||||
formEnctype(): string;
|
formEnctype(): string;
|
||||||
formEnctype(enctype: string): this;
|
formEnctype(enctype: string | undefined): this;
|
||||||
formEnctype(enctype?: string) { return $.fluent(this, arguments, () => this.dom.formEnctype, () => $.set(this.dom, 'formEnctype', enctype))}
|
formEnctype(enctype?: string) { return $.fluent(this, arguments, () => this.dom.formEnctype, () => $.set(this.dom, 'formEnctype', enctype))}
|
||||||
|
|
||||||
formMethod(): string;
|
formMethod(): string;
|
||||||
formMethod(method: string): this;
|
formMethod(method: string | undefined): this;
|
||||||
formMethod(method?: string) { return $.fluent(this, arguments, () => this.dom.formMethod, () => $.set(this.dom, 'formMethod', method))}
|
formMethod(method?: string) { return $.fluent(this, arguments, () => this.dom.formMethod, () => $.set(this.dom, 'formMethod', method))}
|
||||||
|
|
||||||
formNoValidate(): boolean;
|
formNoValidate(): boolean;
|
||||||
formNoValidate(boolean: boolean): this;
|
formNoValidate(boolean: boolean | undefined): this;
|
||||||
formNoValidate(boolean?: boolean) { return $.fluent(this, arguments, () => this.dom.formNoValidate, () => $.set(this.dom, 'formNoValidate', boolean))}
|
formNoValidate(boolean?: boolean) { return $.fluent(this, arguments, () => this.dom.formNoValidate, () => $.set(this.dom, 'formNoValidate', boolean))}
|
||||||
|
|
||||||
formTarget(): string;
|
formTarget(): string;
|
||||||
formTarget(target: string): this;
|
formTarget(target: string | undefined): this;
|
||||||
formTarget(target?: string) { return $.fluent(this, arguments, () => this.dom.formTarget, () => $.set(this.dom, 'formTarget', target))}
|
formTarget(target?: string) { return $.fluent(this, arguments, () => this.dom.formTarget, () => $.set(this.dom, 'formTarget', target))}
|
||||||
|
|
||||||
name(): string;
|
name(): string;
|
||||||
name(name: string): this;
|
name(name?: string | $State<string>): this;
|
||||||
name(name?: string) { return $.fluent(this, arguments, () => this.dom.name, () => $.set(this.dom, 'name', name))}
|
name(name?: string | $State<string>) { return $.fluent(this, arguments, () => this.dom.name, () => $.set(this.dom, 'name', name))}
|
||||||
|
|
||||||
value(): string;
|
value(): string;
|
||||||
value(value: string): this;
|
value(value?: string | $State<string>): this;
|
||||||
value(value?: string) { return $.fluent(this, arguments, () => this.dom.value, () => $.set(this.dom, 'value', value))}
|
value(value?: string | $State<string>) { return $.fluent(this, arguments, () => this.dom.value, () => $.set(this.dom, 'value', value))}
|
||||||
|
|
||||||
get form() { return this.dom.form ? $(this.dom.form) : null }
|
get form() { return this.dom.form ? $(this.dom.form) : null }
|
||||||
get labels() { return Array.from(this.dom.labels ?? []).map(label => $(label)) }
|
get labels() { return Array.from(this.dom.labels ?? []).map(label => $(label)) }
|
||||||
|
85
lib/$Image.ts
Normal file
85
lib/$Image.ts
Normal file
@ -0,0 +1,85 @@
|
|||||||
|
import { $Element, $ElementOptions } from "./$Element";
|
||||||
|
import { $State } from "./$State";
|
||||||
|
export interface $ImageOptions extends $ElementOptions {}
|
||||||
|
export class $Image extends $Element<HTMLImageElement> {
|
||||||
|
constructor(options?: $ImageOptions) {
|
||||||
|
super('img', options);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**HTMLImageElement base property */
|
||||||
|
alt(): string;
|
||||||
|
alt(alt: string): this;
|
||||||
|
alt(alt?: string) { return $.fluent(this, arguments, () => this.dom.alt, () => $.set(this.dom, 'alt', alt))}
|
||||||
|
|
||||||
|
/**HTMLImageElement base property */
|
||||||
|
crossOrigin(): string | null;
|
||||||
|
crossOrigin(crossOrigin: string | null): this;
|
||||||
|
crossOrigin(crossOrigin?: string | null) { return $.fluent(this, arguments, () => this.dom.crossOrigin, () => $.set(this.dom, 'crossOrigin', crossOrigin))}
|
||||||
|
|
||||||
|
/**HTMLImageElement base property */
|
||||||
|
decoding(): ImageDecoding;
|
||||||
|
decoding(decoding: ImageDecoding): this;
|
||||||
|
decoding(decoding?: ImageDecoding) { return $.fluent(this, arguments, () => this.dom.decoding, () => $.set(this.dom, 'decoding', decoding))}
|
||||||
|
|
||||||
|
/**HTMLImageElement base property */
|
||||||
|
height(): number;
|
||||||
|
height(height: number): this;
|
||||||
|
height(height?: number) { return $.fluent(this, arguments, () => this.dom.height, () => $.set(this.dom, 'height', height))}
|
||||||
|
|
||||||
|
/**HTMLImageElement base property */
|
||||||
|
isMap(): boolean;
|
||||||
|
isMap(isMap: boolean): this;
|
||||||
|
isMap(isMap?: boolean) { return $.fluent(this, arguments, () => this.dom.isMap, () => $.set(this.dom, 'isMap', isMap))}
|
||||||
|
|
||||||
|
/**HTMLImageElement base property */
|
||||||
|
loading(): ImageLoading;
|
||||||
|
loading(loading: ImageLoading): this;
|
||||||
|
loading(loading?: ImageLoading) { return $.fluent(this, arguments, () => this.dom.loading, () => $.set(this.dom, 'loading', loading))}
|
||||||
|
|
||||||
|
/**HTMLImageElement base property */
|
||||||
|
referrerPolicy(): string;
|
||||||
|
referrerPolicy(referrerPolicy: string): this;
|
||||||
|
referrerPolicy(referrerPolicy?: string) { return $.fluent(this, arguments, () => this.dom.referrerPolicy, () => $.set(this.dom, 'referrerPolicy', referrerPolicy))}
|
||||||
|
|
||||||
|
/**HTMLImageElement base property */
|
||||||
|
sizes(): string;
|
||||||
|
sizes(sizes: string): this;
|
||||||
|
sizes(sizes?: string) { return $.fluent(this, arguments, () => this.dom.sizes, () => $.set(this.dom, 'sizes', sizes))}
|
||||||
|
|
||||||
|
/**HTMLImageElement base property */
|
||||||
|
src(): string;
|
||||||
|
src(src?: string | $State<string | undefined>): this;
|
||||||
|
src(src?: string | $State<string | undefined>) { return $.fluent(this, arguments, () => this.dom.src, () => $.set(this.dom, 'src', src))}
|
||||||
|
|
||||||
|
/**HTMLImageElement base property */
|
||||||
|
srcset(): string;
|
||||||
|
srcset(srcset: string): this;
|
||||||
|
srcset(srcset?: string) { return $.fluent(this, arguments, () => this.dom.srcset, () => $.set(this.dom, 'srcset', srcset))}
|
||||||
|
|
||||||
|
/**HTMLImageElement base property */
|
||||||
|
useMap(): string;
|
||||||
|
useMap(useMap: string): this;
|
||||||
|
useMap(useMap?: string) { return $.fluent(this, arguments, () => this.dom.useMap, () => $.set(this.dom, 'useMap', useMap))}
|
||||||
|
|
||||||
|
/**HTMLImageElement base property */
|
||||||
|
width(): number;
|
||||||
|
width(width: number): this;
|
||||||
|
width(width?: number) { return $.fluent(this, arguments, () => this.dom.width, () => $.set(this.dom, 'width', width))}
|
||||||
|
|
||||||
|
/**HTMLImageElement base method */
|
||||||
|
decode() { return this.dom.decode() }
|
||||||
|
|
||||||
|
/**HTMLImageElement base property */
|
||||||
|
get complete() { return this.dom.complete }
|
||||||
|
/**HTMLImageElement base property */
|
||||||
|
get currentSrc() { return this.dom.currentSrc }
|
||||||
|
/**HTMLImageElement base property */
|
||||||
|
get naturalHeight() { return this.dom.naturalHeight }
|
||||||
|
/**HTMLImageElement base property */
|
||||||
|
get naturalWidth() { return this.dom.naturalWidth }
|
||||||
|
/**HTMLImageElement base property */
|
||||||
|
get x() { return this.dom.x }
|
||||||
|
/**HTMLImageElement base property */
|
||||||
|
get y() { return this.dom.y }
|
||||||
|
|
||||||
|
}
|
@ -79,7 +79,7 @@ export class $Input extends $Element<HTMLInputElement> {
|
|||||||
pattern(pattern?: string) { return $.fluent(this, arguments, () => this.dom.pattern, () => $.set(this.dom, 'pattern', pattern))}
|
pattern(pattern?: string) { return $.fluent(this, arguments, () => this.dom.pattern, () => $.set(this.dom, 'pattern', pattern))}
|
||||||
|
|
||||||
placeholder(): string;
|
placeholder(): string;
|
||||||
placeholder(placeholder: string): this;
|
placeholder(placeholder?: string): this;
|
||||||
placeholder(placeholder?: string) { return $.fluent(this, arguments, () => this.dom.placeholder, () => $.set(this.dom, 'placeholder', placeholder))}
|
placeholder(placeholder?: string) { return $.fluent(this, arguments, () => this.dom.placeholder, () => $.set(this.dom, 'placeholder', placeholder))}
|
||||||
|
|
||||||
readOnly(): boolean;
|
readOnly(): boolean;
|
||||||
|
@ -6,8 +6,8 @@ export class $Label extends $Container<HTMLLabelElement> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
for(): string;
|
for(): string;
|
||||||
for(name: string): this;
|
for(name?: string): this;
|
||||||
for(name?: string | undefined) { return $.fluent(this, arguments, () => this.dom.htmlFor, () => {if (name) this.dom.htmlFor = name}) }
|
for(name?: string) { return $.fluent(this, arguments, () => this.dom.htmlFor, () => { $.set(this.dom, 'htmlFor', name, 'for')}) }
|
||||||
|
|
||||||
get form() { return this.dom.form }
|
get form() { return this.dom.form }
|
||||||
get control() { return this.dom.control }
|
get control() { return this.dom.control }
|
||||||
|
42
lib/$Node.ts
42
lib/$Node.ts
@ -1,48 +1,68 @@
|
|||||||
import { $Text } from "../index";
|
import { $State, $Text } from "../index";
|
||||||
import { $Container } from "./$Container";
|
import { $Container } from "./$Container";
|
||||||
|
|
||||||
export abstract class $Node<N extends Node = Node> {
|
export abstract class $Node<N extends Node = Node> {
|
||||||
readonly parent?: $Container;
|
readonly parent?: $Container;
|
||||||
abstract readonly dom: N;
|
abstract readonly dom: N;
|
||||||
readonly hidden: boolean = false;
|
readonly $hidden: boolean = false;
|
||||||
private domEvents: {[key: string]: Map<Function, Function>} = {};
|
private domEvents: {[key: string]: Map<Function, Function>} = {};
|
||||||
|
|
||||||
on<K extends keyof HTMLElementEventMap>(type: K, callback: (event: Event, $node: this) => void, options?: AddEventListenerOptions | boolean) {
|
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()
|
if (!this.domEvents[type]) this.domEvents[type] = new Map()
|
||||||
const middleCallback = (e: Event) => callback(e, this);
|
const middleCallback = (e: Event) => callback(e as HTMLElementEventMap[K], this);
|
||||||
this.domEvents[type].set(callback, middleCallback)
|
this.domEvents[type].set(callback, middleCallback)
|
||||||
this.dom.addEventListener(type, middleCallback, options)
|
this.dom.addEventListener(type, middleCallback, options)
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
off<K extends keyof HTMLElementEventMap>(type: K, callback: (event: Event, $node: this) => void, options?: AddEventListenerOptions | boolean) {
|
off<K extends keyof HTMLElementEventMap>(type: K, callback: (event: HTMLElementEventMap[K], $node: this) => void, options?: AddEventListenerOptions | boolean) {
|
||||||
const middleCallback = this.domEvents[type]?.get(callback);
|
const middleCallback = this.domEvents[type]?.get(callback);
|
||||||
if (middleCallback) this.dom.removeEventListener(type, middleCallback as EventListener, options)
|
if (middleCallback) this.dom.removeEventListener(type, middleCallback as EventListener, options)
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
once<K extends keyof HTMLElementEventMap>(type: K, callback: (event: Event, $node: this) => void, options?: AddEventListenerOptions | boolean) {
|
once<K extends keyof HTMLElementEventMap>(type: K, callback: (event: HTMLElementEventMap[K], $node: this) => void, options?: AddEventListenerOptions | boolean) {
|
||||||
const onceFn = (event: Event) => {
|
const onceFn = (event: Event) => {
|
||||||
this.dom.removeEventListener(type, onceFn, options)
|
this.dom.removeEventListener(type, onceFn, options)
|
||||||
callback(event, this);
|
callback(event as HTMLElementEventMap[K], this);
|
||||||
};
|
};
|
||||||
this.dom.addEventListener(type, onceFn, options)
|
this.dom.addEventListener(type, onceFn, options)
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
show() { (this as Mutable<$Node>).hidden = false; this.parent?.children.render(); return this; }
|
hide(): boolean;
|
||||||
hide() {
|
hide(hide?: boolean | $State<boolean>): this;
|
||||||
(this as Mutable<$Node>).hidden = true;
|
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();
|
this.parent?.children.render();
|
||||||
return this;
|
return this;
|
||||||
|
})}
|
||||||
|
|
||||||
|
/**Remove this element from parent */
|
||||||
|
remove() {
|
||||||
|
this.parent?.children.remove(this).render();
|
||||||
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
contains(target: $Node | EventTarget | Node) {
|
/**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);
|
if (target instanceof $Node) return this.dom.contains(target.dom);
|
||||||
else if (target instanceof EventTarget) return this.dom.contains($(target).dom)
|
else if (target instanceof EventTarget) return this.dom.contains($(target).dom)
|
||||||
else return this.dom.contains(target)
|
else return this.dom.contains(target)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
self(callback: ($node: this) => void) { callback(this); return this; }
|
||||||
|
|
||||||
|
inDOM() { return document.contains(this.dom); }
|
||||||
|
|
||||||
static from(element: HTMLElement | Text): $Node {
|
static from(element: HTMLElement | Text): $Node {
|
||||||
if (element.$) return element.$;
|
if (element.$) return element.$;
|
||||||
else if (element instanceof HTMLElement) {
|
else if (element instanceof HTMLElement) {
|
||||||
|
@ -1,7 +1,6 @@
|
|||||||
import { $Container } from "./$Container";
|
import { $Container } from "./$Container";
|
||||||
import { $Node } from "./$Node";
|
import { $Node } from "./$Node";
|
||||||
import { $Text } from "./$Text";
|
import { $Text } from "./$Text";
|
||||||
import { $State } from "./$State";
|
|
||||||
|
|
||||||
export class $NodeManager {
|
export class $NodeManager {
|
||||||
#container: $Container;
|
#container: $Container;
|
||||||
@ -12,18 +11,11 @@ export class $NodeManager {
|
|||||||
this.#dom = this.#container.dom
|
this.#dom = this.#container.dom
|
||||||
}
|
}
|
||||||
|
|
||||||
add(element: $Node | string | $State<any>) {
|
add(element: $Node | string) {
|
||||||
if (typeof element === 'string') {
|
if (typeof element === 'string') {
|
||||||
const text = new $Text(element);
|
const text = new $Text(element);
|
||||||
(text as Mutable<$Text>).parent = this.#container;
|
(text as Mutable<$Text>).parent = this.#container;
|
||||||
this.elementList.add(text);
|
this.elementList.add(text);
|
||||||
} else if (element instanceof $State) {
|
|
||||||
if (typeof element.value === 'string') {
|
|
||||||
const ele = new $Text(element.value);
|
|
||||||
element.contents.add(ele);
|
|
||||||
(ele as Mutable<$Text>).parent = this.#container;
|
|
||||||
this.elementList.add(ele);
|
|
||||||
}
|
|
||||||
} else {
|
} else {
|
||||||
(element as Mutable<$Node>).parent = this.#container;
|
(element as Mutable<$Node>).parent = this.#container;
|
||||||
this.elementList.add(element);
|
this.elementList.add(element);
|
||||||
@ -31,26 +23,42 @@ export class $NodeManager {
|
|||||||
}
|
}
|
||||||
|
|
||||||
remove(element: $Node) {
|
remove(element: $Node) {
|
||||||
if (!this.elementList.has(element)) return;
|
if (!this.elementList.has(element)) return this;
|
||||||
this.elementList.delete(element);
|
this.elementList.delete(element);
|
||||||
(element as Mutable<$Node>).parent = undefined;
|
(element as Mutable<$Node>).parent = undefined;
|
||||||
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
removeAll() {
|
removeAll() {
|
||||||
this.elementList.forEach(ele => this.remove(ele))
|
this.elementList.forEach(ele => this.remove(ele))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
replace(target: $Node, replace: $Node) {
|
||||||
|
const array = this.array.map(node => {
|
||||||
|
if (node === target) return replace;
|
||||||
|
else return node;
|
||||||
|
})
|
||||||
|
this.elementList.clear();
|
||||||
|
array.forEach(node => this.elementList.add(node));
|
||||||
|
(target as Mutable<$Node>).parent = undefined;
|
||||||
|
(replace as Mutable<$Node>).parent = this.#container;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
render() {
|
render() {
|
||||||
const [domList, nodeList] = [this.array.map(node => node.dom), Array.from(this.#dom.childNodes)];
|
const [domList, nodeList] = [this.array.map(node => node.dom), Array.from(this.#dom.childNodes)];
|
||||||
|
const appendedNodeList: Node[] = []; // appended node list
|
||||||
// Rearrange
|
// Rearrange
|
||||||
while (nodeList.length || domList.length) {
|
while (nodeList.length || domList.length) { // while nodeList or domList has item
|
||||||
const [node, dom] = [nodeList.at(0), domList.at(0)];
|
const [node, dom] = [nodeList.at(0), domList.at(0)];
|
||||||
if (!dom) { node?.remove(); nodeList.shift()}
|
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.$.$hidden) this.#dom.append(dom); domList.shift();}
|
||||||
else if (dom !== node) { if (!dom.$.hidden) this.#dom.insertBefore(dom, node); domList.shift();}
|
else if (dom !== node) {
|
||||||
|
if (!dom.$.$hidden) { this.#dom.insertBefore(dom, node); appendedNodeList.push(dom) }
|
||||||
|
domList.shift();
|
||||||
|
}
|
||||||
else {
|
else {
|
||||||
if (dom.$.hidden) this.#dom.removeChild(dom);
|
if (dom.$.$hidden) this.#dom.removeChild(dom);
|
||||||
domList.shift(); nodeList.shift();
|
domList.shift(); nodeList.shift();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,17 +1,17 @@
|
|||||||
import { $Node } from "./$Node";
|
import { $Node } from "./$Node";
|
||||||
import { $Text } from "./$Text";
|
|
||||||
|
|
||||||
export class $State<T> {
|
export class $State<T> {
|
||||||
readonly value: T;
|
readonly value: T;
|
||||||
readonly contents = new Set<$Node>();
|
readonly attributes = new Map<$Node, Set<string | number | symbol>>();
|
||||||
constructor(value: T) {
|
constructor(value: T) {
|
||||||
this.value = value;
|
this.value = value;
|
||||||
}
|
}
|
||||||
set(value: T) {
|
set(value: T) {
|
||||||
(this as Mutable<$State<T>>).value = value;
|
(this as Mutable<$State<T>>).value = value;
|
||||||
for (const content of this.contents.values()) {
|
for (const [node, attrList] of this.attributes.entries()) {
|
||||||
if (content instanceof $Text) {
|
for (const attr of attrList) {
|
||||||
content.content(`${value}`);
|
//@ts-expect-error
|
||||||
|
if (node[attr] instanceof Function) node[attr](value)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -19,4 +19,10 @@ export class $State<T> {
|
|||||||
toString(): string {
|
toString(): string {
|
||||||
return `${this.value}`
|
return `${this.value}`
|
||||||
}
|
}
|
||||||
|
|
||||||
|
use<T extends $Node, K extends keyof T>($node: T, attrName: K) {
|
||||||
|
const attrList = this.attributes.get($node)
|
||||||
|
if (attrList) attrList.add(attrName);
|
||||||
|
else this.attributes.set($node, new Set<string | number | symbol>().add(attrName))
|
||||||
|
}
|
||||||
};
|
};
|
@ -2,8 +2,8 @@ import { $EventManager, $EventMethod, EventMethod } from "../$EventManager";
|
|||||||
import { $Node } from "../$Node";
|
import { $Node } from "../$Node";
|
||||||
export class Route<Path extends string | PathResolverFn> {
|
export class Route<Path extends string | PathResolverFn> {
|
||||||
path: string | PathResolverFn;
|
path: string | PathResolverFn;
|
||||||
builder: (params: PathParamResolver<Path>, record: RouteRecord) => $Node | string;
|
builder: (req: RouteRequest<Path>) => $Node | string;
|
||||||
constructor(path: Path, builder: (params: PathParamResolver<Path>, record: RouteRecord) => $Node | string) {
|
constructor(path: Path, builder: (req: RouteRequest<Path>) => $Node | string) {
|
||||||
this.path = path;
|
this.path = path;
|
||||||
this.builder = builder;
|
this.builder = builder;
|
||||||
}
|
}
|
||||||
@ -36,4 +36,9 @@ export class RouteRecord {
|
|||||||
|
|
||||||
export interface RouteRecordEventMap {
|
export interface RouteRecordEventMap {
|
||||||
'open': [path: string, record: RouteRecord]
|
'open': [path: string, record: RouteRecord]
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface RouteRequest<Path extends PathResolverFn | string> {
|
||||||
|
params: PathParamResolver<Path>,
|
||||||
|
record: RouteRecord,
|
||||||
}
|
}
|
@ -32,8 +32,9 @@ export class Router {
|
|||||||
this.index = history.state.index
|
this.index = history.state.index
|
||||||
}
|
}
|
||||||
addEventListener('popstate', this.popstate)
|
addEventListener('popstate', this.popstate)
|
||||||
this.resolvePath();
|
|
||||||
$.routers.add(this);
|
$.routers.add(this);
|
||||||
|
this.resolvePath();
|
||||||
|
this.events.fire('pathchange', location.href, 'Forward');
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -53,7 +54,7 @@ export class Router {
|
|||||||
|
|
||||||
replace(path: string) {
|
replace(path: string) {
|
||||||
history.replaceState({index: this.index}, '', path)
|
history.replaceState({index: this.index}, '', path)
|
||||||
$.routers.forEach(router => router.resolvePath());
|
$.routers.forEach(router => router.resolvePath(path));
|
||||||
this.events.fire('pathchange', path, 'Forward');
|
this.events.fire('pathchange', path, 'Forward');
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
@ -85,7 +86,7 @@ export class Router {
|
|||||||
}
|
}
|
||||||
const create = (pathId: string, route: Route<any>, data: any) => {
|
const create = (pathId: string, route: Route<any>, data: any) => {
|
||||||
const record = new RouteRecord(pathId);
|
const record = new RouteRecord(pathId);
|
||||||
let content = route.builder(data, record);
|
let content = route.builder({params: data, record: record});
|
||||||
if (typeof content === 'string') content = new $Text(content);
|
if (typeof content === 'string') content = new $Text(content);
|
||||||
(record as Mutable<RouteRecord>).content = content;
|
(record as Mutable<RouteRecord>).content = content;
|
||||||
this.recordMap.set(pathId, record);
|
this.recordMap.set(pathId, record);
|
||||||
@ -117,8 +118,8 @@ export class Router {
|
|||||||
}
|
}
|
||||||
else if (routePart.includes(':')) {
|
else if (routePart.includes(':')) {
|
||||||
const [prefix, param] = routePart.split(':');
|
const [prefix, param] = routePart.split(':');
|
||||||
if (!pathPart.startsWith(prefix)) return;
|
if (!pathPart.startsWith(prefix)) continue;
|
||||||
Object.assign(data, {[param]: pathPart.replace('/', '')})
|
Object.assign(data, {[param]: pathPart.replace(prefix, '')})
|
||||||
pathString += pathPart;
|
pathString += pathPart;
|
||||||
if (routePart === _routeParts.at(-1)) {
|
if (routePart === _routeParts.at(-1)) {
|
||||||
if (!openCached(pathString)) create(pathString, route, data);
|
if (!openCached(pathString)) create(pathString, route, data);
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
{
|
{
|
||||||
"name": "fluentx",
|
"name": "fluentx",
|
||||||
"description": "Fast, fluent, simple web builder",
|
"description": "Fast, fluent, simple web builder",
|
||||||
"version": "0.0.1",
|
"version": "0.0.2",
|
||||||
"type": "module",
|
"type": "module",
|
||||||
"module": "index.ts",
|
"module": "index.ts",
|
||||||
"author": {
|
"author": {
|
||||||
|
Loading…
Reference in New Issue
Block a user