From b51edda800ec00150758cede501abf1a6524d257 Mon Sep 17 00:00:00 2001 From: defaultkavy Date: Sat, 20 Apr 2024 20:46:11 +0800 Subject: [PATCH] v0.0.4 new: $View change: Router.view => $View new: $Select, $Option, $OptGroup, $Textarea, $View add: $ query selector add: $Container.clear() .$() .$all() change: $Element.options => .setOptions remove: EventMethod function --- $index.ts | 33 +++++++++++--- index.ts | 7 ++- lib/$Container.ts | 12 ++++++ lib/$Element.ts | 4 +- lib/$EventManager.ts | 1 - lib/$Input.ts | 18 ++++---- lib/$NodeManager.ts | 12 +++--- lib/$OptGroup.ts | 17 ++++++++ lib/$Option.ts | 37 ++++++++++++++++ lib/$Select.ts | 47 ++++++++++++++++++++ lib/$Textarea.ts | 100 +++++++++++++++++++++++++++++++++++++++++++ lib/$View.ts | 41 ++++++++++++++++++ lib/Router/Route.ts | 15 ++++--- lib/Router/Router.ts | 25 ++++++----- package.json | 2 +- 15 files changed, 328 insertions(+), 43 deletions(-) create mode 100644 lib/$OptGroup.ts create mode 100644 lib/$Option.ts create mode 100644 lib/$Select.ts create mode 100644 lib/$Textarea.ts create mode 100644 lib/$View.ts diff --git a/$index.ts b/$index.ts index 8ba0151..b9091c0 100644 --- a/$index.ts +++ b/$index.ts @@ -10,7 +10,14 @@ import { Router } from "./lib/Router/Router"; import { $Image } from "./lib/$Image"; import { $Canvas } from "./lib/$Canvas"; import { $Dialog } from "./lib/$Dialog"; +import { $View } from "./lib/$View"; +import { $Select } from "./lib/$Select"; +import { $Option } from "./lib/$Option"; +import { $OptGroup } from "./lib/$OptGroup"; +import { $Textarea } from "./lib/$Textarea"; export type $ = typeof $; +export function $(query: `::${string}`): E[]; +export function $(query: `:${string}`): E | null; export function $(element: null): null; export function $(resolver: K): $.TagNameTypeMap[K]; export function $(resolver: K): $Container; @@ -23,7 +30,9 @@ export function $(resolver: any) { if (typeof resolver === 'undefined') return resolver; if (resolver === null) return resolver; if (typeof resolver === 'string') { - if (resolver in $.TagNameElementMap) { + if (resolver.startsWith('::')) return Array.from(document.querySelectorAll(resolver.replace(/^::/, ''))).map(dom => $(dom)); + else if (resolver.startsWith(':')) return $(document.querySelector(resolver.replace(/^:/, ''))); + else if (resolver in $.TagNameElementMap) { const instance = $.TagNameElementMap[resolver as keyof typeof $.TagNameElementMap] switch (instance) { case $Element: return new $Element(resolver); @@ -36,6 +45,11 @@ export function $(resolver: any) { case $Image: return new $Image(); case $Canvas: return new $Canvas(); case $Dialog: return new $Dialog(); + case $View: return new $View(); + case $Select: return new $Select(); + case $Option: return new $Option(); + case $OptGroup: return new $OptGroup(); + case $Textarea: return new $Textarea(); } } else return new $Container(resolver); } @@ -45,7 +59,6 @@ export function $(resolver: any) { } throw '$: NOT SUPPORT TARGET ELEMENT TYPE' } - export namespace $ { export let anchorHandler: null | ((url: string, e: Event) => void) = null; export let anchorPreventDefault: boolean = false; @@ -74,7 +87,12 @@ export namespace $ { 'form': $Form, 'img': $Image, 'dialog': $Dialog, - 'canvas': $Canvas + 'canvas': $Canvas, + 'view': $View, + 'select': $Select, + 'option': $Option, + 'optgroup': $OptGroup, + 'textarea': $Textarea } export type TagNameTypeMap = { [key in keyof typeof $.TagNameElementMap]: InstanceType; @@ -92,7 +110,11 @@ export namespace $ { : H extends HTMLFormElement ? $Form : H extends HTMLCanvasElement ? $Canvas : H extends HTMLDialogElement ? $Dialog - : $Element; + : H extends HTMLSelectElement ? $Select + : H extends HTMLOptionElement ? $Option + : H extends HTMLOptGroupElement ? $OptGroup + : H extends HTMLTextAreaElement ? $Textarea + : $Container; export function fluent(instance: T, args: IArguments, value: () => V, action: (...args: any[]) => void) { if (!args.length) return value(); @@ -198,4 +220,5 @@ export namespace $ { } type BuildNodeFunction = (...args: any[]) => $Node; type BuilderSelfFunction = (self: K) => void; -globalThis.$ = $; \ No newline at end of file +globalThis.$ = $; + diff --git a/index.ts b/index.ts index ebcaea2..0122ef7 100644 --- a/index.ts +++ b/index.ts @@ -41,4 +41,9 @@ export * from "./lib/$Container"; export * from "./lib/$Button"; export * from "./lib/$Form"; export * from "./lib/$EventManager"; -export * from "./lib/$State"; \ No newline at end of file +export * from "./lib/$State"; +export * from "./lib/$View"; +export * from "./lib/$Select"; +export * from "./lib/$Option"; +export * from "./lib/$OptGroup"; +export * from "./lib/$Textarea"; \ No newline at end of file diff --git a/lib/$Container.ts b/lib/$Container.ts index 8cce25d..8fa3693 100644 --- a/lib/$Container.ts +++ b/lib/$Container.ts @@ -35,6 +35,18 @@ export class $Container extends $Element } this.children.render(); })} + + /**Remove all children elemetn from this element */ + clear() { + this.children.removeAll(); + return this; + } + + //**Query selector one of child element */ + $(query: string) { return $(this.dom.querySelector(query)) as E | null } + + //**Query selector of child elements */ + $all(query: string) { return Array.from(this.dom.querySelectorAll(query)).map($dom => $($dom) as E) } } export type $ContainerContentBuilder

= OrMatrix<$ContainerContentType> | (($node: P) => OrMatrix<$ContainerContentType>) diff --git a/lib/$Element.ts b/lib/$Element.ts index 62d9c4f..2de8f0b 100644 --- a/lib/$Element.ts +++ b/lib/$Element.ts @@ -12,10 +12,10 @@ export class $Element extends $Node { super(); this.dom = document.createElement(tagname) as H; this.dom.$ = this; - this.options(options); + this.setOptions(options); } - options(options: $ElementOptions | undefined) { + setOptions(options: $ElementOptions | undefined) { this.id(options?.id) if (options && options.class) this.class(...options.class) return this; diff --git a/lib/$EventManager.ts b/lib/$EventManager.ts index 54c6bca..f6bc676 100644 --- a/lib/$EventManager.ts +++ b/lib/$EventManager.ts @@ -1,4 +1,3 @@ -export function EventMethod(target: T) {return $.mixin(target, $EventMethod)} export abstract class $EventMethod { abstract events: $EventManager; //@ts-expect-error diff --git a/lib/$Input.ts b/lib/$Input.ts index 1b5e7ce..6ad9f56 100644 --- a/lib/$Input.ts +++ b/lib/$Input.ts @@ -34,13 +34,13 @@ export class $Input extends $Element { checked(boolean: boolean): this; checked(boolean?: boolean) { return $.fluent(this, arguments, () => this.dom.checked, () => $.set(this.dom, 'checked', boolean))} - max(): string; - max(max: string): this; - max(max?: string) { return $.fluent(this, arguments, () => this.dom.max, () => $.set(this.dom, 'max', max))} + max(): number; + max(max: number): this; + max(max?: number) { return $.fluent(this, arguments, () => this.dom.max === '' ? null : parseInt(this.dom.min), () => $.set(this.dom, 'max', max?.toString()))} - min(): string; - min(min: string): this; - min(min?: string) { return $.fluent(this, arguments, () => this.dom.min, () => $.set(this.dom, 'min', min))} + min(): number; + min(min: number): this; + min(min?: number) { return $.fluent(this, arguments, () => this.dom.min === '' ? null : parseInt(this.dom.min), () => $.set(this.dom, 'min', min?.toString()))} maxLength(): number; maxLength(maxLength: number): this; @@ -110,9 +110,9 @@ export class $Input extends $Element { src(src: string): this; src(src?: string) { return $.fluent(this, arguments, () => this.dom.src, () => $.set(this.dom, 'src', src))} - step(): string; - step(step: string): this; - step(step?: string) { return $.fluent(this, arguments, () => this.dom.step, () => $.set(this.dom, 'step', step))} + step(): number; + step(step: number): this; + step(step?: number) { return $.fluent(this, arguments, () => Number(this.dom.step), () => $.set(this.dom, 'step', step?.toString()))} type(): InputType; type(type: InputType): this; diff --git a/lib/$NodeManager.ts b/lib/$NodeManager.ts index 45d0b9e..b6c211e 100644 --- a/lib/$NodeManager.ts +++ b/lib/$NodeManager.ts @@ -4,11 +4,9 @@ import { $Text } from "./$Text"; export class $NodeManager { #container: $Container; - #dom: HTMLElement; elementList = new Set<$Node> constructor(container: $Container) { this.#container = container; - this.#dom = this.#container.dom } add(element: $Node | string) { @@ -47,23 +45,25 @@ export class $NodeManager { } 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 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.$.__hidden) this.dom.append(dom); domList.shift();} else if (dom !== node) { - if (!dom.$.__hidden) { this.#dom.insertBefore(dom, node); appendedNodeList.push(dom) } + if (!dom.$.__hidden) { this.dom.insertBefore(dom, node); appendedNodeList.push(dom) } domList.shift(); } else { - if (dom.$.__hidden) this.#dom.removeChild(dom); + if (dom.$.__hidden) this.dom.removeChild(dom); domList.shift(); nodeList.shift(); } } } get array() {return [...this.elementList.values()]}; + + get dom() {return this.#container.dom} } \ No newline at end of file diff --git a/lib/$OptGroup.ts b/lib/$OptGroup.ts new file mode 100644 index 0000000..342d4a6 --- /dev/null +++ b/lib/$OptGroup.ts @@ -0,0 +1,17 @@ +import { $Container, $ContainerOptions } from "./$Container"; +import { $State } from "./$State"; + +export interface $OptGroupOptions extends $ContainerOptions {} +export class $OptGroup extends $Container { + constructor(options?: $OptGroupOptions) { + super('optgroup', options); + } + + disabled(): boolean; + disabled(disabled: boolean | $State): this; + disabled(disabled?: boolean | $State) { return $.fluent(this, arguments, () => this.dom.disabled, () => $.set(this.dom, 'disabled', disabled))} + + label(): string; + label(label: string | $State): this; + label(label?: string | $State) { return $.fluent(this, arguments, () => this.dom.label, () => $.set(this.dom, 'label', label))} +} \ No newline at end of file diff --git a/lib/$Option.ts b/lib/$Option.ts new file mode 100644 index 0000000..e8093e4 --- /dev/null +++ b/lib/$Option.ts @@ -0,0 +1,37 @@ +import { $Container, $ContainerOptions } from "./$Container"; +import { $State } from "./$State"; + +export interface $OptionOptions extends $ContainerOptions {} +export class $Option extends $Container { + constructor(options?: $OptionOptions) { + super('option', options); + } + + defaultSelected(): boolean; + defaultSelected(defaultSelected: boolean | $State): this; + defaultSelected(defaultSelected?: boolean | $State) { return $.fluent(this, arguments, () => this.dom.defaultSelected, () => $.set(this.dom, 'defaultSelected', defaultSelected))} + + disabled(): boolean; + disabled(disabled: boolean | $State): this; + disabled(disabled?: boolean | $State) { return $.fluent(this, arguments, () => this.dom.disabled, () => $.set(this.dom, 'disabled', disabled))} + + label(): string; + label(label: string | $State): this; + label(label?: string | $State) { return $.fluent(this, arguments, () => this.dom.label, () => $.set(this.dom, 'label', label))} + + selected(): boolean; + selected(selected: boolean | $State): this; + selected(selected?: boolean | $State) { return $.fluent(this, arguments, () => this.dom.selected, () => $.set(this.dom, 'selected', selected))} + + text(): string; + text(text: string | $State): this; + text(text?: string | $State) { return $.fluent(this, arguments, () => this.dom.text, () => $.set(this.dom, 'text', text))} + + 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 index() { return this.dom.index } + +} \ No newline at end of file diff --git a/lib/$Select.ts b/lib/$Select.ts new file mode 100644 index 0000000..65e1360 --- /dev/null +++ b/lib/$Select.ts @@ -0,0 +1,47 @@ +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') + } + + add(option: $SelectContentType | OrMatrix<$SelectContentType>) { + this.insert(option); + return this; + } + + item(index: number) { return $(this.dom.item(index)) } + namedItem(name: string) { return $(this.dom.namedItem(name)) } + + disabled(): boolean; + disabled(disabled: boolean | $State): this; + disabled(disabled?: boolean | $State) { return $.fluent(this, arguments, () => this.dom.disabled, () => $.set(this.dom, 'disabled', disabled))} + + multiple(): boolean; + multiple(multiple: boolean | $State): this; + multiple(multiple?: boolean | $State) { return $.fluent(this, arguments, () => this.dom.multiple, () => $.set(this.dom, 'multiple', multiple))} + + required(): boolean; + required(required: boolean): this; + required(required?: boolean) { return $.fluent(this, arguments, () => this.dom.required, () => $.set(this.dom, 'required', required))} + + autocomplete(): AutoFill; + autocomplete(autocomplete: AutoFill): this; + autocomplete(autocomplete?: AutoFill) { return $.fluent(this, arguments, () => this.dom.autocomplete, () => $.set(this.dom, 'autocomplete', autocomplete))} + + get length() { return this.dom.length } + get size() { return this.dom.size } + 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)) } +} + +export type $SelectContentType = $Option | $OptGroup | undefined; \ No newline at end of file diff --git a/lib/$Textarea.ts b/lib/$Textarea.ts new file mode 100644 index 0000000..7378e61 --- /dev/null +++ b/lib/$Textarea.ts @@ -0,0 +1,100 @@ +import { $Container, $ContainerOptions } from "./$Container"; +import { $State } from "./$State"; + +export interface $TextareaOptions extends $ContainerOptions {} +export class $Textarea extends $Container { + constructor(options?: $TextareaOptions) { + super('textarea', options); + } + + cols(): number; + cols(cols: number): this; + cols(cols?: number) { return $.fluent(this, arguments, () => this.dom.cols, () => $.set(this.dom, 'cols', cols))} + + name(): string; + name(name?: string | $State): this; + name(name?: string | $State) { return $.fluent(this, arguments, () => this.dom.name, () => $.set(this.dom, 'name', name))} + + wrap(): string; + wrap(wrap?: string | $State): this; + wrap(wrap?: string | $State) { return $.fluent(this, arguments, () => this.dom.wrap, () => $.set(this.dom, 'wrap', wrap))} + + value(): string; + value(value?: string | $State): this; + value(value?: string | $State) { return $.fluent(this, arguments, () => this.dom.value, () => $.set(this.dom, 'value', value))} + + maxLength(): number; + maxLength(maxLength: number): this; + maxLength(maxLength?: number) { return $.fluent(this, arguments, () => this.dom.maxLength, () => $.set(this.dom, 'maxLength', maxLength))} + + minLength(): number; + minLength(minLength: number): this; + minLength(minLength?: number) { return $.fluent(this, arguments, () => this.dom.minLength, () => $.set(this.dom, 'minLength', minLength))} + + autocomplete(): AutoFill; + autocomplete(autocomplete: AutoFill): this; + autocomplete(autocomplete?: AutoFill) { return $.fluent(this, arguments, () => this.dom.autocomplete, () => $.set(this.dom, 'autocomplete', autocomplete))} + + defaultValue(): string; + defaultValue(defaultValue: string): this; + defaultValue(defaultValue?: string) { return $.fluent(this, arguments, () => this.dom.defaultValue, () => $.set(this.dom, 'defaultValue', defaultValue))} + + dirName(): string; + dirName(dirName: string): this; + dirName(dirName?: string) { return $.fluent(this, arguments, () => this.dom.dirName, () => $.set(this.dom, 'dirName', dirName))} + + disabled(): boolean; + disabled(disabled: boolean): this; + disabled(disabled?: boolean) { return $.fluent(this, arguments, () => this.dom.disabled, () => $.set(this.dom, 'disabled', disabled))} + + placeholder(): string; + placeholder(placeholder?: string): this; + placeholder(placeholder?: string) { return $.fluent(this, arguments, () => this.dom.placeholder, () => $.set(this.dom, 'placeholder', placeholder))} + + readOnly(): boolean; + readOnly(readOnly: boolean): this; + readOnly(readOnly?: boolean) { return $.fluent(this, arguments, () => this.dom.readOnly, () => $.set(this.dom, 'readOnly', readOnly))} + + required(): boolean; + required(required: boolean): this; + required(required?: boolean) { return $.fluent(this, arguments, () => this.dom.required, () => $.set(this.dom, 'required', required))} + + selectionDirection(): SelectionDirection; + selectionDirection(selectionDirection: SelectionDirection): this; + selectionDirection(selectionDirection?: SelectionDirection) { return $.fluent(this, arguments, () => this.dom.selectionDirection, () => $.set(this.dom, 'selectionDirection', selectionDirection))} + + selectionEnd(): number; + selectionEnd(selectionEnd: number): this; + selectionEnd(selectionEnd?: number) { return $.fluent(this, arguments, () => this.dom.selectionEnd, () => $.set(this.dom, 'selectionEnd', selectionEnd))} + + selectionStart(): number; + selectionStart(selectionStart: number): this; + selectionStart(selectionStart?: number) { return $.fluent(this, arguments, () => this.dom.selectionStart, () => $.set(this.dom, 'selectionStart', selectionStart))} + + type(): InputType; + type(type: InputType): this; + type(type?: InputType) { return $.fluent(this, arguments, () => this.dom.type, () => $.set(this.dom, 'type', type))} + + inputMode(): InputMode; + inputMode(mode: InputMode): this; + inputMode(mode?: InputMode) { return $.fluent(this, arguments, () => this.dom.inputMode as InputMode, () => $.set(this.dom, 'inputMode', mode))} + + select() { this.dom.select(); return this } + setCustomValidity(error: string) { this.dom.setCustomValidity(error); return this } + setRangeText(replacement: string): this; + setRangeText(replacement: string, start: number, end: number, selectionMode?: SelectionMode): this; + setRangeText(replacement: string, start?: number, end?: number, selectionMode?: SelectionMode) { + if (typeof start === 'number' && typeof end === 'number') this.dom.setRangeText(replacement, start, end, selectionMode) + this.dom.setRangeText(replacement); + return this + } + setSelectionRange(start: number | null, end: number | null, direction?: SelectionDirection) { this.dom.setSelectionRange(start, end, direction); return this } + + checkValidity() { return this.dom.checkValidity() } + reportValidity() { return this.dom.reportValidity() } + + get validationMessage() { return this.dom.validationMessage } + get validity() { return this.dom.validity } + get form() { return this.dom.form ? $(this.dom.form) : null } + get labels() { return Array.from(this.dom.labels ?? []).map(label => $(label)) } +} \ No newline at end of file diff --git a/lib/$View.ts b/lib/$View.ts new file mode 100644 index 0000000..c5e64ca --- /dev/null +++ b/lib/$View.ts @@ -0,0 +1,41 @@ +import { $Container, $ContainerOptions } from "./$Container"; +import { $EventManager } from "./$EventManager"; +import { $Node } from "./$Node"; + +export interface $ViewOptions extends $ContainerOptions {} +export class $View extends $Container { + protected view_cache = new Map(); + event = new $EventManager<$ViewEventMap>().register('switch') + content_id: string | null = null; + constructor(options?: $ViewOptions) { + super('view', options); + } + + setView(id: string, $node: $Node) { + this.view_cache.set(id, $node); + return this; + } + + deleteView(id: string) { + this.view_cache.delete(id); + return this; + } + + deleteAllView() { + this.view_cache.clear(); + return this; + } + + switchView(id: string) { + const target_content = this.view_cache.get(id); + if (target_content === undefined) throw '$View.switch(): target content is undefined'; + this.content(target_content); + this.content_id = id; + this.event.fire('switch', id); + return this; + } +} + +export interface $ViewEventMap { + 'switch': [id: string] +} \ No newline at end of file diff --git a/lib/Router/Route.ts b/lib/Router/Route.ts index a92469a..fc82115 100644 --- a/lib/Router/Route.ts +++ b/lib/Router/Route.ts @@ -1,11 +1,11 @@ -import { $EventManager, $EventMethod, EventMethod } from "../$EventManager"; +import { $EventManager, $EventMethod } from "../$EventManager"; import { $Node } from "../$Node"; export class Route { path: string | PathResolverFn; - builder: (req: RouteRequest) => $Node | string; - constructor(path: Path, builder: (req: RouteRequest) => $Node | string) { + builder: (req: RouteRequest) => RouteContent; + constructor(path: Path, builder: ((req: RouteRequest) => RouteContent) | RouteContent) { this.path = path; - this.builder = builder; + this.builder = builder instanceof Function ? builder : (req: RouteRequest) => builder; } } @@ -24,7 +24,6 @@ type PathParamResolver

= P extends PathResolv export interface RouteRecord extends $EventMethod {}; -@EventMethod export class RouteRecord { id: string; readonly content?: $Node; @@ -33,7 +32,7 @@ export class RouteRecord { this.id = id; } } - +$.mixin(RouteRecord, $EventMethod) export interface RouteRecordEventMap { 'open': [{path: string, record: RouteRecord}]; 'load': [{path: string, record: RouteRecord}]; @@ -43,4 +42,6 @@ export interface RouteRequest { params: PathParamResolver, record: RouteRecord, loaded: () => void; -} \ No newline at end of file +} + +export type RouteContent = $Node | string | void; \ No newline at end of file diff --git a/lib/Router/Router.ts b/lib/Router/Router.ts index 2595f98..246cb1a 100644 --- a/lib/Router/Router.ts +++ b/lib/Router/Router.ts @@ -1,19 +1,18 @@ -import { $Container } from "../$Container"; -import { $EventManager, $EventMethod, EventMethod } from "../$EventManager"; +import { $EventManager, $EventMethod } from "../$EventManager"; import { $Text } from "../$Text"; +import { $View } from "../$View"; import { PathResolverFn, Route, RouteRecord } from "./Route"; export interface Router extends $EventMethod {}; -@EventMethod export class Router { routeMap = new Map>(); recordMap = new Map(); - view: $Container; + $view: $View; index: number = 0; events = new $EventManager().register('pathchange', 'notfound', 'load'); basePath: string; - constructor(basePath: string, view: $Container) { + constructor(basePath: string, view?: $View) { this.basePath = basePath; - this.view = view + this.$view = view ?? new $View(); } /**Add route to Router. @example Router.addRoute(new Route('/', 'Hello World')) */ @@ -41,7 +40,7 @@ export class Router { /**Open path */ open(path: string | undefined) { if (path === undefined) return; - if (path === location.href) return this; + if (path === location.pathname) return this; this.index += 1; const routeData: RouteData = { index: this.index, data: {} }; history.pushState(routeData, '', path); @@ -53,7 +52,9 @@ export class Router { /**Back to previous page */ back() { history.back(); return this } - replace(path: string) { + replace(path: string | undefined) { + if (path === undefined) return; + if (path === location.pathname) return this; history.replaceState({index: this.index}, '', path) $.routers.forEach(router => router.resolvePath(path)); this.events.fire('pathchange', {path, navigation: 'Forward'}); @@ -84,7 +85,7 @@ export class Router { const record = this.recordMap.get(pathId); if (record) { found = true; - if (record.content && !this.view.contains(record.content)) this.view.content(record.content); + if (record.content && !this.$view.contains(record.content)) this.$view.switchView(pathId); record.events.fire('open', {path, record}); return true; } @@ -101,9 +102,10 @@ export class Router { } }); if (typeof content === 'string') content = new $Text(content); + if (content === undefined) return; (record as Mutable).content = content; this.recordMap.set(pathId, record); - this.view.content(content); + this.$view.setView(pathId, content).switchView(pathId); record.events.fire('open', {path, record}); found = true; } @@ -146,10 +148,11 @@ export class Router { let preventDefaultState = false; const preventDefault = () => preventDefaultState = true; this.events.fire('notfound', {path, preventDefault}); - if (!preventDefaultState) this.view.children.removeAll(); + if (!preventDefaultState) this.$view.clear(); } } } +$.mixin(Router, $EventMethod); interface RouterEventMap { pathchange: [{path: string, navigation: 'Back' | 'Forward'}]; notfound: [{path: string, preventDefault: () => void}]; diff --git a/package.json b/package.json index 507307f..6efed12 100644 --- a/package.json +++ b/package.json @@ -1,7 +1,7 @@ { "name": "fluentx", "description": "Fast, fluent, simple web builder", - "version": "0.0.3", + "version": "0.0.4", "type": "module", "module": "index.ts", "author": {