amateras 0.4.2 → 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.
Files changed (97) hide show
  1. package/README.md +24 -25
  2. package/ext/html/node/$Anchor.ts +3 -3
  3. package/ext/html/node/$Canvas.ts +2 -2
  4. package/ext/html/node/$Dialog.ts +2 -2
  5. package/ext/html/node/$Form.ts +2 -2
  6. package/ext/html/node/$Image.ts +2 -2
  7. package/ext/html/node/$Input.ts +28 -4
  8. package/ext/html/node/$Label.ts +12 -3
  9. package/ext/html/node/$Media.ts +2 -2
  10. package/ext/html/node/$OptGroup.ts +2 -2
  11. package/ext/html/node/$Option.ts +2 -2
  12. package/ext/html/node/$Select.ts +2 -2
  13. package/ext/html/node/$TextArea.ts +2 -2
  14. package/ext/i18n/README.md +20 -0
  15. package/ext/i18n/src/index.ts +106 -12
  16. package/ext/i18n/src/structure/I18n.ts +12 -8
  17. package/ext/i18n/src/structure/I18nDictionary.ts +2 -2
  18. package/ext/i18n/src/structure/I18nTranslation.ts +35 -0
  19. package/ext/idb/README.md +127 -0
  20. package/ext/idb/package.json +13 -0
  21. package/ext/idb/src/core.ts +6 -0
  22. package/ext/idb/src/index.ts +17 -0
  23. package/ext/idb/src/lib/$IDBRequest.ts +8 -0
  24. package/ext/idb/src/structure/$IDB.ts +63 -0
  25. package/ext/idb/src/structure/$IDBCursor.ts +34 -0
  26. package/ext/idb/src/structure/$IDBIndex.ts +48 -0
  27. package/ext/idb/src/structure/$IDBStore.ts +103 -0
  28. package/ext/idb/src/structure/$IDBStoreBase.ts +30 -0
  29. package/ext/idb/src/structure/$IDBTransaction.ts +38 -0
  30. package/ext/idb/src/structure/builder/$IDBBuilder.ts +230 -0
  31. package/ext/idb/src/structure/builder/$IDBStoreBuilder.ts +100 -0
  32. package/ext/markdown/README.md +53 -0
  33. package/ext/markdown/package.json +15 -0
  34. package/ext/markdown/src/index.ts +3 -0
  35. package/ext/markdown/src/lib/type.ts +26 -0
  36. package/ext/markdown/src/lib/util.ts +21 -0
  37. package/ext/markdown/src/structure/Markdown.ts +54 -0
  38. package/ext/markdown/src/structure/MarkdownLexer.ts +111 -0
  39. package/ext/markdown/src/structure/MarkdownParser.ts +33 -0
  40. package/ext/markdown/src/syntax/alert.ts +46 -0
  41. package/ext/markdown/src/syntax/blockquote.ts +35 -0
  42. package/ext/markdown/src/syntax/bold.ts +11 -0
  43. package/ext/markdown/src/syntax/code.ts +11 -0
  44. package/ext/markdown/src/syntax/codeblock.ts +44 -0
  45. package/ext/markdown/src/syntax/heading.ts +14 -0
  46. package/ext/markdown/src/syntax/horizontalRule.ts +11 -0
  47. package/ext/markdown/src/syntax/image.ts +23 -0
  48. package/ext/markdown/src/syntax/italic.ts +11 -0
  49. package/ext/markdown/src/syntax/link.ts +46 -0
  50. package/ext/markdown/src/syntax/list.ts +121 -0
  51. package/ext/markdown/src/syntax/table.ts +67 -0
  52. package/ext/markdown/src/syntax/text.ts +19 -0
  53. package/ext/router/README.md +111 -17
  54. package/ext/router/package.json +10 -0
  55. package/ext/router/src/index.ts +69 -0
  56. package/ext/router/src/node/Page.ts +34 -0
  57. package/ext/router/src/node/Router.ts +191 -0
  58. package/ext/router/src/node/RouterAnchor.ts +24 -0
  59. package/ext/router/src/structure/PageBuilder.ts +24 -0
  60. package/ext/router/src/structure/Route.ts +105 -0
  61. package/ext/signal/README.md +93 -0
  62. package/ext/signal/package.json +9 -0
  63. package/ext/signal/src/index.ts +128 -0
  64. package/{src → ext/signal/src}/structure/Signal.ts +7 -11
  65. package/ext/ssr/index.ts +4 -4
  66. package/ext/ui/lib/VirtualScroll.ts +25 -0
  67. package/ext/ui/node/Accordian.ts +97 -0
  68. package/ext/ui/node/Form.ts +53 -0
  69. package/ext/ui/node/Grid.ts +0 -0
  70. package/ext/ui/node/Table.ts +43 -0
  71. package/ext/ui/node/Tabs.ts +114 -0
  72. package/ext/ui/node/Toast.ts +16 -0
  73. package/ext/ui/node/Waterfall.ts +72 -0
  74. package/ext/ui/package.json +11 -0
  75. package/package.json +9 -3
  76. package/src/core.ts +31 -59
  77. package/src/global.ts +12 -2
  78. package/src/index.ts +1 -2
  79. package/src/lib/assignProperties.ts +57 -0
  80. package/src/lib/native.ts +33 -11
  81. package/src/lib/sleep.ts +3 -1
  82. package/src/lib/toArray.ts +9 -0
  83. package/src/lib/trycatch.ts +17 -0
  84. package/src/lib/uppercase.ts +3 -0
  85. package/src/node/$Element.ts +7 -53
  86. package/src/node/$EventTarget.ts +45 -0
  87. package/src/node/$Node.ts +63 -55
  88. package/src/node/$Virtual.ts +65 -0
  89. package/src/node.ts +7 -6
  90. package/ext/i18n/src/node/I18nText.ts +0 -35
  91. package/ext/router/index.ts +0 -73
  92. package/ext/router/node/Page.ts +0 -27
  93. package/ext/router/node/Route.ts +0 -54
  94. package/ext/router/node/Router.ts +0 -149
  95. package/ext/router/node/RouterAnchor.ts +0 -8
  96. package/src/lib/assign.ts +0 -38
  97. package/src/lib/assignHelper.ts +0 -18
