diff --git a/$index.ts b/$index.ts index b9091c0..d71e95d 100644 --- a/$index.ts +++ b/$index.ts @@ -116,19 +116,27 @@ export namespace $ { : H extends HTMLTextAreaElement ? $Textarea : $Container; + /** + * A helper for fluent method design. Return the `instance` object when arguments length not equal 0. Otherwise, return the `value`. + * @param instance The object to return when arguments length not equal 0. + * @param args The method `arguments`. + * @param value The value to return when arguments length equal 0. + * @param action The action to execute when arguments length not equal 0. + * @returns + */ export function fluent(instance: T, args: IArguments, value: () => V, action: (...args: any[]) => void) { if (!args.length) return value(); action(); return instance; } - export function multableResolve(multable: OrArray) { + export function orArrayResolve(multable: OrArray) { if (multable instanceof Array) return multable; else return [multable]; } export function mixin(target: any, constructors: OrArray) { - multableResolve(constructors).forEach(constructor => { + orArrayResolve(constructors).forEach(constructor => { Object.getOwnPropertyNames(constructor.prototype).forEach(name => { if (name === 'constructor') return; Object.defineProperty( @@ -140,15 +148,24 @@ export namespace $ { }) return target; } - - export function set(object: O, key: K, value: any, methodKey?: string) { + /** + * A helper for $State.set() which apply value to target. + * @param object Target object. + * @param key The key of target object. + * @param value Value of target property or parameter of method(Using Tuple to apply parameter). + * @param methodKey Variant key name when apply value on $State.set() + * @returns + */ + export function set(object: O, key: K, value: O[K] extends (...args: any) => any ? (Parameters | $State>) : O[K] | undefined | $State, methodKey?: string) { if (value === undefined) return; if (value instanceof $State && object instanceof Node) { value.use(object.$, methodKey ?? key as any); - object[key] = value.value; + const prop = object[key]; + if (prop instanceof Function) prop(value.value); + else object[key] = value.value; return; } - object[key] = value; + object[key] = value as any; } export function state(value: T) { diff --git a/lib/$Button.ts b/lib/$Button.ts index 06de082..d575d6f 100644 --- a/lib/$Button.ts +++ b/lib/$Button.ts @@ -1,10 +1,6 @@ import { $Container, $ContainerOptions } from "./$Container"; -import { FormElementMethod, $FormElementMethod } from "./$Form"; import { $State } from "./$State"; export interface $ButtonOptions extends $ContainerOptions {} -//@ts-expect-error -export interface $Button extends $FormElementMethod {} -@FormElementMethod export class $Button extends $Container { constructor(options?: $ButtonOptions) { super('button', options); @@ -16,8 +12,28 @@ export class $Button extends $Container { type(): ButtonType; type(type: ButtonType): this; - type(type?: ButtonType) { return $.fluent(this, arguments, () => this.dom.type as ButtonType, () => $.set(this.dom, 'type', type))} + type(type?: ButtonType) { return $.fluent(this, arguments, () => this.dom.type as ButtonType, () => $.set(this.dom, 'type', type as any))} checkValidity() { return this.dom.checkValidity() } reportValidity() { return this.dom.reportValidity() } + + formAction(): string; + formAction(action: string | undefined): this; + formAction(action?: string) { return $.fluent(this, arguments, () => this.dom.formAction, () => $.set(this.dom, 'formAction', action))} + + formEnctype(): string; + formEnctype(enctype: string | undefined): this; + formEnctype(enctype?: string) { return $.fluent(this, arguments, () => this.dom.formEnctype, () => $.set(this.dom, 'formEnctype', enctype))} + + formMethod(): string; + formMethod(method: string | undefined): this; + formMethod(method?: string) { return $.fluent(this, arguments, () => this.dom.formMethod, () => $.set(this.dom, 'formMethod', method))} + + formNoValidate(): boolean; + formNoValidate(boolean: boolean | undefined): this; + formNoValidate(boolean?: boolean) { return $.fluent(this, arguments, () => this.dom.formNoValidate, () => $.set(this.dom, 'formNoValidate', boolean))} + + formTarget(): string; + formTarget(target: string | undefined): this; + formTarget(target?: string) { return $.fluent(this, arguments, () => this.dom.formTarget, () => $.set(this.dom, 'formTarget', target))} } \ No newline at end of file diff --git a/lib/$Container.ts b/lib/$Container.ts index 8fa3693..9d6177f 100644 --- a/lib/$Container.ts +++ b/lib/$Container.ts @@ -23,7 +23,7 @@ export class $Container extends $Element /**Insert element to this element */ insert(children: $ContainerContentBuilder): this { return $.fluent(this, arguments, () => this, () => { if (children instanceof Function) children = children(this); - children = $.multableResolve(children); + children = $.orArrayResolve(children); for (const child of children) { if (child === undefined) continue; if (child instanceof Array) this.insert(child) diff --git a/lib/$Form.ts b/lib/$Form.ts index 863feba..d39881d 100644 --- a/lib/$Form.ts +++ b/lib/$Form.ts @@ -7,9 +7,9 @@ export class $Form extends $Container { super('form', options); } - autocomplete(): AutoFillBase; + autocomplete(): AutoFill; 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 as AutoFill, () => $.set(this.dom, 'autocomplete', autocomplete as AutoFillBase))} action(): string; action(action: string | undefined): this; @@ -43,43 +43,4 @@ export class $Form extends $Container { get length() { return this.dom.length } get elements() { return Array.from(this.dom.elements).map(ele => $(ele)) } -} - -export function FormElementMethod(target: any) { return $Util.mixin(target, $FormElementMethod) } -export abstract class $FormElementMethod { - abstract dom: HTMLButtonElement | HTMLInputElement; - - formAction(): string; - formAction(action: string | undefined): this; - formAction(action?: string) { return $.fluent(this, arguments, () => this.dom.formAction, () => $.set(this.dom, 'formAction', action))} - - formEnctype(): string; - formEnctype(enctype: string | undefined): this; - formEnctype(enctype?: string) { return $.fluent(this, arguments, () => this.dom.formEnctype, () => $.set(this.dom, 'formEnctype', enctype))} - - formMethod(): string; - formMethod(method: string | undefined): this; - formMethod(method?: string) { return $.fluent(this, arguments, () => this.dom.formMethod, () => $.set(this.dom, 'formMethod', method))} - - formNoValidate(): boolean; - formNoValidate(boolean: boolean | undefined): this; - formNoValidate(boolean?: boolean) { return $.fluent(this, arguments, () => this.dom.formNoValidate, () => $.set(this.dom, 'formNoValidate', boolean))} - - formTarget(): string; - formTarget(target: string | undefined): this; - formTarget(target?: string) { return $.fluent(this, arguments, () => this.dom.formTarget, () => $.set(this.dom, 'formTarget', target))} - - name(): string; - name(name?: string | $State): this; - name(name?: string | $State) { return $.fluent(this, arguments, () => this.dom.name, () => $.set(this.dom, 'name', name))} - - value(): string; - value(value?: string | $State): this; - value(value?: string | $State) { return $.fluent(this, arguments, () => this.dom.value, () => $.set(this.dom, 'value', value))} - - get form() { return this.dom.form ? $(this.dom.form) : null } - get labels() { return Array.from(this.dom.labels ?? []).map(label => $(label)) } - get validationMessage() { return this.dom.validationMessage } - get validity() { return this.dom.validity } - get willValidate() { return this.dom.willValidate } } \ No newline at end of file diff --git a/lib/$Input.ts b/lib/$Input.ts index 6ad9f56..10a7502 100644 --- a/lib/$Input.ts +++ b/lib/$Input.ts @@ -1,10 +1,7 @@ import { $Element, $ElementOptions } from "./$Element"; -import { $FormElementMethod, FormElementMethod } from "./$Form"; +import { $State } from "./$State"; export interface $InputOptions extends $ElementOptions {} -//@ts-expect-error -export interface $Input extends $FormElementMethod {} -@FormElementMethod export class $Input extends $Element { constructor(options?: $InputOptions) { super('input', options); @@ -152,4 +149,39 @@ export class $Input extends $Element { reportValidity() { return this.dom.reportValidity() } get files() { return this.dom.files } get webkitEntries() { return this.dom.webkitEntries } + + + formAction(): string; + formAction(action: string | undefined): this; + formAction(action?: string) { return $.fluent(this, arguments, () => this.dom.formAction, () => $.set(this.dom, 'formAction', action))} + + formEnctype(): string; + formEnctype(enctype: string | undefined): this; + formEnctype(enctype?: string) { return $.fluent(this, arguments, () => this.dom.formEnctype, () => $.set(this.dom, 'formEnctype', enctype))} + + formMethod(): string; + formMethod(method: string | undefined): this; + formMethod(method?: string) { return $.fluent(this, arguments, () => this.dom.formMethod, () => $.set(this.dom, 'formMethod', method))} + + formNoValidate(): boolean; + formNoValidate(boolean: boolean | undefined): this; + formNoValidate(boolean?: boolean) { return $.fluent(this, arguments, () => this.dom.formNoValidate, () => $.set(this.dom, 'formNoValidate', boolean))} + + formTarget(): string; + formTarget(target: string | undefined): this; + formTarget(target?: string) { return $.fluent(this, arguments, () => this.dom.formTarget, () => $.set(this.dom, 'formTarget', target))} + + name(): string; + name(name?: string | $State): this; + name(name?: string | $State) { return $.fluent(this, arguments, () => this.dom.name, () => $.set(this.dom, 'name', name))} + + value(): string; + value(value?: string | $State): this; + value(value?: string | $State) { return $.fluent(this, arguments, () => this.dom.value, () => $.set(this.dom, 'value', value))} + + get form() { return this.dom.form ? $(this.dom.form) : null } + get labels() { return Array.from(this.dom.labels ?? []).map(label => $(label)) } + get validationMessage() { return this.dom.validationMessage } + get validity() { return this.dom.validity } + get willValidate() { return this.dom.willValidate } } \ No newline at end of file diff --git a/lib/$Select.ts b/lib/$Select.ts index 65e1360..6d917e5 100644 --- a/lib/$Select.ts +++ b/lib/$Select.ts @@ -1,13 +1,9 @@ import { $Container, $ContainerOptions } from "./$Container"; -import { $FormElementMethod, FormElementMethod } from "./$Form"; import { $OptGroup } from "./$OptGroup"; import { $Option } from "./$Option"; import { $State } from "./$State"; export interface $SelectOptions extends $ContainerOptions {} -//@ts-expect-error -export interface $Select extends $FormElementMethod {} -@FormElementMethod export class $Select extends $Container { constructor() { super('select') @@ -42,6 +38,20 @@ export class $Select extends $Container { get options() { return Array.from(this.dom.options).map($option => $($option)) } get selectedIndex() { return this.dom.selectedIndex } get selectedOptions() { return Array.from(this.dom.selectedOptions).map($option => $($option)) } + + name(): string; + name(name?: string | $State): this; + name(name?: string | $State) { return $.fluent(this, arguments, () => this.dom.name, () => $.set(this.dom, 'name', name))} + + value(): string; + value(value?: string | $State): this; + value(value?: string | $State) { return $.fluent(this, arguments, () => this.dom.value, () => $.set(this.dom, 'value', value))} + + get form() { return this.dom.form ? $(this.dom.form) : null } + get labels() { return Array.from(this.dom.labels ?? []).map(label => $(label)) } + get validationMessage() { return this.dom.validationMessage } + get validity() { return this.dom.validity } + get willValidate() { return this.dom.willValidate } } export type $SelectContentType = $Option | $OptGroup | undefined; \ No newline at end of file diff --git a/lib/Router/Router.ts b/lib/Router/Router.ts index 246cb1a..1412aae 100644 --- a/lib/Router/Router.ts +++ b/lib/Router/Router.ts @@ -17,7 +17,7 @@ export class Router { /**Add route to Router. @example Router.addRoute(new Route('/', 'Hello World')) */ addRoute(routes: OrArray>) { - routes = $.multableResolve(routes); + routes = $.orArrayResolve(routes); for (const route of routes) this.routeMap.set(route.path, route); return this; } diff --git a/package.json b/package.json index 6efed12..384f3f4 100644 --- a/package.json +++ b/package.json @@ -1,7 +1,7 @@ { "name": "fluentx", "description": "Fast, fluent, simple web builder", - "version": "0.0.4", + "version": "0.0.5", "type": "module", "module": "index.ts", "author": {