amateras 0.4.0 → 0.4.2
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 +1 -1
- package/ext/router/index.ts +5 -5
- package/ext/router/node/Page.ts +2 -3
- package/package.json +1 -1
- package/src/core.ts +14 -17
- package/src/lib/assign.ts +3 -3
- package/src/lib/assignHelper.ts +8 -7
- package/src/lib/chain.ts +11 -1
- package/src/lib/native.ts +6 -3
- package/src/lib/randomId.ts +1 -3
- package/src/lib/sleep.ts +1 -3
- package/src/node/$Element.ts +7 -10
- package/src/node/$HTMLElement.ts +1 -2
- package/src/node/$Node.ts +21 -20
- package/ext/markdown/index.ts +0 -123
- package/ext/ui/lib/VirtualScroll.ts +0 -24
- package/ext/ui/node/Accordian.ts +0 -97
- package/ext/ui/node/Tabs.ts +0 -114
- package/ext/ui/node/Toast.ts +0 -16
- package/ext/ui/node/Waterfall.ts +0 -73
- package/ext/ui/package.json +0 -11
- package/src/node/$Virtual.ts +0 -58
package/README.md
CHANGED
|
@@ -102,7 +102,7 @@ $(document.body).content([
|
|
|
102
102
|
The packages size result using Vite 7.0 with default bundle settings, polyfills code size included.
|
|
103
103
|
| Package name | Size | Size(gzip) | Description |
|
|
104
104
|
| --- | --- | --- | --- |
|
|
105
|
-
| amateras | 5.
|
|
105
|
+
| amateras | 5.64 kB | 2.48 kB | Core |
|
|
106
106
|
| amateras/html | 0.97 kB | 0.25 kB | Import HTMLElement types and methods |
|
|
107
107
|
| [amateras/css](./ext/css/README.md) | 3.65 kB | 1.41 kB | Style in JS |
|
|
108
108
|
| [amateras/router](./ext/router/README.md) | 3.74 kB | 1.69 kB | Amateras Router |
|
package/ext/router/index.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import type { AnchorTarget } from "#html/$Anchor";
|
|
2
|
-
import { _Object_assign, forEach } from "#lib/native";
|
|
2
|
+
import { _bind, _Object_assign, forEach } from "#lib/native";
|
|
3
3
|
import type { $NodeContentResolver } from "#node/$Node";
|
|
4
4
|
import type { Page } from "./node/Page";
|
|
5
5
|
import { Route } from "./node/Route";
|
|
@@ -30,10 +30,10 @@ declare global {
|
|
|
30
30
|
|
|
31
31
|
// assign methods
|
|
32
32
|
_Object_assign($, {
|
|
33
|
-
open: Router.open
|
|
34
|
-
replace: Router.replace
|
|
35
|
-
back: Router.back
|
|
36
|
-
forward: Router.forward
|
|
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
37
|
});
|
|
38
38
|
// define styles
|
|
39
39
|
forEach([
|
package/ext/router/node/Page.ts
CHANGED
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { chain } from "#lib/chain";
|
|
1
2
|
import { isUndefined } from "#lib/native";
|
|
2
3
|
import { $HTMLElement } from "#node/$HTMLElement";
|
|
3
4
|
import type { RouteData } from "..";
|
|
@@ -21,8 +22,6 @@ export class Page<R extends Route<any> = any, Data extends RouteData = any> exte
|
|
|
21
22
|
pageTitle(): string | null;
|
|
22
23
|
pageTitle(title: string | null): this;
|
|
23
24
|
pageTitle(title?: string | null) {
|
|
24
|
-
|
|
25
|
-
if (!isUndefined(title)) this.#pageTitle = title;
|
|
26
|
-
return this;
|
|
25
|
+
return chain(this, arguments, () => this.#pageTitle, title, title => this.#pageTitle = title)
|
|
27
26
|
}
|
|
28
27
|
}
|
package/package.json
CHANGED
package/src/core.ts
CHANGED
|
@@ -3,7 +3,7 @@ import './node';
|
|
|
3
3
|
import { Signal } from "#structure/Signal";
|
|
4
4
|
import { $Element, type $Event } from "#node/$Element";
|
|
5
5
|
import { $Node, type $NodeContentResolver, type $NodeContentTypes } from '#node/$Node';
|
|
6
|
-
import { _instanceof, isString, isFunction, _Object_assign, isObject, isNull, _Object_entries, _Object_defineProperty, forEach, isNumber, _Array_from, isUndefined } from '#lib/native';
|
|
6
|
+
import { _instanceof, isString, isFunction, _Object_assign, isObject, isNull, _Object_entries, _Object_defineProperty, forEach, isNumber, _Array_from, isUndefined, _bind, _null } from '#lib/native';
|
|
7
7
|
import { $HTMLElement } from '#node/$HTMLElement';
|
|
8
8
|
import { _document } from '#lib/env';
|
|
9
9
|
|
|
@@ -52,11 +52,10 @@ export function $(resolver: string | number | null | undefined | Element | HTMLE
|
|
|
52
52
|
export namespace $ {
|
|
53
53
|
export const stylesheet = _stylesheet;
|
|
54
54
|
_document.adoptedStyleSheets.push(_stylesheet);
|
|
55
|
-
export const style = _stylesheet.insertRule
|
|
55
|
+
export const style = _bind(_stylesheet.insertRule, _stylesheet);
|
|
56
56
|
type SignalProcess<T> = T extends Array<any> ? {} : T extends object ? { [key in keyof T as `${string & key}$`]: SignalFunction<T[key]> } : {};
|
|
57
57
|
export type SignalFunction<T> = {signal: Signal<T>, set: (newValue: T | ((oldValue: T) => T)) => SignalFunction<T>} & (() => T) & SignalProcess<T>;
|
|
58
|
-
export
|
|
59
|
-
export function signal<T>(value: T) {
|
|
58
|
+
export const signal = <T>(value: T): SignalFunction<T> => {
|
|
60
59
|
const signal = new Signal<T>(value);
|
|
61
60
|
const signalFn = function () { return signal.value(); }
|
|
62
61
|
_Object_assign(signalFn, {
|
|
@@ -74,14 +73,14 @@ export namespace $ {
|
|
|
74
73
|
}
|
|
75
74
|
|
|
76
75
|
export type ComputeFunction<T> = ({(): T}) & { signal: Signal<T> };
|
|
77
|
-
export
|
|
76
|
+
export const compute = <T>(process: () => T): ComputeFunction<T> => {
|
|
78
77
|
let subscribed = false;
|
|
79
|
-
const signalFn: SignalFunction<any> = signal(
|
|
80
|
-
|
|
78
|
+
const signalFn: SignalFunction<any> = signal(_null);
|
|
79
|
+
const computeFn = () => {
|
|
81
80
|
if (!subscribed) return signalFn.set(subscribe())();
|
|
82
81
|
else return signalFn.set(process())();
|
|
83
82
|
}
|
|
84
|
-
|
|
83
|
+
const subscribe = () => {
|
|
85
84
|
const signalHandler = (signal: Signal<any>) => {
|
|
86
85
|
signal.subscribe(() => signalFn.set(process()))
|
|
87
86
|
}
|
|
@@ -95,21 +94,19 @@ export namespace $ {
|
|
|
95
94
|
return computeFn as ComputeFunction<T>
|
|
96
95
|
}
|
|
97
96
|
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
97
|
+
type assign = {
|
|
98
|
+
(resolver: [nodeName: string, $node: Constructor<$Node>][]): $;
|
|
99
|
+
(nodeName: string, $node: Constructor<$Node>): $;
|
|
100
|
+
}
|
|
101
|
+
export const assign: assign = (resolver: string | [nodeName: string, $node: Constructor<$Node>][], $node?: Constructor<$Node>) => {
|
|
101
102
|
if (isString(resolver)) $node && (nodeNameMap[resolver] = $node);
|
|
102
103
|
else forEach(resolver, ([nodeName, $node]) => nodeNameMap[nodeName] = $node);
|
|
103
104
|
return $;
|
|
104
105
|
}
|
|
105
106
|
|
|
106
|
-
export
|
|
107
|
-
return _instanceof(item, Array) ? item : [item];
|
|
108
|
-
}
|
|
107
|
+
export const toArray = <T>(item: OrArray<T>): T[] => _instanceof(item, Array) ? item : [item];
|
|
109
108
|
|
|
110
|
-
export
|
|
111
|
-
return $('span').content(content);
|
|
112
|
-
}
|
|
109
|
+
export const span = (content: string) => $('span').content(content);
|
|
113
110
|
}
|
|
114
111
|
export type $ = typeof $;
|
|
115
112
|
globalThis.$ = $;
|
package/src/lib/assign.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { Signal } from "../structure/Signal";
|
|
2
|
-
import { _instanceof, _Object_defineProperty, isUndefined } from "./native";
|
|
2
|
+
import { _instanceof, _Object_defineProperty, forEach, isUndefined } from "./native";
|
|
3
3
|
|
|
4
4
|
export const assign = (target: any, {set, get, fn}: {
|
|
5
5
|
set?: string[],
|
|
@@ -9,7 +9,7 @@ export const assign = (target: any, {set, get, fn}: {
|
|
|
9
9
|
const [GET, SET, FN] = ['get', 'set', 'fn'] as const;
|
|
10
10
|
const filterAndMap = (type: 'get' | 'set' | 'fn', arr: string[] | undefined) => arr?.map(prop => [type, prop]) ?? []
|
|
11
11
|
const list = [...filterAndMap(GET, get), ...filterAndMap(SET, set), ...filterAndMap(FN, fn)] as [string, string][];
|
|
12
|
-
|
|
12
|
+
forEach(list, ([type, prop]) =>
|
|
13
13
|
_Object_defineProperty(target.prototype, prop, {
|
|
14
14
|
...(type === GET ? {
|
|
15
15
|
get() { return this.node[prop as any] }
|
|
@@ -34,5 +34,5 @@ export const assign = (target: any, {set, get, fn}: {
|
|
|
34
34
|
}),
|
|
35
35
|
|
|
36
36
|
})
|
|
37
|
-
|
|
37
|
+
)
|
|
38
38
|
}
|
package/src/lib/assignHelper.ts
CHANGED
|
@@ -1,16 +1,17 @@
|
|
|
1
1
|
import type { $Node } from "#node/$Node";
|
|
2
2
|
import { assign } from "./assign";
|
|
3
|
-
import { _Object_entries, _Object_getOwnPropertyDescriptors } from "./native";
|
|
3
|
+
import { _Object_entries, _Object_getOwnPropertyDescriptors, forEach } from "./native";
|
|
4
4
|
|
|
5
5
|
export const assignHelper = (object: Constructor<EventTarget>, target: Constructor<$Node>, tagname?: string) => {
|
|
6
6
|
const [set, get, fn] = [[], [], []] as [string[], string[], string[]]
|
|
7
7
|
// assign native object properties to target
|
|
8
|
-
|
|
9
|
-
if (prop in target.prototype)
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
8
|
+
forEach(_Object_entries(_Object_getOwnPropertyDescriptors(object.prototype)), ([prop, value]) => {
|
|
9
|
+
if (!(prop in target.prototype)) {
|
|
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);
|
|
13
|
+
}
|
|
14
|
+
})
|
|
14
15
|
assign(target, {set, get, fn})
|
|
15
16
|
// register tagname
|
|
16
17
|
if (tagname) $.assign(tagname, target)
|
package/src/lib/chain.ts
CHANGED
|
@@ -1,3 +1,13 @@
|
|
|
1
1
|
import { isUndefined } from "./native";
|
|
2
2
|
|
|
3
|
-
export const chain = <T, R, V>(_this: T, args: IArguments, get: () => R, value: V, set: (value: Exclude<V, undefined>) => any) =>
|
|
3
|
+
export const chain: chain = <T, R, V>(_this: T, args: IArguments | null, get: (() => R) | null, value: V, set: (value: Exclude<V, undefined>) => any) =>
|
|
4
|
+
args && get && !args.length
|
|
5
|
+
? get()
|
|
6
|
+
: isUndefined(value)
|
|
7
|
+
? _this
|
|
8
|
+
: (set(value as any), _this);
|
|
9
|
+
|
|
10
|
+
interface chain {
|
|
11
|
+
<T, V>(_this: T, args: null, get: null, value: V, set: (value: Exclude<V, undefined>) => any): T
|
|
12
|
+
<T, R, V>(_this: T, args: IArguments, get: (() => R), value: V, set: (value: Exclude<V, undefined>) => any): T | R
|
|
13
|
+
}
|
package/src/lib/native.ts
CHANGED
|
@@ -1,3 +1,5 @@
|
|
|
1
|
+
// Value
|
|
2
|
+
export const _null = null;
|
|
1
3
|
// Object
|
|
2
4
|
export const _Object_fromEntries = Object.fromEntries;
|
|
3
5
|
export const _Object_entries = Object.entries;
|
|
@@ -15,15 +17,16 @@ export const isString = (target: any): target is string => _typeof(target, 'stri
|
|
|
15
17
|
export const isNumber = (target: any): target is number => _typeof(target, 'number')
|
|
16
18
|
export const isObject = (target: any): target is object => _typeof(target, 'object')
|
|
17
19
|
export const isFunction = (target: any): target is Function => _typeof(target, 'function')
|
|
18
|
-
export const isUndefined = (target: any): target is undefined =>
|
|
19
|
-
export const isNull = (target: any): target is null => target ===
|
|
20
|
+
export const isUndefined = (target: any): target is undefined => target === undefined;
|
|
21
|
+
export const isNull = (target: any): target is null => target === _null;
|
|
20
22
|
export const _instanceof = <T extends (abstract new (...args: any[]) => any)[]>(target: any, ...instance: T): target is InstanceType<T[number]> => !!instance.find(i => target instanceof i);
|
|
21
23
|
// JSON
|
|
22
24
|
export const _JSON_stringify = JSON.stringify;
|
|
23
25
|
export const _JSON_parse = JSON.parse;
|
|
24
26
|
// String
|
|
25
27
|
export const startsWith = (target: string, ...str: string[]) => !!str.find(s => target.startsWith(s));
|
|
26
|
-
|
|
28
|
+
// Function
|
|
29
|
+
export const _bind = (target: Function, obj: Object) => target.bind(obj);
|
|
27
30
|
interface forEach {
|
|
28
31
|
<T>(arr: Array<T>, fn: (value: T, index: number, array: Array<T>) => any, thisArgs?: any): void;
|
|
29
32
|
<T>(set: Set<T>, fn: (value: T, index: number, set: Set<T>) => any, thisArgs?: any): void;
|
package/src/lib/randomId.ts
CHANGED
|
@@ -5,7 +5,5 @@ const UPPER = LOWER.toUpperCase();
|
|
|
5
5
|
export const randomId = (options?: {length?: number, lettercase?: 'any' | 'lower' | 'upper'}): string => {
|
|
6
6
|
options = {length: 5, lettercase: 'any', ...options};
|
|
7
7
|
const char = options.lettercase === 'any' ? LOWER + UPPER : options.lettercase === 'lower' ? LOWER : UPPER;
|
|
8
|
-
return _Array_from({length: options.length as number}, (_, i) =>
|
|
9
|
-
const rand = Math.round(Math.random() * char.length); return char[rand]
|
|
10
|
-
}).join('');
|
|
8
|
+
return _Array_from({length: options.length as number}, (_, i) => char[Math.round(Math.random() * char.length)]).join('');
|
|
11
9
|
}
|
package/src/lib/sleep.ts
CHANGED
package/src/node/$Element.ts
CHANGED
|
@@ -26,8 +26,7 @@ export class $Element<Ele extends Element = Element, EvMap = ElementEventMap> ex
|
|
|
26
26
|
}
|
|
27
27
|
|
|
28
28
|
class(...token: (string | null | undefined)[]) {
|
|
29
|
-
this.classList(token.filter(isString).join(' '));
|
|
30
|
-
return this;
|
|
29
|
+
return this.classList(token.filter(isString).join(' '));
|
|
31
30
|
}
|
|
32
31
|
|
|
33
32
|
addClass(...token: (string | null | undefined)[]) {
|
|
@@ -41,13 +40,11 @@ export class $Element<Ele extends Element = Element, EvMap = ElementEventMap> ex
|
|
|
41
40
|
}
|
|
42
41
|
|
|
43
42
|
on<K extends keyof EvMap, Ev extends EvMap[K]>(type: K, listener: $EventListener<this, Ev> | $EventListenerObject<this, Ev>, options?: boolean | AddEventListenerOptions) {
|
|
44
|
-
this.addEventListener(type as string, listener as any, options);
|
|
45
|
-
return this;
|
|
43
|
+
return this.addEventListener(type as string, listener as any, options);
|
|
46
44
|
}
|
|
47
45
|
|
|
48
46
|
off<K extends keyof EvMap, Ev extends EvMap[K]>(type: K, listener: $EventListener<this, Ev> | $EventListenerObject<this, Ev>, options?: boolean | EventListenerOptions) {
|
|
49
|
-
this.removeEventListener(type as string, listener as any, options);
|
|
50
|
-
return this;
|
|
47
|
+
return this.removeEventListener(type as string, listener as any, options);
|
|
51
48
|
}
|
|
52
49
|
|
|
53
50
|
once<K extends keyof EvMap, Ev extends EvMap[K]>(type: K, listener: $EventListener<this, Ev> | $EventListenerObject<this, Ev>, options?: boolean | AddEventListenerOptions) {
|
|
@@ -386,10 +383,10 @@ export interface $Element<Ele extends Element, EvMap> {
|
|
|
386
383
|
/** {@link ARIAMixin.role} */
|
|
387
384
|
role(): string | null;
|
|
388
385
|
role(role: $Parameter<string | null>): this;
|
|
389
|
-
addEventListener<K extends keyof EvMap, Ev extends EvMap[K]>(type: K, listener: $EventListener<this, Ev> | $EventListenerObject<this, Ev>, options?: boolean | AddEventListenerOptions):
|
|
390
|
-
addEventListener(type: string, listener: $EventListener<this, Event> | $EventListenerObject<this, Event>, options?: boolean | AddEventListenerOptions):
|
|
391
|
-
removeEventListener<K extends keyof EvMap, Ev extends EvMap[K]>(type: K, listener: $EventListener<this, Ev> | $EventListenerObject<this, Ev>, options?: boolean | EventListenerOptions):
|
|
392
|
-
removeEventListener(type: string, listener: $EventListener<this, Event> | $EventListenerObject<this, Event>, options?: boolean | EventListenerOptions):
|
|
386
|
+
addEventListener<K extends keyof EvMap, Ev extends EvMap[K]>(type: K, listener: $EventListener<this, Ev> | $EventListenerObject<this, Ev>, options?: boolean | AddEventListenerOptions): this;
|
|
387
|
+
addEventListener(type: string, listener: $EventListener<this, Event> | $EventListenerObject<this, Event>, options?: boolean | AddEventListenerOptions): this;
|
|
388
|
+
removeEventListener<K extends keyof EvMap, Ev extends EvMap[K]>(type: K, listener: $EventListener<this, Ev> | $EventListenerObject<this, Ev>, options?: boolean | EventListenerOptions): this;
|
|
389
|
+
removeEventListener(type: string, listener: $EventListener<this, Event> | $EventListenerObject<this, Event>, options?: boolean | EventListenerOptions): this;
|
|
393
390
|
|
|
394
391
|
|
|
395
392
|
on(type: string, listener: $EventListener<this, Event> | $EventListenerObject<this, Event>, options?: boolean | AddEventListenerOptions): this;
|
package/src/node/$HTMLElement.ts
CHANGED
|
@@ -11,8 +11,7 @@ export class $HTMLElement<Ele extends HTMLElement = HTMLElement, EvMap = HTMLEle
|
|
|
11
11
|
style(style?: Partial<CSSStyleDeclarationOptions> | undefined) {
|
|
12
12
|
let _style = this.node.style
|
|
13
13
|
if (!arguments.length) return _style
|
|
14
|
-
if (
|
|
15
|
-
forEach(_Object_entries(style), ([key, value]) => _style[key as any] = value ?? '')
|
|
14
|
+
if (style) forEach(_Object_entries(style), ([key, value]) => _style[key as any] = value ?? '');
|
|
16
15
|
return this;
|
|
17
16
|
}
|
|
18
17
|
}
|
package/src/node/$Node.ts
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
|
+
import { chain } from "#lib/chain";
|
|
1
2
|
import { _document } from "#lib/env";
|
|
2
|
-
import { _Array_from, _instanceof, _JSON_stringify, forEach, isFunction, isNull, isObject, isUndefined } from "#lib/native";
|
|
3
|
+
import { _Array_from, _instanceof, _JSON_stringify, _null, forEach, isFunction, isNull, isObject, isUndefined } from "#lib/native";
|
|
3
4
|
import { Signal } from "#structure/Signal";
|
|
4
5
|
|
|
5
6
|
export class $Node {
|
|
@@ -11,9 +12,10 @@ export class $Node {
|
|
|
11
12
|
}
|
|
12
13
|
|
|
13
14
|
content(children: $NodeContentResolver<this>) {
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
15
|
+
return chain(this, _null, _null, children, children => {
|
|
16
|
+
forEach(_Array_from(this.childNodes), node => node.remove());
|
|
17
|
+
this.insert(children);
|
|
18
|
+
})
|
|
17
19
|
}
|
|
18
20
|
|
|
19
21
|
insert(resolver: $NodeContentResolver<this>, position = -1) {
|
|
@@ -23,15 +25,16 @@ export class $Node {
|
|
|
23
25
|
}
|
|
24
26
|
|
|
25
27
|
await<T>(promise: OrPromise<T>, callback: ($node: this, result: T) => void): this {
|
|
26
|
-
if (_instanceof(promise, Promise))
|
|
27
|
-
else
|
|
28
|
+
if (_instanceof(promise, Promise)) promise.then(result => callback(this, result));
|
|
29
|
+
else callback(this, promise);
|
|
30
|
+
return this;
|
|
28
31
|
}
|
|
29
32
|
|
|
30
33
|
replace($node: $NodeContentResolver<$Node>) {
|
|
31
|
-
if (
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
34
|
+
if ($node)
|
|
35
|
+
this.replaceWith(
|
|
36
|
+
...$.toArray($Node.process(this, $node)).filter($node => $node).map($node => $node?.node) as Node[]
|
|
37
|
+
)
|
|
35
38
|
return this;
|
|
36
39
|
}
|
|
37
40
|
|
|
@@ -43,9 +46,7 @@ export class $Node {
|
|
|
43
46
|
return this.textContent();
|
|
44
47
|
}
|
|
45
48
|
|
|
46
|
-
mounted($parent: $Node) {
|
|
47
|
-
return this;
|
|
48
|
-
}
|
|
49
|
+
mounted($parent: $Node) {}
|
|
49
50
|
|
|
50
51
|
use<F extends ($ele: this, ...args: any) => void>(callback: F, ...args: F extends ($ele: this, ...args: infer P) => void ? P : never) {
|
|
51
52
|
callback(this, ...args);
|
|
@@ -97,13 +98,13 @@ export class $Node {
|
|
|
97
98
|
|
|
98
99
|
/** */
|
|
99
100
|
static append($node: $Node, child: $Node | undefined | null, position: number) {
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
101
|
+
if (child) {
|
|
102
|
+
// get child node at position
|
|
103
|
+
let positionChild = _Array_from($node.childNodes).at(position);
|
|
104
|
+
if (!positionChild) $node.appendChild(child.node);
|
|
105
|
+
else $node.insertBefore(child.node, position < 0 ? positionChild.nextSibling : positionChild);
|
|
106
|
+
child.mounted($node);
|
|
107
|
+
}
|
|
107
108
|
}
|
|
108
109
|
}
|
|
109
110
|
|
package/ext/markdown/index.ts
DELETED
|
@@ -1,123 +0,0 @@
|
|
|
1
|
-
import { _Array_from, forEach } from "#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) => {
|
|
54
|
-
return replaceAll(str, /(?:.+?\n?)+/gm, $0 => `<p>${trim($0)}</p>`)
|
|
55
|
-
}
|
|
56
|
-
// Headings
|
|
57
|
-
blockProcess({
|
|
58
|
-
regexp: /^(#+) (.+)/gm,
|
|
59
|
-
handle: text => replaceAll(text, /^(#+) (.+)/gm, (_, $1: string, $2) => `<h${$1.length}>${$2}</h${$1.length}>`)
|
|
60
|
-
});
|
|
61
|
-
blockProcess({
|
|
62
|
-
regexp: /^(.+)\n==+$/gm,
|
|
63
|
-
handle: text => replaceAll(text, /^(.+)\n==+$/gm, (_, $1) => `<h1>${$1}</h1>`)
|
|
64
|
-
});
|
|
65
|
-
blockProcess({
|
|
66
|
-
regexp: /^(.+)\n--+$/gm,
|
|
67
|
-
handle: text => replaceAll(text, /^(.+)\n--+$/gm, (_, $1) => `<h2>${$1}</h2>`)
|
|
68
|
-
});
|
|
69
|
-
// Blockquote
|
|
70
|
-
blockProcess({
|
|
71
|
-
regexp: /(?:^> ?.*(?:\n|$))+/gm,
|
|
72
|
-
handle: text => {
|
|
73
|
-
const fn = (str: string) => {
|
|
74
|
-
const blocks = _Array_from(str.matchAll(/(?:^> ?.*(?:\n|$))+/gm));
|
|
75
|
-
forEach(blocks, block => {
|
|
76
|
-
const blocked = fn(replaceAll(block[0], /^> ?/gm, ''));
|
|
77
|
-
str = str.replace(block[0], `<blockquote>\n${paragraph(blocked)}\n</blockquote>`);
|
|
78
|
-
})
|
|
79
|
-
return str;
|
|
80
|
-
}
|
|
81
|
-
return fn(text);
|
|
82
|
-
}
|
|
83
|
-
});
|
|
84
|
-
// List
|
|
85
|
-
blockProcess({
|
|
86
|
-
regexp: /(?:^(?:\t|(?: )+)?(?:-|[0-9]+\.) (?:.+\n?))+/gm,
|
|
87
|
-
handle: text => {
|
|
88
|
-
const fn = (str: string) => {
|
|
89
|
-
const blocks = _Array_from(str.matchAll(/(?:^(?:\t|(?: )+)?(?:-|[0-9]+\.) (?:.+\n?))+/gm));
|
|
90
|
-
forEach(blocks, block => {
|
|
91
|
-
let haveList = false // check this loop have list
|
|
92
|
-
const type = block[0].match(/^(-|[0-9]+\.) /)?.[1] === '-' ? 'ul' : 'ol';
|
|
93
|
-
const listed = replaceAll(block[0], /^(?:-|[0-9]+\.) (.+)/gm, (_, $1: string) => (haveList = true, `<li>\n${trim($1)}\n</li>`));
|
|
94
|
-
const clearTabbed = replaceAll(listed, /^(?:\t|(?: ))/gm, '');
|
|
95
|
-
const convertedList = fn(clearTabbed);
|
|
96
|
-
str = str.replace(block[0], haveList ? `<${type}>\n${trim(convertedList)}\n</${type}>` : convertedList);
|
|
97
|
-
})
|
|
98
|
-
return str;
|
|
99
|
-
}
|
|
100
|
-
return fn(text);
|
|
101
|
-
}
|
|
102
|
-
})
|
|
103
|
-
// Codeblock
|
|
104
|
-
blockProcess({
|
|
105
|
-
regexp: /^```([^`\n]+)\n([^`]+)?```/gm,
|
|
106
|
-
handle: text => replaceAll(text, /^```([^`\n]+)\n([^`]+)?```/gm, (_, $1, $2: string) => `<pre><code>\n${trim($2)}\n</code></pre>`)
|
|
107
|
-
})
|
|
108
|
-
// Horizontal Rule
|
|
109
|
-
blockProcess({
|
|
110
|
-
regexp: /^(?:---|\*\*\*|___)(\s+)?$/gm,
|
|
111
|
-
handle: text => replaceAll(text, /^(?:---|\*\*\*|___)(\s+)?$/gm, _ => `<hr>`)
|
|
112
|
-
})
|
|
113
|
-
// Bold
|
|
114
|
-
inlineProcess(text => replaceAll(text, /\*\*([^*]+?)\*\*/g, (_, $1) => `<b>${$1}</b>`));
|
|
115
|
-
// Italic
|
|
116
|
-
inlineProcess(text => replaceAll(text, /\*([^*]+?)\*/g, (_, $1) => `<i>${$1}</i>`));
|
|
117
|
-
// Image
|
|
118
|
-
inlineProcess(text => replaceAll(text, /!\[(.+?)\]\((.+?)(?: "(.+?)?")?\)/g, (_, alt, src, title) => `<img src="${src}" alt="${alt}"${title ? ` title="${title}"` : ''}>`));
|
|
119
|
-
// Link
|
|
120
|
-
inlineProcess(text => replaceAll(text, /\[(.+?)\]\((?:(\w\w+?:[^\s]+?)(?: "(.+?)?")?)\)/g, (_, content, href, title) => `<a href="${href}"${title ? ` title="${title}"` : ''}>${content}</a>`));
|
|
121
|
-
inlineProcess(text => replaceAll(text, /\[(.+?)\]\((?:(\w+?@(?:\w|\.\w)+?)(?: "(.+)?")?)\)/g, (_, content, mail, title) => `<a href="mailto:${mail}"${title ? ` title="${title}"` : ''}>${content}</a>`));
|
|
122
|
-
inlineProcess(text => replaceAll(text, /<(\w\w+?:[^\s]+?)>/g, (_, href) => `<a href="${href}">${href}</a>`));
|
|
123
|
-
inlineProcess(text => replaceAll(text, /<(\w+?@(?:\w|\.\w)+?)>/g, (_, mail) => `<a href="mailto:${mail}">${mail}</a>`));
|
|
@@ -1,24 +0,0 @@
|
|
|
1
|
-
import { _Array_from, _instanceof, forEach } from "amateras/lib/native";
|
|
2
|
-
import type { $Virtual } from "amateras/node/$Virtual";
|
|
3
|
-
import { $HTMLElement } from "amateras/node/$HTMLElement";
|
|
4
|
-
import { _document } from "../../../src/lib/env";
|
|
5
|
-
|
|
6
|
-
export function VirtualScroll($parent: $Virtual, scroller: Node = _document) {
|
|
7
|
-
scroller.addEventListener('scroll', render, true);
|
|
8
|
-
$parent.on('layout', render);
|
|
9
|
-
function render() {
|
|
10
|
-
const getRect = ($node: $HTMLElement) => $node.getBoundingClientRect();
|
|
11
|
-
const number = (str: string) => parseInt(str);
|
|
12
|
-
const parentRect = getRect($parent);
|
|
13
|
-
const children = _Array_from($parent.nodes);
|
|
14
|
-
forEach(children, $child => {
|
|
15
|
-
if (!_instanceof($child, $HTMLElement)) return;
|
|
16
|
-
const { top, height } = $child.style();
|
|
17
|
-
const topPos = parentRect.top + number(top);
|
|
18
|
-
const bottomPos = topPos + number(height);
|
|
19
|
-
if (bottomPos < 0 || topPos > innerHeight) $parent.hide($child);
|
|
20
|
-
else $parent.show($child);
|
|
21
|
-
})
|
|
22
|
-
$parent.render();
|
|
23
|
-
}
|
|
24
|
-
}
|
package/ext/ui/node/Accordian.ts
DELETED
|
@@ -1,97 +0,0 @@
|
|
|
1
|
-
import { _Array_from, _instanceof, forEach, isNull } from "amateras/lib/native";
|
|
2
|
-
import { $HTMLElement } from "amateras/node/$HTMLElement";
|
|
3
|
-
import type { $Node, $NodeContentResolver } from "amateras/node/$Node";
|
|
4
|
-
import { chain } from "../../../src/lib/chain";
|
|
5
|
-
|
|
6
|
-
const [ACCORDIAN, ACCORDIAN_ITEM, ACCORDIAN_TRIGGER, ACCORDIAN_CONTENT, ACCORDIAN_CONTAINER] = ['accordian', 'accordian-item', 'accordian-trigger', 'accordian-content', 'accordian-container'] as const;
|
|
7
|
-
forEach([
|
|
8
|
-
`${ACCORDIAN},${ACCORDIAN_ITEM},${ACCORDIAN_TRIGGER}{display:block}`,
|
|
9
|
-
`${ACCORDIAN_CONTENT}{display:grid;grid-template-rows:0fr}`,
|
|
10
|
-
`${ACCORDIAN_CONTENT}[opened]{grid-template-rows:1fr}`,
|
|
11
|
-
`${ACCORDIAN_CONTAINER}{overflow:hidden}`,
|
|
12
|
-
], $.style)
|
|
13
|
-
|
|
14
|
-
export class Accordian extends $HTMLElement {
|
|
15
|
-
#autoclose = false;
|
|
16
|
-
constructor() {
|
|
17
|
-
super(ACCORDIAN);
|
|
18
|
-
}
|
|
19
|
-
|
|
20
|
-
autoclose(): boolean;
|
|
21
|
-
autoclose(autoclose: boolean): this;
|
|
22
|
-
autoclose(autoclose?: boolean) {
|
|
23
|
-
return chain(this, arguments, () => this.#autoclose, autoclose, autoclose => this.#autoclose = autoclose);
|
|
24
|
-
}
|
|
25
|
-
|
|
26
|
-
get items() {
|
|
27
|
-
return _Array_from($(this.childNodes)).filter($child => _instanceof($child, AccordianItem))
|
|
28
|
-
}
|
|
29
|
-
}
|
|
30
|
-
|
|
31
|
-
export class AccordianItem extends $HTMLElement {
|
|
32
|
-
$content: null | AccordianContent = null;
|
|
33
|
-
$trigger: null | AccordianTrigger = null;
|
|
34
|
-
$root: null | Accordian = null;
|
|
35
|
-
constructor() {
|
|
36
|
-
super(ACCORDIAN_ITEM);
|
|
37
|
-
}
|
|
38
|
-
|
|
39
|
-
mounted($parent: $Node): this {
|
|
40
|
-
if (_instanceof($parent, Accordian)) this.$root = $parent;
|
|
41
|
-
forEach($(this.childNodes), $c => {
|
|
42
|
-
if (_instanceof($c, AccordianTrigger)) this.$trigger = $c;
|
|
43
|
-
if (_instanceof($c, AccordianContent)) this.$content = $c;
|
|
44
|
-
})
|
|
45
|
-
return this;
|
|
46
|
-
}
|
|
47
|
-
}
|
|
48
|
-
|
|
49
|
-
export class AccordianTrigger extends $HTMLElement {
|
|
50
|
-
$item: null | AccordianItem = null;
|
|
51
|
-
constructor() {
|
|
52
|
-
super(ACCORDIAN_TRIGGER);
|
|
53
|
-
this.on('click', _ => {
|
|
54
|
-
const $item = this.$item;
|
|
55
|
-
const $root = $item?.$root;
|
|
56
|
-
this.$item?.$content?.use($content => isNull($content.attr('opened')) ? $content.open() : $content.close());
|
|
57
|
-
$root?.autoclose() && $root.items.forEach($i => $i !== $item && $i.$content?.close())
|
|
58
|
-
})
|
|
59
|
-
}
|
|
60
|
-
|
|
61
|
-
mounted($parent: $Node): this {
|
|
62
|
-
if (_instanceof($parent, AccordianItem)) this.$item = $parent;
|
|
63
|
-
return this;
|
|
64
|
-
}
|
|
65
|
-
}
|
|
66
|
-
|
|
67
|
-
export class AccordianContent extends $HTMLElement {
|
|
68
|
-
$container = $(AccordianContainer);
|
|
69
|
-
constructor() {
|
|
70
|
-
super(ACCORDIAN_CONTENT);
|
|
71
|
-
super.insert(this.$container);
|
|
72
|
-
}
|
|
73
|
-
|
|
74
|
-
content(children: $NodeContentResolver<AccordianContainer>): this {
|
|
75
|
-
this.$container.content(children);
|
|
76
|
-
return this;
|
|
77
|
-
}
|
|
78
|
-
|
|
79
|
-
insert(resolver: $NodeContentResolver<AccordianContainer>, position?: number): this {
|
|
80
|
-
this.$container.insert(resolver, position);
|
|
81
|
-
return this;
|
|
82
|
-
}
|
|
83
|
-
|
|
84
|
-
open() {
|
|
85
|
-
return this.attr({opened: ''})
|
|
86
|
-
}
|
|
87
|
-
|
|
88
|
-
close() {
|
|
89
|
-
return this.attr({opened: null});
|
|
90
|
-
}
|
|
91
|
-
}
|
|
92
|
-
|
|
93
|
-
export class AccordianContainer extends $HTMLElement {
|
|
94
|
-
constructor() {
|
|
95
|
-
super(ACCORDIAN_CONTAINER);
|
|
96
|
-
}
|
|
97
|
-
}
|
package/ext/ui/node/Tabs.ts
DELETED
|
@@ -1,114 +0,0 @@
|
|
|
1
|
-
import { _instanceof, isUndefined } from "amateras/lib/native";
|
|
2
|
-
import { $HTMLElement } from "amateras/node/$HTMLElement";
|
|
3
|
-
import type { $Node } from "amateras/node/$Node";
|
|
4
|
-
|
|
5
|
-
$.style('tabs,tabs-container,tabs-list,tabs-content{display: block}')
|
|
6
|
-
|
|
7
|
-
export class Tabs extends $HTMLElement {
|
|
8
|
-
#value: null | string = null;
|
|
9
|
-
currentContent: null | TabsContent = null;
|
|
10
|
-
$container: null | TabsContainer = null;
|
|
11
|
-
$list: null | TabsList = null;
|
|
12
|
-
constructor() {
|
|
13
|
-
super('tabs');
|
|
14
|
-
}
|
|
15
|
-
|
|
16
|
-
value(): string | null;
|
|
17
|
-
value(value: string | undefined): this;
|
|
18
|
-
value(value?: string) {
|
|
19
|
-
return chain(this, arguments, () => this.#value, value, value => {
|
|
20
|
-
this.#value = value;
|
|
21
|
-
this.$container?.content(this.$container.contentMap.get(value));
|
|
22
|
-
this.$list?.check();
|
|
23
|
-
})
|
|
24
|
-
}
|
|
25
|
-
}
|
|
26
|
-
|
|
27
|
-
export class TabsContainer extends $HTMLElement {
|
|
28
|
-
$tabs?: Tabs;
|
|
29
|
-
contentMap = new Map<string, TabsContent>();
|
|
30
|
-
constructor($tabs?: Tabs) {
|
|
31
|
-
super('tabs-container');
|
|
32
|
-
this.$tabs = $tabs;
|
|
33
|
-
}
|
|
34
|
-
|
|
35
|
-
mounted($parent: $Node) {
|
|
36
|
-
if (_instanceof($parent, Tabs)) this.$tabs = $parent;
|
|
37
|
-
if (this.$tabs) {
|
|
38
|
-
this.$tabs.$container = this;
|
|
39
|
-
this.content(this.contentMap.get(this.$tabs.value() ?? ''))
|
|
40
|
-
}
|
|
41
|
-
return this;
|
|
42
|
-
}
|
|
43
|
-
}
|
|
44
|
-
|
|
45
|
-
export class TabsContent extends $HTMLElement {
|
|
46
|
-
#value: string;
|
|
47
|
-
$container: null | TabsContainer = null;
|
|
48
|
-
constructor(value: string) {
|
|
49
|
-
super('tabs-content');
|
|
50
|
-
this.#value = value
|
|
51
|
-
}
|
|
52
|
-
|
|
53
|
-
value(): string;
|
|
54
|
-
value(value: string): this;
|
|
55
|
-
value(value?: string) {
|
|
56
|
-
return chain(this, arguments, () => this.#value, value, value => {
|
|
57
|
-
this.#value = value;
|
|
58
|
-
this.$container?.contentMap.set(value, this).delete(this.#value ?? '')
|
|
59
|
-
})
|
|
60
|
-
}
|
|
61
|
-
|
|
62
|
-
mounted($parent: $Node) {
|
|
63
|
-
if (!_instanceof($parent, TabsContainer)) return this;
|
|
64
|
-
if ($parent && this.#value) {
|
|
65
|
-
this.$container = $parent;
|
|
66
|
-
$parent.contentMap.set(this.#value, this);
|
|
67
|
-
}
|
|
68
|
-
return this;
|
|
69
|
-
}
|
|
70
|
-
}
|
|
71
|
-
|
|
72
|
-
export class TabsList extends $HTMLElement {
|
|
73
|
-
$tabs?: null | Tabs = null;
|
|
74
|
-
triggers = new Map<string, TabsTrigger>();
|
|
75
|
-
constructor($tabs?: Tabs) {
|
|
76
|
-
super('tabs-list');
|
|
77
|
-
this.$tabs = $tabs;
|
|
78
|
-
this.on('click', _ => this.check())
|
|
79
|
-
}
|
|
80
|
-
|
|
81
|
-
check() {
|
|
82
|
-
this.triggers.forEach($trigger => $trigger.attr({selected: $trigger.value() === this.$tabs?.value() ? '' : null}))
|
|
83
|
-
return this;
|
|
84
|
-
}
|
|
85
|
-
|
|
86
|
-
mounted($parent: $Node): this {
|
|
87
|
-
if (_instanceof($parent, Tabs)) this.$tabs = $parent, $parent.$list = this;
|
|
88
|
-
if (this.$tabs) this.childNodes.forEach(child => $(child).is(TabsTrigger)?.use($child => {
|
|
89
|
-
this.triggers.set($child.value(), $child);
|
|
90
|
-
$child.$tabs = this.$tabs;
|
|
91
|
-
}));
|
|
92
|
-
return this;
|
|
93
|
-
}
|
|
94
|
-
}
|
|
95
|
-
|
|
96
|
-
export class TabsTrigger extends $HTMLElement {
|
|
97
|
-
#value: string;
|
|
98
|
-
$tabs?: null | Tabs = null;
|
|
99
|
-
constructor(value: string) {
|
|
100
|
-
super('tabs-trigger');
|
|
101
|
-
this.#value = value;
|
|
102
|
-
this.on('click', _ => this.$tabs?.value(this.#value ?? undefined))
|
|
103
|
-
}
|
|
104
|
-
|
|
105
|
-
value(): string;
|
|
106
|
-
value(value: string): this;
|
|
107
|
-
value(value?: string) {
|
|
108
|
-
return chain(this, arguments, () => this.#value, value, value => this.#value = value)
|
|
109
|
-
}
|
|
110
|
-
}
|
|
111
|
-
|
|
112
|
-
function chain<T, R, V>(_this: T, args: IArguments, get: () => R, value: V, set: (value: Exclude<V, undefined>) => any) {
|
|
113
|
-
return !args.length ? get() : isUndefined(value) ? _this : (set(value as any), _this);
|
|
114
|
-
}
|
package/ext/ui/node/Toast.ts
DELETED
|
@@ -1,16 +0,0 @@
|
|
|
1
|
-
import { _document } from "../../../src/lib/env";
|
|
2
|
-
import { $HTMLElement } from "../../../src/node/$HTMLElement";
|
|
3
|
-
|
|
4
|
-
$.style('toast{position:absolute}')
|
|
5
|
-
|
|
6
|
-
export class Toast extends $HTMLElement {
|
|
7
|
-
constructor() {
|
|
8
|
-
super('toast');
|
|
9
|
-
}
|
|
10
|
-
|
|
11
|
-
popup(duration = 3000) {
|
|
12
|
-
$(_document.body).insert(this);
|
|
13
|
-
setTimeout(() => this.remove(), duration)
|
|
14
|
-
return this;
|
|
15
|
-
}
|
|
16
|
-
}
|
package/ext/ui/node/Waterfall.ts
DELETED
|
@@ -1,73 +0,0 @@
|
|
|
1
|
-
import { _Array_from, _instanceof, equal, forEach, isNumber } from "amateras/lib/native";
|
|
2
|
-
import { $HTMLElement } from "amateras/node/$HTMLElement";
|
|
3
|
-
import { $Element } from "amateras/node/$Element";
|
|
4
|
-
import { $Virtual } from "amateras/node/$Virtual";
|
|
5
|
-
|
|
6
|
-
const getRect = (el: $Element) => el.getBoundingClientRect();
|
|
7
|
-
const px = (value: number) => `${value}px`;
|
|
8
|
-
$.style(`waterfall { display: block; position: relative }`)
|
|
9
|
-
|
|
10
|
-
export class Waterfall extends $Virtual {
|
|
11
|
-
#column = 1;
|
|
12
|
-
#gap = 0;
|
|
13
|
-
#width = 0;
|
|
14
|
-
constructor() {
|
|
15
|
-
super('waterfall');
|
|
16
|
-
new ResizeObserver(_ => this.inDOM() && this.#width !== getRect(this).width && (this.dispatchEvent(new Event('resize', {cancelable: true})) && this.layout())).observe(this.node);
|
|
17
|
-
}
|
|
18
|
-
|
|
19
|
-
column(): number;
|
|
20
|
-
column(column: $Parameter<number>): this;
|
|
21
|
-
column(column?: $Parameter<number>) {
|
|
22
|
-
if (!arguments.length) return this.#column;
|
|
23
|
-
if (isNumber(column)) this.#column = column;
|
|
24
|
-
return this;
|
|
25
|
-
}
|
|
26
|
-
|
|
27
|
-
gap(): number;
|
|
28
|
-
gap(gap: $Parameter<number>): this;
|
|
29
|
-
gap(gap?: $Parameter<number>) {
|
|
30
|
-
if (!arguments.length) return this.#gap;
|
|
31
|
-
if (isNumber(gap)) this.#gap = gap;
|
|
32
|
-
return this;
|
|
33
|
-
}
|
|
34
|
-
|
|
35
|
-
layout() {
|
|
36
|
-
const items = _Array_from(this.nodes).map(item => item);
|
|
37
|
-
const { width } = getRect(this);
|
|
38
|
-
this.#width = width;
|
|
39
|
-
const columnCount = this.#column;
|
|
40
|
-
const gap = this.#gap;
|
|
41
|
-
const columnWidth = ((width - ((columnCount - 1) * gap)) / columnCount);
|
|
42
|
-
const columns = _Array_from({length: columnCount}).map((_, i) => ({ i, h: 0, x: i * (columnWidth + gap) }))
|
|
43
|
-
const getColumnByHeight = (i: number) => columns.sort((a, b) => a.h - b.h ? a.h - b.h : a.i - b.i).at(i) as typeof columns[number];
|
|
44
|
-
forEach(items, item => {
|
|
45
|
-
if (!_instanceof(item, $HTMLElement)) return;
|
|
46
|
-
item.attr();
|
|
47
|
-
const { height, width } = item.attr();
|
|
48
|
-
const shortestSection = getColumnByHeight(0);
|
|
49
|
-
item.style({
|
|
50
|
-
position: 'absolute',
|
|
51
|
-
top: px(shortestSection.h),
|
|
52
|
-
left: px(shortestSection.x),
|
|
53
|
-
width: px(columnWidth),
|
|
54
|
-
});
|
|
55
|
-
if (width && height) {
|
|
56
|
-
// get ratio from attributes and calculate item's height
|
|
57
|
-
let itemHeight = columnWidth / (+width / +height);
|
|
58
|
-
item.style({ height: px(itemHeight) })
|
|
59
|
-
shortestSection.h += +itemHeight + gap;
|
|
60
|
-
} else {
|
|
61
|
-
item.style({ visibility: 'hidden', height: '' })
|
|
62
|
-
if (this.hiddenNodes.has(item)) this.show(item).render().hide(item);
|
|
63
|
-
let itemHeight = getRect(item).height;
|
|
64
|
-
item.style({visibility: '', height: px(itemHeight)})
|
|
65
|
-
shortestSection.h += +itemHeight + gap;
|
|
66
|
-
}
|
|
67
|
-
})
|
|
68
|
-
this.render();
|
|
69
|
-
this.style({ height: px(getColumnByHeight(-1).h) });
|
|
70
|
-
this.dispatchEvent(new Event('layout'))
|
|
71
|
-
return this;
|
|
72
|
-
}
|
|
73
|
-
}
|
package/ext/ui/package.json
DELETED
package/src/node/$Virtual.ts
DELETED
|
@@ -1,58 +0,0 @@
|
|
|
1
|
-
import { forEach, _Array_from, isUndefined, _instanceof } from "#lib/native";
|
|
2
|
-
import { $HTMLElement } from "#node/$HTMLElement";
|
|
3
|
-
import { $Node, type $NodeContentResolver } from "#node/$Node";
|
|
4
|
-
|
|
5
|
-
export class $Virtual<Ele extends HTMLElement = HTMLElement, EvMap = HTMLElementEventMap> extends $HTMLElement<Ele, EvMap> {
|
|
6
|
-
nodes = new Set<$Node>;
|
|
7
|
-
hiddenNodes = new Set<$Node>;
|
|
8
|
-
constructor(resolver: string | Ele) {
|
|
9
|
-
super(resolver);
|
|
10
|
-
}
|
|
11
|
-
|
|
12
|
-
content(children: $NodeContentResolver<this>) {
|
|
13
|
-
this.nodes.clear();
|
|
14
|
-
forEach(_Array_from(this.childNodes), node => node.remove());
|
|
15
|
-
return this.insert(children);
|
|
16
|
-
}
|
|
17
|
-
|
|
18
|
-
insert(resolver: $NodeContentResolver<this>, position = -1) {
|
|
19
|
-
// process nodes
|
|
20
|
-
forEach($.toArray(resolver), resolve_child => {
|
|
21
|
-
forEach($Node.process(this, resolve_child), $node => $Virtual.append(this, $node, position))
|
|
22
|
-
});
|
|
23
|
-
this.render();
|
|
24
|
-
return this;
|
|
25
|
-
}
|
|
26
|
-
|
|
27
|
-
hide($node?: $Node | null) {
|
|
28
|
-
if (!$node || !this.nodes.has($node)) return this;
|
|
29
|
-
this.hiddenNodes.add($node);
|
|
30
|
-
return this;
|
|
31
|
-
}
|
|
32
|
-
|
|
33
|
-
show($node?: $Node | null) {
|
|
34
|
-
if (!$node) return this;
|
|
35
|
-
this.hiddenNodes.delete($node);
|
|
36
|
-
return this;
|
|
37
|
-
}
|
|
38
|
-
|
|
39
|
-
render() {
|
|
40
|
-
// remove hidden node
|
|
41
|
-
forEach(_Array_from(this.childNodes), node => this.hiddenNodes.has($(node)) && node.remove());
|
|
42
|
-
// add visible node with position
|
|
43
|
-
forEach(_Array_from(this.nodes), ($node, i) => {
|
|
44
|
-
if (this.hiddenNodes.has($node)) return;
|
|
45
|
-
if (_Array_from(this.childNodes).at(i) !== $node.node) $Node.append(this, $node, i);
|
|
46
|
-
})
|
|
47
|
-
return this;
|
|
48
|
-
}
|
|
49
|
-
|
|
50
|
-
static append($node: $Virtual, child: $Node | undefined | null, position: number) {
|
|
51
|
-
if (!child) return;
|
|
52
|
-
const childList = _Array_from($node.nodes);
|
|
53
|
-
let $positionChild = childList.at(position);
|
|
54
|
-
if (!$positionChild) childList.push(child);
|
|
55
|
-
else childList.splice(position >= 0 ? position : childList.length + 1 + position, 0, child);
|
|
56
|
-
$node.nodes = new Set(childList);
|
|
57
|
-
}
|
|
58
|
-
}
|