amateras 0.2.0 → 0.4.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 (78) hide show
  1. package/README.md +25 -7
  2. package/ext/css/README.md +19 -0
  3. package/ext/css/src/index.ts +395 -322
  4. package/ext/css/src/lib/colorAssign.ts +6 -0
  5. package/ext/css/src/lib/colors/amber.ts +25 -0
  6. package/ext/css/src/lib/colors/blackwhite.ts +13 -0
  7. package/ext/css/src/lib/colors/blue.ts +25 -0
  8. package/ext/css/src/lib/colors/cyan.ts +25 -0
  9. package/ext/css/src/lib/colors/emerald.ts +25 -0
  10. package/ext/css/src/lib/colors/fuchsia.ts +25 -0
  11. package/ext/css/src/lib/colors/gray.ts +25 -0
  12. package/ext/css/src/lib/colors/green.ts +25 -0
  13. package/ext/css/src/lib/colors/indigo.ts +25 -0
  14. package/ext/css/src/lib/colors/lime.ts +25 -0
  15. package/ext/css/src/lib/colors/neutral.ts +25 -0
  16. package/ext/css/src/lib/colors/orange.ts +25 -0
  17. package/ext/css/src/lib/colors/pink.ts +25 -0
  18. package/ext/css/src/lib/colors/purple.ts +25 -0
  19. package/ext/css/src/lib/colors/red.ts +25 -0
  20. package/ext/css/src/lib/colors/rose.ts +25 -0
  21. package/ext/css/src/lib/colors/sky.ts +25 -0
  22. package/ext/css/src/lib/colors/slate.ts +25 -0
  23. package/ext/css/src/lib/colors/stone.ts +25 -0
  24. package/ext/css/src/lib/colors/teal.ts +25 -0
  25. package/ext/css/src/lib/colors/violet.ts +25 -0
  26. package/ext/css/src/lib/colors/yellow.ts +25 -0
  27. package/ext/css/src/lib/colors/zinc.ts +25 -0
  28. package/ext/css/src/lib/colors.ts +23 -0
  29. package/ext/css/src/structure/$CSSContainerRule.ts +13 -0
  30. package/ext/css/src/structure/$CSSKeyframesRule.ts +1 -5
  31. package/ext/css/src/structure/$CSSMediaRule.ts +3 -23
  32. package/ext/css/src/structure/$CSSRule.ts +6 -18
  33. package/ext/css/src/structure/$CSSStyleRule.ts +5 -14
  34. package/ext/css/src/structure/$CSSVariable.ts +3 -3
  35. package/ext/html/html.ts +1 -13
  36. package/ext/html/node/$Anchor.ts +31 -1
  37. package/ext/html/node/$Image.ts +54 -1
  38. package/ext/html/node/$Input.ts +154 -1
  39. package/ext/html/node/$OptGroup.ts +8 -1
  40. package/ext/html/node/$Option.ts +25 -1
  41. package/ext/html/node/$Select.ts +61 -1
  42. package/ext/i18n/README.md +53 -0
  43. package/ext/i18n/package.json +10 -0
  44. package/ext/i18n/src/index.ts +54 -0
  45. package/ext/i18n/src/node/I18nText.ts +35 -0
  46. package/ext/i18n/src/structure/I18n.ts +40 -0
  47. package/ext/i18n/src/structure/I18nDictionary.ts +31 -0
  48. package/ext/markdown/index.ts +123 -0
  49. package/ext/router/index.ts +13 -4
  50. package/ext/router/node/Page.ts +1 -0
  51. package/ext/router/node/Route.ts +4 -3
  52. package/ext/router/node/Router.ts +62 -17
  53. package/ext/router/node/RouterAnchor.ts +1 -1
  54. package/ext/ssr/index.ts +7 -5
  55. package/ext/ui/lib/VirtualScroll.ts +24 -0
  56. package/ext/ui/node/Accordian.ts +97 -0
  57. package/ext/ui/node/Tabs.ts +114 -0
  58. package/ext/ui/node/Toast.ts +16 -0
  59. package/ext/ui/node/Waterfall.ts +73 -0
  60. package/ext/ui/package.json +11 -0
  61. package/package.json +6 -7
  62. package/src/core.ts +36 -19
  63. package/src/global.ts +4 -0
  64. package/src/lib/assign.ts +12 -12
  65. package/src/lib/assignHelper.ts +2 -2
  66. package/src/lib/chain.ts +3 -0
  67. package/src/lib/debounce.ts +7 -0
  68. package/src/lib/env.ts +2 -0
  69. package/src/lib/native.ts +22 -24
  70. package/src/lib/randomId.ts +1 -1
  71. package/src/lib/sleep.ts +1 -1
  72. package/src/node/$Element.ts +301 -35
  73. package/src/node/$HTMLElement.ts +94 -1
  74. package/src/node/$Node.ts +148 -54
  75. package/src/node/$Virtual.ts +58 -0
  76. package/src/{node/node.ts → node.ts} +2 -4
  77. package/src/structure/Signal.ts +3 -3
  78. package/ext/css/src/structure/$CSSKeyframeRule.ts +0 -14