@@ -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}
@@ -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>
@@ -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
- }
@@ -1,54 +0,0 @@
1
- import { _instanceof, _Object_fromEntries, _Array_from } 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
- }
@@ -1,149 +0,0 @@
1
- import type { AnchorTarget } from "#html/$Anchor";
2
- import { _document } from "#lib/env";
3
- import { _Array_from, _instanceof, _JSON_parse, _JSON_stringify, _Object_entries, _Object_fromEntries, forEach, startsWith } from "#lib/native";
4
- import { Page } from "./Page";
5
- import { BaseRouteNode, Route } from "./Route";
6
-
7
- // history index
8
- let index = 0;
9
- const _addEventListener = addEventListener;
10
- const _location = location;
11
- const {origin} = _location;
12
- const _history = history;
13
- const _sessionStorage = sessionStorage;
14
- const documentElement = _document.documentElement;
15
- const [PUSH, REPLACE] = [1, 2] as const;
16
- const [FORWARD, BACK] = ['forward', 'back'] as const;
17
- const scrollStorageKey = '__scroll__';
18
- /** convert path string to URL object */
19
- const toURL = (path: string | URL) =>
20
- _instanceof(path, URL) ? path : startsWith(path, 'http') ? new URL(path) : new URL(startsWith(path, origin) ? path : origin + path);
21
-
22
- type ScrollData = {[key: number]: {x: number, y: number}};
23
- const scrollRecord = (e?: Event) => {
24
- const data = _JSON_parse(_sessionStorage.getItem(scrollStorageKey) ?? '{}') as ScrollData;
25
- data[index] = { x: documentElement.scrollLeft, y: documentElement.scrollTop };
26
- // e is Event when called from scroll or beforeload
27
- if (!e) forEach(_Object_entries(data), ([i]) => +i > index && delete data[+i])
28
- _sessionStorage.setItem(scrollStorageKey, _JSON_stringify(data));
29
- }
30
- /** handle history state with push and replace state. */
31
- const historyHandler = async (path: string | URL | Nullish, mode: 1 | 2, target?: AnchorTarget) => {
32
- if (!path) return;
33
- const url = toURL(path);
34
- if (url.href === _location.href) return;
35
- if (target && target !== '_self') return open(url, target);
36
- if (url.origin !== origin) return open(url, target);
37
- scrollRecord();
38
- if (mode === PUSH) index += 1;
39
- Router.direction = FORWARD;
40
- _history[mode === PUSH ? 'pushState' : 'replaceState']({index}, '' , url);
41
- for (let router of Router.routers) router.routes.size && await router.resolve(path);
42
- }
43
- // disable browser scroll restoration
44
- _history.scrollRestoration = 'manual';
45
-
46
- export class Router extends BaseRouteNode<''> {
47
- static pageRouters = new Map<Page, Router>();
48
- static routers = new Set<Router>();
49
- pageMap = new Map<string, Page>();
50
- static direction: 'back' | 'forward' = FORWARD;
51
- constructor(page?: Page) {
52
- super('', () => [], 'router')
53
- Router.routers.add(this);
54
- if (page) Router.pageRouters.set(page, this);
55
- }
56
-
57
- static open(path: string | URL | Nullish, target?: AnchorTarget) {
58
- historyHandler(path, PUSH, target);
59
- return this;
60
- }
61
-
62
- static back() {
63
- _history.back();
64
- return this;
65
- }
66
-
67
- static forward() {
68
- _history.forward();
69
- return this;
70
- }
71
-
72
- static replace(path: string | URL | Nullish) {
73
- historyHandler(path, REPLACE);
74
- return this;
75
- }
76
-
77
- async resolve(path: string | URL) {
78
- const {pathname, searchParams, hash, href} = toURL(path);
79
- const routeData = { params: {} as {[key: string]: string}, query: _Object_fromEntries(searchParams) }
80
- const split = (p: string) => p.replaceAll(/\/+/g, '/').split('/').map(path => `/${path}`);
81
-
82
- function determineRoute(parentRoute: BaseRouteNode<any>, path: string, hash: string | undefined): [route: Route | null, pathId: string][] {
83
- const targetPathSplit = split(path);
84
- hash && targetPathSplit.push(hash);
85
- if (!parentRoute.routes.size) return [];
86
- routeLoop: for (const route of _Array_from(parentRoute.routes.values()).sort((a, b) => b.path.length - a.path.length)) {
87
- const routePathSplit = split(route.path);
88
- let pathId = '';
89
- splitLoop: for (let i = 0; i < routePathSplit.length; i++) {
90
- const pass = () => pathId += targetSnippet;
91
- const [routeSnippet, targetSnippet] = [routePathSplit[i], targetPathSplit[i]];
92
- if (!routeSnippet || !targetSnippet) continue routeLoop;
93
- // process params in path
94
- if (routeSnippet.includes(':')) {
95
- if (targetSnippet === '/') continue routeLoop;
96
- const [prefix, paramName] = routeSnippet.split(':') as [string, string];
97
- if (!startsWith(targetSnippet, prefix)) continue routeLoop;
98
- routeData.params[paramName] = targetSnippet.replace(`${prefix}`, '');
99
- pass();
100
- continue splitLoop;
101
- }
102
- if (routeSnippet === '/' && route.routes.size) continue splitLoop;
103
- if (routeSnippet !== targetSnippet) continue routeLoop;
104
- pass()
105
- }
106
- return [[route, pathId], ...determineRoute(route, path, hash)];
107
- }
108
- return [[null, parentRoute.path + '$$NOT_FOUND$$']];
109
- }
110
- // analytics
111
- const targetRoutes = determineRoute(this, pathname + '/', hash);
112
- // build pages
113
- let prevPage: null | Page = null, prevRoute: BaseRouteNode<any> = this;
114
- const appendPage = (prevRouter: Router | undefined, page: Page) => page.parentNode !== prevRouter?.node && prevRouter?.content(page);
115
-
116
- for (const [route, pathId] of targetRoutes) {
117
- const page = this.pageMap.get(pathId) ?? new Page(route ?? prevRoute.routes.get('404') ?? new Route('404', () => null), routeData);
118
- if (!page.initial) await route?.build(routeData, page);
119
- _document && (_document.title = page.pageTitle() ?? _document.title);
120
- this.pageMap.set(pathId, page);
121
- if (href === _location.href) appendPage(prevPage ? Router.pageRouters.get(prevPage) : this, page);
122
- prevPage = page;
123
- if (route) prevRoute = route;
124
- }
125
- let { x, y } = Router.scroll ?? {x: 0, y: 0};
126
- scrollTo(x, y);
127
- this.dispatchEvent(new Event('routeopen', {bubbles: true}));
128
- return this;
129
- }
130
-
131
- listen() {
132
- const resolve = () => {
133
- const stateIndex = _history.state?.index ?? 0;
134
- if (index > stateIndex) Router.direction = BACK;
135
- if (index < stateIndex) Router.direction = FORWARD;
136
- index = stateIndex;
137
- this.resolve(_location.href);
138
- }
139
- _addEventListener('popstate', resolve);
140
- _addEventListener('beforeunload', scrollRecord);
141
- _addEventListener('scroll', scrollRecord, false);
142
- resolve();
143
- return this;
144
- }
145
-
146
- static get scroll(): ScrollData[number] {
147
- return _JSON_parse(_sessionStorage.getItem(scrollStorageKey) ?? '{}')[index] ?? {x: 0, y: 0}
148
- }
149
- }
@@ -1,8 +0,0 @@
1
- import { $Anchor } from "#html/$Anchor";
2
-
3
- export class RouterAnchor extends $Anchor {
4
- constructor() {
5
- super();
6
- this.on('click', e => { e.preventDefault(); $.open(this.href(), this.target()) })
7
- }
8
- }
package/src/lib/assign.ts DELETED
@@ -1,38 +0,0 @@
1
- import { Signal } from "../structure/Signal";
2
- import { _instanceof, _Object_defineProperty, forEach, isUndefined } from "./native";
3
-
4
- export const assign = (target: any, {set, get, fn}: {
5
- set?: string[],
6
- get?: string[],
7
- fn?: string[]
8
- }) => {
9
- const [GET, SET, FN] = ['get', 'set', 'fn'] as const;
10
- const filterAndMap = (type: 'get' | 'set' | 'fn', arr: string[] | undefined) => arr?.map(prop => [type, prop]) ?? []
11
- const list = [...filterAndMap(GET, get), ...filterAndMap(SET, set), ...filterAndMap(FN, fn)] as [string, string][];
12
- forEach(list, ([type, prop]) =>
13
- _Object_defineProperty(target.prototype, prop, {
14
- ...(type === GET ? {
15
- get() { return this.node[prop as any] }
16
- } : {
17
- writable: true,
18
- ...(type === SET ? {
19
- // set
20
- value(this, args: any) {
21
- if (!arguments.length) return this.node[prop];
22
- let set = (value: any) => !isUndefined(value) && (this.node[prop] = value);
23
- if (_instanceof(args, Signal)) args = args.subscribe(set).value();
24
- set(args)
25
- return this;
26
- }
27
- } : {
28
- // fn
29
- value(this, ...args : any[]) {
30
- let result = this.node[prop](...args)
31
- return isUndefined(result) ? this : result;
32
- }
33
- })
34
- }),
35
-
36
- })
37
- )
38
- }
@@ -1,18 +0,0 @@
1
- import type { $Node } from "#node/$Node";
2
- import { assign } from "./assign";
3
- import { _Object_entries, _Object_getOwnPropertyDescriptors, forEach } from "./native";
4
-
5
- export const assignHelper = (object: Constructor<EventTarget>, target: Constructor<$Node>, tagname?: string) => {
6
- const [set, get, fn] = [[], [], []] as [string[], string[], string[]]
7
- // assign native object properties to target
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
- })
15
- assign(target, {set, get, fn})
16
- // register tagname
17
- if (tagname) $.assign(tagname, target)
18
- }