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.
- package/README.md +24 -25
- package/ext/html/node/$Anchor.ts +3 -3
- package/ext/html/node/$Canvas.ts +2 -2
- package/ext/html/node/$Dialog.ts +2 -2
- package/ext/html/node/$Form.ts +2 -2
- package/ext/html/node/$Image.ts +2 -2
- package/ext/html/node/$Input.ts +28 -4
- package/ext/html/node/$Label.ts +12 -3
- package/ext/html/node/$Media.ts +2 -2
- package/ext/html/node/$OptGroup.ts +2 -2
- package/ext/html/node/$Option.ts +2 -2
- package/ext/html/node/$Select.ts +2 -2
- package/ext/html/node/$TextArea.ts +2 -2
- package/ext/i18n/README.md +20 -0
- package/ext/i18n/src/index.ts +106 -12
- package/ext/i18n/src/structure/I18n.ts +12 -8
- package/ext/i18n/src/structure/I18nDictionary.ts +2 -2
- package/ext/i18n/src/structure/I18nTranslation.ts +35 -0
- package/ext/idb/README.md +127 -0
- package/ext/idb/package.json +13 -0
- package/ext/idb/src/core.ts +6 -0
- package/ext/idb/src/index.ts +17 -0
- package/ext/idb/src/lib/$IDBRequest.ts +8 -0
- package/ext/idb/src/structure/$IDB.ts +63 -0
- package/ext/idb/src/structure/$IDBCursor.ts +34 -0
- package/ext/idb/src/structure/$IDBIndex.ts +48 -0
- package/ext/idb/src/structure/$IDBStore.ts +103 -0
- package/ext/idb/src/structure/$IDBStoreBase.ts +30 -0
- package/ext/idb/src/structure/$IDBTransaction.ts +38 -0
- package/ext/idb/src/structure/builder/$IDBBuilder.ts +230 -0
- package/ext/idb/src/structure/builder/$IDBStoreBuilder.ts +100 -0
- package/ext/markdown/README.md +53 -0
- package/ext/markdown/package.json +15 -0
- package/ext/markdown/src/index.ts +3 -0
- package/ext/markdown/src/lib/type.ts +26 -0
- package/ext/markdown/src/lib/util.ts +21 -0
- package/ext/markdown/src/structure/Markdown.ts +54 -0
- package/ext/markdown/src/structure/MarkdownLexer.ts +111 -0
- package/ext/markdown/src/structure/MarkdownParser.ts +33 -0
- package/ext/markdown/src/syntax/alert.ts +46 -0
- package/ext/markdown/src/syntax/blockquote.ts +35 -0
- package/ext/markdown/src/syntax/bold.ts +11 -0
- package/ext/markdown/src/syntax/code.ts +11 -0
- package/ext/markdown/src/syntax/codeblock.ts +44 -0
- package/ext/markdown/src/syntax/heading.ts +14 -0
- package/ext/markdown/src/syntax/horizontalRule.ts +11 -0
- package/ext/markdown/src/syntax/image.ts +23 -0
- package/ext/markdown/src/syntax/italic.ts +11 -0
- package/ext/markdown/src/syntax/link.ts +46 -0
- package/ext/markdown/src/syntax/list.ts +121 -0
- package/ext/markdown/src/syntax/table.ts +67 -0
- package/ext/markdown/src/syntax/text.ts +19 -0
- package/ext/router/README.md +111 -17
- package/ext/router/package.json +10 -0
- package/ext/router/src/index.ts +69 -0
- package/ext/router/src/node/Page.ts +34 -0
- package/ext/router/src/node/Router.ts +191 -0
- package/ext/router/src/node/RouterAnchor.ts +24 -0
- package/ext/router/src/structure/PageBuilder.ts +24 -0
- package/ext/router/src/structure/Route.ts +105 -0
- package/ext/signal/README.md +93 -0
- package/ext/signal/package.json +9 -0
- package/ext/signal/src/index.ts +128 -0
- package/{src → ext/signal/src}/structure/Signal.ts +7 -11
- package/ext/ssr/index.ts +4 -4
- package/ext/ui/lib/VirtualScroll.ts +25 -0
- package/ext/ui/node/Accordian.ts +97 -0
- package/ext/ui/node/Form.ts +53 -0
- package/ext/ui/node/Grid.ts +0 -0
- package/ext/ui/node/Table.ts +43 -0
- package/ext/ui/node/Tabs.ts +114 -0
- package/ext/ui/node/Toast.ts +16 -0
- package/ext/ui/node/Waterfall.ts +72 -0
- package/ext/ui/package.json +11 -0
- package/package.json +9 -3
- package/src/core.ts +31 -59
- package/src/global.ts +12 -2
- package/src/index.ts +1 -2
- package/src/lib/assignProperties.ts +57 -0
- package/src/lib/native.ts +33 -11
- package/src/lib/sleep.ts +3 -1
- package/src/lib/toArray.ts +9 -0
- package/src/lib/trycatch.ts +17 -0
- package/src/lib/uppercase.ts +3 -0
- package/src/node/$Element.ts +7 -53
- package/src/node/$EventTarget.ts +45 -0
- package/src/node/$Node.ts +63 -55
- package/src/node/$Virtual.ts +65 -0
- package/src/node.ts +7 -6
- package/ext/i18n/src/node/I18nText.ts +0 -35
- package/ext/router/index.ts +0 -73
- package/ext/router/node/Page.ts +0 -27
- package/ext/router/node/Route.ts +0 -54
- package/ext/router/node/Router.ts +0 -149
- package/ext/router/node/RouterAnchor.ts +0 -8
- package/src/lib/assign.ts +0 -38
- package/src/lib/assignHelper.ts +0 -18
package/README.md
CHANGED
|
@@ -29,37 +29,33 @@ $('p').css(paragraphStyle).content([
|
|
|
29
29
|
])
|
|
30
30
|
```
|
|
31
31
|
|
|
32
|
-
##
|
|
32
|
+
## State Management
|
|
33
33
|
```ts
|
|
34
34
|
import 'amateras';
|
|
35
|
+
import 'amateras/signal';
|
|
35
36
|
|
|
37
|
+
// define a signal with value 0
|
|
36
38
|
const count$ = $.signal(0);
|
|
37
39
|
|
|
38
|
-
$
|
|
39
|
-
|
|
40
|
-
.content('Click me')
|
|
41
|
-
.class('class1', 'class2')
|
|
42
|
-
.on('click', () => count$(oldValue => oldValue + 1)),
|
|
43
|
-
$('p').content($`Clicked ${count$} times.`)
|
|
44
|
-
])
|
|
45
|
-
```
|
|
40
|
+
// this variable will be auto recalculate when count$ changes
|
|
41
|
+
const doubleCount$ = $.compute(() => count$() * 2);
|
|
46
42
|
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
import 'amateras';
|
|
43
|
+
// the console message will fired when count$ changes
|
|
44
|
+
$.effect(() => console.log( count$() ))
|
|
50
45
|
|
|
51
|
-
|
|
52
|
-
|
|
46
|
+
$(document.body).content([
|
|
47
|
+
// Display Counts
|
|
48
|
+
$('p').content( $`Counts: ${count$}` ),
|
|
53
49
|
|
|
54
|
-
|
|
50
|
+
// Display Double Counts
|
|
51
|
+
$('p').content( $`Double Counts: ${doubleCount$}` ),
|
|
55
52
|
|
|
56
|
-
|
|
57
|
-
$('
|
|
58
|
-
$('p').content($`Double: ${doubleCount$}`)
|
|
53
|
+
// Create a button that make counts plus 1 on click
|
|
54
|
+
$('button').content('Add Count').on('click', () => count$.set(value => value + 1))
|
|
59
55
|
])
|
|
60
56
|
```
|
|
61
57
|
|
|
62
|
-
## HTMLElement Methods Import
|
|
58
|
+
## HTMLElement Native Methods Import
|
|
63
59
|
```ts
|
|
64
60
|
import 'amateras';
|
|
65
61
|
import 'amateras/html';
|
|
@@ -99,11 +95,14 @@ $(document.body).content([
|
|
|
99
95
|
```
|
|
100
96
|
|
|
101
97
|
## Packages
|
|
102
|
-
The packages size result using Vite 7.0 with default bundle settings, polyfills code
|
|
98
|
+
The packages size result using Vite 7.0 with default bundle settings, polyfills code included.
|
|
103
99
|
| Package name | Size | Size(gzip) | Description |
|
|
104
100
|
| --- | --- | --- | --- |
|
|
105
|
-
| amateras |
|
|
106
|
-
| amateras/html | 0.
|
|
107
|
-
| [amateras/
|
|
108
|
-
| [amateras/
|
|
109
|
-
| [amateras/
|
|
101
|
+
| amateras | 4.79 kB | 2.20 kB | Core |
|
|
102
|
+
| amateras/html | 0.98 kB | 0.26 kB | Import HTMLElement types and methods |
|
|
103
|
+
| [amateras/signal](./ext/signal/README.md) | 1.26 kB | 0.49 kB | Reactive data |
|
|
104
|
+
| [amateras/css](./ext/css/README.md) | 3.70 kB | 1.44 kB | Style in JS |
|
|
105
|
+
| [amateras/router](./ext/router/README.md) | 3.70 kB | 1.64 kB | Amateras Router |
|
|
106
|
+
| [amateras/i18n](./ext/i18n/README.md) | 3.02 kB | 1.13 kB | I18n translations |
|
|
107
|
+
| [amateras/idb](./ext/idb/README.md) | 5.39 kB | 2.06 kB | IndexedDB Builder and API Wrapper |
|
|
108
|
+
| [amateras/markdown](./ext/markdown/README.md) | 6.81 kB | 2.68 kB | Markdown Converter |
|
package/ext/html/node/$Anchor.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { assignProperties } from "#lib/assignProperties";
|
|
2
2
|
import { $HTMLElement } from "#node/$HTMLElement";
|
|
3
3
|
|
|
4
4
|
export class $Anchor extends $HTMLElement<HTMLAnchorElement> {
|
|
@@ -40,10 +40,10 @@ export interface $Anchor extends $HTMLElement<HTMLAnchorElement> {
|
|
|
40
40
|
type(): string;
|
|
41
41
|
}
|
|
42
42
|
|
|
43
|
-
export type AnchorTarget = '_self' | '_blank' | '_parent' | '_top' | '_unfenced_top' | '';
|
|
43
|
+
export type AnchorTarget = '_self' | '_blank' | '_parent' | '_top' | '_unfenced_top' | '' | '_replace';
|
|
44
44
|
|
|
45
45
|
declare module '#core' {
|
|
46
46
|
export function $(nodeName: 'a'): $Anchor
|
|
47
47
|
}
|
|
48
48
|
|
|
49
|
-
|
|
49
|
+
assignProperties(HTMLAnchorElement, $Anchor, 'a');
|
package/ext/html/node/$Canvas.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { assignProperties } from "#lib/assignProperties";
|
|
2
2
|
import { $HTMLElement } from "#node/$HTMLElement";
|
|
3
3
|
|
|
4
4
|
export class $Canvas extends $HTMLElement<HTMLCanvasElement> {
|
|
@@ -9,7 +9,7 @@ export class $Canvas extends $HTMLElement<HTMLCanvasElement> {
|
|
|
9
9
|
|
|
10
10
|
export interface $Canvas extends $HTMLElement<HTMLCanvasElement> {}
|
|
11
11
|
|
|
12
|
-
|
|
12
|
+
assignProperties(HTMLCanvasElement, $Canvas, 'canvas');
|
|
13
13
|
|
|
14
14
|
declare module '#core' {
|
|
15
15
|
export function $(nodeName: 'canvas'): $Canvas
|
package/ext/html/node/$Dialog.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { assignProperties } from "#lib/assignProperties";
|
|
2
2
|
import { $HTMLElement } from "#node/$HTMLElement";
|
|
3
3
|
|
|
4
4
|
export class $Dialog extends $HTMLElement<HTMLDialogElement> {
|
|
@@ -9,7 +9,7 @@ export class $Dialog extends $HTMLElement<HTMLDialogElement> {
|
|
|
9
9
|
|
|
10
10
|
export interface $Dialog extends $HTMLElement<HTMLDialogElement> {}
|
|
11
11
|
|
|
12
|
-
|
|
12
|
+
assignProperties(HTMLDialogElement, $Dialog, 'dialog');
|
|
13
13
|
|
|
14
14
|
declare module '#core' {
|
|
15
15
|
export function $(nodeName: 'dialog'): $Dialog
|
package/ext/html/node/$Form.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { assignProperties } from "#lib/assignProperties";
|
|
2
2
|
import { $HTMLElement } from "#node/$HTMLElement";
|
|
3
3
|
|
|
4
4
|
export class $Form extends $HTMLElement<HTMLFormElement> {
|
|
@@ -9,7 +9,7 @@ export class $Form extends $HTMLElement<HTMLFormElement> {
|
|
|
9
9
|
|
|
10
10
|
export interface $Form extends $HTMLElement<HTMLFormElement> {}
|
|
11
11
|
|
|
12
|
-
|
|
12
|
+
assignProperties(HTMLFormElement, $Form, 'form');
|
|
13
13
|
|
|
14
14
|
declare module '#core' {
|
|
15
15
|
export function $(nodeName: 'form'): $Form
|
package/ext/html/node/$Image.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { assignProperties } from "#lib/assignProperties";
|
|
2
2
|
import { $HTMLElement } from "#node/$HTMLElement";
|
|
3
3
|
|
|
4
4
|
export class $Image extends $HTMLElement<HTMLImageElement> {
|
|
@@ -65,7 +65,7 @@ export interface $Image extends $HTMLElement<HTMLImageElement> {
|
|
|
65
65
|
width(): number;
|
|
66
66
|
}
|
|
67
67
|
|
|
68
|
-
|
|
68
|
+
assignProperties(HTMLImageElement, $Image, 'img');
|
|
69
69
|
|
|
70
70
|
declare module '#core' {
|
|
71
71
|
export function $(nodeName: 'img'): $Image
|
package/ext/html/node/$Input.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { assignProperties } from "#lib/assignProperties";
|
|
2
2
|
import { $HTMLElement } from "#node/$HTMLElement";
|
|
3
3
|
|
|
4
4
|
export class $Input extends $HTMLElement<HTMLInputElement> {
|
|
@@ -143,8 +143,8 @@ export interface $Input extends $HTMLElement<HTMLInputElement> {
|
|
|
143
143
|
step(step: $Parameter<string>): this;
|
|
144
144
|
step(): string;
|
|
145
145
|
/** {@link HTMLInputElement.type} */
|
|
146
|
-
type(type: $Parameter<
|
|
147
|
-
type():
|
|
146
|
+
type(type: $Parameter<InputType>): this;
|
|
147
|
+
type(): InputType;
|
|
148
148
|
/** {@link HTMLInputElement.value} */
|
|
149
149
|
value(value: $Parameter<string>): this;
|
|
150
150
|
value(): string;
|
|
@@ -162,7 +162,31 @@ export interface $Input extends $HTMLElement<HTMLInputElement> {
|
|
|
162
162
|
width(): number;
|
|
163
163
|
}
|
|
164
164
|
|
|
165
|
-
|
|
165
|
+
export type InputType =
|
|
166
|
+
| 'button'
|
|
167
|
+
| 'checkbox'
|
|
168
|
+
| 'color'
|
|
169
|
+
| 'date'
|
|
170
|
+
| 'datetime-local'
|
|
171
|
+
| 'email'
|
|
172
|
+
| 'file'
|
|
173
|
+
| 'hidden'
|
|
174
|
+
| 'image'
|
|
175
|
+
| 'month'
|
|
176
|
+
| 'number'
|
|
177
|
+
| 'password'
|
|
178
|
+
| 'radio'
|
|
179
|
+
| 'range'
|
|
180
|
+
| 'reset'
|
|
181
|
+
| 'search'
|
|
182
|
+
| 'submit'
|
|
183
|
+
| 'tel'
|
|
184
|
+
| 'text'
|
|
185
|
+
| 'time'
|
|
186
|
+
| 'url'
|
|
187
|
+
| 'week';
|
|
188
|
+
|
|
189
|
+
assignProperties(HTMLInputElement, $Input, 'input');
|
|
166
190
|
|
|
167
191
|
declare module '#core' {
|
|
168
192
|
export function $(nodeName: 'input'): $Input
|
package/ext/html/node/$Label.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { assignProperties } from "#lib/assignProperties";
|
|
2
2
|
import { $HTMLElement } from "#node/$HTMLElement";
|
|
3
3
|
|
|
4
4
|
export class $Label extends $HTMLElement<HTMLLabelElement> {
|
|
@@ -7,9 +7,18 @@ export class $Label extends $HTMLElement<HTMLLabelElement> {
|
|
|
7
7
|
}
|
|
8
8
|
}
|
|
9
9
|
|
|
10
|
-
export interface $Label extends $HTMLElement<HTMLLabelElement> {
|
|
10
|
+
export interface $Label extends $HTMLElement<HTMLLabelElement> {
|
|
11
|
+
/** {@link HTMLLabelElement.control} */
|
|
12
|
+
readonly control: HTMLElement | null;
|
|
13
|
+
/** {@link HTMLLabelElement.form} */
|
|
14
|
+
readonly form: HTMLFormElement | null;
|
|
15
|
+
|
|
16
|
+
/** {@link HTMLLabelElement.htmlFor} */
|
|
17
|
+
htmlFor(htmlFor: $Parameter<string>): this;
|
|
18
|
+
htmlFor(): string;
|
|
19
|
+
}
|
|
11
20
|
|
|
12
|
-
|
|
21
|
+
assignProperties(HTMLLabelElement, $Label, 'label');
|
|
13
22
|
|
|
14
23
|
declare module '#core' {
|
|
15
24
|
export function $(nodeName: 'label'): $Label
|
package/ext/html/node/$Media.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { assignProperties } from "#lib/assignProperties";
|
|
2
2
|
import { $HTMLElement } from "#node/$HTMLElement";
|
|
3
3
|
|
|
4
4
|
export class $Media extends $HTMLElement<HTMLMediaElement> {
|
|
@@ -9,7 +9,7 @@ export class $Media extends $HTMLElement<HTMLMediaElement> {
|
|
|
9
9
|
|
|
10
10
|
export interface $Media extends $HTMLElement<HTMLMediaElement> {}
|
|
11
11
|
|
|
12
|
-
|
|
12
|
+
assignProperties(HTMLMediaElement, $Media, 'media');
|
|
13
13
|
|
|
14
14
|
declare module '#core' {
|
|
15
15
|
export function $(nodeName: 'media'): $Media
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { assignProperties } from "#lib/assignProperties";
|
|
2
2
|
import { $HTMLElement } from "#node/$HTMLElement";
|
|
3
3
|
|
|
4
4
|
export class $OptGroup extends $HTMLElement<HTMLOptGroupElement> {
|
|
@@ -16,7 +16,7 @@ export interface $OptGroup extends $HTMLElement<HTMLOptGroupElement> {
|
|
|
16
16
|
label(): string;
|
|
17
17
|
}
|
|
18
18
|
|
|
19
|
-
|
|
19
|
+
assignProperties(HTMLOptGroupElement, $OptGroup, 'optgroup');
|
|
20
20
|
|
|
21
21
|
declare module '#core' {
|
|
22
22
|
export function $(nodeName: 'optgroup'): $OptGroup
|
package/ext/html/node/$Option.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { assignProperties } from "#lib/assignProperties";
|
|
2
2
|
import { $HTMLElement } from "#node/$HTMLElement";
|
|
3
3
|
|
|
4
4
|
export class $Option extends $HTMLElement<HTMLOptionElement> {
|
|
@@ -33,7 +33,7 @@ export interface $Option extends $HTMLElement<HTMLOptionElement> {
|
|
|
33
33
|
value(): string;
|
|
34
34
|
}
|
|
35
35
|
|
|
36
|
-
|
|
36
|
+
assignProperties(HTMLOptionElement, $Option, 'option');
|
|
37
37
|
|
|
38
38
|
declare module '#core' {
|
|
39
39
|
export function $(nodeName: 'option'): $Option
|
package/ext/html/node/$Select.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { assignProperties } from "#lib/assignProperties";
|
|
2
2
|
import { $HTMLElement } from "#node/$HTMLElement";
|
|
3
3
|
|
|
4
4
|
export class $Select extends $HTMLElement<HTMLSelectElement> {
|
|
@@ -69,7 +69,7 @@ export interface $Select extends $HTMLElement<HTMLSelectElement> {
|
|
|
69
69
|
showPicker(): this;
|
|
70
70
|
}
|
|
71
71
|
|
|
72
|
-
|
|
72
|
+
assignProperties(HTMLSelectElement, $Select, 'select');
|
|
73
73
|
|
|
74
74
|
declare module '#core' {
|
|
75
75
|
export function $(nodeName: 'select'): $Select
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { assignProperties } from "#lib/assignProperties";
|
|
2
2
|
import { $HTMLElement } from "#node/$HTMLElement";
|
|
3
3
|
|
|
4
4
|
export class $TextArea extends $HTMLElement<HTMLTextAreaElement> {
|
|
@@ -9,7 +9,7 @@ export class $TextArea extends $HTMLElement<HTMLTextAreaElement> {
|
|
|
9
9
|
|
|
10
10
|
export interface $TextArea extends $HTMLElement<HTMLTextAreaElement> {}
|
|
11
11
|
|
|
12
|
-
|
|
12
|
+
assignProperties(HTMLTextAreaElement, $TextArea, 'textarea');
|
|
13
13
|
|
|
14
14
|
declare module '#core' {
|
|
15
15
|
export function $(nodeName: 'textarea'): $TextArea
|
package/ext/i18n/README.md
CHANGED
|
@@ -50,4 +50,24 @@ const $t = $.i18n()
|
|
|
50
50
|
// set 'zh' as locale language
|
|
51
51
|
// and fetch file automatically from path
|
|
52
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!
|
|
53
73
|
```
|
package/ext/i18n/src/index.ts
CHANGED
|
@@ -1,13 +1,29 @@
|
|
|
1
1
|
import { _Array_from, _instanceof, _Object_assign } from "amateras/lib/native"
|
|
2
2
|
import { $ } from "amateras/core"
|
|
3
3
|
import { I18n } from "#structure/I18n"
|
|
4
|
-
import type { I18nText as _I18nText, I18nTextOptions } from "#node/I18nText";
|
|
5
4
|
import { I18nDictionary, type I18nDictionaryContext, type I18nDictionaryContextImporter } from "#structure/I18nDictionary";
|
|
5
|
+
import { $Node, $Text } from "amateras/node/$Node";
|
|
6
|
+
import { I18nTranslation as _I18nTranslation, type I18nTranslationOptions } from "#structure/I18nTranslation";
|
|
7
|
+
|
|
8
|
+
$Node.processors.add((_, content) => {
|
|
9
|
+
if (_instanceof(content, _I18nTranslation)) {
|
|
10
|
+
const text = new $Text();
|
|
11
|
+
$.effect(() => text.textContent(content.content$()))
|
|
12
|
+
return [text]
|
|
13
|
+
}
|
|
14
|
+
})
|
|
15
|
+
|
|
16
|
+
$Node.setters.add((value, set) => {
|
|
17
|
+
if (_instanceof(value, _I18nTranslation)) {
|
|
18
|
+
value.content$.signal.subscribe(set);
|
|
19
|
+
return value.content$.value();
|
|
20
|
+
}
|
|
21
|
+
})
|
|
6
22
|
|
|
7
23
|
_Object_assign($, {
|
|
8
24
|
i18n(defaultLocale: string) {
|
|
9
25
|
const i18n = new I18n(defaultLocale);
|
|
10
|
-
const i18nFn = (key: string, options?:
|
|
26
|
+
const i18nFn = (key: string, options?: I18nTranslationOptions) => i18n.translate(key, options);
|
|
11
27
|
_Object_assign(i18nFn, {
|
|
12
28
|
i18n,
|
|
13
29
|
locale(locale: string) {
|
|
@@ -22,6 +38,9 @@ _Object_assign($, {
|
|
|
22
38
|
delete(lang: string) {
|
|
23
39
|
i18n.map.delete(lang);
|
|
24
40
|
return this;
|
|
41
|
+
},
|
|
42
|
+
dir(path: string) {
|
|
43
|
+
return (key: string, options?: I18nTranslationOptions) => i18n.translate(`${path}.${key}`, options)
|
|
25
44
|
}
|
|
26
45
|
})
|
|
27
46
|
return i18nFn
|
|
@@ -30,25 +49,100 @@ _Object_assign($, {
|
|
|
30
49
|
|
|
31
50
|
type ResolvedAsyncDictionary<F extends I18nDictionaryContextImporter> = Awaited<ReturnType<F>>['default'];
|
|
32
51
|
|
|
33
|
-
type
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
52
|
+
type I18nTranslationKey<T> =
|
|
53
|
+
T extends I18nDictionaryContext
|
|
54
|
+
? {
|
|
55
|
+
[K in keyof T]: K extends string
|
|
56
|
+
? K extends '_'
|
|
57
|
+
? never
|
|
58
|
+
: T[K] extends string
|
|
59
|
+
? `${K}`
|
|
60
|
+
: '_' extends keyof T[K]
|
|
61
|
+
? `${K}` | `${K}.${I18nTranslationKey<T[K]>}`
|
|
62
|
+
: `${K}.${I18nTranslationKey<T[K]>}`
|
|
63
|
+
: never;
|
|
64
|
+
}[keyof T]
|
|
65
|
+
: never;
|
|
66
|
+
|
|
67
|
+
type I18nTranslationDirKey<T> =
|
|
68
|
+
T extends I18nDictionaryContext
|
|
69
|
+
? {
|
|
70
|
+
[K in keyof T]: K extends string
|
|
71
|
+
? T[K] extends string
|
|
72
|
+
? never
|
|
73
|
+
: `${K}` | `${K}.${I18nTranslationDirKey<T[K]>}`
|
|
74
|
+
: never;
|
|
75
|
+
}[keyof T]
|
|
76
|
+
: never;
|
|
77
|
+
|
|
78
|
+
type I18nTranslationParams<K extends string, T extends I18nDictionaryContext> =
|
|
79
|
+
FindTranslationByKey<K, T> extends infer O
|
|
80
|
+
? O extends string
|
|
81
|
+
? FindParam<O>
|
|
82
|
+
: never
|
|
83
|
+
: never
|
|
84
|
+
|
|
85
|
+
type FindParam<T extends string> =
|
|
86
|
+
T extends `${string}$${infer Param}$${infer Rest}`
|
|
87
|
+
? Param extends `${string}${' '}${string}`
|
|
88
|
+
? Prettify<{} & FindParam<Rest>>
|
|
89
|
+
: Prettify<Record<Param, any> & FindParam<Rest>>
|
|
90
|
+
: {}
|
|
91
|
+
|
|
92
|
+
type FindTranslationByKey<K extends string, T extends I18nDictionaryContext> =
|
|
93
|
+
K extends `${infer Prefix}.${infer Rest}`
|
|
94
|
+
? Prefix extends keyof T
|
|
95
|
+
? T[Prefix] extends I18nDictionaryContext
|
|
96
|
+
? FindTranslationByKey<Rest, T[Prefix]>
|
|
97
|
+
: T[Prefix]
|
|
98
|
+
: ''
|
|
99
|
+
: T[K] extends object
|
|
100
|
+
? '_' extends keyof T[K]
|
|
101
|
+
? T[K]['_']
|
|
102
|
+
: never
|
|
103
|
+
: T[K]
|
|
104
|
+
|
|
105
|
+
type GetDictionaryContextByKey<K extends string, T extends I18nDictionaryContext> =
|
|
106
|
+
K extends `${infer Prefix}.${infer Rest}`
|
|
107
|
+
? Prefix extends keyof T
|
|
108
|
+
? T[Prefix] extends I18nDictionaryContext
|
|
109
|
+
? GetDictionaryContextByKey<Rest, T[Prefix]>
|
|
110
|
+
: never
|
|
111
|
+
: never
|
|
112
|
+
: T[K] extends object
|
|
113
|
+
? T[K]
|
|
114
|
+
: never
|
|
115
|
+
|
|
116
|
+
type Mixin<A, B> =
|
|
117
|
+
(Omit<A, keyof B> & Omit<B, keyof A>) & {
|
|
118
|
+
[key in (keyof A & keyof B)]:
|
|
119
|
+
A[key] extends object
|
|
120
|
+
? B[key] extends object
|
|
121
|
+
? Mixin<A[key], B[key]>
|
|
122
|
+
: A[key] | B[key]
|
|
123
|
+
: A[key] | B[key]
|
|
124
|
+
}
|
|
40
125
|
|
|
41
126
|
declare module "amateras/core" {
|
|
42
127
|
export namespace $ {
|
|
43
128
|
export interface I18nFunction<D extends I18nDictionaryContext = {}> {
|
|
44
|
-
|
|
129
|
+
<K extends I18nTranslationKey<D>, P extends I18nTranslationParams<K, D>>(path: K, ...params: P extends Record<string, never> ? [] : [P]): I18nTranslation;
|
|
45
130
|
i18n: I18n;
|
|
46
131
|
locale(): string;
|
|
47
132
|
locale(lang?: $Parameter<string>): this;
|
|
48
|
-
add<F extends I18nDictionaryContext | I18nDictionaryContextImporter>(lang: string, dictionary: F): I18nFunction<D
|
|
133
|
+
add<F extends I18nDictionaryContext | I18nDictionaryContextImporter>(lang: string, dictionary: F): I18nFunction<Mixin<D, (F extends I18nDictionaryContextImporter ? ResolvedAsyncDictionary<F> : F)>>;
|
|
49
134
|
delete(lang: string): this;
|
|
135
|
+
dir<K extends I18nTranslationDirKey<D>>(path: K): I18nFunction<GetDictionaryContextByKey<K, D>>
|
|
50
136
|
}
|
|
51
137
|
export function i18n(defaultLocale: string): I18nFunction;
|
|
52
|
-
export type
|
|
138
|
+
export type I18nTranslation = _I18nTranslation;
|
|
139
|
+
|
|
140
|
+
export interface $NodeContentMap {
|
|
141
|
+
i18n: I18nTranslation
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
export interface $NodeParameterMap<T> {
|
|
145
|
+
i18n: I18nTranslation
|
|
146
|
+
}
|
|
53
147
|
}
|
|
54
148
|
}
|
|
@@ -1,6 +1,7 @@
|
|
|
1
|
-
import { _instanceof } from "amateras/lib/native";
|
|
2
|
-
import { I18nText, type I18nTextOptions } from "#node/I18nText";
|
|
1
|
+
import { _instanceof, _null } from "amateras/lib/native";
|
|
3
2
|
import { I18nDictionary } from "#structure/I18nDictionary";
|
|
3
|
+
import 'amateras/signal';
|
|
4
|
+
import { I18nTranslation, type I18nTranslationOptions } from "./I18nTranslation";
|
|
4
5
|
|
|
5
6
|
export class I18n {
|
|
6
7
|
locale$ = $.signal<string>('');
|
|
@@ -22,19 +23,22 @@ export class I18n {
|
|
|
22
23
|
locale(): string;
|
|
23
24
|
locale(locale: string): this;
|
|
24
25
|
locale(locale?: string) {
|
|
25
|
-
if (!arguments.length) return this.locale
|
|
26
|
-
if (locale)
|
|
26
|
+
if (!arguments.length) return this.locale$.value();
|
|
27
|
+
if (locale) {
|
|
28
|
+
if (!this.map.has(locale)) locale = locale.split('-')[0]!;
|
|
29
|
+
if (this.map.has(locale)) this.locale$.set(locale)
|
|
30
|
+
}
|
|
27
31
|
return this;
|
|
28
32
|
}
|
|
29
33
|
|
|
30
|
-
dictionary(locale = this.locale
|
|
31
|
-
if (!locale) return
|
|
34
|
+
dictionary(locale = this.locale$.value()) {
|
|
35
|
+
if (!locale) return _null;
|
|
32
36
|
const dictionary = this.map.get(locale);
|
|
33
37
|
return dictionary;
|
|
34
38
|
}
|
|
35
39
|
|
|
36
|
-
translate(key: string, options?:
|
|
37
|
-
return new
|
|
40
|
+
translate(key: string, options?: I18nTranslationOptions) {
|
|
41
|
+
return new I18nTranslation(this, key, options);
|
|
38
42
|
}
|
|
39
43
|
}
|
|
40
44
|
|
|
@@ -1,10 +1,10 @@
|
|
|
1
|
-
import { _instanceof, isObject } from "amateras/lib/native";
|
|
1
|
+
import { _instanceof, isFunction, isObject } from "amateras/lib/native";
|
|
2
2
|
|
|
3
3
|
export class I18nDictionary {
|
|
4
4
|
#context: I18nDictionaryContext | Promise<I18nDictionaryContext> | null = null;
|
|
5
5
|
#fetch: I18nDictionaryContextImporter | null = null;
|
|
6
6
|
constructor(resolver: I18nDictionaryContext | I18nDictionaryContextImporter) {
|
|
7
|
-
if (
|
|
7
|
+
if (isFunction(resolver)) this.#fetch = resolver;
|
|
8
8
|
else this.#context = resolver;
|
|
9
9
|
}
|
|
10
10
|
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
import { _Array_from, _null, isUndefined } from "amateras/lib/native";
|
|
2
|
+
import type { I18n } from "#structure/I18n";
|
|
3
|
+
|
|
4
|
+
export class I18nTranslation {
|
|
5
|
+
i18n: I18n;
|
|
6
|
+
key: string;
|
|
7
|
+
options: I18nTranslationOptions | undefined;
|
|
8
|
+
content$ = $.signal('');
|
|
9
|
+
constructor(i18n: I18n, key: string, options?: I18nTranslationOptions) {
|
|
10
|
+
this.i18n = i18n;
|
|
11
|
+
this.key = key;
|
|
12
|
+
this.options = options;
|
|
13
|
+
i18n.locale$.signal.subscribe(() => this.update())
|
|
14
|
+
this.update();
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
async update() {
|
|
18
|
+
const {key, i18n, options} = this;
|
|
19
|
+
const contentUpdate = (content: string) => this.content$.set(content);
|
|
20
|
+
update: {
|
|
21
|
+
const dictionary = i18n.dictionary();
|
|
22
|
+
if (!dictionary) { contentUpdate(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 || !options) { contentUpdate(target); break update }
|
|
27
|
+
const matches = target.matchAll(/(\$([a-zA-Z0-9_]+)\$)/g);
|
|
28
|
+
const content = snippets.map(text => [text, options[matches.next().value?.at(2)!] ?? null].join('')).join('');
|
|
29
|
+
contentUpdate(content);
|
|
30
|
+
}
|
|
31
|
+
return this;
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
export type I18nTranslationOptions = {[key: string]: any}
|