@@ -1,30 +1,10 @@
1
1
  import { _Array_from, _instanceof } from "amateras/lib/native";
2
2
  import { $CSSRule } from "#structure/$CSSRule";
3
- import { $CSSStyleRule } from "./$CSSStyleRule";
4
3
 
5
4
  export class $CSSMediaRule extends $CSSRule {
6
5
  condition: string;
7
- constructor(condition: string) {
8
- super();
9
- this.condition = condition;
10
- }
11
-
12
- get css(): string {
13
- function findOwnerMediaRule(rule: $CSSRule, contextRules: $CSSMediaRule[]) {
14
- const ownerRule = rule.ownerRule;
15
- if (!ownerRule) return contextRules;
16
- if (_instanceof(ownerRule, $CSSMediaRule)) return findOwnerMediaRule(ownerRule, [ownerRule, ...contextRules]);
17
- else return findOwnerMediaRule(ownerRule, contextRules);
18
- }
19
-
20
- function findChildRules(rule: $CSSRule, rules: $CSSStyleRule[] = []) {
21
- _Array_from(rule.rules).forEach((_rule => {
22
- if (!_instanceof(_rule, $CSSStyleRule)) return;
23
- rules.push(_rule);
24
- return findChildRules(_rule, rules);
25
- }))
26
- return rules
27
- }
28
- return `@media ${findOwnerMediaRule(this, [this]).map(rule => rule.condition).join(' and ')} { ${findChildRules(this).map(rule => rule.css).join(' ')} }`
6
+ constructor(selector: string) {
7
+ super(selector);
8
+ this.condition = selector.replace('@media ', '');
29
9
  }
30
10
  }
@@ -1,25 +1,13 @@
1
- import { _instanceof } from "amateras/lib/native";
2
- import { $CSSMediaRule } from "#structure/$CSSMediaRule";
1
+ import { _Array_from, _instanceof, _Object_fromEntries } from "amateras/lib/native";
3
2
 
4
3
  export abstract class $CSSRule {
5
4
  rules = new Set<$CSSRule>();
6
- ownerRule: $CSSRule | null = null;
7
- constructor() {}
8
-
9
- abstract get css(): string;
10
-
11
- get mediaRules() {
12
- const rules: $CSSMediaRule[] = []
13
- this.rules.forEach(rule => {
14
- if (_instanceof(rule, $CSSMediaRule)) rules.push(rule);
15
- rules.push(...rule.mediaRules)
16
- })
17
- return rules;
5
+ selector: string;
6
+ constructor(selector: string) {
7
+ this.selector = selector;
18
8
  }
19
9
 
20
- addRule(rule: $CSSRule) {
21
- this.rules.add(rule);
22
- rule.ownerRule = this;
23
- return this;
10
+ get options(): {[key: string]: any} {
11
+ return _Object_fromEntries(_Array_from(this.rules).map(rule => [rule.selector, rule.options]))
24
12
  }
25
13
  }
@@ -1,23 +1,14 @@
1
1
  import type { $CSSDeclaration } from "#structure/$CSSDeclaration";
2
2
  import { $CSSRule } from "#structure/$CSSRule";
3
- import { _Array_from, _instanceof } from "amateras/lib/native";
3
+ import { _Array_from, _instanceof, _Object_fromEntries } from "amateras/lib/native";
4
4
 
5
5
  export class $CSSStyleRule extends $CSSRule {
6
- context: string[] = [];
7
6
  declarations = new Map<string, $CSSDeclaration>();
8
- className: string = '';
9
- constructor(context: string[] = []) {
10
- super();
11
- this.context = context;
7
+ constructor(selector: string) {
8
+ super(selector);
12
9
  }
13
10
 
14
- get css(): string {
15
- return `${this.selector} { ${_Array_from(this.declarations).map(([_, dec]) => `${dec}`).join(' ')} }`
16
- }
17
-
18
- get selector() {
19
- const ctx: string[][] = [];
20
- this.context.forEach((part, i) => ctx.push(part.split(',').map(sel => ctx[i - 1] ? ctx[i - 1]!.map(prefix => `${prefix} ${sel.trim()}`) : [sel.trim()]).flat()))
21
- return ctx.at(-1)?.join(', ')
11
+ get options(): {[key: string]: any} {
12
+ return {..._Object_fromEntries(_Array_from(this.declarations).map(([_, dec]) => [dec.key, dec])), ...super.options}
22
13
  }
23
14
  }
@@ -1,7 +1,7 @@
1
- export class $CSSVariable {
1
+ export class $CSSVariable<V = string> {
2
2
  key: string;
3
- value: string;
4
- constructor(key: string, value: string) {
3
+ value: V;
4
+ constructor(key: string, value: V) {
5
5
  this.key = key;
6
6
  this.value = value;
7
7
  }
package/ext/html/html.ts CHANGED
@@ -10,16 +10,4 @@ import './node/$Media';
10
10
  import './node/$OptGroup';
11
11
  import './node/$Option';
12
12
  import './node/$Select';
13
- import './node/$TextArea';
14
- export * from './node/$Anchor';
15
- export * from './node/$Canvas';
16
- export * from './node/$Dialog';
17
- export * from './node/$Form';
18
- export * from './node/$Image';
19
- export * from './node/$Input';
20
- export * from './node/$Label';
21
- export * from './node/$Media';
22
- export * from './node/$OptGroup';
23
- export * from './node/$Option';
24
- export * from './node/$Select';
25
- export * from './node/$TextArea';
13
+ import './node/$TextArea';
@@ -8,10 +8,40 @@ export class $Anchor extends $HTMLElement<HTMLAnchorElement> {
8
8
  }
9
9
 
10
10
  export interface $Anchor extends $HTMLElement<HTMLAnchorElement> {
11
- href(href: string): this;
11
+ /** {@link HTMLAnchorElement.href} */
12
+ href(href: $Parameter<string>): this;
12
13
  href(): string;
14
+ /** {@link HTMLAnchorElement.hreflang} */
15
+ hreflang(hreflang: $Parameter<string>): this;
16
+ hreflang(): string;
17
+ /** {@link HTMLAnchorElement.download} */
18
+ download(download: $Parameter<string>): this;
19
+ download(): string;
20
+ /** {@link HTMLAnchorElement.ping} */
21
+ ping(ping: $Parameter<string>): this;
22
+ ping(): string;
23
+ /** {@link HTMLAnchorElement.referrerPolicy} */
24
+ referrerPolicy(referrerPolicy: $Parameter<string>): this;
25
+ referrerPolicy(): string;
26
+ /** {@link HTMLAnchorElement.rel} */
27
+ rel(rel: $Parameter<string>): this;
28
+ rel(): string;
29
+ /** {@link HTMLAnchorElement.relList} */
30
+ relList(relList: $Parameter<string>): this;
31
+ relList(): DOMTokenList;
32
+ /** {@link HTMLAnchorElement.target} */
33
+ target(target: $Parameter<AnchorTarget>): this;
34
+ target(): AnchorTarget;
35
+ /** {@link HTMLAnchorElement.text} */
36
+ text(text: $Parameter<string>): this;
37
+ text(): string;
38
+ /** {@link HTMLAnchorElement.type} */
39
+ type(type: $Parameter<string>): this;
40
+ type(): string;
13
41
  }
14
42
 
43
+ export type AnchorTarget = '_self' | '_blank' | '_parent' | '_top' | '_unfenced_top' | '';
44
+
15
45
  declare module '#core' {
16
46
  export function $(nodeName: 'a'): $Anchor
17
47
  }
@@ -8,8 +8,61 @@ export class $Image extends $HTMLElement<HTMLImageElement> {
8
8
  }
9
9
 
10
10
  export interface $Image extends $HTMLElement<HTMLImageElement> {
11
- src(src: string): this;
11
+ /** {@link HTMLImageElement.complete} */
12
+ readonly complete: boolean;
13
+ /** {@link HTMLImageElement.currentSrc} */
14
+ readonly currentSrc: string;
15
+ /** {@link HTMLImageElement.naturalHeight} */
16
+ readonly naturalHeight: number;
17
+ /** {@link HTMLImageElement.naturalWidth} */
18
+ readonly naturalWidth: number;
19
+ /** {@link HTMLImageElement.x} */
20
+ readonly x: number;
21
+ /** {@link HTMLImageElement.y} */
22
+ readonly y: number;
23
+
24
+ /** {@link HTMLImageElement.decode} */
25
+ decode(): Promise<this>;
26
+
27
+ /** {@link HTMLImageElement.alt} */
28
+ alt(alt: $Parameter<string>): this;
29
+ alt(): string;
30
+ /** {@link HTMLImageElement.crossOrigin} */
31
+ crossOrigin(crossOrigin: $Parameter<string | null>): this;
32
+ crossOrigin(): string | null;
33
+ /** {@link HTMLImageElement.decoding} */
34
+ decoding(decoding: $Parameter<"async" | "sync" | "auto">): this;
35
+ decoding(): "async" | "sync" | "auto";
36
+ /** {@link HTMLImageElement.fetchPriority} */
37
+ fetchPriority(fetchPriority: $Parameter<"high" | "low" | "auto">): this;
38
+ fetchPriority(): "high" | "low" | "auto";
39
+ /** {@link HTMLImageElement.height} */
40
+ height(height: $Parameter<number>): this;
41
+ height(): number;
42
+ /** {@link HTMLImageElement.isMap} */
43
+ isMap(isMap: $Parameter<boolean>): this;
44
+ isMap(): boolean;
45
+ /** {@link HTMLImageElement.loading} */
46
+ loading(loading: $Parameter<"eager" | "lazy">): this;
47
+ loading(): "eager" | "lazy";
48
+ /** {@link HTMLImageElement.referrerPolicy} */
49
+ referrerPolicy(referrerPolicy: $Parameter<string>): this;
50
+ referrerPolicy(): string;
51
+ /** {@link HTMLImageElement.sizes} */
52
+ sizes(sizes: $Parameter<string>): this;
53
+ sizes(): string;
54
+ /** {@link HTMLImageElement.src} */
55
+ src(src: $Parameter<string>): this;
12
56
  src(): string;
57
+ /** {@link HTMLImageElement.srcset} */
58
+ srcset(srcset: $Parameter<string>): this;
59
+ srcset(): string;
60
+ /** {@link HTMLImageElement.useMap} */
61
+ useMap(useMap: $Parameter<string>): this;
62
+ useMap(): string;
63
+ /** {@link HTMLImageElement.width} */
64
+ width(width: $Parameter<number>): this;
65
+ width(): number;
13
66
  }
14
67
 
15
68
  assignHelper(HTMLImageElement, $Image, 'img');
@@ -7,7 +7,160 @@ export class $Input extends $HTMLElement<HTMLInputElement> {
7
7
  }
8
8
  }
9
9
 
10
- export interface $Input extends $HTMLElement<HTMLInputElement> {}
10
+ export interface $Input extends $HTMLElement<HTMLInputElement> {
11
+ /** {@link HTMLInputElement.form} */
12
+ readonly form: HTMLFormElement | null;
13
+ /** {@link HTMLInputElement.labels} */
14
+ readonly labels: NodeListOf<HTMLLabelElement> | null;
15
+ /** {@link HTMLInputElement.list} */
16
+ readonly list: HTMLDataListElement | null;
17
+ /** {@link HTMLInputElement.validationMessage} */
18
+ readonly validationMessage: string;
19
+ /** {@link HTMLInputElement.validity} */
20
+ readonly validity: ValidityState;
21
+ /** {@link HTMLInputElement.webkitEntries} */
22
+ readonly webkitEntries: ReadonlyArray<FileSystemEntry>;
23
+ /** {@link HTMLInputElement.willValidate} */
24
+ readonly willValidate: boolean;
25
+
26
+ /** {@link HTMLInputElement.checkValidity} */
27
+ checkValidity(): boolean;
28
+ /** {@link HTMLInputElement.reportValidity} */
29
+ reportValidity(): boolean;
30
+ /** {@link HTMLInputElement.select} */
31
+ select(): this;
32
+ /** {@link HTMLInputElement.setCustomValidity} */
33
+ setCustomValidity(error: string): this;
34
+ /** {@link HTMLInputElement.setRangeText} */
35
+ setRangeText(replacement: string): this;
36
+ setRangeText(replacement: string, start: number, end: number, selectionMode?: SelectionMode): this;
37
+ /** {@link HTMLInputElement.setSelectionRange} */
38
+ setSelectionRange(start: number | null, end: number | null, direction?: "forward" | "backward" | "none"): this;
39
+ /** {@link HTMLInputElement.showPicker} */
40
+ showPicker(): this;
41
+ /** {@link HTMLInputElement.stepDown} */
42
+ stepDown(n?: number): this;
43
+ /** {@link HTMLInputElement.stepUp} */
44
+ stepUp(n?: number): this;
45
+
46
+ /** {@link HTMLInputElement.accept} */
47
+ accept(accept: $Parameter<string>): this;
48
+ accept(): string;
49
+ /** {@link HTMLInputElement.alt} */
50
+ alt(alt: $Parameter<string>): this;
51
+ alt(): string;
52
+ /** {@link HTMLInputElement.autoFill} */
53
+ autoFill(autoFill: $Parameter<AutoFill>): this;
54
+ autoFill(): AutoFill;
55
+ /** {@link HTMLInputElement.capture} */
56
+ capture(capture: $Parameter<string>): this;
57
+ capture(): string;
58
+ /** {@link HTMLInputElement.checked} */
59
+ checked(checked: $Parameter<boolean>): this;
60
+ checked(): boolean;
61
+ /** {@link HTMLInputElement.defaultChecked} */
62
+ defaultChecked(defaultChecked: $Parameter<boolean>): this;
63
+ defaultChecked(): string;
64
+ /** {@link HTMLInputElement.defaultValue} */
65
+ defaultValue(defaultValue: $Parameter<string>): this;
66
+ defaultValue(): string;
67
+ /** {@link HTMLInputElement.dirName} */
68
+ dirName(dirName: $Parameter<string>): this;
69
+ dirName(): string;
70
+ /** {@link HTMLInputElement.disabled} */
71
+ disabled(disabled: $Parameter<boolean>): this;
72
+ disabled(): boolean;
73
+ /** {@link HTMLInputElement.files} */
74
+ files(files: $Parameter<FileList | null>): this;
75
+ files(): string;
76
+ /** {@link HTMLInputElement.formAction} */
77
+ formAction(formAction: $Parameter<string>): this;
78
+ formAction(): string;
79
+ /** {@link HTMLInputElement.formEnctype} */
80
+ formEnctype(formEnctype: $Parameter<string>): this;
81
+ formEnctype(): string;
82
+ /** {@link HTMLInputElement.formMethod} */
83
+ formMethod(formMethod: $Parameter<string>): this;
84
+ formMethod(): string;
85
+ /** {@link HTMLInputElement.formNoValidate} */
86
+ formNoValidate(formNoValidate: $Parameter<boolean>): this;
87
+ formNoValidate(): boolean;
88
+ /** {@link HTMLInputElement.formTarget} */
89
+ formTarget(formTarget: $Parameter<string>): this;
90
+ formTarget(): string;
91
+ /** {@link HTMLInputElement.height} */
92
+ height(height: $Parameter<number>): this;
93
+ height(): number;
94
+ /** {@link HTMLInputElement.indeterminate} */
95
+ indeterminate(indeterminate: $Parameter<boolean>): this;
96
+ indeterminate(): boolean;
97
+ /** {@link HTMLInputElement.max} */
98
+ max(max: $Parameter<string>): this;
99
+ max(): string;
100
+ /** {@link HTMLInputElement.maxLength} */
101
+ maxLength(maxLength: $Parameter<number>): this;
102
+ maxLength(): number;
103
+ /** {@link HTMLInputElement.min} */
104
+ min(min: $Parameter<string>): this;
105
+ min(): string;
106
+ /** {@link HTMLInputElement.minLength} */
107
+ minLength(minLength: $Parameter<string>): this;
108
+ minLength(): string;
109
+ /** {@link HTMLInputElement.multiple} */
110
+ multiple(multiple: $Parameter<boolean>): this;
111
+ multiple(): boolean;
112
+ /** {@link HTMLInputElement.name} */
113
+ name(name: $Parameter<string>): this;
114
+ name(): string;
115
+ /** {@link HTMLInputElement.pattern} */
116
+ pattern(pattern: $Parameter<string>): this;
117
+ pattern(): string;
118
+ /** {@link HTMLInputElement.placeholder} */
119
+ placeholder(placeholder: $Parameter<string>): this;
120
+ placeholder(): string;
121
+ /** {@link HTMLInputElement.readOnly} */
122
+ readOnly(readOnly: $Parameter<boolean>): this;
123
+ readOnly(): boolean;
124
+ /** {@link HTMLInputElement.required} */
125
+ required(required: $Parameter<boolean>): this;
126
+ required(): boolean;
127
+ /** {@link HTMLInputElement.selectionDirection} */
128
+ selectionDirection(selectionDirection: $Parameter<'forward' | 'backward' | 'none' | null>): this;
129
+ selectionDirection(): 'forward' | 'backward' | 'none' | null;
130
+ /** {@link HTMLInputElement.selectionEnd} */
131
+ selectionEnd(selectionEnd: $Parameter<number | null>): this;
132
+ selectionEnd(): number | null;
133
+ /** {@link HTMLInputElement.selectionStart} */
134
+ selectionStart(selectionStart: $Parameter<number>): this;
135
+ selectionStart(): number;
136
+ /** {@link HTMLInputElement.size} */
137
+ size(size: $Parameter<number>): this;
138
+ size(): number;
139
+ /** {@link HTMLInputElement.src} */
140
+ src(src: $Parameter<string>): this;
141
+ src(): string;
142
+ /** {@link HTMLInputElement.step} */
143
+ step(step: $Parameter<string>): this;
144
+ step(): string;
145
+ /** {@link HTMLInputElement.type} */
146
+ type(type: $Parameter<string>): this;
147
+ type(): string;
148
+ /** {@link HTMLInputElement.value} */
149
+ value(value: $Parameter<string>): this;
150
+ value(): string;
151
+ /** {@link HTMLInputElement.valueAsDate} */
152
+ valueAsDate(valueAsDate: $Parameter<Date | null>): this;
153
+ valueAsDate(): Date | null;
154
+ /** {@link HTMLInputElement.valueAsNumber} */
155
+ valueAsNumber(valueAsNumber: $Parameter<number>): this;
156
+ valueAsNumber(): number;
157
+ /** {@link HTMLInputElement.webkitdirectory} */
158
+ webkitdirectory(webkitdirectory: $Parameter<boolean>): this;
159
+ webkitdirectory(): boolean;
160
+ /** {@link HTMLInputElement.width} */
161
+ width(width: $Parameter<number>): this;
162
+ width(): number;
163
+ }
11
164
 
12
165
  assignHelper(HTMLInputElement, $Input, 'input');
13
166
 
@@ -7,7 +7,14 @@ export class $OptGroup extends $HTMLElement<HTMLOptGroupElement> {
7
7
  }
8
8
  }
9
9
 
10
- export interface $OptGroup extends $HTMLElement<HTMLOptGroupElement> {}
10
+ export interface $OptGroup extends $HTMLElement<HTMLOptGroupElement> {
11
+ /** {@link HTMLOptGroupElement.disabled} */
12
+ disabled(disabled: $Parameter<boolean>): this;
13
+ disabled(): boolean;
14
+ /** {@link HTMLOptGroupElement.label} */
15
+ label(label: $Parameter<string>): this;
16
+ label(): string;
17
+ }
11
18
 
12
19
  assignHelper(HTMLOptGroupElement, $OptGroup, 'optgroup');
13
20
 
@@ -7,7 +7,31 @@ export class $Option extends $HTMLElement<HTMLOptionElement> {
7
7
  }
8
8
  }
9
9
 
10
- export interface $Option extends $HTMLElement<HTMLOptionElement> {}
10
+ export interface $Option extends $HTMLElement<HTMLOptionElement> {
11
+ /** {@link HTMLOptionElement.form} */
12
+ readonly form: HTMLFormElement | null;
13
+ /** {@link HTMLOptionElement.index} */
14
+ readonly index: number;
15
+
16
+ /** {@link HTMLOptionElement.defaultSelected} */
17
+ defaultSelected(defaultSelected: $Parameter<boolean>): this;
18
+ defaultSelected(): boolean;
19
+ /** {@link HTMLOptionElement.disabled} */
20
+ disabled(disabled: $Parameter<boolean>): this;
21
+ disabled(): boolean;
22
+ /** {@link HTMLOptionElement.label} */
23
+ label(label: $Parameter<string>): this;
24
+ label(): string;
25
+ /** {@link HTMLOptionElement.selected} */
26
+ selected(selected: $Parameter<boolean>): this;
27
+ selected(): boolean;
28
+ /** {@link HTMLOptionElement.text} */
29
+ text(text: $Parameter<string>): this;
30
+ text(): string;
31
+ /** {@link HTMLOptionElement.value} */
32
+ value(value: $Parameter<string>): this;
33
+ value(): string;
34
+ }
11
35
 
12
36
  assignHelper(HTMLOptionElement, $Option, 'option');
13
37
 
@@ -7,7 +7,67 @@ export class $Select extends $HTMLElement<HTMLSelectElement> {
7
7
  }
8
8
  }
9
9
 
10
- export interface $Select extends $HTMLElement<HTMLSelectElement> {}
10
+ export interface $Select extends $HTMLElement<HTMLSelectElement> {
11
+ /** {@link HTMLSelectElement.form} */
12
+ readonly form: HTMLFormElement | null;
13
+ /** {@link HTMLSelectElement.labels} */
14
+ readonly labels: NodeListOf<HTMLLabelElement>;
15
+ /** {@link HTMLSelectElement.options} */
16
+ readonly options: HTMLOptionsCollection;
17
+ /** {@link HTMLSelectElement.selectedOptions} */
18
+ readonly selectedOptions: HTMLCollectionOf<HTMLOptionElement>;
19
+ /** {@link HTMLSelectElement.type} */
20
+ readonly type: "select-one" | "select-multiple";
21
+ /** {@link HTMLSelectElement.validationMessage} */
22
+ readonly validationMessage: string;
23
+ /** {@link HTMLSelectElement.validity} */
24
+ readonly validity: ValidityState;
25
+ /** {@link HTMLSelectElement.willValidate} */
26
+ readonly willValidate: boolean;
27
+
28
+ /** {@link HTMLSelectElement.autocomplete} */
29
+ autocomplete(autocomplete: $Parameter<AutoFill>): this;
30
+ autocomplete(): AutoFill;
31
+ /** {@link HTMLSelectElement.disabled} */
32
+ disabled(disabled: $Parameter<boolean>): this;
33
+ disabled(): boolean;
34
+ /** {@link HTMLSelectElement.length} */
35
+ length(length: $Parameter<number>): this;
36
+ length(): number;
37
+ /** {@link HTMLSelectElement.multiple} */
38
+ multiple(multiple: $Parameter<boolean>): this;
39
+ multiple(): boolean;
40
+ /** {@link HTMLSelectElement.name} */
41
+ name(name: $Parameter<string>): this;
42
+ name(): string;
43
+ /** {@link HTMLSelectElement.required} */
44
+ required(required: $Parameter<boolean>): this;
45
+ required(): boolean;
46
+ /** {@link HTMLSelectElement.selectedIndex} */
47
+ selectedIndex(selectedIndex: $Parameter<number>): this;
48
+ selectedIndex(): number;
49
+ /** {@link HTMLSelectElement.size} */
50
+ size(size: $Parameter<number>): this;
51
+ size(): number;
52
+ /** {@link HTMLSelectElement.value} */
53
+ value(value: $Parameter<string>): this;
54
+ value(): string;
55
+
56
+ /** {@link HTMLSelectElement.add} */
57
+ add(element: HTMLOptionElement | HTMLOptGroupElement, before?: HTMLElement | number | null): this;
58
+ /** {@link HTMLSelectElement.checkValidity} */
59
+ checkValidity(): boolean;
60
+ /** {@link HTMLSelectElement.item} */
61
+ item(index: number): HTMLOptionElement | null;
62
+ /** {@link HTMLSelectElement.namedItem} */
63
+ namedItem(name: string): HTMLOptionElement | null;
64
+ /** {@link HTMLSelectElement.reportValidity} */
65
+ reportValidity(): boolean;
66
+ /** {@link HTMLSelectElement.setCustomValidity} */
67
+ setCustomValidity(error: string): this;
68
+ /** {@link HTMLSelectElement.showPicker} */
69
+ showPicker(): this;
70
+ }
11
71
 
12
72
  assignHelper(HTMLSelectElement, $Select, 'select');
13
73
 
@@ -0,0 +1,53 @@
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
+ ```
@@ -0,0 +1,10 @@
1
+ {
2
+ "name": "@amateras/i18n",
3
+ "peerDependencies": {
4
+ "amateras": "../../"
5
+ },
6
+ "imports": {
7
+ "#structure/*": "./src/structure/*.ts",
8
+ "#node/*": "./src/node/*.ts"
9
+ }
10
+ }
@@ -0,0 +1,54 @@
1
+ import { _Array_from, _instanceof, _Object_assign } from "amateras/lib/native"
2
+ import { $ } from "amateras/core"
3
+ import { I18n } from "#structure/I18n"
4
+ import type { I18nText as _I18nText, I18nTextOptions } from "#node/I18nText";
5
+ import { I18nDictionary, type I18nDictionaryContext, type I18nDictionaryContextImporter } from "#structure/I18nDictionary";
6
+
7
+ _Object_assign($, {
8
+ i18n(defaultLocale: string) {
9
+ const i18n = new I18n(defaultLocale);
10
+ const i18nFn = (key: string, options?: I18nTextOptions) => i18n.translate(key, options);
11
+ _Object_assign(i18nFn, {
12
+ i18n,
13
+ locale(locale: string) {
14
+ if (!arguments.length) return i18n.locale();
15
+ i18n.locale(locale);
16
+ return this;
17
+ },
18
+ add(lang: string, context: I18nDictionaryContext | I18nDictionaryContextImporter) {
19
+ i18n.map.set(lang, new I18nDictionary(context));
20
+ return this;
21
+ },
22
+ delete(lang: string) {
23
+ i18n.map.delete(lang);
24
+ return this;
25
+ }
26
+ })
27
+ return i18nFn
28
+ }
29
+ })
30
+
31
+ type ResolvedAsyncDictionary<F extends I18nDictionaryContextImporter> = Awaited<ReturnType<F>>['default'];
32
+
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;
40
+
41
+ declare module "amateras/core" {
42
+ export namespace $ {
43
+ export interface I18nFunction<D extends I18nDictionaryContext = {}> {
44
+ (path: DeepKeys<D>, ...args: any[]): I18nText;
45
+ i18n: I18n;
46
+ locale(): string;
47
+ locale(lang?: $Parameter<string>): this;
48
+ add<F extends I18nDictionaryContext | I18nDictionaryContextImporter>(lang: string, dictionary: F): I18nFunction<D | (F extends I18nDictionaryContextImporter ? ResolvedAsyncDictionary<F> : F)>;
49
+ delete(lang: string): this;
50
+ }
51
+ export function i18n(defaultLocale: string): I18nFunction;
52
+ export type I18nText = _I18nText;
53
+ }
54
+ }
@@ -0,0 +1,35 @@
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}