amateras 0.5.0 → 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 (81) hide show
  1. package/README.md +23 -26
  2. package/ext/html/node/$Anchor.ts +2 -2
  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 +2 -2
  8. package/ext/html/node/$Label.ts +2 -2
  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/I18nTranslation.ts +35 -0
  18. package/ext/idb/src/structure/builder/$IDBBuilder.ts +8 -8
  19. package/ext/markdown/README.md +53 -0
  20. package/ext/markdown/package.json +7 -0
  21. package/ext/markdown/src/index.ts +3 -0
  22. package/ext/markdown/src/lib/type.ts +26 -0
  23. package/ext/markdown/src/lib/util.ts +21 -0
  24. package/ext/markdown/src/structure/Markdown.ts +54 -0
  25. package/ext/markdown/src/structure/MarkdownLexer.ts +111 -0
  26. package/ext/markdown/src/structure/MarkdownParser.ts +33 -0
  27. package/ext/markdown/src/syntax/alert.ts +46 -0
  28. package/ext/markdown/src/syntax/blockquote.ts +35 -0
  29. package/ext/markdown/src/syntax/bold.ts +11 -0
  30. package/ext/markdown/src/syntax/code.ts +11 -0
  31. package/ext/markdown/src/syntax/codeblock.ts +44 -0
  32. package/ext/markdown/src/syntax/heading.ts +14 -0
  33. package/ext/markdown/src/syntax/horizontalRule.ts +11 -0
  34. package/ext/markdown/src/syntax/image.ts +23 -0
  35. package/ext/markdown/src/syntax/italic.ts +11 -0
  36. package/ext/markdown/src/syntax/link.ts +46 -0
  37. package/ext/markdown/src/syntax/list.ts +121 -0
  38. package/ext/markdown/src/syntax/table.ts +67 -0
  39. package/ext/markdown/src/syntax/text.ts +19 -0
  40. package/ext/router/README.md +111 -17
  41. package/ext/router/package.json +10 -0
  42. package/ext/router/src/index.ts +69 -0
  43. package/ext/router/src/node/Page.ts +34 -0
  44. package/ext/router/src/node/Router.ts +191 -0
  45. package/ext/router/{node → src/node}/RouterAnchor.ts +13 -2
  46. package/ext/router/src/structure/PageBuilder.ts +24 -0
  47. package/ext/router/src/structure/Route.ts +105 -0
  48. package/ext/signal/README.md +93 -0
  49. package/ext/signal/package.json +9 -0
  50. package/ext/signal/src/index.ts +128 -0
  51. package/{src → ext/signal/src}/structure/Signal.ts +6 -10
  52. package/ext/ssr/index.ts +4 -4
  53. package/ext/ui/lib/VirtualScroll.ts +25 -0
  54. package/ext/ui/node/Accordian.ts +97 -0
  55. package/ext/ui/node/Form.ts +53 -0
  56. package/ext/ui/node/Grid.ts +0 -0
  57. package/ext/ui/node/Table.ts +43 -0
  58. package/ext/ui/node/Tabs.ts +114 -0
  59. package/ext/ui/node/Toast.ts +16 -0
  60. package/ext/ui/node/Waterfall.ts +72 -0
  61. package/ext/ui/package.json +11 -0
  62. package/package.json +6 -3
  63. package/src/core.ts +30 -60
  64. package/src/global.ts +9 -2
  65. package/src/index.ts +1 -2
  66. package/src/lib/assignProperties.ts +57 -0
  67. package/src/lib/native.ts +25 -8
  68. package/src/lib/uppercase.ts +3 -0
  69. package/src/node/$Element.ts +7 -41
  70. package/src/node/$EventTarget.ts +45 -0
  71. package/src/node/$Node.ts +60 -65
  72. package/src/node/$Virtual.ts +65 -0
  73. package/src/node.ts +7 -6
  74. package/ext/i18n/src/node/I18nText.ts +0 -35
  75. package/ext/markdown/index.ts +0 -121
  76. package/ext/router/index.ts +0 -73
  77. package/ext/router/node/Page.ts +0 -27
  78. package/ext/router/node/Route.ts +0 -54
  79. package/ext/router/node/Router.ts +0 -149
  80. package/src/lib/assign.ts +0 -38
  81. 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
