amateras 0.1.1 → 0.3.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +25 -8
- package/ext/css/src/index.ts +103 -49
- package/ext/css/src/lib/colorAssign.ts +6 -0
- package/ext/css/src/lib/colors/amber.ts +25 -0
- package/ext/css/src/lib/colors/blackwhite.ts +13 -0
- package/ext/css/src/lib/colors/blue.ts +25 -0
- package/ext/css/src/lib/colors/cyan.ts +25 -0
- package/ext/css/src/lib/colors/emerald.ts +25 -0
- package/ext/css/src/lib/colors/fuchsia.ts +25 -0
- package/ext/css/src/lib/colors/gray.ts +25 -0
- package/ext/css/src/lib/colors/green.ts +25 -0
- package/ext/css/src/lib/colors/indigo.ts +25 -0
- package/ext/css/src/lib/colors/lime.ts +25 -0
- package/ext/css/src/lib/colors/neutral.ts +25 -0
- package/ext/css/src/lib/colors/orange.ts +25 -0
- package/ext/css/src/lib/colors/pink.ts +25 -0
- package/ext/css/src/lib/colors/purple.ts +25 -0
- package/ext/css/src/lib/colors/red.ts +25 -0
- package/ext/css/src/lib/colors/rose.ts +25 -0
- package/ext/css/src/lib/colors/sky.ts +25 -0
- package/ext/css/src/lib/colors/slate.ts +25 -0
- package/ext/css/src/lib/colors/stone.ts +25 -0
- package/ext/css/src/lib/colors/teal.ts +25 -0
- package/ext/css/src/lib/colors/violet.ts +25 -0
- package/ext/css/src/lib/colors/yellow.ts +25 -0
- package/ext/css/src/lib/colors/zinc.ts +25 -0
- package/ext/css/src/lib/colors.ts +23 -0
- package/ext/css/src/structure/$CSSKeyframesRule.ts +2 -5
- package/ext/css/src/structure/$CSSMediaRule.ts +3 -23
- package/ext/css/src/structure/$CSSRule.ts +6 -18
- package/ext/css/src/structure/$CSSStyleRule.ts +10 -12
- package/ext/html/html.ts +24 -58
- package/ext/html/node/$Anchor.ts +49 -0
- package/ext/html/node/$Canvas.ts +16 -0
- package/ext/html/node/$Dialog.ts +16 -0
- package/ext/html/node/$Form.ts +16 -0
- package/ext/html/node/$Image.ts +72 -0
- package/ext/html/node/$Input.ts +169 -0
- package/ext/html/node/$Label.ts +16 -0
- package/ext/html/node/$Media.ts +16 -0
- package/ext/html/node/$OptGroup.ts +23 -0
- package/ext/html/node/$Option.ts +40 -0
- package/ext/html/node/$Select.ts +76 -0
- package/ext/html/node/$TextArea.ts +16 -0
- package/ext/router/README.md +81 -0
- package/ext/router/index.ts +66 -0
- package/ext/router/node/Page.ts +27 -0
- package/ext/router/node/Route.ts +53 -0
- package/ext/router/node/Router.ts +138 -0
- package/ext/router/node/RouterAnchor.ts +8 -0
- package/ext/ssr/env.ts +61 -0
- package/ext/ssr/index.ts +47 -0
- package/ext/ssr/package.json +10 -0
- package/package.json +8 -4
- package/src/core.ts +43 -30
- package/src/global.ts +6 -0
- package/src/lib/assign.ts +4 -3
- package/src/lib/assignHelper.ts +11 -13
- package/src/lib/native.ts +14 -3
- package/src/node/$Element.ts +204 -23
- package/src/node/$HTMLElement.ts +76 -0
- package/src/node/$Node.ts +145 -53
- package/src/node/node.ts +8 -6
- package/src/structure/Signal.ts +4 -4
- package/tsconfig.json +1 -1
- package/ext/css/src/structure/$CSSKeyframeRule.ts +0 -13
- package/ext/html/node/$HTMLElement.ts +0 -7
- package/ext/html/node/type.ts +0 -96
|
@@ -0,0 +1,138 @@
|
|
|
1
|
+
import type { AnchorTarget } from "#html/$Anchor";
|
|
2
|
+
import { _Array_from, _document, _instanceof, _Object_fromEntries, forEach } from "#lib/native";
|
|
3
|
+
import { Page } from "./Page";
|
|
4
|
+
import { BaseRouteNode, Route } from "./Route";
|
|
5
|
+
|
|
6
|
+
// history index
|
|
7
|
+
let index = 0;
|
|
8
|
+
const _location = location;
|
|
9
|
+
const {origin} = _location;
|
|
10
|
+
const _history = history;
|
|
11
|
+
const documentElement = _document.documentElement;
|
|
12
|
+
const [PUSH, REPLACE] = [1, 2] as const;
|
|
13
|
+
const [FORWARD, BACK] = ['forward', 'back'] as const;
|
|
14
|
+
|
|
15
|
+
// disable browser scroll restoration
|
|
16
|
+
_history.scrollRestoration = 'manual';
|
|
17
|
+
|
|
18
|
+
/** convert path string to URL object */
|
|
19
|
+
const toURL = (path: string | URL) =>
|
|
20
|
+
_instanceof(path, URL) ? path : path.startsWith('http') ? new URL(path) : new URL(path.startsWith(origin) ? path : origin + path);
|
|
21
|
+
|
|
22
|
+
/** handle history state with push and replace state. */
|
|
23
|
+
const historyHandler = async (path: string | URL | Nullish, mode: 1 | 2, target?: AnchorTarget) => {
|
|
24
|
+
if (!path) return;
|
|
25
|
+
const url = toURL(path);
|
|
26
|
+
if (url.href === _location.href) return;
|
|
27
|
+
if (target && target !== '_self') return open(url, target);
|
|
28
|
+
if (url.origin !== origin) return open(url, target);
|
|
29
|
+
_history.replaceState({
|
|
30
|
+
index: index,
|
|
31
|
+
x: documentElement.scrollLeft,
|
|
32
|
+
y: documentElement.scrollTop
|
|
33
|
+
}, '', _location.href);
|
|
34
|
+
if (mode === PUSH) index += 1;
|
|
35
|
+
Router.direction = FORWARD;
|
|
36
|
+
history[mode === PUSH ? 'pushState' : 'replaceState']({index}, '' , url)
|
|
37
|
+
for (let router of Router.routers) router.routes.size && await router.resolve(path)
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
export class Router extends BaseRouteNode<''> {
|
|
41
|
+
static pageRouters = new Map<Page, Router>();
|
|
42
|
+
static routers = new Set<Router>();
|
|
43
|
+
pageMap = new Map<string, Page>();
|
|
44
|
+
static direction: 'back' | 'forward' = FORWARD;
|
|
45
|
+
constructor(page?: Page) {
|
|
46
|
+
super('', () => [], 'router')
|
|
47
|
+
Router.routers.add(this);
|
|
48
|
+
if (page) Router.pageRouters.set(page, this);
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
static open(path: string | URL | Nullish, target?: AnchorTarget) {
|
|
52
|
+
historyHandler(path, PUSH, target);
|
|
53
|
+
return this;
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
static back() {
|
|
57
|
+
_history.back();
|
|
58
|
+
return this;
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
static forward() {
|
|
62
|
+
_history.forward();
|
|
63
|
+
return this;
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
static replace(path: string | URL | Nullish) {
|
|
67
|
+
historyHandler(path, REPLACE);
|
|
68
|
+
return this;
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
async resolve(path: string | URL) {
|
|
72
|
+
const {pathname, searchParams, hash, href} = toURL(path);
|
|
73
|
+
const routeData = { params: {} as {[key: string]: string}, query: _Object_fromEntries(searchParams) }
|
|
74
|
+
const split = (p: string) => p.replaceAll(/\/+/g, '/').split('/').map(path => `/${path}`);
|
|
75
|
+
|
|
76
|
+
function determineRoute(parentRoute: BaseRouteNode<any>, path: string, hash: string | undefined): [route: Route | null, pathId: string][] {
|
|
77
|
+
const targetPathSplit = split(path);
|
|
78
|
+
hash && targetPathSplit.push(hash);
|
|
79
|
+
if (!parentRoute.routes.size) return [];
|
|
80
|
+
routeLoop: for (const route of _Array_from(parentRoute.routes.values()).sort((a, b) => b.path.length - a.path.length)) {
|
|
81
|
+
const routePathSplit = split(route.path);
|
|
82
|
+
let pathId = '';
|
|
83
|
+
splitLoop: for (let i = 0; i < routePathSplit.length; i++) {
|
|
84
|
+
const pass = () => pathId += targetSnippet;
|
|
85
|
+
const [routeSnippet, targetSnippet] = [routePathSplit[i], targetPathSplit[i]];
|
|
86
|
+
if (!routeSnippet || !targetSnippet) continue routeLoop;
|
|
87
|
+
// process params in path
|
|
88
|
+
if (routeSnippet.includes(':')) {
|
|
89
|
+
if (targetSnippet === '/') continue routeLoop;
|
|
90
|
+
const [prefix, paramName] = routeSnippet.split(':') as [string, string];
|
|
91
|
+
if (!targetSnippet.startsWith(prefix)) continue routeLoop;
|
|
92
|
+
routeData.params[paramName] = targetSnippet.replace(`${prefix}`, '');
|
|
93
|
+
pass();
|
|
94
|
+
continue splitLoop;
|
|
95
|
+
}
|
|
96
|
+
if (routeSnippet === '/' && route.routes.size) continue splitLoop;
|
|
97
|
+
if (routeSnippet !== targetSnippet) continue routeLoop;
|
|
98
|
+
pass()
|
|
99
|
+
}
|
|
100
|
+
return [[route, pathId], ...determineRoute(route, path, hash)];
|
|
101
|
+
}
|
|
102
|
+
return [[null, parentRoute.path + '$$NOT_FOUND$$']];
|
|
103
|
+
}
|
|
104
|
+
// analytics
|
|
105
|
+
const targetRoutes = determineRoute(this, pathname + '/', hash);
|
|
106
|
+
// build pages
|
|
107
|
+
let prevPage: null | Page = null, prevRoute: BaseRouteNode<any> = this;
|
|
108
|
+
for (const [route, pathId] of targetRoutes) {
|
|
109
|
+
const page = this.pageMap.get(pathId) ?? new Page(route ?? prevRoute.routes.get('404') ?? new Route('404', () => null), routeData);
|
|
110
|
+
await route?.build(routeData, page);
|
|
111
|
+
_document && (_document.title = page.pageTitle() ?? _document.title);
|
|
112
|
+
this.pageMap.set(pathId, page);
|
|
113
|
+
|
|
114
|
+
if (href === _location.href) {
|
|
115
|
+
if (prevPage) Router.pageRouters.get(prevPage)?.content(page);
|
|
116
|
+
else this.content(page);
|
|
117
|
+
}
|
|
118
|
+
prevPage = page;
|
|
119
|
+
if (route) prevRoute = route;
|
|
120
|
+
}
|
|
121
|
+
let { x, y } = _history.state ?? {x: 0, y: 0};
|
|
122
|
+
scrollTo(x, y);
|
|
123
|
+
return this;
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
listen() {
|
|
127
|
+
const resolve = () => {
|
|
128
|
+
const stateIndex = _history.state?.index ?? 0;
|
|
129
|
+
if (index > stateIndex) Router.direction = BACK;
|
|
130
|
+
if (index < stateIndex) Router.direction = FORWARD;
|
|
131
|
+
index = stateIndex;
|
|
132
|
+
this.resolve(_location.href);
|
|
133
|
+
}
|
|
134
|
+
addEventListener('popstate', resolve);
|
|
135
|
+
resolve();
|
|
136
|
+
return this;
|
|
137
|
+
}
|
|
138
|
+
}
|
package/ext/ssr/env.ts
ADDED
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
import { NODE } from "esm-env";
|
|
2
|
+
import { _Array_from, _Object_assign, _Object_entries, isNull } from "../../src/lib/native";
|
|
3
|
+
import type { $Node } from "../../src/node/$Node";
|
|
4
|
+
|
|
5
|
+
if (NODE) {
|
|
6
|
+
//@ts-expect-error
|
|
7
|
+
global.window = undefined;
|
|
8
|
+
//@ts-expect-error
|
|
9
|
+
global.document = undefined;
|
|
10
|
+
//@ts-expect-error
|
|
11
|
+
global.Node = class Node {
|
|
12
|
+
nodeName: string;
|
|
13
|
+
attributes = {};
|
|
14
|
+
childNodes = new Set<Node>();
|
|
15
|
+
parent?: Node;
|
|
16
|
+
$!: $Node;
|
|
17
|
+
constructor(nodeName: string) {
|
|
18
|
+
this.nodeName = nodeName.toUpperCase();
|
|
19
|
+
}
|
|
20
|
+
get textContent() { return _Array_from(this.childNodes.values()).map(node => node.textContent).join() }
|
|
21
|
+
set textContent(content: string | null) {
|
|
22
|
+
this.childNodes.clear();
|
|
23
|
+
content && this.childNodes.add(new Text(content) as unknown as Node);
|
|
24
|
+
}
|
|
25
|
+
appendChild(node: Node) {
|
|
26
|
+
if (this.childNodes.has(node)) this.childNodes.delete(node);
|
|
27
|
+
this.childNodes.add(node);
|
|
28
|
+
node.parent = this;
|
|
29
|
+
}
|
|
30
|
+
addEventListener(type: string, cb: () => void) { }
|
|
31
|
+
setAttribute(key: string, value: string) { _Object_assign(this.attributes, {[key]: value}) }
|
|
32
|
+
remove() {
|
|
33
|
+
this.parent?.removeChild(this);
|
|
34
|
+
}
|
|
35
|
+
removeChild(node: Node) {
|
|
36
|
+
this.childNodes.delete(node);
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
get outerHTML() {
|
|
40
|
+
const attr = _Object_entries(this.attributes).map(([key, value]) => `${key}="${value}"`).join(' ');
|
|
41
|
+
const tagName = this.nodeName.toLowerCase();
|
|
42
|
+
const IS_VOID_ELEMENT = ['area', 'base', 'br', 'col', 'embed', 'hr', 'img', 'input', 'link', 'meta', 'param', 'source', 'track', 'wbr'].includes(tagName);
|
|
43
|
+
return `<${tagName}${attr ? ` ${attr}` : ''}>${_Array_from(this.childNodes).map(node => `${node.$}`).join('')}${IS_VOID_ELEMENT && !this.childNodes.size ? '' : `</${tagName}>`}`;
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
//@ts-expect-error
|
|
47
|
+
global.Element = class Element {}
|
|
48
|
+
//@ts-expect-error
|
|
49
|
+
global.HTMLElement = class HTMLElement {}
|
|
50
|
+
//@ts-expect-error
|
|
51
|
+
global.Text = class Text {
|
|
52
|
+
textContent: string;
|
|
53
|
+
$: this;
|
|
54
|
+
constructor(textContent: string) {
|
|
55
|
+
this.textContent = textContent;
|
|
56
|
+
this.$ = this;
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
toString() { return this.textContent }
|
|
60
|
+
}
|
|
61
|
+
}
|
package/ext/ssr/index.ts
ADDED
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
import './env';
|
|
2
|
+
import 'amateras';
|
|
3
|
+
import { _Array_from, _document, _instanceof, _Object_assign, _Object_defineProperty, forEach } from "amateras/lib/native";
|
|
4
|
+
import { $Element, $Node, $Text } from "amateras/node";
|
|
5
|
+
import { BROWSER, NODE } from 'esm-env';
|
|
6
|
+
|
|
7
|
+
declare module 'amateras/core' {
|
|
8
|
+
export namespace $ {
|
|
9
|
+
export function mount(id: string, $node: $Element): void;
|
|
10
|
+
export function serverSide(cb: () => any): void;
|
|
11
|
+
}
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
export function onserver<T>(cb: () => T): T | '' {
|
|
15
|
+
if (NODE) return cb();
|
|
16
|
+
return '';
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
export function onclient<T>(cb: () => T): T | undefined {
|
|
20
|
+
if (BROWSER) return cb();
|
|
21
|
+
return;
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
_Object_assign($, {
|
|
25
|
+
mount(id: string, $node: $Element) {
|
|
26
|
+
if (!BROWSER) return;
|
|
27
|
+
const node = _document.querySelector(`#${id}`);
|
|
28
|
+
if (!node) throw 'Target node of mounting not found';
|
|
29
|
+
getData(node, $node);
|
|
30
|
+
node.replaceWith($node.node);
|
|
31
|
+
|
|
32
|
+
function getData(node: Node, $node: $Node) {
|
|
33
|
+
if (node.nodeName === 'SIGNAL' && _instanceof(node, Element) && _instanceof($node, $Text)) {
|
|
34
|
+
const type = $(node).attr()['type'];
|
|
35
|
+
return forEach($node.signals, signal => signal.value(type === 'number' ? Number(node.textContent) : type === 'boolean' ? node.textContent == 'true' ? true : false : node.textContent));
|
|
36
|
+
}
|
|
37
|
+
if (_instanceof(node, Text)) return $node.textContent(node.textContent);
|
|
38
|
+
if (_instanceof(node, Element) && _instanceof($node, $Element)) $node.attr($(node).attr());
|
|
39
|
+
const arr = _Array_from($node.childNodes);
|
|
40
|
+
forEach(node.childNodes, (_node, i) => {
|
|
41
|
+
const targetChildNode = arr.at(i);
|
|
42
|
+
if (!targetChildNode) throw 'Target DOM tree not matched';
|
|
43
|
+
getData(_node, targetChildNode.$)
|
|
44
|
+
})
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
})
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "amateras",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.3.0",
|
|
4
4
|
"description": "Amateras is a DOM Utility library.",
|
|
5
5
|
"module": "index.ts",
|
|
6
6
|
"type": "module",
|
|
@@ -20,8 +20,7 @@
|
|
|
20
20
|
"#lib/*": "./src/lib/*.ts",
|
|
21
21
|
"#structure/*": "./src/structure/*.ts",
|
|
22
22
|
"#html": "./ext/html/html.ts",
|
|
23
|
-
"#
|
|
24
|
-
"#css/*": "./ext/css/*.ts"
|
|
23
|
+
"#html/*": "./ext/html/node/*.ts"
|
|
25
24
|
},
|
|
26
25
|
"exports": {
|
|
27
26
|
".": "./src/index.ts",
|
|
@@ -31,7 +30,12 @@
|
|
|
31
30
|
"./lib/*": "./src/lib/*.ts",
|
|
32
31
|
"./structure/*": "./src/structure/*.ts",
|
|
33
32
|
"./html": "./ext/html/html.ts",
|
|
34
|
-
"./
|
|
33
|
+
"./html/*": "./ext/html/node/*.ts",
|
|
34
|
+
"./css": "./ext/css/src/index.ts",
|
|
35
|
+
"./css/colors": "./ext/css/src/lib/colors.ts",
|
|
36
|
+
"./css/color/*": "./ext/css/src/lib/colors/*.ts",
|
|
37
|
+
"./router": "./ext/router/index.ts",
|
|
38
|
+
"./ssr": "./ext/ssr/index.ts"
|
|
35
39
|
},
|
|
36
40
|
"workspaces": [
|
|
37
41
|
"./ext/*"
|
package/src/core.ts
CHANGED
|
@@ -1,46 +1,56 @@
|
|
|
1
1
|
import './global';
|
|
2
2
|
import { Signal } from "#structure/Signal";
|
|
3
|
-
import { $Element } from "#node/$Element";
|
|
4
|
-
import { $Node, type $NodeContentTypes } from '#node/$Node';
|
|
3
|
+
import { $Element, type $Event } from "#node/$Element";
|
|
4
|
+
import { $Node, type $NodeContentResolver, type $NodeContentTypes } from '#node/$Node';
|
|
5
5
|
import '#node/node';
|
|
6
|
-
import { _instanceof,
|
|
6
|
+
import { _instanceof, isString, isFunction, _Object_assign, isObject, isNull, _Object_entries, _Object_defineProperty, forEach, isNumber, _Array_from, _document } from '#lib/native';
|
|
7
|
+
import type { $HTMLElement } from '#node/$HTMLElement';
|
|
7
8
|
|
|
8
|
-
const
|
|
9
|
-
|
|
10
|
-
export function $<
|
|
11
|
-
export function $<
|
|
9
|
+
const nodeNameMap: {[key: string]: Constructor<$Node>} = {}
|
|
10
|
+
|
|
11
|
+
export function $<F extends (...args: any[]) => $NodeContentResolver<$Node>, N extends number>(number: N, fn: F, ...args: Parameters<F>): Repeat<ReturnType<F>, N>;
|
|
12
|
+
export function $<F extends (...args: any[]) => $NodeContentResolver<$Node>>(fn: F, ...args: Parameters<F>): ReturnType<F>;
|
|
13
|
+
export function $<T extends Constructor<$Node>, P extends ConstructorParameters<T>, N extends number>(number: N, construct: T, ...args: P): Repeat<InstanceType<T>, N>;
|
|
14
|
+
export function $<T extends Constructor<$Node>, P extends ConstructorParameters<T>>(construct: T, ...args: P): InstanceType<T>;
|
|
15
|
+
export function $<N extends $Node>($node: N, ...args: any[]): N;
|
|
16
|
+
export function $<H extends HTMLElement>(element: H, ...args: any[]): $HTMLElement<H>;
|
|
17
|
+
export function $<E extends Element>(element: E, ...args: any[]): $Element<E>;
|
|
12
18
|
export function $<K extends TemplateStringsArray>(string: K, ...values: any[]): $NodeContentTypes[];
|
|
13
|
-
export function $<K extends
|
|
14
|
-
export function $<K extends
|
|
15
|
-
export function $<
|
|
16
|
-
export function
|
|
17
|
-
export function $(
|
|
19
|
+
export function $<K extends keyof HTMLElementTagNameMap, N extends number>(number: N, tagname: K): Repeat<$HTMLElement<HTMLElementTagNameMap[K]>, N>;
|
|
20
|
+
export function $<K extends keyof HTMLElementTagNameMap>(tagname: K): $HTMLElement<HTMLElementTagNameMap[K]>;
|
|
21
|
+
export function $<Ev extends $Event<$Element, Event>>(event: Ev): Ev['target']['$'];
|
|
22
|
+
export function $<N extends number>(number: N, tagname: string): Repeat<$HTMLElement<HTMLElement>, N>;
|
|
23
|
+
export function $(tagname: string): $HTMLElement<HTMLElement>
|
|
24
|
+
export function $(resolver: string | number | Element | $Node | Function | TemplateStringsArray | Event, ...args: any[]) {
|
|
18
25
|
if (_instanceof(resolver, $Node)) return resolver;
|
|
19
|
-
if (isString(resolver) &&
|
|
26
|
+
if (isString(resolver) && nodeNameMap[resolver]) return new nodeNameMap[resolver](...args);
|
|
20
27
|
if (isFunction(resolver))
|
|
21
|
-
if (resolver.prototype?.constructor) return resolver.prototype.constructor(...args);
|
|
28
|
+
if (resolver.prototype?.constructor) return new resolver.prototype.constructor(...args);
|
|
22
29
|
else return resolver(...args);
|
|
23
30
|
if (resolver instanceof Array) {
|
|
24
31
|
const iterate = args.values();
|
|
25
32
|
return resolver.map(str => [str ?? undefined, iterate.next().value]).flat().filter(item => item);
|
|
26
33
|
}
|
|
27
34
|
if (_instanceof(resolver, Node) && _instanceof(resolver.$, $Node)) return resolver.$;
|
|
35
|
+
if (_instanceof(resolver, Event)) return $(resolver.currentTarget as Element);
|
|
36
|
+
if (isNumber(resolver)) return _Array_from({length: resolver}).map(_ => $(args[0], ...args.slice(1)));
|
|
28
37
|
return new $Element(resolver);
|
|
29
38
|
}
|
|
30
39
|
|
|
31
40
|
export namespace $ {
|
|
41
|
+
export const stylesheet = new CSSStyleSheet();
|
|
42
|
+
_document.adoptedStyleSheets.push(stylesheet);
|
|
32
43
|
type SignalProcess<T> = T extends Array<any> ? {} : T extends object ? { [key in keyof T as `${string & key}$`]: SignalFunction<T[key]> } : {};
|
|
33
|
-
export type SignalFunction<T> =
|
|
44
|
+
export type SignalFunction<T> = {signal: Signal<T>, set: (newValue: T | ((oldValue: T) => T)) => SignalFunction<T>} & (() => T) & SignalProcess<T>;
|
|
34
45
|
export function signal<T>(value: T): SignalFunction<T>
|
|
35
46
|
export function signal<T>(value: T) {
|
|
36
47
|
const signal = new Signal<T>(value);
|
|
37
|
-
const signalFn = function (
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
return signalFn;
|
|
41
|
-
}
|
|
42
|
-
|
|
43
|
-
if (isObject(value)) {
|
|
48
|
+
const signalFn = function () { return signal.value(); }
|
|
49
|
+
_Object_assign(signalFn, {
|
|
50
|
+
signal,
|
|
51
|
+
set(newValue: T) { return signal.value(newValue), signalFn; }
|
|
52
|
+
})
|
|
53
|
+
if (isObject(value) && !isNull(value)) {
|
|
44
54
|
for (const [key, val] of _Object_entries(value)) {
|
|
45
55
|
const val$ = $.signal(val);
|
|
46
56
|
val$.signal.subscribe(newValue => { value[key as keyof typeof value] = newValue; signal.emit() });
|
|
@@ -53,14 +63,14 @@ export namespace $ {
|
|
|
53
63
|
export type ComputeFunction<T> = ({(): T}) & { signal: Signal<T> };
|
|
54
64
|
export function compute<T>(process: () => T) {
|
|
55
65
|
let subscribed = false;
|
|
56
|
-
const signalFn: SignalFunction<any> =
|
|
66
|
+
const signalFn: SignalFunction<any> = signal(null);
|
|
57
67
|
function computeFn() {
|
|
58
|
-
if (!subscribed) return signalFn(subscribe())();
|
|
59
|
-
else return signalFn(process())();
|
|
68
|
+
if (!subscribed) return signalFn.set(subscribe())();
|
|
69
|
+
else return signalFn.set(process())();
|
|
60
70
|
}
|
|
61
71
|
function subscribe () {
|
|
62
72
|
const signalHandler = (signal: Signal<any>) => {
|
|
63
|
-
signal.subscribe(() => signalFn(process()))
|
|
73
|
+
signal.subscribe(() => signalFn.set(process()))
|
|
64
74
|
}
|
|
65
75
|
Signal.listeners.add(signalHandler);
|
|
66
76
|
const result = process();
|
|
@@ -68,16 +78,19 @@ export namespace $ {
|
|
|
68
78
|
subscribed = true;
|
|
69
79
|
return result;
|
|
70
80
|
}
|
|
71
|
-
|
|
81
|
+
_Object_assign(computeFn, { signal: signalFn.signal });
|
|
72
82
|
return computeFn as ComputeFunction<T>
|
|
73
83
|
}
|
|
74
84
|
|
|
75
|
-
export function
|
|
76
|
-
|
|
85
|
+
export function assign(resolver: [nodeName: string, $node: Constructor<$Node>][]): void;
|
|
86
|
+
export function assign(nodeName: string, $node: Constructor<$Node>): void;
|
|
87
|
+
export function assign(resolver: string | [nodeName: string, $node: Constructor<$Node>][], $node?: Constructor<$Node>) {
|
|
88
|
+
if (isString(resolver)) $node && (nodeNameMap[resolver] = $node);
|
|
89
|
+
else forEach(resolver, ([nodeName, $node]) => nodeNameMap[nodeName] = $node);
|
|
77
90
|
return $;
|
|
78
91
|
}
|
|
79
92
|
|
|
80
|
-
export function
|
|
93
|
+
export function toArray<T>(item: OrArray<T>): T[] {
|
|
81
94
|
return _instanceof(item, Array) ? item : [item];
|
|
82
95
|
}
|
|
83
96
|
|
package/src/global.ts
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import type { $Element } from '#node/$Element';
|
|
2
2
|
import type { $Node } from '#node/$Node';
|
|
3
3
|
import * as core from '#core';
|
|
4
|
+
import type { Signal } from '#structure/Signal';
|
|
4
5
|
|
|
5
6
|
declare global {
|
|
6
7
|
export import $ = core.$;
|
|
@@ -10,6 +11,11 @@ declare global {
|
|
|
10
11
|
type OrPromise<T> = T | Promise<T>;
|
|
11
12
|
type OrNullish<T> = T | Nullish;
|
|
12
13
|
type Constructor<T> = { new (...args: any[]): T }
|
|
14
|
+
type $Parameter<T> = T | undefined | Signal<T> | Signal<T | undefined>
|
|
15
|
+
type Repeat<T, N extends number, Acc extends T[] = []> =
|
|
16
|
+
Acc['length'] extends N
|
|
17
|
+
? Acc
|
|
18
|
+
: Repeat<T, N, [...Acc, T]>;
|
|
13
19
|
type Prettify<T> = {
|
|
14
20
|
[K in keyof T]: T[K];
|
|
15
21
|
} & {};
|
package/src/lib/assign.ts
CHANGED
|
@@ -6,17 +6,18 @@ export function assign(target: any, {set, get, fn}: {
|
|
|
6
6
|
get?: string[],
|
|
7
7
|
fn?: string[]
|
|
8
8
|
}) {
|
|
9
|
+
const [GET, SET, FN] = ['get', 'set', 'fn'] as const;
|
|
9
10
|
const filterAndMap = (type: 'get' | 'set' | 'fn', arr: string[] | undefined) => {
|
|
10
11
|
return arr?.map(prop => [type, prop]) ?? []
|
|
11
12
|
}
|
|
12
|
-
const list = [...filterAndMap(
|
|
13
|
+
const list = [...filterAndMap(GET, get), ...filterAndMap(SET, set), ...filterAndMap(FN, fn)] as [string, string][];
|
|
13
14
|
for (const [type, prop] of list) {
|
|
14
15
|
_Object_defineProperty(target.prototype, prop, {
|
|
15
|
-
...(type ===
|
|
16
|
+
...(type === GET ? {
|
|
16
17
|
get() { return this.node[prop as any] }
|
|
17
18
|
} : {
|
|
18
19
|
writable: true,
|
|
19
|
-
...(type ===
|
|
20
|
+
...(type === SET ? {
|
|
20
21
|
// set
|
|
21
22
|
value: function (this, args: any) {
|
|
22
23
|
if (!arguments.length) return this.node[prop];
|
package/src/lib/assignHelper.ts
CHANGED
|
@@ -2,18 +2,16 @@ import type { $Node } from "#node/$Node";
|
|
|
2
2
|
import { assign } from "./assign";
|
|
3
3
|
import { _Object_entries, _Object_getOwnPropertyDescriptors } from "./native";
|
|
4
4
|
|
|
5
|
-
export function assignHelper(
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
else if (value.get && value.set) set.push(prop);
|
|
14
|
-
}
|
|
15
|
-
assign(target, {set, get, fn})
|
|
16
|
-
// register tagname
|
|
17
|
-
if (tagname) $.registerTagName(tagname, target)
|
|
5
|
+
export function assignHelper(object: Constructor<Node>, target: Constructor<$Node>, tagname?: string) {
|
|
6
|
+
const [set, get, fn] = [[], [], []] as [string[], string[], string[]]
|
|
7
|
+
// assign native object properties to target
|
|
8
|
+
for (const [prop, value] of _Object_entries(_Object_getOwnPropertyDescriptors(object.prototype))) {
|
|
9
|
+
if (prop in target.prototype) continue;
|
|
10
|
+
if (value.get && !value.set) get.push(prop);
|
|
11
|
+
else if (value.value) fn.push(prop);
|
|
12
|
+
else if (value.get && value.set) set.push(prop);
|
|
18
13
|
}
|
|
14
|
+
assign(target, {set, get, fn})
|
|
15
|
+
// register tagname
|
|
16
|
+
if (tagname) $.assign(tagname, target)
|
|
19
17
|
}
|
package/src/lib/native.ts
CHANGED
|
@@ -1,3 +1,5 @@
|
|
|
1
|
+
// document
|
|
2
|
+
export const _document = document;
|
|
1
3
|
// Object
|
|
2
4
|
export const _Object_fromEntries = Object.fromEntries;
|
|
3
5
|
export const _Object_entries = Object.entries;
|
|
@@ -7,6 +9,13 @@ export const _Object_defineProperty = Object.defineProperty;
|
|
|
7
9
|
export const _Object_getOwnPropertyDescriptors = Object.getOwnPropertyDescriptors;
|
|
8
10
|
// Array
|
|
9
11
|
export const _Array_from = Array.from;
|
|
12
|
+
export function forEach<N extends Node>(set: NodeListOf<N>, fn: (value: N, index: number, parent: NodeListOf<N>) => any, thisArgs?: any): void;
|
|
13
|
+
export function forEach<K, V>(set: Map<K, V>, fn: (value: V, key: K, index: number, map: Map<K, V>) => any, thisArgs?: any): void;
|
|
14
|
+
export function forEach<T>(set: Set<T>, fn: (value: T, index: number, set: Set<T>) => any, thisArgs?: any): void;
|
|
15
|
+
export function forEach<T>(arr: Array<T>, fn: (value: T, index: number, array: Array<T>) => any, thisArgs?: any): void;
|
|
16
|
+
export function forEach<T>(arr: any, fn: any, thisArgs?: any) {
|
|
17
|
+
arr.forEach(fn, thisArgs)
|
|
18
|
+
}
|
|
10
19
|
// typeof
|
|
11
20
|
export function _typeof(target: any, type: 'string' | 'number' | 'object' | 'boolean' | 'function' | 'bigint' | 'symbol' | 'undefined') {
|
|
12
21
|
return typeof target === type;
|
|
@@ -29,6 +38,8 @@ export function isUndefined(target: any): target is undefined {
|
|
|
29
38
|
export function isNull(target: any): target is null {
|
|
30
39
|
return target === null;
|
|
31
40
|
}
|
|
32
|
-
export function _instanceof<T
|
|
33
|
-
return target instanceof
|
|
34
|
-
}
|
|
41
|
+
export function _instanceof<T extends (abstract new (...args: any[]) => any)[]>(target: any, ...instance: T): target is InstanceType<T[number]> {
|
|
42
|
+
return !!instance.find(i => target instanceof i);
|
|
43
|
+
}
|
|
44
|
+
// JSON
|
|
45
|
+
export const _JSON_stringify = JSON.stringify;
|