amateras 0.10.0 → 0.10.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 +29 -25
- package/build/core.js +1 -0
- package/build/css.js +1 -0
- package/build/for.js +1 -0
- package/build/i18n.js +1 -0
- package/build/idb.js +1 -0
- package/build/if.js +1 -0
- package/build/import-map.js +1 -0
- package/build/markdown.js +1 -0
- package/build/match.js +1 -0
- package/build/meta.js +1 -0
- package/build/prefetch.js +1 -0
- package/build/router.js +1 -0
- package/build/signal.js +1 -0
- package/build/ui.js +1 -0
- package/build/widget.js +1 -0
- package/package.json +7 -5
- package/packages/core/package.json +19 -0
- package/packages/core/src/env.browser.ts +21 -0
- package/packages/core/src/env.node.ts +21 -0
- package/packages/core/src/global.ts +5 -0
- package/packages/core/src/index.ts +184 -0
- package/packages/core/src/lib/hmr.ts +145 -0
- package/packages/core/src/lib/symbols.ts +2 -0
- package/packages/core/src/structure/ElementProto.ts +95 -0
- package/packages/core/src/structure/GlobalState.ts +9 -0
- package/packages/core/src/structure/NodeProto.ts +18 -0
- package/packages/core/src/structure/Proto.ts +90 -0
- package/packages/core/src/structure/ProxyProto.ts +20 -0
- package/packages/core/src/structure/TextProto.ts +22 -0
- package/packages/core/src/structure/WidgetEvent.ts +17 -0
- package/packages/css/README.md +128 -0
- package/packages/css/package.json +15 -0
- package/packages/css/src/ext/colors/amber.ts +25 -0
- package/packages/css/src/ext/colors/blackwhite.ts +13 -0
- package/packages/css/src/ext/colors/blue.ts +25 -0
- package/packages/css/src/ext/colors/cyan.ts +25 -0
- package/packages/css/src/ext/colors/emerald.ts +25 -0
- package/packages/css/src/ext/colors/fuchsia.ts +25 -0
- package/packages/css/src/ext/colors/gray.ts +25 -0
- package/packages/css/src/ext/colors/green.ts +25 -0
- package/packages/css/src/ext/colors/indigo.ts +25 -0
- package/packages/css/src/ext/colors/lime.ts +25 -0
- package/packages/css/src/ext/colors/neutral.ts +25 -0
- package/packages/css/src/ext/colors/orange.ts +25 -0
- package/packages/css/src/ext/colors/pink.ts +25 -0
- package/packages/css/src/ext/colors/purple.ts +25 -0
- package/packages/css/src/ext/colors/red.ts +25 -0
- package/packages/css/src/ext/colors/rose.ts +25 -0
- package/packages/css/src/ext/colors/sky.ts +25 -0
- package/packages/css/src/ext/colors/slate.ts +25 -0
- package/packages/css/src/ext/colors/stone.ts +25 -0
- package/packages/css/src/ext/colors/teal.ts +25 -0
- package/packages/css/src/ext/colors/violet.ts +25 -0
- package/packages/css/src/ext/colors/yellow.ts +25 -0
- package/packages/css/src/ext/colors/zinc.ts +25 -0
- package/packages/css/src/ext/colors.ts +23 -0
- package/packages/css/src/ext/keyframes.ts +37 -0
- package/packages/css/src/ext/property.ts +68 -0
- package/packages/css/src/ext/variable.ts +51 -0
- package/packages/css/src/index.ts +103 -0
- package/packages/css/src/lib/cache.ts +27 -0
- package/packages/css/src/lib/colorAssign.ts +6 -0
- package/packages/css/src/lib/createRule.ts +31 -0
- package/packages/css/src/lib/utils.ts +1 -0
- package/packages/css/src/structure/$CSS.ts +4 -0
- package/packages/css/src/structure/$CSSKeyframes.ts +13 -0
- package/packages/css/src/structure/$CSSProperty.ts +21 -0
- package/packages/css/src/structure/$CSSRule.ts +39 -0
- package/packages/css/src/structure/$CSSVariable.ts +34 -0
- package/packages/css/src/types.ts +300 -0
- package/packages/for/package.json +15 -0
- package/packages/for/src/global.ts +7 -0
- package/packages/for/src/index.ts +15 -0
- package/packages/for/src/structure/For.ts +74 -0
- package/packages/hmr/package.json +13 -0
- package/packages/hmr/src/index.ts +27 -0
- package/packages/i18n/README.md +73 -0
- package/packages/i18n/package.json +15 -0
- package/packages/i18n/src/index.ts +78 -0
- package/packages/i18n/src/structure/I18n.ts +51 -0
- package/packages/i18n/src/structure/I18nDictionary.ts +31 -0
- package/packages/i18n/src/structure/I18nTranslation.ts +51 -0
- package/packages/i18n/src/types.ts +77 -0
- package/packages/idb/README.md +127 -0
- package/packages/idb/package.json +16 -0
- package/packages/idb/src/core.ts +6 -0
- package/packages/idb/src/index.ts +17 -0
- package/packages/idb/src/lib/$IDBRequest.ts +8 -0
- package/packages/idb/src/structure/$IDB.ts +63 -0
- package/packages/idb/src/structure/$IDBCursor.ts +34 -0
- package/packages/idb/src/structure/$IDBIndex.ts +48 -0
- package/packages/idb/src/structure/$IDBStore.ts +103 -0
- package/packages/idb/src/structure/$IDBStoreBase.ts +30 -0
- package/packages/idb/src/structure/$IDBTransaction.ts +38 -0
- package/packages/idb/src/structure/builder/$IDBBuilder.ts +229 -0
- package/packages/idb/src/structure/builder/$IDBStoreBuilder.ts +100 -0
- package/packages/if/package.json +15 -0
- package/packages/if/src/global.ts +15 -0
- package/packages/if/src/index.ts +51 -0
- package/packages/if/src/structure/Condition.ts +44 -0
- package/packages/if/src/structure/ConditionStatement.ts +25 -0
- package/packages/if/src/structure/Else.ts +6 -0
- package/packages/if/src/structure/ElseIf.ts +6 -0
- package/packages/if/src/structure/If.ts +6 -0
- package/packages/markdown/README.md +53 -0
- package/packages/markdown/package.json +15 -0
- package/packages/markdown/src/index.ts +3 -0
- package/packages/markdown/src/lib/type.ts +26 -0
- package/packages/markdown/src/lib/util.ts +21 -0
- package/packages/markdown/src/structure/Markdown.ts +57 -0
- package/packages/markdown/src/structure/MarkdownLexer.ts +111 -0
- package/packages/markdown/src/structure/MarkdownParser.ts +34 -0
- package/packages/markdown/src/syntax/alert.ts +46 -0
- package/packages/markdown/src/syntax/blockquote.ts +35 -0
- package/packages/markdown/src/syntax/bold.ts +11 -0
- package/packages/markdown/src/syntax/code.ts +11 -0
- package/packages/markdown/src/syntax/codeblock.ts +44 -0
- package/packages/markdown/src/syntax/heading.ts +14 -0
- package/packages/markdown/src/syntax/horizontalRule.ts +11 -0
- package/packages/markdown/src/syntax/image.ts +23 -0
- package/packages/markdown/src/syntax/italic.ts +11 -0
- package/packages/markdown/src/syntax/link.ts +46 -0
- package/packages/markdown/src/syntax/list.ts +121 -0
- package/packages/markdown/src/syntax/table.ts +67 -0
- package/packages/markdown/src/syntax/text.ts +19 -0
- package/packages/match/package.json +15 -0
- package/packages/match/src/global.ts +14 -0
- package/packages/match/src/index.ts +33 -0
- package/packages/match/src/structure/Case.ts +15 -0
- package/packages/match/src/structure/Default.ts +12 -0
- package/packages/match/src/structure/Match.ts +78 -0
- package/packages/meta/package.json +14 -0
- package/packages/meta/src/index.ts +36 -0
- package/packages/meta/src/lib/resolveMeta.ts +27 -0
- package/packages/meta/src/types.ts +36 -0
- package/packages/prefetch/package.json +14 -0
- package/packages/prefetch/src/index.ts +70 -0
- package/packages/router/README.md +18 -0
- package/packages/router/package.json +16 -0
- package/packages/router/src/global.ts +22 -0
- package/packages/router/src/index.ts +106 -0
- package/packages/router/src/structure/Link.ts +17 -0
- package/packages/router/src/structure/NavLink.ts +19 -0
- package/packages/router/src/structure/Page.ts +30 -0
- package/packages/router/src/structure/Route.ts +123 -0
- package/packages/router/src/structure/RouteGroup.ts +24 -0
- package/packages/router/src/structure/RouteNode.ts +54 -0
- package/packages/router/src/structure/RouteSlot.ts +34 -0
- package/packages/router/src/structure/Router.ts +192 -0
- package/packages/router/src/structure/RouterConstructor.ts +18 -0
- package/packages/router/src/types.ts +41 -0
- package/packages/signal/README.md +93 -0
- package/packages/signal/package.json +15 -0
- package/packages/signal/src/index.ts +97 -0
- package/packages/signal/src/lib/track.ts +18 -0
- package/packages/signal/src/structure/Signal.ts +59 -0
- package/packages/ui/package.json +14 -0
- package/packages/ui/src/index.ts +4 -0
- package/packages/ui/src/lib/slideshowAnimations.ts +39 -0
- package/packages/ui/src/structure/Radio.ts +77 -0
- package/packages/ui/src/structure/Slide.ts +11 -0
- package/packages/ui/src/structure/Slideshow.ts +99 -0
- package/packages/utils/package.json +18 -0
- package/packages/utils/src/global.ts +39 -0
- package/packages/utils/src/index.bun.ts +3 -0
- package/packages/utils/src/index.ts +2 -0
- package/packages/utils/src/lib/debugger.ts +14 -0
- package/packages/utils/src/lib/utils.ts +119 -0
- package/packages/utils/src/structure/UID.ts +18 -0
- package/packages/widget/README.md +29 -0
- package/packages/widget/package.json +14 -0
- package/packages/widget/src/index.ts +82 -0
- package/packages/widget/src/structure/Widget.ts +42 -0
|
@@ -0,0 +1,74 @@
|
|
|
1
|
+
import { onclient } from "@amateras/core";
|
|
2
|
+
import { symbol_Statement } from "@amateras/core";
|
|
3
|
+
import { Proto } from "@amateras/core";
|
|
4
|
+
import { ProxyProto } from "@amateras/core";
|
|
5
|
+
import type { Signal } from "@amateras/signal";
|
|
6
|
+
import { forEach } from "@amateras/utils";
|
|
7
|
+
|
|
8
|
+
export type ForLayout<T> = (item: T, index: number) => void;
|
|
9
|
+
export type ForList<T extends object = object> = Signal<T[]> | Signal<Set<T>>
|
|
10
|
+
|
|
11
|
+
export class For<T extends object = object> extends ProxyProto {
|
|
12
|
+
static override [symbol_Statement] = true;
|
|
13
|
+
#layout: ForLayout<T>;
|
|
14
|
+
list$: ForList<T>;
|
|
15
|
+
#itemProtoMap = new WeakMap<T, ForItem>();
|
|
16
|
+
declare protos: Set<ForItem>;
|
|
17
|
+
constructor(list: ForList<T>, layout: ForLayout<T>) {
|
|
18
|
+
super();
|
|
19
|
+
this.list$ = list;
|
|
20
|
+
this.#layout = layout;
|
|
21
|
+
|
|
22
|
+
let update = () => {
|
|
23
|
+
let {n: newItemList, d: deleteItemList} = this.run();
|
|
24
|
+
forEach(newItemList, proto => proto.build());
|
|
25
|
+
// 如果 For node 没有 parentNode,代表 For 并不在 DOM 树中
|
|
26
|
+
// 跳过处理 DOM 的步骤
|
|
27
|
+
if (!this.node?.parentNode) return;
|
|
28
|
+
forEach(deleteItemList, proto => proto.removeNode());
|
|
29
|
+
let nodes = onclient() ? this.toDOM() : [];
|
|
30
|
+
let prevNode: Node | undefined
|
|
31
|
+
forEach(nodes, node => {
|
|
32
|
+
if (node.parentNode) prevNode = node;
|
|
33
|
+
else prevNode = prevNode?.parentNode?.insertBefore(node, prevNode.nextSibling)
|
|
34
|
+
})
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
this.list$.subscribe(update);
|
|
38
|
+
this.disposers.add(() => this.list$.unsubscribe(update));
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
override build() {
|
|
42
|
+
this.#itemProtoMap = new WeakMap();
|
|
43
|
+
this.run();
|
|
44
|
+
forEach(this.protos, itemProto => itemProto.build());
|
|
45
|
+
return this;
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
run() {
|
|
49
|
+
let newItemList: ForItem[] = [];
|
|
50
|
+
let oldItemList = new Set(this.protos);
|
|
51
|
+
this.clear();
|
|
52
|
+
forEach(this.list$.value, (item, i) => {
|
|
53
|
+
let itemProto = this.#itemProtoMap.get(item);
|
|
54
|
+
if (!itemProto) {
|
|
55
|
+
itemProto = new ForItem();
|
|
56
|
+
newItemList.push(itemProto);
|
|
57
|
+
this.#itemProtoMap.set(item, itemProto);
|
|
58
|
+
itemProto.layout = () => this.#layout(item, i);
|
|
59
|
+
}
|
|
60
|
+
else oldItemList.delete(itemProto);
|
|
61
|
+
itemProto.parent = this;
|
|
62
|
+
})
|
|
63
|
+
return { n: newItemList, d: oldItemList }
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
override removeNode(): void {
|
|
67
|
+
this.node?.remove();
|
|
68
|
+
forEach(this.protos, proto => proto.removeNode())
|
|
69
|
+
}
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
export class ForItem extends Proto {
|
|
73
|
+
static override [symbol_Statement] = true
|
|
74
|
+
}
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
export const viteHMR = {
|
|
2
|
+
name: 'auto-hmr-accept',
|
|
3
|
+
apply: 'serve',
|
|
4
|
+
handleHotUpdate({file, server}: {file: string, server: any}) {
|
|
5
|
+
if (file.includes('amateras/packages/')) {
|
|
6
|
+
console.log(`package file changes, force reload: ${file}`)
|
|
7
|
+
server.ws.send({
|
|
8
|
+
type: 'full-reload',
|
|
9
|
+
path: '*'
|
|
10
|
+
})
|
|
11
|
+
return []
|
|
12
|
+
}
|
|
13
|
+
},
|
|
14
|
+
transform(content: string, pathname: string) {
|
|
15
|
+
if (pathname.includes('node_modules') || !/\.(js|ts)$/.test(pathname)) return;
|
|
16
|
+
if (pathname.includes('amateras/packages/')) return {
|
|
17
|
+
code: `${content}\nif (import.meta.hot) import.meta.hot.decline();`,
|
|
18
|
+
map: null
|
|
19
|
+
}
|
|
20
|
+
if (!content.includes('import.meta.hot.accept')) return {
|
|
21
|
+
code: `${content}\nif (import.meta.hot) import.meta.hot.accept();`,
|
|
22
|
+
map: null
|
|
23
|
+
};
|
|
24
|
+
}
|
|
25
|
+
} as const
|
|
26
|
+
|
|
27
|
+
export default viteHMR;
|
|
@@ -0,0 +1,73 @@
|
|
|
1
|
+
# amateras/i18n
|
|
2
|
+
|
|
3
|
+
## Usage
|
|
4
|
+
```ts
|
|
5
|
+
import 'amateras';
|
|
6
|
+
import 'amateras/i18n';
|
|
7
|
+
|
|
8
|
+
const $t = $.i18n()
|
|
9
|
+
// add 'en' locale dictionary context
|
|
10
|
+
.add('en', {
|
|
11
|
+
homepage: {
|
|
12
|
+
_: 'Home',
|
|
13
|
+
hello: 'Hello, $name$!',
|
|
14
|
+
}
|
|
15
|
+
})
|
|
16
|
+
// set 'en' as locale language
|
|
17
|
+
.locale('en')
|
|
18
|
+
|
|
19
|
+
$(document.body).content([
|
|
20
|
+
$('h1').content( $t('homepage') )
|
|
21
|
+
// <h1><text>Home</text></h1>
|
|
22
|
+
$t('homepage.hello', {name: 'Amateras'})
|
|
23
|
+
// <text>Hello, Amateras!</text>
|
|
24
|
+
])
|
|
25
|
+
```
|
|
26
|
+
|
|
27
|
+
## Change Language
|
|
28
|
+
```ts
|
|
29
|
+
$t.locale('zh')
|
|
30
|
+
// all translation text will be updated
|
|
31
|
+
```
|
|
32
|
+
|
|
33
|
+
## Import Dictionary Context
|
|
34
|
+
|
|
35
|
+
```ts
|
|
36
|
+
// ./i18n/en.ts
|
|
37
|
+
export default {
|
|
38
|
+
hello: 'Hello, $name$!'
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
// ./i18n/zh.ts
|
|
42
|
+
export default {
|
|
43
|
+
hello: '您好,$name$!'
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
// ./entry_file.ts
|
|
47
|
+
const $t = $.i18n()
|
|
48
|
+
.add('en', () => import('./i18n/en.ts'))
|
|
49
|
+
.add('zh', () => import('./i18n/zh.ts'))
|
|
50
|
+
// set 'zh' as locale language
|
|
51
|
+
// and fetch file automatically from path
|
|
52
|
+
.locale('zh');
|
|
53
|
+
```
|
|
54
|
+
|
|
55
|
+
## Directory Shortcut
|
|
56
|
+
|
|
57
|
+
```ts
|
|
58
|
+
const $t = $.i18n('en')
|
|
59
|
+
.add('en', {
|
|
60
|
+
page1: {
|
|
61
|
+
section2: {
|
|
62
|
+
button3: {
|
|
63
|
+
text: 'Too deep!'
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
})
|
|
68
|
+
|
|
69
|
+
const $t_button = $t.dir('page1.section2.button3');
|
|
70
|
+
|
|
71
|
+
$t_button('text') // Too deep!
|
|
72
|
+
$t('page1.section2.button3.text') // Too deep!
|
|
73
|
+
```
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@amateras/i18n",
|
|
3
|
+
"peerDependencies": {
|
|
4
|
+
"@amateras/core": "workspace:*",
|
|
5
|
+
"@amateras/utils": "workspace:*"
|
|
6
|
+
},
|
|
7
|
+
"imports": {
|
|
8
|
+
"#structure/*": "./src/structure/*.ts",
|
|
9
|
+
"#lib/*": "./src/lib/*.ts",
|
|
10
|
+
"#node/*": "./src/node/*.ts"
|
|
11
|
+
},
|
|
12
|
+
"exports": {
|
|
13
|
+
".": "./src/index.ts"
|
|
14
|
+
}
|
|
15
|
+
}
|
|
@@ -0,0 +1,78 @@
|
|
|
1
|
+
import { I18n } from "#structure/I18n";
|
|
2
|
+
import { I18nDictionary, type I18nDictionaryContext, type I18nDictionaryContextImporter } from "#structure/I18nDictionary";
|
|
3
|
+
import { I18nTranslation as _I18nTranslation, I18nTranslation, type I18nTranslationOptions } from "#structure/I18nTranslation";
|
|
4
|
+
import { _instanceof, _Object_assign } from "@amateras/utils";
|
|
5
|
+
import type { GetDictionaryContextByKey, I18nTranslationDirKey, I18nTranslationKey, I18nTranslationParams, Mixin, ResolvedAsyncDictionary } from "./types";
|
|
6
|
+
import { GlobalState } from "@amateras/core";
|
|
7
|
+
|
|
8
|
+
declare global {
|
|
9
|
+
export namespace $ {
|
|
10
|
+
export interface I18nFunction<D extends I18nDictionaryContext = {}> {
|
|
11
|
+
<K extends I18nTranslationKey<D>, P extends I18nTranslationParams<K, D>>(path: K, ...params: P extends Record<string, never> ? [] : [P]): I18nTranslation;
|
|
12
|
+
i18n: I18n;
|
|
13
|
+
locale(lang?: string): Promise<void>;
|
|
14
|
+
add<F extends I18nDictionaryContext | I18nDictionaryContextImporter>(lang: string, dictionary: F): I18nFunction<Mixin<D, (F extends I18nDictionaryContextImporter ? ResolvedAsyncDictionary<F> : F)>>;
|
|
15
|
+
delete(lang: string): this;
|
|
16
|
+
dir<K extends I18nTranslationDirKey<D>>(path: K): I18nFunction<GetDictionaryContextByKey<K, D>>
|
|
17
|
+
}
|
|
18
|
+
export function i18n(defaultLocale: string): I18nFunction;
|
|
19
|
+
export type I18nTranslation = _I18nTranslation;
|
|
20
|
+
|
|
21
|
+
export interface TextProcessorValueMap {
|
|
22
|
+
i18n: I18nTranslation
|
|
23
|
+
}
|
|
24
|
+
}
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
declare module '@amateras/core' {
|
|
28
|
+
export interface GlobalState {
|
|
29
|
+
i18n: {
|
|
30
|
+
promises: Promise<any>[]
|
|
31
|
+
}
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
_Object_assign(GlobalState.prototype, {
|
|
36
|
+
i18n: {
|
|
37
|
+
promises: []
|
|
38
|
+
}
|
|
39
|
+
})
|
|
40
|
+
|
|
41
|
+
GlobalState.disposers.add(global => {
|
|
42
|
+
global.i18n.promises = [];
|
|
43
|
+
})
|
|
44
|
+
|
|
45
|
+
_Object_assign($, {
|
|
46
|
+
i18n(defaultLocale: string) {
|
|
47
|
+
const i18n = new I18n(defaultLocale);
|
|
48
|
+
const i18nFn = (key: string, options?: I18nTranslationOptions) => i18n.translate(key, options);
|
|
49
|
+
_Object_assign(i18nFn, {
|
|
50
|
+
i18n,
|
|
51
|
+
async locale(locale: string) {
|
|
52
|
+
await i18n.setLocale(locale);
|
|
53
|
+
},
|
|
54
|
+
add(lang: string, context: I18nDictionaryContext | I18nDictionaryContextImporter) {
|
|
55
|
+
i18n.map.set(lang, new I18nDictionary(context));
|
|
56
|
+
return this;
|
|
57
|
+
},
|
|
58
|
+
delete(lang: string) {
|
|
59
|
+
i18n.map.delete(lang);
|
|
60
|
+
return this;
|
|
61
|
+
},
|
|
62
|
+
dir(path: string) {
|
|
63
|
+
return (key: string, options?: I18nTranslationOptions) => i18n.translate(`${path}.${key}`, options)
|
|
64
|
+
}
|
|
65
|
+
})
|
|
66
|
+
return i18nFn
|
|
67
|
+
}
|
|
68
|
+
})
|
|
69
|
+
|
|
70
|
+
$.process.text.add(value => {
|
|
71
|
+
if (_instanceof(value, I18nTranslation)) {
|
|
72
|
+
return value
|
|
73
|
+
}
|
|
74
|
+
})
|
|
75
|
+
|
|
76
|
+
export * from "#structure/I18n";
|
|
77
|
+
export * from "#structure/I18nDictionary";
|
|
78
|
+
export * from "#structure/I18nTranslation";
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
import { _instanceof, _null, map } from "@amateras/utils";
|
|
2
|
+
import { I18nDictionary } from "#structure/I18nDictionary";
|
|
3
|
+
import { I18nTranslation, type I18nTranslationOptions } from "./I18nTranslation";
|
|
4
|
+
|
|
5
|
+
export class I18n {
|
|
6
|
+
map = new Map<string, I18nDictionary>();
|
|
7
|
+
#defaultLocale: string;
|
|
8
|
+
translations = new Set<I18nTranslation>();
|
|
9
|
+
locale: string;
|
|
10
|
+
constructor(defaultLocale: string) {
|
|
11
|
+
this.#defaultLocale = defaultLocale;
|
|
12
|
+
this.locale = defaultLocale;
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
defaultLocale(): string;
|
|
16
|
+
defaultLocale(locale: string): this;
|
|
17
|
+
defaultLocale(locale?: string) {
|
|
18
|
+
if (!arguments.length) return this.#defaultLocale;
|
|
19
|
+
if (locale) this.#defaultLocale = locale;
|
|
20
|
+
return this;
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
async setLocale(): Promise<string>;
|
|
24
|
+
async setLocale(locale: string): Promise<this>;
|
|
25
|
+
async setLocale(locale?: string) {
|
|
26
|
+
if (!arguments.length) return this.locale;
|
|
27
|
+
if (locale) {
|
|
28
|
+
let dictionary = this.map.get(locale);
|
|
29
|
+
if (!dictionary) {
|
|
30
|
+
locale = locale.split('-')[0]!;
|
|
31
|
+
return this.setLocale(locale);
|
|
32
|
+
}
|
|
33
|
+
if (locale !== this.locale) {
|
|
34
|
+
this.locale = locale;
|
|
35
|
+
await Promise.all(map(this.translations, translation => translation.update()))
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
return this;
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
dictionary(locale = this.locale) {
|
|
42
|
+
if (!locale) return _null;
|
|
43
|
+
const dictionary = this.map.get(locale);
|
|
44
|
+
return dictionary;
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
translate(key: string, options?: I18nTranslationOptions) {
|
|
48
|
+
return new I18nTranslation(this, key, options);
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
import { _instanceof, isFunction, isObject } from "@amateras/utils";
|
|
2
|
+
|
|
3
|
+
export class I18nDictionary {
|
|
4
|
+
#context: I18nDictionaryContext | Promise<I18nDictionaryContext> | null = null;
|
|
5
|
+
#fetch: I18nDictionaryContextImporter | null = null;
|
|
6
|
+
constructor(resolver: I18nDictionaryContext | I18nDictionaryContextImporter) {
|
|
7
|
+
if (isFunction(resolver)) this.#fetch = resolver;
|
|
8
|
+
else this.#context = resolver;
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
async context(): Promise<I18nDictionaryContext> {
|
|
12
|
+
if (this.#context) return await this.#context;
|
|
13
|
+
if (!this.#fetch) throw 'I18n Context Fetch Error';
|
|
14
|
+
return this.#context = this.#fetch().then((module) => module.default);
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
async find(path: string, context?: I18nDictionaryContext): Promise<string | undefined> {
|
|
18
|
+
if (!context) context = await this.context();
|
|
19
|
+
const [snippet, ...rest] = path.split('.') as [string, ...string[]];
|
|
20
|
+
const target = context[snippet];
|
|
21
|
+
if (isObject(target)) {
|
|
22
|
+
if (rest.length) return this.find(rest.join('.'), target);
|
|
23
|
+
else return target['_'] as string;
|
|
24
|
+
}
|
|
25
|
+
if (rest.length) return path;
|
|
26
|
+
else return target;
|
|
27
|
+
}
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
export type I18nDictionaryContext = {[key: string]: string | I18nDictionaryContext}
|
|
31
|
+
export type I18nDictionaryContextImporter = () => Promise<{default: I18nDictionaryContext}>
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
import type { I18n } from "#structure/I18n";
|
|
2
|
+
import { ProxyProto } from "@amateras/core";
|
|
3
|
+
import { forEach, isUndefined, map } from "@amateras/utils";
|
|
4
|
+
|
|
5
|
+
export class I18nTranslation extends ProxyProto {
|
|
6
|
+
i18n: I18n;
|
|
7
|
+
key: string;
|
|
8
|
+
options: I18nTranslationOptions | undefined;
|
|
9
|
+
constructor(i18n: I18n, key: string, options?: I18nTranslationOptions) {
|
|
10
|
+
super()
|
|
11
|
+
this.i18n = i18n;
|
|
12
|
+
this.key = key;
|
|
13
|
+
this.options = options;
|
|
14
|
+
this.i18n.translations.add(this);
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
override build(): this {
|
|
18
|
+
this.update();
|
|
19
|
+
return this;
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
async update() {
|
|
23
|
+
const {key, i18n, options} = this;
|
|
24
|
+
const contentUpdate = (content: string[], args: any[] = []) => {
|
|
25
|
+
this.layout = () => {
|
|
26
|
+
// make this array become Template String Array;
|
|
27
|
+
//@ts-ignore
|
|
28
|
+
content.raw = content;
|
|
29
|
+
$(content as any, ...args);
|
|
30
|
+
}
|
|
31
|
+
forEach(this.protos, proto => proto.removeNode());
|
|
32
|
+
super.build();
|
|
33
|
+
this.node?.replaceWith(...this.toDOM());
|
|
34
|
+
}
|
|
35
|
+
update: {
|
|
36
|
+
const dictionary = i18n.dictionary();
|
|
37
|
+
if (!dictionary) { contentUpdate([key]); break update }
|
|
38
|
+
const request = dictionary.find(key);
|
|
39
|
+
this.global.i18n.promises.push(request);
|
|
40
|
+
const translate = await request;
|
|
41
|
+
if (isUndefined(translate)) break update;
|
|
42
|
+
const snippets = translate.split(/\$[a-zA-Z0-9_]+\$/);
|
|
43
|
+
if (snippets.length === 1 || !options) { contentUpdate([translate]); break update }
|
|
44
|
+
const matches = translate.matchAll(/(\$([a-zA-Z0-9_]+)\$)/g);
|
|
45
|
+
contentUpdate(snippets, map(matches as unknown as [string, string, string][], ([,,value]) => options[value]));
|
|
46
|
+
}
|
|
47
|
+
return this;
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
export type I18nTranslationOptions = {[key: string]: any}
|
|
@@ -0,0 +1,77 @@
|
|
|
1
|
+
import type { I18nDictionaryContext, I18nDictionaryContextImporter } from "#structure/I18nDictionary";
|
|
2
|
+
|
|
3
|
+
export type ResolvedAsyncDictionary<F extends I18nDictionaryContextImporter> = Awaited<ReturnType<F>>['default'];
|
|
4
|
+
|
|
5
|
+
export type I18nTranslationKey<T> =
|
|
6
|
+
T extends I18nDictionaryContext
|
|
7
|
+
? {
|
|
8
|
+
[K in keyof T]: K extends string
|
|
9
|
+
? K extends '_'
|
|
10
|
+
? never
|
|
11
|
+
: T[K] extends string
|
|
12
|
+
? `${K}`
|
|
13
|
+
: '_' extends keyof T[K]
|
|
14
|
+
? `${K}` | `${K}.${I18nTranslationKey<T[K]>}`
|
|
15
|
+
: `${K}.${I18nTranslationKey<T[K]>}`
|
|
16
|
+
: never;
|
|
17
|
+
}[keyof T]
|
|
18
|
+
: never;
|
|
19
|
+
|
|
20
|
+
export type I18nTranslationDirKey<T> =
|
|
21
|
+
T extends I18nDictionaryContext
|
|
22
|
+
? {
|
|
23
|
+
[K in keyof T]: K extends string
|
|
24
|
+
? T[K] extends string
|
|
25
|
+
? never
|
|
26
|
+
: `${K}` | `${K}.${I18nTranslationDirKey<T[K]>}`
|
|
27
|
+
: never;
|
|
28
|
+
}[keyof T]
|
|
29
|
+
: never;
|
|
30
|
+
|
|
31
|
+
export type I18nTranslationParams<K extends string, T extends I18nDictionaryContext> =
|
|
32
|
+
FindTranslationByKey<K, T> extends infer O
|
|
33
|
+
? O extends string
|
|
34
|
+
? FindParam<O>
|
|
35
|
+
: never
|
|
36
|
+
: never
|
|
37
|
+
|
|
38
|
+
export type FindParam<T extends string> =
|
|
39
|
+
T extends `${string}$${infer Param}$${infer Rest}`
|
|
40
|
+
? Param extends `${string}${' '}${string}`
|
|
41
|
+
? Prettify<{} & FindParam<Rest>>
|
|
42
|
+
: Prettify<Record<Param, $.Layout> & FindParam<Rest>>
|
|
43
|
+
: {}
|
|
44
|
+
|
|
45
|
+
export type FindTranslationByKey<K extends string, T extends I18nDictionaryContext> =
|
|
46
|
+
K extends `${infer Prefix}.${infer Rest}`
|
|
47
|
+
? Prefix extends keyof T
|
|
48
|
+
? T[Prefix] extends I18nDictionaryContext
|
|
49
|
+
? FindTranslationByKey<Rest, T[Prefix]>
|
|
50
|
+
: T[Prefix]
|
|
51
|
+
: ''
|
|
52
|
+
: T[K] extends object
|
|
53
|
+
? '_' extends keyof T[K]
|
|
54
|
+
? T[K]['_']
|
|
55
|
+
: never
|
|
56
|
+
: T[K]
|
|
57
|
+
|
|
58
|
+
export type GetDictionaryContextByKey<K extends string, T extends I18nDictionaryContext> =
|
|
59
|
+
K extends `${infer Prefix}.${infer Rest}`
|
|
60
|
+
? Prefix extends keyof T
|
|
61
|
+
? T[Prefix] extends I18nDictionaryContext
|
|
62
|
+
? GetDictionaryContextByKey<Rest, T[Prefix]>
|
|
63
|
+
: never
|
|
64
|
+
: never
|
|
65
|
+
: T[K] extends object
|
|
66
|
+
? T[K]
|
|
67
|
+
: never
|
|
68
|
+
|
|
69
|
+
export type Mixin<A, B> =
|
|
70
|
+
(Omit<A, keyof B> & Omit<B, keyof A>) & {
|
|
71
|
+
[key in (keyof A & keyof B)]:
|
|
72
|
+
A[key] extends object
|
|
73
|
+
? B[key] extends object
|
|
74
|
+
? Mixin<A[key], B[key]>
|
|
75
|
+
: A[key] | B[key]
|
|
76
|
+
: A[key] | B[key]
|
|
77
|
+
}
|
|
@@ -0,0 +1,127 @@
|
|
|
1
|
+
# amateras/idb
|
|
2
|
+
|
|
3
|
+
## Usage
|
|
4
|
+
```ts
|
|
5
|
+
import 'amateras';
|
|
6
|
+
import 'amateras/idb';
|
|
7
|
+
|
|
8
|
+
// configure indexedDB
|
|
9
|
+
const idb = await $.idb('MyDB', 1)
|
|
10
|
+
// add store
|
|
11
|
+
.store('userStore', store => store
|
|
12
|
+
.keyPath('id')
|
|
13
|
+
.autoIncrement(true)
|
|
14
|
+
// define store object type
|
|
15
|
+
.schema<{
|
|
16
|
+
id: number,
|
|
17
|
+
name: string,
|
|
18
|
+
age: number
|
|
19
|
+
}>()
|
|
20
|
+
.index('by_age', { keyPath: 'age' })
|
|
21
|
+
)
|
|
22
|
+
// open idb
|
|
23
|
+
.open();
|
|
24
|
+
|
|
25
|
+
// open `readwrite` transaction with `userStore`
|
|
26
|
+
const result = await idb.store('userStore', true, async store => {
|
|
27
|
+
store.put({name: 'Amateras', age: 16});
|
|
28
|
+
return store.getAll();
|
|
29
|
+
})
|
|
30
|
+
|
|
31
|
+
console.log(result); // [ { name: 'Amateras', age: 16, id: 1 } ]
|
|
32
|
+
```
|
|
33
|
+
|
|
34
|
+
## Quick Examples
|
|
35
|
+
|
|
36
|
+
### Get object from store
|
|
37
|
+
```ts
|
|
38
|
+
await idb.store('userStore', store => store.get(1))
|
|
39
|
+
```
|
|
40
|
+
|
|
41
|
+
### Get all object from store
|
|
42
|
+
```ts
|
|
43
|
+
await idb.store('userStore', store => store.getAll())
|
|
44
|
+
```
|
|
45
|
+
|
|
46
|
+
### Add object to store
|
|
47
|
+
Any changes to database without `readwrite` mode is resisted, pass `true` value to `writable` argument to enable `readwrite` mode.
|
|
48
|
+
``` ts
|
|
49
|
+
await idb.store('userStore', true, store => store.add({name: 'Tsukimi', age: 16}))
|
|
50
|
+
```
|
|
51
|
+
|
|
52
|
+
### Put object to store
|
|
53
|
+
The `.put()` method is different with `.add()` method, put object will replace the object of existed key.
|
|
54
|
+
``` ts
|
|
55
|
+
await idb.store('userStore', true, store => store.put({name: 'Amateras', age: 17}))
|
|
56
|
+
```
|
|
57
|
+
|
|
58
|
+
### Use index
|
|
59
|
+
```ts
|
|
60
|
+
await idb.store('userStore', true, store => store.index('by_age').getAll(16))
|
|
61
|
+
```
|
|
62
|
+
|
|
63
|
+
### Operating multiple stores in one transaction
|
|
64
|
+
```ts
|
|
65
|
+
await idb.transaction(['userStore', 'itemStore'], true, async transaction => {
|
|
66
|
+
transaction.store('itemStore').put({id: 2, name: 'Item 2'})
|
|
67
|
+
return {
|
|
68
|
+
users: await transaction.store('userStore').getAll(),
|
|
69
|
+
items: await transaction.store('itemStore').getAll()
|
|
70
|
+
}
|
|
71
|
+
})
|
|
72
|
+
```
|
|
73
|
+
|
|
74
|
+
### Open cursor for advance operations
|
|
75
|
+
```ts
|
|
76
|
+
await idb.store('userStore', true, async store => {
|
|
77
|
+
const teenagers = []
|
|
78
|
+
await store.cursor(cursor => {
|
|
79
|
+
if (cursor.value.age < 18) teenagers.push(cursor.value);
|
|
80
|
+
cursor.continue();
|
|
81
|
+
})
|
|
82
|
+
return teenagers;
|
|
83
|
+
})
|
|
84
|
+
```
|
|
85
|
+
|
|
86
|
+
## Upgrade Database
|
|
87
|
+
Using `.upgrade()` in `$IDBStoreBuilder` can set the store upgrade handle function to list. The store upgrade function is used for change object structure when the store is upgrading.
|
|
88
|
+
|
|
89
|
+
For example, in version 10:
|
|
90
|
+
```ts
|
|
91
|
+
{
|
|
92
|
+
id: number,
|
|
93
|
+
name: string
|
|
94
|
+
}
|
|
95
|
+
```
|
|
96
|
+
|
|
97
|
+
After version 11, we want to change the object structure:
|
|
98
|
+
```ts
|
|
99
|
+
{
|
|
100
|
+
id: string,
|
|
101
|
+
name: string,
|
|
102
|
+
intro: string
|
|
103
|
+
}
|
|
104
|
+
```
|
|
105
|
+
|
|
106
|
+
You see the `id` is change to `string` type, and come with the new property `intro`. In the following example, we will upgrade this object structure, and this upgrade is only executed when client IDB version is lower than argument `version`.
|
|
107
|
+
|
|
108
|
+
```ts
|
|
109
|
+
store.upgrade(11, (objects) => {
|
|
110
|
+
return objects.map({key, value} => {
|
|
111
|
+
// since we didn't defined the object type in every different version,
|
|
112
|
+
// the object is any type, please handle the upgrade carefully
|
|
113
|
+
return { key,
|
|
114
|
+
value: {
|
|
115
|
+
...value,
|
|
116
|
+
id: value.id.toString(), // convert to string
|
|
117
|
+
intro: `Hi, my name is ${object.name}` // add new intro property
|
|
118
|
+
}
|
|
119
|
+
}
|
|
120
|
+
})
|
|
121
|
+
})
|
|
122
|
+
```
|
|
123
|
+
|
|
124
|
+
The upgrade function is set, this will be executed on `$IDBBuilder.open()`.
|
|
125
|
+
|
|
126
|
+
> [!NOTE]
|
|
127
|
+
> You should leave all the upgrade function in your codebase, unless you are sure the client database version is larger than this upgrade function.
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@amateras/idb",
|
|
3
|
+
"peerDependencies": {
|
|
4
|
+
"@amateras/core": "workspace:*",
|
|
5
|
+
"@amateras/signal": "workspace:*",
|
|
6
|
+
"@amateras/utils": "workspace:*"
|
|
7
|
+
},
|
|
8
|
+
"imports": {
|
|
9
|
+
"#structure/*": "./src/structure/*.ts",
|
|
10
|
+
"#lib/*": "./src/lib/*.ts",
|
|
11
|
+
"#node/*": "./src/node/*.ts"
|
|
12
|
+
},
|
|
13
|
+
"exports": {
|
|
14
|
+
".": "./src/index.ts"
|
|
15
|
+
}
|
|
16
|
+
}
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import { _Object_assign } from "@amateras/utils"
|
|
2
|
+
import { $IDBBuilder } from "#structure/builder/$IDBBuilder"
|
|
3
|
+
|
|
4
|
+
declare global {
|
|
5
|
+
export namespace $ {
|
|
6
|
+
/**
|
|
7
|
+
* Create {@link $IDBBuilder} with IDB name and version number.
|
|
8
|
+
* @param name - Database name
|
|
9
|
+
* @param version - Version number
|
|
10
|
+
*/
|
|
11
|
+
export function idb<N extends string, V extends number>(name: N, version: V): $IDBBuilder<{name: N, version: V, stores: {}}>;
|
|
12
|
+
}
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
_Object_assign($, {
|
|
16
|
+
idb: (name: string, version: number) => new $IDBBuilder({name, version, stores: {}})
|
|
17
|
+
})
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
import { _Promise } from "@amateras/utils";
|
|
2
|
+
|
|
3
|
+
export const $IDBRequest = <T>(req: IDBRequest<T>, handle?: (req: IDBRequest<T>, resolve: (value: T) => void) => void) => {
|
|
4
|
+
return new _Promise<T>((resolve, reject) => {
|
|
5
|
+
req.onsuccess = _ => handle ? handle(req, resolve) : resolve(req.result);
|
|
6
|
+
req.onerror = _ => reject(req.error);
|
|
7
|
+
})
|
|
8
|
+
}
|