- ## DOM Operating
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
- $(document.body).content([
39
- $('button')
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
- ## State Management
48
- ```ts
49
- import 'amateras';
43
+ // the console message will fired when count$ changes
44
+ $.effect(() => console.log( count$() ))
50
45
 
51
- const count$ = $.signal(0);
52
- const doubleCount$ = $.compute(() => count() * 2);
46
+ $(document.body).content([
47
+ // Display Counts
48
+ $('p').content( $`Counts: ${count$}` ),
53
49
 
54
- setInterval(() => count$(oldValue => oldValue + 1), 1000);
50
+ // Display Double Counts
51
+ $('p').content( $`Double Counts: ${doubleCount$}` ),
55
52
 
56
- $(document.body).content([
57
- $('p').content($`Count: ${count$}`),
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';
@@ -102,10 +98,11 @@ $(document.body).content([
102
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 | 5.67 kB | 2.52 kB | Core |
106
- | amateras/html | 0.99 kB | 0.26 kB | Import HTMLElement types and methods |
107
- | [amateras/css](./ext/css/README.md) | 3.65 kB | 1.41 kB | Style in JS |
108
- | [amateras/router](./ext/router/README.md) | 3.79 kB | 1.71 kB | Amateras Router |
109
- | [amateras/i18n](./ext/i18n/README.md) | 1.48 kB | 0.58 kB | I18n translations |
110
- | [amateras/idb](./ext/idb/README.md) | 5.35 kB | 2.03 kB | IndexedDB Builder and API Wrapper |
111
- | amateras/markdown | 1.89 kB | 0.67 kB | Markdown Converter |
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 |
@@ -1,4 +1,4 @@
1
- import { assignHelper } from "#lib/assignHelper";
1
+ import { assignProperties } from "#lib/assignProperties";
2
2
  import { $HTMLElement } from "#node/$HTMLElement";
3
3
 
4
4
  export class $Anchor extends $HTMLElement<HTMLAnchorElement> {
@@ -46,4 +46,4 @@ declare module '#core' {
46
46
  export function $(nodeName: 'a'): $Anchor
47
47
  }
48
48
 
49
- assignHelper(HTMLAnchorElement, $Anchor, 'a');
49
+ assignProperties(HTMLAnchorElement, $Anchor, 'a');
@@ -1,4 +1,4 @@
1
- import { assignHelper } from "#lib/assignHelper";
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
- assignHelper(HTMLCanvasElement, $Canvas, 'canvas');
12
+ assignProperties(HTMLCanvasElement, $Canvas, 'canvas');
13
13
 
14
14
  declare module '#core' {
15
15
  export function $(nodeName: 'canvas'): $Canvas
@@ -1,4 +1,4 @@
1
- import { assignHelper } from "#lib/assignHelper";
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
- assignHelper(HTMLDialogElement, $Dialog, 'dialog');
12
+ assignProperties(HTMLDialogElement, $Dialog, 'dialog');
13
13
 
14
14
  declare module '#core' {
15
15
  export function $(nodeName: 'dialog'): $Dialog
@@ -1,4 +1,4 @@
1
- import { assignHelper } from "#lib/assignHelper";
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
- assignHelper(HTMLFormElement, $Form, 'form');
12
+ assignProperties(HTMLFormElement, $Form, 'form');
13
13
 
14
14
  declare module '#core' {
15
15
  export function $(nodeName: 'form'): $Form
@@ -1,4 +1,4 @@
1
- import { assignHelper } from "#lib/assignHelper";
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
- assignHelper(HTMLImageElement, $Image, 'img');
68
+ assignProperties(HTMLImageElement, $Image, 'img');
69
69
 
70
70
  declare module '#core' {
71
71
  export function $(nodeName: 'img'): $Image
@@ -1,4 +1,4 @@
1
- import { assignHelper } from "#lib/assignHelper";
1
+ import { assignProperties } from "#lib/assignProperties";
2
2
  import { $HTMLElement } from "#node/$HTMLElement";
3
3
 
4
4
  export class $Input extends $HTMLElement<HTMLInputElement> {
@@ -186,7 +186,7 @@ export type InputType =
186
186
  | 'url'
187
187
  | 'week';
188
188
 
189
- assignHelper(HTMLInputElement, $Input, 'input');
189
+ assignProperties(HTMLInputElement, $Input, 'input');
190
190
 
191
191
  declare module '#core' {
192
192
  export function $(nodeName: 'input'): $Input
@@ -1,4 +1,4 @@
1
- import { assignHelper } from "#lib/assignHelper";
1
+ import { assignProperties } from "#lib/assignProperties";
2
2
  import { $HTMLElement } from "#node/$HTMLElement";
3
3
 
4
4
  export class $Label extends $HTMLElement<HTMLLabelElement> {
@@ -18,7 +18,7 @@ export interface $Label extends $HTMLElement<HTMLLabelElement> {
18
18
  htmlFor(): string;
19
19
  }
20
20
 
21
- assignHelper(HTMLLabelElement, $Label, 'label');
21
+ assignProperties(HTMLLabelElement, $Label, 'label');
22
22
 
23
23
  declare module '#core' {
24
24
  export function $(nodeName: 'label'): $Label
@@ -1,4 +1,4 @@
1
- import { assignHelper } from "#lib/assignHelper";
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
- assignHelper(HTMLMediaElement, $Media, 'media');
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 { assignHelper } from "#lib/assignHelper";
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
- assignHelper(HTMLOptGroupElement, $OptGroup, 'optgroup');
19
+ assignProperties(HTMLOptGroupElement, $OptGroup, 'optgroup');
20
20
 
21
21
  declare module '#core' {
22
22
  export function $(nodeName: 'optgroup'): $OptGroup
@@ -1,4 +1,4 @@
1
- import { assignHelper } from "#lib/assignHelper";
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
- assignHelper(HTMLOptionElement, $Option, 'option');
36
+ assignProperties(HTMLOptionElement, $Option, 'option');
37
37
 
38
38
  declare module '#core' {
39
39
  export function $(nodeName: 'option'): $Option
@@ -1,4 +1,4 @@
1
- import { assignHelper } from "#lib/assignHelper";
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
- assignHelper(HTMLSelectElement, $Select, 'select');
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 { assignHelper } from "#lib/assignHelper";
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
- assignHelper(HTMLTextAreaElement, $TextArea, 'textarea');
12
+ assignProperties(HTMLTextAreaElement, $TextArea, 'textarea');
13
13
 
14
14
  declare module '#core' {
15
15
  export function $(nodeName: 'textarea'): $TextArea
@@ -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
  ```
@@ -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?: I18nTextOptions) => i18n.translate(key, 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 DeepKeys<T> = T extends I18nDictionaryContext
34
- ? {
35
- [K in keyof T]: K extends string
36
- ? K extends '_' ? never : `${K}` | `${K}.${DeepKeys<T[K]>}`
37
- : never;
38
- }[keyof T]
39
- : never;
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
- (path: DeepKeys<D>, ...args: any[]): I18nText;
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 | (F extends I18nDictionaryContextImporter ? ResolvedAsyncDictionary<F> : F)>;
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 I18nText = _I18nText;
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) this.locale$.set(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 null;
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?: I18nTextOptions) {
37
- return new I18nText(this, key, options);
40
+ translate(key: string, options?: I18nTranslationOptions) {
41
+ return new I18nTranslation(this, key, options);
38
42
  }
39
43
  }
40
44
 
@@ -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}
@@ -84,7 +84,7 @@ export class $IDBBuilder<Config extends $IDBConfig = { name: string, stores: {},
84
84
  const initialCreateDB = () => {
85
85
  debug(`No IDB detected, create IDB`);
86
86
  const {transaction, result: idb} = initDBRequest;
87
- forEach(storeMap, (storeBuilder, name) => {
87
+ forEach(storeMap, ([name, storeBuilder]) => {
88
88
  createStoresMap.set(name, storeBuilder);
89
89
  createIndexMap.set(storeBuilder, new Map(storeBuilder.indexes))
90
90
  })
@@ -108,11 +108,11 @@ export class $IDBBuilder<Config extends $IDBConfig = { name: string, stores: {},
108
108
  // get unused stores
109
109
  transaction && forEach(_Array_from(transaction[objectStoreNames]), name => storeMap.has(name) && unusedStoreNameList.push(name))
110
110
  // check store config matches
111
- forEach(storeMap, (storeBuilder, storeName) => {
111
+ forEach(storeMap, ([storeName, storeBuilder]) => {
112
112
  const {keyPath, autoIncrement} = storeBuilder.config;
113
113
  const indexMap = new Map();
114
114
  const checkIndexes = () =>
115
- forEach(storeBuilder.indexes, (indexBuilder, indexName) => {
115
+ forEach(storeBuilder.indexes, ([indexName, indexBuilder]) => {
116
116
  const [index] = trycatch(() => store?.index(indexName));
117
117
  const CONFIG_CHANGED = _JSON_stringify(indexBuilder.keyPath) !== _JSON_stringify(index?.keyPath)
118
118
  || !!indexBuilder.multiEntry !== index?.multiEntry
@@ -182,20 +182,20 @@ export class $IDBBuilder<Config extends $IDBConfig = { name: string, stores: {},
182
182
  /** 'versionchange' type transaction */
183
183
  const transaction = req.transaction as IDBTransaction;
184
184
  // create stores
185
- forEach(createStoresMap, ({config}, name) => {
185
+ forEach(createStoresMap, ([name, {config}]) => {
186
186
  idb[createObjectStore](name, config);
187
187
  debug(`Store Created: ${name}`);
188
188
  });
189
189
  // upgrade stores
190
- forEach(upgradeStoreMap, ({config}, name) => {
190
+ forEach(upgradeStoreMap, ([name, {config}]) => {
191
191
  idb[deleteObjectStore](name);
192
192
  idb[createObjectStore](name, config);
193
193
  debug(`Store Upgraded: ${name}`);
194
194
  })
195
195
  // create indexes
196
- forEach(createIndexMap, (indexMap, {config: {name}}) => {
196
+ forEach(createIndexMap, ([{config: {name}}, indexMap]) => {
197
197
  const store = transaction.objectStore(name);
198
- forEach(indexMap, ({keyPath, ...config}, indexName) => {
198
+ forEach(indexMap, ([indexName, {keyPath, ...config}]) => {
199
199
  // if indexes existed, delete and create again
200
200
  if (store.indexNames.contains(indexName)) store.deleteIndex(indexName);
201
201
  store.createIndex(indexName, keyPath, config);
@@ -208,7 +208,7 @@ export class $IDBBuilder<Config extends $IDBConfig = { name: string, stores: {},
208
208
  debug(`Unused Store Deleted: ${name}`);
209
209
  });
210
210
  // open db again for insert objects
211
- forEach(cachedObjectMap, (objectList, storeName) => {
211
+ forEach(cachedObjectMap, ([storeName, objectList]) => {
212
212
  const store = transaction.objectStore(storeName);
213
213
  forEach(objectList, ({key, value}) => {
214
214
  if (store.autoIncrement || store.keyPath) store.add(value);
@@ -0,0 +1,53 @@
1
+ # amateras/markdown
2
+
3
+ ## Usage
4
+ ```ts
5
+ import { Markdown } from 'amateras/markdown';
6
+
7
+ const markdown = new Markdown();
8
+
9
+ markdown.parseHTML('# Title'); // => <h1>Title</h1>
10
+ ```
11
+
12
+ ## Add Custom Markdown Rules
13
+ ```ts
14
+ markdown.lexer.blockTokenizers.set('CUSTOM_TYPE', {
15
+ regex: /#! (.+)/,
16
+ handle: matches => {
17
+ content: lexer.inlineTokenize(matches[1]!)
18
+ }
19
+ })
20
+
21
+ markdown.parser.processors.set('CUSTOM_TYPE', token => {
22
+ return `<custom>${markdown.parser.parse(token.content!)}</custom>`
23
+ })
24
+ ```
25
+
26
+ ## Import Syntax
27
+ ```ts
28
+ import { MarkdownParser, MarkdownLexer } from 'amateras/markdown';
29
+ import { blockquoteProcessor, blockquoteTokenizer } from 'amateras/markdown/syntax/blockquote';
30
+ import { headingProcessor, headingTokenizer } from 'amateras/markdown/syntax/heading';
31
+
32
+ const lexer = new MarkdownLexer();
33
+ const parser = new MarkdownParser();
34
+
35
+ lexer.use(
36
+ blockquoteTokenizer,
37
+ headingTokenizer
38
+ )
39
+
40
+ parser.use(
41
+ blockquoteProcessor,
42
+ headingProcessor
43
+ )
44
+
45
+ function parseHTML(str: string) {
46
+ const tokens = lexer.blockTokenize(str);
47
+ return parser.parse(tokens);
48
+ }
49
+
50
+ parseHTML('# Title') // => <h1>Title</h1>
51
+ parseHTML('> This is Blockquote') // => <blockquote>This is Blockquote</blockquote>
52
+ parseHTML('- List') // => - List
53
+ ```
@@ -4,5 +4,12 @@
4
4
  "amateras": "../../"
5
5
  },
6
6
  "imports": {
7
+ "#structure/*": "./src/structure/*.ts",
8
+ "#lib/*": "./src/lib/*.ts"
9
+ },
10
+ "exports": {
11
+ "./structure/*": "./src/structure/*.ts",
12
+ "./lib/*": "./src/lib/*.ts",
13
+ "./syntax/*": "./src/syntax/*.ts"
7
14
  }
8
15
  }
@@ -0,0 +1,3 @@
1
+ export * from '#structure/MarkdownLexer';
2
+ export * from '#structure/MarkdownParser';
3
+ export * from "#structure/Markdown";