amateras 0.4.0 → 0.4.1

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 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.71 kB | 2.48 kB | Core |
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 |
@@ -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.bind(Router),
34
- replace: Router.replace.bind(Router),
35
- back: Router.back.bind(Router),
36
- forward: Router.forward.bind(Router)
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([
@@ -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
- if (!arguments.length) return this.#pageTitle;
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
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "amateras",
3
- "version": "0.4.0",
3
+ "version": "0.4.1",
4
4
  "description": "Amateras is a DOM Utility library.",
5
5
  "module": "index.ts",
6
6
  "type": "module",
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.bind(_stylesheet);
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 function signal<T>(value: T): SignalFunction<T>
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 function compute<T>(process: () => T) {
76
+ export const compute = <T>(process: () => T): ComputeFunction<T> => {
78
77
  let subscribed = false;
79
- const signalFn: SignalFunction<any> = signal(null);
80
- function computeFn() {
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
- function subscribe () {
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
- export function assign(resolver: [nodeName: string, $node: Constructor<$Node>][]): void;
99
- export function assign(nodeName: string, $node: Constructor<$Node>): void;
100
- export function assign(resolver: string | [nodeName: string, $node: Constructor<$Node>][], $node?: Constructor<$Node>) {
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 function toArray<T>(item: OrArray<T>): T[] {
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 function span(content: string) {
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
- for (const [type, prop] of list) {
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
  }
@@ -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
- 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);
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) => !args.length ? get() : isUndefined(value) ? _this : (set(value as any), _this);
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 => _typeof(target, 'undefined')
19
- export const isNull = (target: any): target is null => target === null;
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;
@@ -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
@@ -1,3 +1 @@
1
- export const sleep = async (ms: number) => {
2
- return new Promise(resolve => setTimeout(resolve, ms))
3
- }
1
+ export const sleep = async (ms: number) => new Promise(resolve => setTimeout(resolve, ms));
@@ -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): void;
390
- addEventListener(type: string, listener: $EventListener<this, Event> | $EventListenerObject<this, Event>, options?: boolean | AddEventListenerOptions): void;
391
- removeEventListener<K extends keyof EvMap, Ev extends EvMap[K]>(type: K, listener: $EventListener<this, Ev> | $EventListenerObject<this, Ev>, options?: boolean | EventListenerOptions): void;
392
- removeEventListener(type: string, listener: $EventListener<this, Event> | $EventListenerObject<this, Event>, options?: boolean | EventListenerOptions): void;
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;
@@ -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 (!style) return this;
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,3 +1,4 @@
1
+ import { chain } from "#lib/chain";
1
2
  import { _document } from "#lib/env";
2
3
  import { _Array_from, _instanceof, _JSON_stringify, forEach, isFunction, isNull, isObject, isUndefined } from "#lib/native";
3
4
  import { Signal } from "#structure/Signal";
@@ -11,9 +12,10 @@ export class $Node {
11
12
  }
12
13
 
13
14
  content(children: $NodeContentResolver<this>) {
14
- if (isUndefined(children)) return this;
15
- forEach(_Array_from(this.childNodes), node => node.remove());
16
- return this.insert(children);
15
+ return chain(this, null, null, children, children => {
16
+ forEach(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)) return promise.then(result => callback(this, result)), this;
27
- else return callback(this, promise), this;
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 (!$node) return this;
32
- this.replaceWith(
33
- ...$.toArray($Node.process(this, $node)).filter($node => $node).map($node => $node?.node) as Node[]
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
- // insert each child, child may be an array
101
- if (!child) return;
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);
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
 
@@ -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
- }
@@ -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
- }
@@ -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
- }
@@ -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
- }
@@ -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
- }
@@ -1,11 +0,0 @@
1
- {
2
- "name": "@amateras/ui",
3
- "peerDependencies": {
4
- "amateras": "../../"
5
- },
6
- "exports": {
7
- "./waterfall": "./node/Waterfall.ts",
8
- "./tabs": "./node/Tabs.ts",
9
- "./virtual-scroll": "./lib/VirtualScroll.ts"
10
- }
11
- }
@@ -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
- }