amateras 0.5.0 → 0.6.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 +23 -26
- package/ext/html/node/$Anchor.ts +2 -2
- package/ext/html/node/$Canvas.ts +2 -2
- package/ext/html/node/$Dialog.ts +2 -2
- package/ext/html/node/$Form.ts +2 -2
- package/ext/html/node/$Image.ts +2 -2
- package/ext/html/node/$Input.ts +2 -2
- package/ext/html/node/$Label.ts +2 -2
- package/ext/html/node/$Media.ts +2 -2
- package/ext/html/node/$OptGroup.ts +2 -2
- package/ext/html/node/$Option.ts +2 -2
- package/ext/html/node/$Select.ts +2 -2
- package/ext/html/node/$TextArea.ts +2 -2
- package/ext/i18n/README.md +20 -0
- package/ext/i18n/src/index.ts +106 -12
- package/ext/i18n/src/structure/I18n.ts +12 -8
- package/ext/i18n/src/structure/I18nTranslation.ts +35 -0
- package/ext/idb/src/structure/builder/$IDBBuilder.ts +8 -8
- package/ext/markdown/README.md +53 -0
- package/ext/markdown/package.json +7 -0
- package/ext/markdown/src/index.ts +3 -0
- package/ext/markdown/src/lib/type.ts +26 -0
- package/ext/markdown/src/lib/util.ts +21 -0
- package/ext/markdown/src/structure/Markdown.ts +54 -0
- package/ext/markdown/src/structure/MarkdownLexer.ts +111 -0
- package/ext/markdown/src/structure/MarkdownParser.ts +33 -0
- package/ext/markdown/src/syntax/alert.ts +46 -0
- package/ext/markdown/src/syntax/blockquote.ts +35 -0
- package/ext/markdown/src/syntax/bold.ts +11 -0
- package/ext/markdown/src/syntax/code.ts +11 -0
- package/ext/markdown/src/syntax/codeblock.ts +44 -0
- package/ext/markdown/src/syntax/heading.ts +14 -0
- package/ext/markdown/src/syntax/horizontalRule.ts +11 -0
- package/ext/markdown/src/syntax/image.ts +23 -0
- package/ext/markdown/src/syntax/italic.ts +11 -0
- package/ext/markdown/src/syntax/link.ts +46 -0
- package/ext/markdown/src/syntax/list.ts +121 -0
- package/ext/markdown/src/syntax/table.ts +67 -0
- package/ext/markdown/src/syntax/text.ts +19 -0
- package/ext/router/README.md +111 -17
- package/ext/router/package.json +10 -0
- package/ext/router/src/index.ts +69 -0
- package/ext/router/src/node/Page.ts +34 -0
- package/ext/router/src/node/Router.ts +191 -0
- package/ext/router/{node → src/node}/RouterAnchor.ts +13 -2
- package/ext/router/src/structure/PageBuilder.ts +24 -0
- package/ext/router/src/structure/Route.ts +105 -0
- package/ext/signal/README.md +93 -0
- package/ext/signal/package.json +9 -0
- package/ext/signal/src/index.ts +128 -0
- package/{src → ext/signal/src}/structure/Signal.ts +6 -10
- package/ext/ssr/index.ts +4 -4
- package/ext/ui/lib/VirtualScroll.ts +25 -0
- package/ext/ui/node/Accordian.ts +97 -0
- package/ext/ui/node/Form.ts +53 -0
- package/ext/ui/node/Grid.ts +0 -0
- package/ext/ui/node/Table.ts +43 -0
- package/ext/ui/node/Tabs.ts +114 -0
- package/ext/ui/node/Toast.ts +16 -0
- package/ext/ui/node/Waterfall.ts +72 -0
- package/ext/ui/package.json +11 -0
- package/package.json +6 -3
- package/src/core.ts +30 -60
- package/src/global.ts +9 -2
- package/src/index.ts +1 -2
- package/src/lib/assignProperties.ts +57 -0
- package/src/lib/native.ts +25 -8
- package/src/lib/uppercase.ts +3 -0
- package/src/node/$Element.ts +7 -41
- package/src/node/$EventTarget.ts +45 -0
- package/src/node/$Node.ts +60 -65
- package/src/node/$Virtual.ts +65 -0
- package/src/node.ts +7 -6
- package/ext/i18n/src/node/I18nText.ts +0 -35
- package/ext/markdown/index.ts +0 -121
- package/ext/router/index.ts +0 -73
- package/ext/router/node/Page.ts +0 -27
- package/ext/router/node/Route.ts +0 -54
- package/ext/router/node/Router.ts +0 -149
- package/src/lib/assign.ts +0 -38
- package/src/lib/assignHelper.ts +0 -18
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
import { isBoolean } from "#lib/native";
|
|
2
|
+
|
|
3
|
+
export class $EventTarget<EvMap = {}> {
|
|
4
|
+
node: EventTarget;
|
|
5
|
+
constructor(node: EventTarget) {
|
|
6
|
+
this.node = node;
|
|
7
|
+
if (node !== window) (node as Mutable<EventTarget>).$ = this;
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
on(type: string, listener: $EventListener<this, Event> | $EventListenerObject<this, Event>, options?: boolean | AddEventListenerOptions): this {
|
|
11
|
+
return this.addEventListener(type, listener, options);
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
off(type: string, listener: $EventListener<this, Event> | $EventListenerObject<this, Event>, options?: boolean | EventListenerOptions): this {
|
|
15
|
+
return this.removeEventListener(type, listener, options);
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
once(type: string, listener: $EventListener<this, Event> | $EventListenerObject<this, Event>, options?: boolean | AddEventListenerOptions): this {
|
|
19
|
+
return this.on(type, listener, { once: true, ...(isBoolean(options) ? {capture: options} : options ?? {}) })
|
|
20
|
+
}
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
export type $Event<E extends $EventTarget, Ev = any> = Ev & {currentTarget: {$: E}};
|
|
24
|
+
export type $EventListener<E extends $EventTarget, Ev> = (event: $Event<E, Ev>) => void;
|
|
25
|
+
export type $EventListenerObject<E extends $EventTarget, Ev> = { handleEvent(object: $Event<E, Ev>): void; }
|
|
26
|
+
|
|
27
|
+
export interface $EventTarget<EvMap = {}> {
|
|
28
|
+
/** {@link EventTarget.addEventListener} */
|
|
29
|
+
addEventListener<K extends keyof EvMap, Ev extends EvMap[K]>(type: K, listener: $EventListener<this, Ev> | $EventListenerObject<this, Ev>, options?: boolean | AddEventListenerOptions): this;
|
|
30
|
+
addEventListener(type: string, listener: $EventListener<this, Event> | $EventListenerObject<this, Event>, options?: boolean | AddEventListenerOptions): this;
|
|
31
|
+
/** {@link EventTarget.removeEventListener} */
|
|
32
|
+
removeEventListener<K extends keyof EvMap, Ev extends EvMap[K]>(type: K, listener: $EventListener<this, Ev> | $EventListenerObject<this, Ev>, options?: boolean | EventListenerOptions): this;
|
|
33
|
+
removeEventListener(type: string, listener: $EventListener<this, Event> | $EventListenerObject<this, Event>, options?: boolean | EventListenerOptions): this;
|
|
34
|
+
/** {@link EventTarget.dispatchEvent} */
|
|
35
|
+
dispatchEvent(event: Event): boolean;
|
|
36
|
+
|
|
37
|
+
on<K extends keyof EvMap, Ev extends EvMap[K]>(type: K, listener: $EventListener<this, Ev> | $EventListenerObject<this, Ev>, options?: boolean | AddEventListenerOptions): this;
|
|
38
|
+
on(type: string, listener: $EventListener<this, Event> | $EventListenerObject<this, Event>, options?: boolean | AddEventListenerOptions): this;
|
|
39
|
+
|
|
40
|
+
off<K extends keyof EvMap, Ev extends EvMap[K]>(type: K, listener: $EventListener<this, Ev> | $EventListenerObject<this, Ev>, options?: boolean | EventListenerOptions): this;
|
|
41
|
+
off(type: string, listener: $EventListener<this, Event> | $EventListenerObject<this, Event>, options?: boolean | EventListenerOptions): this;
|
|
42
|
+
|
|
43
|
+
once<K extends keyof EvMap, Ev extends EvMap[K]>(type: K, listener: $EventListener<this, Ev> | $EventListenerObject<this, Ev>, options?: boolean | AddEventListenerOptions): this;
|
|
44
|
+
once(type: string, listener: $EventListener<this, Event> | $EventListenerObject<this, Event>, options?: boolean | AddEventListenerOptions): this;
|
|
45
|
+
}
|
package/src/node/$Node.ts
CHANGED
|
@@ -1,15 +1,15 @@
|
|
|
1
1
|
import { chain } from "#lib/chain";
|
|
2
2
|
import { _document } from "#lib/env";
|
|
3
|
-
import { _Array_from, _instanceof, _JSON_stringify, _null, _Promise, forEach,
|
|
3
|
+
import { _Array_from, _instanceof, _JSON_stringify, _null, _Promise, forEach, isFunction, isNull, isUndefined } from "#lib/native";
|
|
4
4
|
import { toArray } from "#lib/toArray";
|
|
5
|
-
import {
|
|
5
|
+
import { $EventTarget } from "./$EventTarget";
|
|
6
6
|
|
|
7
|
-
export class $Node {
|
|
8
|
-
node: Node & ChildNode;
|
|
7
|
+
export class $Node<EvMap = {}> extends $EventTarget<EvMap> {
|
|
8
|
+
declare node: Node & ChildNode & ParentNode;
|
|
9
|
+
static processors = new Set<$NodeContentProcessor>();
|
|
10
|
+
static setters = new Set<$NodeSetterHandler>();
|
|
9
11
|
constructor(node: Node & ChildNode) {
|
|
10
|
-
|
|
11
|
-
//@ts-expect-error
|
|
12
|
-
this.node.$ = this;
|
|
12
|
+
super(node);
|
|
13
13
|
}
|
|
14
14
|
|
|
15
15
|
content(children: $NodeContentResolver<this>) {
|
|
@@ -21,7 +21,10 @@ export class $Node {
|
|
|
21
21
|
|
|
22
22
|
insert(resolver: $NodeContentResolver<this>, position = -1) {
|
|
23
23
|
// process nodes
|
|
24
|
-
forEach(toArray(resolver), resolve_child => forEach($Node.process(this, resolve_child), $node =>
|
|
24
|
+
forEach(toArray(resolver), resolve_child => forEach($Node.process(this, resolve_child), $node => {
|
|
25
|
+
$Node.append(this, $node, position);
|
|
26
|
+
if (position >= 0) position++;
|
|
27
|
+
}));
|
|
25
28
|
return this;
|
|
26
29
|
}
|
|
27
30
|
|
|
@@ -58,50 +61,19 @@ export class $Node {
|
|
|
58
61
|
return _instanceof(this, instance) ? this : null;
|
|
59
62
|
}
|
|
60
63
|
|
|
61
|
-
on(type: string, listener: any, options?: boolean | AddEventListenerOptions) {
|
|
62
|
-
return this.addEventListener(type, listener, options);
|
|
63
|
-
}
|
|
64
|
-
|
|
65
|
-
off(type: string, listener: any, options?: boolean | EventListenerOptions) {
|
|
66
|
-
return this.removeEventListener(type, listener, options);
|
|
67
|
-
}
|
|
68
|
-
|
|
69
|
-
once(type: string, listener: any, options?: boolean | AddEventListenerOptions) {
|
|
70
|
-
return this.on(type, listener, { once: true, ...(isBoolean(options) ? {capture: options} : options ?? {}) })
|
|
71
|
-
}
|
|
72
|
-
|
|
73
64
|
static process<T extends $Node>($node: T, content: $NodeContentResolver<any>): Array<$Node | undefined | null> {
|
|
65
|
+
for (const processor of this.processors) {
|
|
66
|
+
const result = processor($node, content);
|
|
67
|
+
if (result) return result;
|
|
68
|
+
}
|
|
74
69
|
if (isUndefined(content) || isNull(content) || _instanceof(content, $Node)) return [content];
|
|
75
70
|
// is Promise
|
|
76
71
|
if (_instanceof(content, _Promise)) return [$('async').await(content, ($async, $child) => $async.replace($child as any))];
|
|
77
72
|
// is SignalFunction or ContentHandler
|
|
78
73
|
if (isFunction(content)) {
|
|
79
|
-
const
|
|
80
|
-
if (_instanceof(
|
|
81
|
-
|
|
82
|
-
if (_instanceof(resolver, $Node)) {
|
|
83
|
-
// handler signal $Node result
|
|
84
|
-
let node = resolver;
|
|
85
|
-
const set = (value: any) => {
|
|
86
|
-
node.replace(value);
|
|
87
|
-
node = value;
|
|
88
|
-
}
|
|
89
|
-
signal.subscribe(set);
|
|
90
|
-
return [resolver];
|
|
91
|
-
} else {
|
|
92
|
-
// handler signal other type result
|
|
93
|
-
const $text = _document ? new $Text() : $('signal').attr({ type: typeof signal.value() });
|
|
94
|
-
const set = (value: any) => $text.textContent(isObject(value) ? _JSON_stringify(value) : value);
|
|
95
|
-
if (_instanceof($text, $Text)) $text.signals.add(signal);
|
|
96
|
-
signal.subscribe(set);
|
|
97
|
-
set(resolver);
|
|
98
|
-
return [$text];
|
|
99
|
-
}
|
|
100
|
-
} else {
|
|
101
|
-
const _content = content($node) as $NodeContentResolver<$Node>;
|
|
102
|
-
if (_instanceof(_content, _Promise)) return this.process($node, _content as any);
|
|
103
|
-
else return toArray(_content).map(content => this.process($node, content)).flat();
|
|
104
|
-
}
|
|
74
|
+
const _content = content($node) as $NodeContentResolver<$Node>;
|
|
75
|
+
if (_instanceof(_content, _Promise)) return this.process($node, _content as any);
|
|
76
|
+
else return toArray(_content).map(content => this.process($node, content)).flat();
|
|
105
77
|
}
|
|
106
78
|
// is nested array
|
|
107
79
|
if (_instanceof(content, Array)) return content.map(c => this.process($node, c)).flat();
|
|
@@ -122,17 +94,18 @@ export class $Node {
|
|
|
122
94
|
}
|
|
123
95
|
|
|
124
96
|
export class $Text extends $Node {
|
|
125
|
-
signals = new Set<Signal<any>>();
|
|
126
97
|
constructor(textContent?: string) {
|
|
127
98
|
super(new Text(textContent));
|
|
128
99
|
}
|
|
129
100
|
}
|
|
130
101
|
|
|
102
|
+
export type $NodeSetterHandler = (value: any, set: (value: any) => void) => any;
|
|
103
|
+
export type $NodeContentProcessor = <N extends $Node>($node: N, content: $NodeContentResolver<N>) => Array<$Node | undefined | null> | void | undefined;
|
|
131
104
|
export type $NodeContentHandler<T extends $Node> = ($node: T) => OrPromise<$NodeContentResolver<T>>;
|
|
132
|
-
export type $NodeContentTypes = $Node | string | number | boolean |
|
|
105
|
+
export type $NodeContentTypes = $Node | string | number | boolean | $.$NodeContentTypeExtends | null | undefined;
|
|
133
106
|
export type $NodeContentResolver<T extends $Node> = OrPromise<$NodeContentTypes | $NodeContentHandler<T> | $NodeContentResolver<T>[]>;
|
|
134
107
|
|
|
135
|
-
export interface $Node {
|
|
108
|
+
export interface $Node<EvMap = {}> extends $EventTarget<EvMap> {
|
|
136
109
|
/** {@link Node.baseURI} */
|
|
137
110
|
readonly baseURI: string;
|
|
138
111
|
/** {@link Node.childNodes} */
|
|
@@ -157,13 +130,21 @@ export interface $Node {
|
|
|
157
130
|
readonly parentNode?: ParentNode | null;
|
|
158
131
|
/** {@link Node.previousSibling} */
|
|
159
132
|
readonly previousSibling?: ChildNode | null;
|
|
133
|
+
/** {@link ParentNode.childElementCount} */
|
|
134
|
+
readonly childElementCount: number;
|
|
135
|
+
/** {@link ParentNode.children} */
|
|
136
|
+
readonly children: HTMLCollection;
|
|
137
|
+
/** {@link ParentNode.firstElementChild} */
|
|
138
|
+
readonly firstElementChild: Element | null;
|
|
139
|
+
/** {@link ParentNode.lastElementChild} */
|
|
140
|
+
readonly lastElementChild: Element | null;
|
|
160
141
|
|
|
161
142
|
/** {@link Node.appendChild} */
|
|
162
143
|
appendChild<T extends Node>(node: T): T;
|
|
163
144
|
/** {@link Node.cloneNode} */
|
|
164
145
|
cloneNode(subtree?: boolean): Node;
|
|
165
146
|
/** {@link Node.compareDocumentPosition} */
|
|
166
|
-
compareDocumentPosition(other: Node): number;
|
|
147
|
+
compareDocumentPosition(other: $EventTarget | Node): number;
|
|
167
148
|
/** {@link Node.getRootNode} */
|
|
168
149
|
getRootNode(options?: GetRootNodeOptions): Node;
|
|
169
150
|
/** {@link Node.hasChildNodes} */
|
|
@@ -173,9 +154,9 @@ export interface $Node {
|
|
|
173
154
|
/** {@link Node.isDefaultNamespace} */
|
|
174
155
|
isDefaultNamespace(namespace: string | null): boolean;
|
|
175
156
|
/** {@link Node.isEqualNode} */
|
|
176
|
-
isEqualNode(otherNode: Node | null): boolean;
|
|
157
|
+
isEqualNode(otherNode: $EventTarget | Node | null): boolean;
|
|
177
158
|
/** {@link Node.isSameNode} */
|
|
178
|
-
isSameNode(otherNode: Node | null): boolean;
|
|
159
|
+
isSameNode(otherNode: $EventTarget | Node | null): boolean;
|
|
179
160
|
/** {@link Node.lookupNamespaceURI} */
|
|
180
161
|
lookupNamespaceURI(prefix: string | null): string | null;
|
|
181
162
|
/** {@link Node.lookupPrefix} */
|
|
@@ -186,20 +167,34 @@ export interface $Node {
|
|
|
186
167
|
removeChild<T extends Node>(child: T): T;
|
|
187
168
|
/** {@link Node.replaceChild} */
|
|
188
169
|
replaceChild<T extends Node>(node: Node, child: T): T;
|
|
189
|
-
/** {@link Node.
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
170
|
+
/** {@link Node.contains} */
|
|
171
|
+
contains(other: $EventTarget | Node | null | undefined): boolean;
|
|
172
|
+
|
|
173
|
+
/** {@link ChildNode.after} */
|
|
174
|
+
after(...nodes: ($EventTarget | Node | string)[]): this;
|
|
175
|
+
/** {@link ChildNode.before} */
|
|
176
|
+
before(...nodes: ($EventTarget | Node | string)[]): this;
|
|
177
|
+
/** {@link ChildNode.remove} */
|
|
194
178
|
remove(): this;
|
|
195
|
-
/** {@link
|
|
196
|
-
replaceWith(...nodes: (Node | string)[]): this;
|
|
197
|
-
/** {@link
|
|
198
|
-
|
|
199
|
-
/** {@link
|
|
200
|
-
|
|
201
|
-
/** {@link
|
|
202
|
-
|
|
179
|
+
/** {@link ChildNode.replaceWith} */
|
|
180
|
+
replaceWith(...nodes: ($EventTarget | Node | string)[]): this;
|
|
181
|
+
/** {@link ParentNode.append} */
|
|
182
|
+
append(...nodes: ($EventTarget | Node | string)[]): this;
|
|
183
|
+
/** {@link ParentNode.prepend} */
|
|
184
|
+
prepend(...nodes: ($EventTarget | Node | string)[]): this;
|
|
185
|
+
/** {@link ParentNode.querySelector} */
|
|
186
|
+
querySelector<K extends keyof HTMLElementTagNameMap>(selectors: K): HTMLElementTagNameMap[K] | null;
|
|
187
|
+
querySelector<K extends keyof SVGElementTagNameMap>(selectors: K): SVGElementTagNameMap[K] | null;
|
|
188
|
+
querySelector<K extends keyof MathMLElementTagNameMap>(selectors: K): MathMLElementTagNameMap[K] | null;
|
|
189
|
+
querySelector<E extends Element = Element>(selectors: string): E | null;
|
|
190
|
+
/** {@link ParentNode.querySelectorAll} */
|
|
191
|
+
querySelectorAll<K extends keyof HTMLElementTagNameMap>(selectors: K): NodeListOf<HTMLElementTagNameMap[K]>;
|
|
192
|
+
querySelectorAll<K extends keyof SVGElementTagNameMap>(selectors: K): NodeListOf<SVGElementTagNameMap[K]>;
|
|
193
|
+
querySelectorAll<K extends keyof MathMLElementTagNameMap>(selectors: K): NodeListOf<MathMLElementTagNameMap[K]>;
|
|
194
|
+
querySelectorAll<K extends keyof HTMLElementDeprecatedTagNameMap>(selectors: K): NodeListOf<HTMLElementDeprecatedTagNameMap[K]>;
|
|
195
|
+
querySelectorAll<E extends Element = Element>(selectors: string): NodeListOf<E>;
|
|
196
|
+
/** {@link ParentNode.replaceChildren} */
|
|
197
|
+
replaceChildren(...nodes: ($EventTarget | Node | string)[]): this;
|
|
203
198
|
|
|
204
199
|
/** {@link Node.nodeValue} */
|
|
205
200
|
nodeValue(nodeValue: $Parameter<string | null>): this;
|
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
import { chain } from "#lib/chain";
|
|
2
|
+
import { forEach, _Array_from, _instanceof, _null } from "#lib/native";
|
|
3
|
+
import { toArray } from "#lib/toArray";
|
|
4
|
+
import { $HTMLElement } from "#node/$HTMLElement";
|
|
5
|
+
import { $Node, type $NodeContentResolver } from "#node/$Node";
|
|
6
|
+
|
|
7
|
+
export class $Virtual<Ele extends HTMLElement = HTMLElement, EvMap = HTMLElementEventMap> extends $HTMLElement<Ele, EvMap> {
|
|
8
|
+
nodes = new Set<$Node>();
|
|
9
|
+
hiddenNodes = new Set<$Node>();
|
|
10
|
+
constructor(resolver: string | Ele) {
|
|
11
|
+
super(resolver);
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
content(children: $NodeContentResolver<this>) {
|
|
15
|
+
return chain(this, _null, _null, children, children => {
|
|
16
|
+
this.nodes.clear();
|
|
17
|
+
forEach(this.childNodes, node => node.remove());
|
|
18
|
+
this.insert(children);
|
|
19
|
+
})
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
insert(resolver: $NodeContentResolver<this>, position = -1) {
|
|
23
|
+
// process nodes
|
|
24
|
+
forEach(toArray(resolver), resolve_child => {
|
|
25
|
+
forEach($Node.process(this, resolve_child), $node => {
|
|
26
|
+
$Virtual.append(this, $node, position)
|
|
27
|
+
if (position >= 0) position++;
|
|
28
|
+
})
|
|
29
|
+
});
|
|
30
|
+
return this;
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
hide($node?: $Node | null) {
|
|
34
|
+
if ($node && this.nodes.has($node)) this.hiddenNodes.add($node);
|
|
35
|
+
return this;
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
show($node?: $Node | null) {
|
|
39
|
+
if ($node) this.hiddenNodes.delete($node);
|
|
40
|
+
return this;
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
render() {
|
|
44
|
+
// remove hidden node
|
|
45
|
+
forEach(this.childNodes, node => this.hiddenNodes.has($(node)) && node.remove());
|
|
46
|
+
// add visible node with position
|
|
47
|
+
forEach(this.nodes, ($node, i) => {
|
|
48
|
+
if (
|
|
49
|
+
!this.hiddenNodes.has($node) // node is not hidden
|
|
50
|
+
&& !$node.inDOM() // node is not in dom tree
|
|
51
|
+
&& $(this.childNodes).at(i) !== $node // node not matched at position
|
|
52
|
+
) $Node.append(this, $node, i);
|
|
53
|
+
})
|
|
54
|
+
return this;
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
static append($node: $Virtual, child: $Node | undefined | null, position: number) {
|
|
58
|
+
if (child) {
|
|
59
|
+
const childList = _Array_from($node.nodes);
|
|
60
|
+
if (!childList.at(position)) childList.push(child);
|
|
61
|
+
else childList.splice(position >= 0 ? position : childList.length + 1 + position, 0, child);
|
|
62
|
+
$node.nodes = new Set(childList);
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
}
|
package/src/node.ts
CHANGED
|
@@ -1,10 +1,11 @@
|
|
|
1
1
|
import { $HTMLElement } from '#node/$HTMLElement';
|
|
2
|
-
import {
|
|
2
|
+
import { assignProperties } from '#lib/assignProperties';
|
|
3
3
|
import { $Element } from '#node/$Element';
|
|
4
4
|
import { $Node, $Text } from '#node/$Node';
|
|
5
|
+
import { $EventTarget } from '#node/$EventTarget';
|
|
5
6
|
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
7
|
+
assignProperties(EventTarget, $EventTarget);
|
|
8
|
+
assignProperties(Node, $Node);
|
|
9
|
+
assignProperties(Text, $Text);
|
|
10
|
+
assignProperties(Element, $Element);
|
|
11
|
+
assignProperties(HTMLElement, $HTMLElement);
|
|
@@ -1,35 +0,0 @@
|
|
|
1
|
-
import { _Array_from, isUndefined } from "amateras/lib/native";
|
|
2
|
-
import { $HTMLElement } from "amateras/node/$HTMLElement";
|
|
3
|
-
import type { I18n } from "#structure/I18n";
|
|
4
|
-
|
|
5
|
-
export class I18nText extends $HTMLElement<HTMLElement, { i18nupdate: Event }> {
|
|
6
|
-
i18n: I18n;
|
|
7
|
-
key: string;
|
|
8
|
-
options: I18nTextOptions | undefined;
|
|
9
|
-
constructor(i18n: I18n, key: string, options?: I18nTextOptions) {
|
|
10
|
-
super('text');
|
|
11
|
-
this.i18n = i18n;
|
|
12
|
-
this.key = key;
|
|
13
|
-
this.options = options;
|
|
14
|
-
i18n.locale$.signal.subscribe(() => this.update())
|
|
15
|
-
this.update();
|
|
16
|
-
}
|
|
17
|
-
|
|
18
|
-
async update() {
|
|
19
|
-
update: {
|
|
20
|
-
const {key, i18n} = this;
|
|
21
|
-
const dictionary = i18n.dictionary();
|
|
22
|
-
if (!dictionary) {this.content(key); break update}
|
|
23
|
-
const target = await dictionary.find(key);
|
|
24
|
-
if (isUndefined(target)) break update;
|
|
25
|
-
const snippets = target.split(/\$[a-zA-Z0-9_]+\$/);
|
|
26
|
-
if (snippets.length === 1 || !this.options) {this.content(target); break update}
|
|
27
|
-
const matches = target.matchAll(/(\$([a-zA-Z0-9_]+)\$)/g);
|
|
28
|
-
this.content(snippets.map(text => [text, this.options?.[matches.next().value?.at(2)!] ?? null]));
|
|
29
|
-
}
|
|
30
|
-
this.dispatchEvent(new Event('i18nupdate'));
|
|
31
|
-
return this;
|
|
32
|
-
}
|
|
33
|
-
}
|
|
34
|
-
|
|
35
|
-
export type I18nTextOptions = {[key: string]: any}
|
package/ext/markdown/index.ts
DELETED
|
@@ -1,121 +0,0 @@
|
|
|
1
|
-
import { _Array_from, forEach } from "amateras/lib/native";
|
|
2
|
-
|
|
3
|
-
const blockProcesses = new Set<MarkdownBlockProcessOptions>();
|
|
4
|
-
const inlineProcesses = new Set<MarkdownProcessFunction>();
|
|
5
|
-
|
|
6
|
-
export class Markdown {
|
|
7
|
-
blockProcessSet = new Set(blockProcesses);
|
|
8
|
-
inlineProcessSet = new Set(inlineProcesses);
|
|
9
|
-
constructor() {}
|
|
10
|
-
|
|
11
|
-
blockProcess(options: MarkdownBlockProcessOptions) {
|
|
12
|
-
this.blockProcessSet.add(options);
|
|
13
|
-
return this;
|
|
14
|
-
}
|
|
15
|
-
|
|
16
|
-
inlineProcess(handle: MarkdownProcessFunction) {
|
|
17
|
-
this.inlineProcessSet.add(handle);
|
|
18
|
-
return this;
|
|
19
|
-
}
|
|
20
|
-
|
|
21
|
-
toHTML(text: string) {
|
|
22
|
-
const blocks = _Array_from(text.matchAll(/(?:.+?\n?)+/gm));
|
|
23
|
-
return blocks.map(block => {
|
|
24
|
-
let matched, blockText = block[0]
|
|
25
|
-
for (const blockProcess of blockProcesses) {
|
|
26
|
-
matched = blockText.match(blockProcess.regexp);
|
|
27
|
-
if (!matched) continue;
|
|
28
|
-
blockText = blockProcess.handle(blockText);
|
|
29
|
-
const removeHTML = blockText.replaceAll(/<.+>[^<]+?<\/.+>/gm, '');
|
|
30
|
-
if (!removeHTML) break;
|
|
31
|
-
}
|
|
32
|
-
if (!matched) blockText = paragraph(blockText);
|
|
33
|
-
inlineProcesses.forEach(fn => blockText = fn(blockText))
|
|
34
|
-
return blockText;
|
|
35
|
-
}).join('')
|
|
36
|
-
}
|
|
37
|
-
|
|
38
|
-
toDOM(text: string) {
|
|
39
|
-
return $('article').innerHTML(this.toHTML(text))
|
|
40
|
-
}
|
|
41
|
-
}
|
|
42
|
-
|
|
43
|
-
export type MarkdownProcessFunction = (text: string) => string;
|
|
44
|
-
export interface MarkdownBlockProcessOptions {
|
|
45
|
-
regexp: RegExp,
|
|
46
|
-
handle: (text: string) => string;
|
|
47
|
-
}
|
|
48
|
-
|
|
49
|
-
const blockProcess = (options: MarkdownBlockProcessOptions) => blockProcesses.add(options);
|
|
50
|
-
const inlineProcess = (handle: MarkdownProcessFunction) => inlineProcesses.add(handle);
|
|
51
|
-
const replaceAll = (str: string, searchValue: string | RegExp, replacer: ((substring: string, ...args: any[]) => string) | string): string => str.replaceAll(searchValue, replacer as any);
|
|
52
|
-
const trim = (str: string) => str.trim();
|
|
53
|
-
const paragraph = (str: string) => replaceAll(str, /(?:.+?\n?)+/gm, $0 => `<p>${trim($0)}</p>`);
|
|
54
|
-
// Headings
|
|
55
|
-
blockProcess({
|
|
56
|
-
regexp: /^(#+) (.+)/gm,
|
|
57
|
-
handle: text => replaceAll(text, /^(#+) (.+)/gm, (_, $1: string, $2) => `<h${$1.length}>${$2}</h${$1.length}>`)
|
|
58
|
-
});
|
|
59
|
-
blockProcess({
|
|
60
|
-
regexp: /^(.+)\n==+$/gm,
|
|
61
|
-
handle: text => replaceAll(text, /^(.+)\n==+$/gm, (_, $1) => `<h1>${$1}</h1>`)
|
|
62
|
-
});
|
|
63
|
-
blockProcess({
|
|
64
|
-
regexp: /^(.+)\n--+$/gm,
|
|
65
|
-
handle: text => replaceAll(text, /^(.+)\n--+$/gm, (_, $1) => `<h2>${$1}</h2>`)
|
|
66
|
-
});
|
|
67
|
-
// Blockquote
|
|
68
|
-
blockProcess({
|
|
69
|
-
regexp: /(?:^> ?.*(?:\n|$))+/gm,
|
|
70
|
-
handle: text => {
|
|
71
|
-
const fn = (str: string) => {
|
|
72
|
-
const blocks = _Array_from(str.matchAll(/(?:^> ?.*(?:\n|$))+/gm));
|
|
73
|
-
forEach(blocks, block => {
|
|
74
|
-
const blocked = fn(replaceAll(block[0], /^> ?/gm, ''));
|
|
75
|
-
str = str.replace(block[0], `<blockquote>\n${paragraph(blocked)}\n</blockquote>`);
|
|
76
|
-
})
|
|
77
|
-
return str;
|
|
78
|
-
}
|
|
79
|
-
return fn(text);
|
|
80
|
-
}
|
|
81
|
-
});
|
|
82
|
-
// List
|
|
83
|
-
blockProcess({
|
|
84
|
-
regexp: /(?:^(?:\t|(?: )+)?(?:-|[0-9]+\.) (?:.+\n?))+/gm,
|
|
85
|
-
handle: text => {
|
|
86
|
-
const fn = (str: string) => {
|
|
87
|
-
const blocks = _Array_from(str.matchAll(/(?:^(?:\t|(?: )+)?(?:-|[0-9]+\.) (?:.+\n?))+/gm));
|
|
88
|
-
forEach(blocks, block => {
|
|
89
|
-
let haveList = false // check this loop have list
|
|
90
|
-
const type = block[0].match(/^(-|[0-9]+\.) /)?.[1] === '-' ? 'ul' : 'ol';
|
|
91
|
-
const listed = replaceAll(block[0], /^(?:-|[0-9]+\.) (.+)/gm, (_, $1: string) => (haveList = true, `<li>\n${trim($1)}\n</li>`));
|
|
92
|
-
const clearTabbed = replaceAll(listed, /^(?:\t|(?: ))/gm, '');
|
|
93
|
-
const convertedList = fn(clearTabbed);
|
|
94
|
-
str = str.replace(block[0], haveList ? `<${type}>\n${trim(convertedList)}\n</${type}>` : convertedList);
|
|
95
|
-
})
|
|
96
|
-
return str;
|
|
97
|
-
}
|
|
98
|
-
return fn(text);
|
|
99
|
-
}
|
|
100
|
-
})
|
|
101
|
-
// Codeblock
|
|
102
|
-
blockProcess({
|
|
103
|
-
regexp: /^```([^`\n]+)\n([^`]+)?```/gm,
|
|
104
|
-
handle: text => replaceAll(text, /^```([^`\n]+)\n([^`]+)?```/gm, (_, $1, $2: string) => `<pre><code>\n${trim($2)}\n</code></pre>`)
|
|
105
|
-
})
|
|
106
|
-
// Horizontal Rule
|
|
107
|
-
blockProcess({
|
|
108
|
-
regexp: /^(?:---|\*\*\*|___)(\s+)?$/gm,
|
|
109
|
-
handle: text => replaceAll(text, /^(?:---|\*\*\*|___)(\s+)?$/gm, _ => `<hr>`)
|
|
110
|
-
})
|
|
111
|
-
// Bold
|
|
112
|
-
inlineProcess(text => replaceAll(text, /\*\*([^*]+?)\*\*/g, (_, $1) => `<b>${$1}</b>`));
|
|
113
|
-
// Italic
|
|
114
|
-
inlineProcess(text => replaceAll(text, /\*([^*]+?)\*/g, (_, $1) => `<i>${$1}</i>`));
|
|
115
|
-
// Image
|
|
116
|
-
inlineProcess(text => replaceAll(text, /!\[(.+?)\]\((.+?)(?: "(.+?)?")?\)/g, (_, alt, src, title) => `<img src="${src}" alt="${alt}"${title ? ` title="${title}"` : ''}>`));
|
|
117
|
-
// Link
|
|
118
|
-
inlineProcess(text => replaceAll(text, /\[(.+?)\]\((?:(\w\w+?:[^\s]+?)(?: "(.+?)?")?)\)/g, (_, content, href, title) => `<a href="${href}"${title ? ` title="${title}"` : ''}>${content}</a>`));
|
|
119
|
-
inlineProcess(text => replaceAll(text, /\[(.+?)\]\((?:(\w+?@(?:\w|\.\w)+?)(?: "(.+)?")?)\)/g, (_, content, mail, title) => `<a href="mailto:${mail}"${title ? ` title="${title}"` : ''}>${content}</a>`));
|
|
120
|
-
inlineProcess(text => replaceAll(text, /<(\w\w+?:[^\s]+?)>/g, (_, href) => `<a href="${href}">${href}</a>`));
|
|
121
|
-
inlineProcess(text => replaceAll(text, /<(\w+?@(?:\w|\.\w)+?)>/g, (_, mail) => `<a href="mailto:${mail}">${mail}</a>`));
|
package/ext/router/index.ts
DELETED
|
@@ -1,73 +0,0 @@
|
|
|
1
|
-
import type { AnchorTarget } from "#html/$Anchor";
|
|
2
|
-
import { _bind, _Object_assign, forEach } from "#lib/native";
|
|
3
|
-
import type { $NodeContentResolver } from "#node/$Node";
|
|
4
|
-
import type { Page } from "./node/Page";
|
|
5
|
-
import { Route } from "./node/Route";
|
|
6
|
-
import { Router } from "./node/Router";
|
|
7
|
-
import { RouterAnchor } from "./node/RouterAnchor";
|
|
8
|
-
export * from "./node/Route";
|
|
9
|
-
export * from "./node/Router";
|
|
10
|
-
export * from "./node/Page";
|
|
11
|
-
export * from "./node/RouterAnchor";
|
|
12
|
-
|
|
13
|
-
declare module 'amateras/core' {
|
|
14
|
-
export function $<P extends string>(nodeName: 'route', path: P, builder: RouteBuilder<Route<P>, RouteDataResolver<P>>): Route<P>;
|
|
15
|
-
export function $(nodeName: 'router', page?: Page<any>): Router;
|
|
16
|
-
export function $(nodeName: 'ra'): RouterAnchor;
|
|
17
|
-
export namespace $ {
|
|
18
|
-
export function open(url: string | URL | Nullish, target?: AnchorTarget): typeof Router;
|
|
19
|
-
export function replace(url: string | URL | Nullish): typeof Router;
|
|
20
|
-
export function back(): typeof Router;
|
|
21
|
-
export function forward(): typeof Router;
|
|
22
|
-
}
|
|
23
|
-
}
|
|
24
|
-
|
|
25
|
-
declare global {
|
|
26
|
-
interface GlobalEventHandlersEventMap {
|
|
27
|
-
'routeopen': Event;
|
|
28
|
-
}
|
|
29
|
-
}
|
|
30
|
-
|
|
31
|
-
// assign methods
|
|
32
|
-
_Object_assign($, {
|
|
33
|
-
open: _bind(Router.open, Router),
|
|
34
|
-
replace: _bind(Router.replace, Router),
|
|
35
|
-
back: _bind(Router.back, Router),
|
|
36
|
-
forward: _bind(Router.forward, Router)
|
|
37
|
-
});
|
|
38
|
-
// define styles
|
|
39
|
-
forEach([
|
|
40
|
-
`router{display:block}`,
|
|
41
|
-
`page{display:block}`
|
|
42
|
-
], $.style);
|
|
43
|
-
// assign nodes
|
|
44
|
-
$.assign([
|
|
45
|
-
['router', Router],
|
|
46
|
-
['route', Route],
|
|
47
|
-
['ra', RouterAnchor]
|
|
48
|
-
])
|
|
49
|
-
|
|
50
|
-
export type RouteData = { params: any, query: any }
|
|
51
|
-
export type RouteDataResolver<P extends string> = { params: Prettify<PathParams<P>>, query: Prettify<PathQuery<P>> }
|
|
52
|
-
export type AsyncRoute<P extends string> = () => Promise<{default: Route<P>}>
|
|
53
|
-
export type RouteBuilder<R extends Route<any>, D extends RouteData> = (page: Page<R, D>) => OrPromise<$NodeContentResolver<Page<R, D>>>;
|
|
54
|
-
|
|
55
|
-
type PathParams<Path> = Path extends `${infer Segment}/${infer Rest}`
|
|
56
|
-
? Segment extends `${string}:${infer Param}`
|
|
57
|
-
? Record<Param, string> & PathParams<Rest>
|
|
58
|
-
: PathParams<Rest>
|
|
59
|
-
: Path extends `${string}:${infer Param}?${infer Query}`
|
|
60
|
-
? Record<Param, string>
|
|
61
|
-
: Path extends `${string}:${infer Param}`
|
|
62
|
-
? Record<Param, string>
|
|
63
|
-
: {}
|
|
64
|
-
|
|
65
|
-
type PathQuery<Path> = Path extends `${string}?${infer Segment}`
|
|
66
|
-
? PathQuery_SetRecord<Segment>
|
|
67
|
-
: Path extends `&${infer Segment}`
|
|
68
|
-
? PathQuery_SetRecord<Segment>
|
|
69
|
-
: {}
|
|
70
|
-
|
|
71
|
-
type PathQuery_SetRecord<Segment extends string> = Segment extends `${infer Param}&${infer Rest}`
|
|
72
|
-
? Record<Param, string> & PathQuery<`&${Rest}`>
|
|
73
|
-
: Record<Segment, string>
|
package/ext/router/node/Page.ts
DELETED
|
@@ -1,27 +0,0 @@
|
|
|
1
|
-
import { chain } from "#lib/chain";
|
|
2
|
-
import { isUndefined } from "#lib/native";
|
|
3
|
-
import { $HTMLElement } from "#node/$HTMLElement";
|
|
4
|
-
import type { RouteData } from "..";
|
|
5
|
-
import type { Route } from "./Route";
|
|
6
|
-
|
|
7
|
-
export class Page<R extends Route<any> = any, Data extends RouteData = any> extends $HTMLElement {
|
|
8
|
-
route: R;
|
|
9
|
-
page: this;
|
|
10
|
-
params: Data['params'];
|
|
11
|
-
query: Data['query'];
|
|
12
|
-
#pageTitle: null | string = null;
|
|
13
|
-
initial = false;
|
|
14
|
-
constructor(route: R, data?: {params: any, query: any}) {
|
|
15
|
-
super('page');
|
|
16
|
-
this.route = route;
|
|
17
|
-
this.page = this;
|
|
18
|
-
this.params = data?.params ?? {};
|
|
19
|
-
this.query = data?.query ?? {};
|
|
20
|
-
}
|
|
21
|
-
|
|
22
|
-
pageTitle(): string | null;
|
|
23
|
-
pageTitle(title: string | null): this;
|
|
24
|
-
pageTitle(title?: string | null) {
|
|
25
|
-
return chain(this, arguments, () => this.#pageTitle, title, title => this.#pageTitle = title)
|
|
26
|
-
}
|
|
27
|
-
}
|
package/ext/router/node/Route.ts
DELETED
|
@@ -1,54 +0,0 @@
|
|
|
1
|
-
import { _instanceof, _Object_fromEntries, _Array_from, _Promise } from "#lib/native";
|
|
2
|
-
import { $Element } from "#node/$Element";
|
|
3
|
-
import type { AsyncRoute, RouteBuilder, RouteDataResolver } from "..";
|
|
4
|
-
import { Page } from "./Page";
|
|
5
|
-
|
|
6
|
-
export abstract class BaseRouteNode<Path extends string = string> extends $Element {
|
|
7
|
-
readonly path: Path;
|
|
8
|
-
routes = new Map<string, Route<any>>()
|
|
9
|
-
parent: BaseRouteNode<any> | null = null;
|
|
10
|
-
builder: RouteBuilder<Route<Path>, RouteDataResolver<Path>> | AsyncRoute<Path>
|
|
11
|
-
constructor(path: Path, builder: RouteBuilder<Route<Path>, RouteDataResolver<Path>> | AsyncRoute<Path>, nodeName: string) {
|
|
12
|
-
super(nodeName);
|
|
13
|
-
this.path = path;
|
|
14
|
-
this.builder = builder;
|
|
15
|
-
}
|
|
16
|
-
|
|
17
|
-
route<P extends string, J extends `${Path}${P}`>(
|
|
18
|
-
path: P,
|
|
19
|
-
resolver: RouteBuilder<Route<J>, RouteDataResolver<J>> | Route<J> | AsyncRoute<J>,
|
|
20
|
-
fn?: (route: Route<J>) => Route<J>
|
|
21
|
-
) {
|
|
22
|
-
const fullPath = `${this.path}${path}`;
|
|
23
|
-
if (_instanceof(resolver, Route) && fullPath !== resolver.path) throw `Pathname not matched: ${path}`
|
|
24
|
-
const route = resolver instanceof Route ? resolver : new Route(fullPath as J, resolver);
|
|
25
|
-
route.parent = this;
|
|
26
|
-
fn && fn(route);
|
|
27
|
-
this.routes.set(path, route);
|
|
28
|
-
return this;
|
|
29
|
-
}
|
|
30
|
-
}
|
|
31
|
-
|
|
32
|
-
export class Route<Path extends string = string> extends BaseRouteNode<Path> {
|
|
33
|
-
constructor(path: Path, builder: RouteBuilder<Route<Path>, RouteDataResolver<Path>> | AsyncRoute<Path>) {
|
|
34
|
-
super(path, builder, 'route');
|
|
35
|
-
}
|
|
36
|
-
|
|
37
|
-
async build(data: {params: any, query: any} = {params: {}, query: {}}, page?: Page) {
|
|
38
|
-
page = page ?? new Page(this, data);
|
|
39
|
-
page.params = data.params;
|
|
40
|
-
page.initial = true;
|
|
41
|
-
let resolver: any = this.builder(page);
|
|
42
|
-
if (_instanceof(resolver, _Promise)) {
|
|
43
|
-
const result = await resolver as any;
|
|
44
|
-
// Module import
|
|
45
|
-
if (result[Symbol.toStringTag] === 'Module') {
|
|
46
|
-
page.route = this;
|
|
47
|
-
resolver = result.default.builder(page);
|
|
48
|
-
}
|
|
49
|
-
else resolver = result;
|
|
50
|
-
}
|
|
51
|
-
if (!_instanceof(resolver, Page)) page.content(resolver);
|
|
52
|
-
return page;
|
|
53
|
-
}
|
|
54
|
-
}
|