amateras 0.13.2 → 0.14.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 (89) hide show
  1. package/README.md +39 -38
  2. package/build/core.js +1 -1
  3. package/build/css-keyframes.js +1 -0
  4. package/build/css-property.js +1 -0
  5. package/build/css-variable.js +1 -0
  6. package/build/for.js +1 -1
  7. package/build/i18n.js +1 -1
  8. package/build/if.js +1 -1
  9. package/build/import-map.js +1 -1
  10. package/build/match.js +1 -1
  11. package/build/meta.js +1 -1
  12. package/build/prefetch.js +1 -1
  13. package/build/router.js +1 -1
  14. package/build/signal.js +1 -1
  15. package/build/store.js +1 -1
  16. package/build/ui.js +1 -1
  17. package/build/utils.js +1 -1
  18. package/build/widget.js +1 -1
  19. package/package.json +3 -2
  20. package/packages/core/src/index.ts +86 -31
  21. package/packages/core/src/lib/hmr.ts +4 -4
  22. package/packages/core/src/structure/ElementProto.ts +33 -11
  23. package/packages/core/src/structure/GlobalState.ts +13 -4
  24. package/packages/core/src/structure/NodeProto.ts +2 -4
  25. package/packages/core/src/structure/Proto.ts +37 -23
  26. package/packages/core/src/structure/TextProto.ts +1 -2
  27. package/packages/css/README.md +18 -15
  28. package/packages/css/src/ext/property.ts +2 -3
  29. package/packages/css/src/index.ts +1 -1
  30. package/packages/css/src/structure/$CSSProperty.ts +4 -0
  31. package/packages/css/src/structure/$CSSVariable.ts +1 -1
  32. package/packages/css/src/types.ts +5 -0
  33. package/packages/for/src/global.ts +12 -3
  34. package/packages/for/src/structure/For.ts +5 -3
  35. package/packages/i18n/README.md +16 -24
  36. package/packages/i18n/src/index.ts +26 -5
  37. package/packages/i18n/src/structure/I18n.ts +2 -4
  38. package/packages/i18n/src/structure/I18nSession.ts +4 -2
  39. package/packages/i18n/src/structure/I18nTranslation.ts +15 -26
  40. package/packages/idb/src/structure/$IDBStore.ts +2 -2
  41. package/packages/if/src/global.ts +15 -4
  42. package/packages/if/src/index.ts +18 -8
  43. package/packages/if/src/structure/Condition.ts +16 -13
  44. package/packages/if/src/structure/ConditionStatement.ts +9 -9
  45. package/packages/match/src/global.ts +9 -3
  46. package/packages/match/src/structure/Match.ts +1 -1
  47. package/packages/meta/src/index.ts +4 -5
  48. package/packages/prefetch/src/index.ts +30 -9
  49. package/packages/router/src/global.ts +17 -4
  50. package/packages/router/src/index.ts +25 -18
  51. package/packages/router/src/structure/Route.ts +2 -1
  52. package/packages/router/src/structure/RouteNode.ts +8 -6
  53. package/packages/router/src/structure/RouteSlot.ts +15 -2
  54. package/packages/router/src/structure/Router.ts +28 -19
  55. package/packages/router/src/structure/RouterConstructor.ts +5 -5
  56. package/packages/router/src/types.ts +2 -2
  57. package/packages/signal/README.md +28 -48
  58. package/packages/signal/src/index.ts +61 -38
  59. package/packages/signal/src/structure/Signal.ts +40 -7
  60. package/packages/store/src/structure/Store.ts +1 -1
  61. package/packages/ui/package.json +2 -1
  62. package/packages/ui/src/icon/check.svg.ts +1 -0
  63. package/packages/ui/src/icon/x.svg.ts +1 -0
  64. package/packages/ui/src/index.ts +9 -2
  65. package/packages/ui/src/lib/combobox_style.ts +20 -0
  66. package/packages/ui/src/lib/hover.ts +2 -0
  67. package/packages/ui/src/structure/Badge.ts +10 -1
  68. package/packages/ui/src/structure/Button.ts +54 -27
  69. package/packages/ui/src/structure/Card.ts +3 -4
  70. package/packages/ui/src/structure/Combobox/Combobox.ts +312 -0
  71. package/packages/ui/src/structure/Combobox/ComboboxChips.ts +178 -0
  72. package/packages/ui/src/structure/Combobox/ComboboxList.ts +209 -0
  73. package/packages/ui/src/structure/ContextMenu.ts +89 -0
  74. package/packages/ui/src/structure/Field.ts +109 -0
  75. package/packages/ui/src/structure/Input.ts +29 -0
  76. package/packages/ui/src/structure/Select/Select.ts +18 -8
  77. package/packages/ui/src/structure/Select/SelectContent.ts +6 -1
  78. package/packages/ui/src/structure/Select/SelectItem.ts +2 -1
  79. package/packages/ui/src/structure/Slideshow.ts +2 -2
  80. package/packages/ui/src/structure/Switch.ts +45 -0
  81. package/packages/ui/src/structure/Tabs.ts +3 -3
  82. package/packages/ui/src/structure/Toggle.ts +155 -0
  83. package/packages/ui/src/structure/Waterfall.ts +1 -1
  84. package/packages/ui/src/structure/WaterfallItem.ts +1 -1
  85. package/packages/utils/src/lib/utils.ts +30 -8
  86. package/packages/utils/src/structure/UID.ts +1 -1
  87. package/packages/widget/src/index.ts +29 -9
  88. package/packages/widget/src/structure/Widget.ts +7 -3
  89. package/packages/ui/src/structure/TextBlock.ts +0 -11
@@ -1,7 +1,16 @@
1
- import * as proto from "#structure/For";
1
+ import * as _For from "#structure/For";
2
2
 
3
3
  declare global {
4
- export var For: typeof proto.For
4
+ export var For: typeof _For.For
5
+ // export function $<T>(For: typeof proto.For, signal: proto.ForList<T>, layout: proto.ForLayout<T>): proto.For<T>
5
6
 
6
- export function $<T>(For: typeof proto.For, signal: proto.ForList<T>, layout: proto.ForLayout<T>): proto.For<T>
7
+ export namespace $ {
8
+ export interface Overload<I> {
9
+ for: [
10
+ input: [typeof _For.For, _For.ForList<any>],
11
+ output: _For.For,
12
+ args: [layout: I[1] extends _For.ForList<infer T> ? _For.ForLayout<T> : never]
13
+ ]
14
+ }
15
+ }
7
16
  }
@@ -27,12 +27,14 @@ export class For<T = any> extends ProxyProto {
27
27
  let nodes = this.toDOM();
28
28
  let arr = _Array_from(parentNode.childNodes);
29
29
  let currentPosition = arr.indexOf(thisNode);
30
+ let nextNode: Node | null = _null;
30
31
  forEach(nodes, node => {
31
32
  if (node !== thisNode) {
32
33
  let currentNode = parentNode.childNodes[currentPosition];
33
34
  if (currentNode !== node) {
34
- let nextNode = parentNode.childNodes[currentPosition + 1] ?? _null;
35
- parentNode.insertBefore(node, nextNode)
35
+ if (!nodes.includes(currentNode as any)) nextNode = currentNode ?? _null;
36
+ else nextNode = parentNode.childNodes[currentPosition + 1] ?? _null;
37
+ parentNode.insertBefore(node, nextNode);
36
38
  }
37
39
  }
38
40
  currentPosition++;
@@ -42,7 +44,7 @@ export class For<T = any> extends ProxyProto {
42
44
  }
43
45
 
44
46
  this.list$.subscribe(update);
45
- this.ondispose(() => this.list$.unsubscribe(update));
47
+ this.listen('dispose', () => this.list$.unsubscribe(update));
46
48
  }
47
49
 
48
50
  override build() {
@@ -1,37 +1,33 @@
1
1
  # amateras/i18n
2
2
 
3
- ## Usage
3
+ ## Import
4
4
  ```ts
5
5
  import 'amateras';
6
6
  import 'amateras/i18n';
7
+ ```
7
8
 
8
- const $t = $.i18n()
9
- // add 'en' locale dictionary context
9
+ ## Usage
10
+ ```ts
11
+ const i18n = $.i18n('en')
10
12
  .add('en', {
11
13
  homepage: {
12
14
  _: 'Home',
13
15
  hello: 'Hello, $name$!',
14
16
  }
15
17
  })
16
- // set 'en' as locale language
17
- .locale('en')
18
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
- ])
19
+ $('div', $$ => {
20
+ $('h1', $$ => $([ i18n.t('homepage') ]))
21
+ $('p', $$ => $([ i18n.t('homepage.hello', { name: 'Amateras' }) ]))
22
+ })
25
23
  ```
26
24
 
27
25
  ## Change Language
28
26
  ```ts
29
- $t.locale('zh')
30
- // all translation text will be updated
27
+ i18n.locale('zh');
31
28
  ```
32
29
 
33
30
  ## Import Dictionary Context
34
-
35
31
  ```ts
36
32
  // ./i18n/en.ts
37
33
  export default {
@@ -47,27 +43,23 @@ export default {
47
43
  const $t = $.i18n()
48
44
  .add('en', () => import('./i18n/en.ts'))
49
45
  .add('zh', () => import('./i18n/zh.ts'))
50
- // set 'zh' as locale language
51
- // and fetch file automatically from path
52
- .locale('zh');
53
46
  ```
54
47
 
55
- ## Directory Shortcut
56
-
48
+ ## Translation Directory Shortcut
57
49
  ```ts
58
- const $t = $.i18n('en')
50
+ const i18n = $.i18n('en')
59
51
  .add('en', {
60
52
  page1: {
61
53
  section2: {
62
54
  button3: {
63
- text: 'Too deep!'
55
+ text: 'Deep Button Text'
64
56
  }
65
57
  }
66
58
  }
67
59
  })
68
60
 
69
- const $t_button = $t.dir('page1.section2.button3');
61
+ const i18n_button = i18n.dir('page1.section2.button3');
70
62
 
71
- $t_button('text') // Too deep!
72
- $t('page1.section2.button3.text') // Too deep!
63
+ i18n.t('page1.section2.button3.text') // Deep Button Text
64
+ i18n_button.t('text') // Deep Button Text
73
65
  ```
@@ -1,7 +1,7 @@
1
1
  import { I18n } from "#structure/I18n";
2
2
  import { I18nTranslation as _I18nTranslation, I18nTranslation } from "#structure/I18nTranslation";
3
- import { _instanceof, _null, _Object_assign } from "@amateras/utils";
4
- import { GlobalState } from "@amateras/core";
3
+ import { _instanceof, _null, _Object_assign, forEach } from "@amateras/utils";
4
+ import { GlobalState, Proto, ProxyProto } from "@amateras/core";
5
5
  import type { I18nSession } from "#structure/I18nSession";
6
6
 
7
7
  declare global {
@@ -13,7 +13,7 @@ declare global {
13
13
  i18n: I18nTranslation
14
14
  }
15
15
 
16
- export interface ProtoEventMap {
16
+ export interface ProtoEventMap<P extends Proto> {
17
17
  i18nupdate: [I18nTranslation]
18
18
  }
19
19
  }
@@ -31,10 +31,14 @@ declare module '@amateras/core' {
31
31
  }
32
32
  }
33
33
 
34
- GlobalState.assign({
34
+ GlobalState.assign(() => ({
35
35
  i18n: {
36
36
  session: _null
37
37
  }
38
+ }))
39
+
40
+ GlobalState.disposers.add(global => {
41
+ global.i18n.session = _null;
38
42
  })
39
43
 
40
44
  _Object_assign($, {
@@ -45,7 +49,24 @@ _Object_assign($, {
45
49
 
46
50
  $.process.text.add(value => {
47
51
  if (_instanceof(value, I18nTranslation)) {
48
- return value
52
+ const $proxy = new ProxyProto();
53
+ value.onupdate(result => {
54
+ $proxy.layout = () => $([ ...result ]);
55
+ forEach($proxy.protos, proto => proto.removeNode());
56
+ $proxy.build();
57
+ $proxy.node?.replaceWith(...$proxy.toDOM());
58
+ $proxy.dispatch('i18nupdate', [this], {bubbles: true})
59
+ })
60
+ value.update();
61
+ return $proxy;
62
+ }
63
+ })
64
+ $.process.attr.add((name, value, proto) => {
65
+ if (_instanceof(value, I18nTranslation)) {
66
+ value.onupdate((result) => {
67
+ proto.attr(name, result.join(''))
68
+ })
69
+ value.update();
49
70
  }
50
71
  })
51
72
 
@@ -10,13 +10,11 @@ export class I18n<D extends I18nDictionaryContext = {}> {
10
10
  dictionaries = new Map<string, I18nDictionary>();
11
11
  defaultLocale: string;
12
12
  sessions = new Set<I18nSession>();
13
- session = new I18nSession(this);
14
13
  path = '';
15
14
  static key = '__locale__';
16
15
  constructor(defaultLocale: string) {
17
16
  this.defaultLocale = defaultLocale;
18
17
  this.#locale = defaultLocale;
19
- this.sessions.add(this.session);
20
18
  }
21
19
 
22
20
  add(lang: string, dictionary: I18nDictionaryContext | I18nDictionaryContextImporter) {
@@ -75,11 +73,11 @@ export class I18n<D extends I18nDictionaryContext = {}> {
75
73
  private getSession() {
76
74
  let parentProto = Proto.proto;
77
75
  if (parentProto) {
78
- let session = parentProto.global.i18n.session ?? new I18nSession(this);
76
+ let session = parentProto.global.i18n.session ?? new I18nSession(this, parentProto.global);
79
77
  parentProto.global.i18n.session = session;
80
78
  return session;
81
79
  }
82
- else return this.session;
80
+ else throw 'I18n.getSession(): session not found from Proto.proto'
83
81
  }
84
82
 
85
83
  private readStoreLocale() {
@@ -2,14 +2,16 @@ import { isUndefined, map } from "@amateras/utils";
2
2
  import type { I18nTranslationResult } from "../types";
3
3
  import type { I18n } from "./I18n";
4
4
  import type { I18nTranslation, I18nTranslationOptions } from "./I18nTranslation";
5
- import { onclient } from "@amateras/core";
5
+ import { GlobalState, onclient } from "@amateras/core";
6
6
 
7
7
  export class I18nSession {
8
8
  translations = new Set<I18nTranslation>()
9
9
  i18n: I18n;
10
10
  #locale: string;
11
- constructor(i18n: I18n) {
11
+ global: GlobalState;
12
+ constructor(i18n: I18n, global: GlobalState) {
12
13
  this.i18n = i18n;
14
+ this.global = global;
13
15
  this.#locale = i18n.locale();
14
16
  i18n.sessions.add(this);
15
17
  }
@@ -1,45 +1,34 @@
1
- import { ProxyProto } from "@amateras/core";
2
- import { forEach } from "@amateras/utils";
1
+ import { forEach, map } from "@amateras/utils";
3
2
  import type { I18nSession } from "./I18nSession";
4
3
 
5
- export class I18nTranslation extends ProxyProto {
4
+ export class I18nTranslation {
6
5
  session: I18nSession;
7
6
  key: string;
8
7
  options: I18nTranslationOptions | undefined;
8
+ private updating = false;
9
+ private updaters: ((result: any[]) => void)[] = [];
9
10
  constructor(session: I18nSession, key: string, options?: I18nTranslationOptions) {
10
- super()
11
11
  this.session = session;
12
12
  this.key = key;
13
13
  this.options = options;
14
- session.translations.add(this);
15
- }
16
-
17
- override dispose(): void {
18
- super.dispose();
19
- this.session.translations.delete(this)
20
- }
21
-
22
- override build(): this {
23
- this.update();
24
- return this;
14
+ this.session.translations.add(this);
25
15
  }
26
16
 
27
17
  async update() {
18
+ if (this.updating) return;
19
+ this.updating = true;
28
20
  const request = this.session.fetch(this.key, this.options);
29
- this.global.asyncTask(request);
21
+ this.session.global.asyncTask(request);
30
22
  const {text, args} = await request;
31
- this.layout = () => {
32
- // make this array become Template String Array;
33
- //@ts-ignore
34
- text.raw = text;
35
- $(text as any, ...args);
36
- }
37
- forEach(this.protos, proto => proto.removeNode());
38
- super.build();
39
- this.node?.replaceWith(...this.toDOM());
40
- this.dispatch('i18nupdate', this, {bubbles: true})
23
+ const arr = map(text, (str, index) => index < args.length ? [str, args[index]] : [str]).flat();
24
+ forEach(this.updaters, updaters => updaters(arr))
25
+ this.updating = false;
41
26
  return this;
42
27
  }
28
+
29
+ onupdate(handle: (result: any[]) => void) {
30
+ this.updaters.push(handle);
31
+ }
43
32
  }
44
33
 
45
34
  export type I18nTranslationOptions = {[key: string]: any}
@@ -1,4 +1,4 @@
1
- import { _instanceof, _Object_assign, _Promise } from "@amateras/utils";
1
+ import { _Object_assign } from "@amateras/utils";
2
2
  import { $IDBIndex, type $IDBIndexConfig } from "./$IDBIndex";
3
3
  import { $IDBRequest } from "#lib/$IDBRequest";
4
4
  import { $IDBStoreBase } from "./$IDBStoreBase";
@@ -59,7 +59,7 @@ export interface $IDBStore<Config extends $IDBStoreConfig = any> {
59
59
  count(query?: $IDBStoreKey<Config> | IDBKeyRange): Promise<number>;
60
60
 
61
61
  /** {@link IDBObjectStore.get} */
62
- get(query: $IDBStoreKey<Config> | IDBKeyRange): Promise<Config['schema']>
62
+ get(query: $IDBStoreKey<Config> | IDBKeyRange): Promise<Config['schema'] | undefined>
63
63
 
64
64
  /** {@link IDBObjectStore.getAll} */
65
65
  getAll(query?: $IDBStoreKey<Config> | IDBKeyRange): Promise<Config['schema'][]>
@@ -2,7 +2,7 @@ import type { Condition } from "#structure/Condition";
2
2
  import * as _Else from "#structure/Else";
3
3
  import * as _ElseIf from "#structure/ElseIf";
4
4
  import * as _If from "#structure/If";
5
- import type { Signal, SignalObject } from "@amateras/signal";
5
+ import type { Signal } from "@amateras/signal";
6
6
  import type { IfLayout } from ".";
7
7
 
8
8
  declare global {
@@ -10,7 +10,18 @@ declare global {
10
10
  export var Else: typeof _Else.Else
11
11
  export var ElseIf: typeof _ElseIf.ElseIf
12
12
 
13
- export function $<T extends Signal | SignalObject<any>>(statement: typeof _If.If, signal: T, layout: IfLayout<T>): Condition;
14
- export function $<T extends Signal | SignalObject<any>>(statement: typeof _ElseIf.ElseIf, signal: T, layout: IfLayout<T>): Condition;
15
- export function $(statement: typeof _Else.Else, layout: _Else.ElseLayout): Condition;
13
+ export namespace $ {
14
+ export interface Overload<I> {
15
+ if: [
16
+ input: [typeof _If.If | typeof _ElseIf.ElseIf, signal: OrArray<Signal>],
17
+ output: Condition,
18
+ args: [layout: I[1] extends OrArray<Signal> ? IfLayout<I[1]> : never]
19
+ ]
20
+ else: [
21
+ input: [typeof _Else.Else],
22
+ output: Condition,
23
+ args: [layout: _Else.ElseLayout]
24
+ ]
25
+ }
26
+ }
16
27
  }
@@ -1,7 +1,7 @@
1
1
  import './global';
2
2
  import { If } from "#structure/If";
3
- import { _instanceof, _null, isIncluded } from "@amateras/utils";
4
- import { Signal, type SignalObject, type SignalTypes } from '@amateras/signal';
3
+ import { _instanceof, _null, isArray, isIncluded } from "@amateras/utils";
4
+ import { Signal, type SignalObject } from '@amateras/signal';
5
5
  import { ElseIf } from '#structure/ElseIf';
6
6
  import { Else } from '#structure/Else';
7
7
  import { Condition } from '#structure/Condition';
@@ -10,13 +10,24 @@ globalThis.If = If;
10
10
  globalThis.Else = Else;
11
11
  globalThis.ElseIf = ElseIf;
12
12
 
13
- export type IfLayout<T> = (value: T extends SignalTypes<infer V>
13
+ export type IfLayout<T> = (...value: T extends any[]
14
+ ? ResolveArray<T>
15
+ : [ResolveSignal<T>]
16
+ ) => void;
17
+
18
+ type ResolveArray<T> = T extends [infer A, ...infer Rest]
19
+ ? [A] extends [Signal<any>]
20
+ ? [ResolveSignal<A>, ...ResolveArray<Rest>]
21
+ : []
22
+ : [];
23
+
24
+ type ResolveSignal<T> = [T] extends [Signal<infer V>]
14
25
  ? V extends object
15
26
  ? SignalObject<V>
16
- : V extends null
27
+ : V extends null
17
28
  ? never
18
29
  : Signal<V>
19
- : never) => void;
30
+ : T;
20
31
 
21
32
  /**This map store condition with parent Proto,
22
33
  * all related Statement should be under same parent Proto.
@@ -39,10 +50,9 @@ $.process.craft.add((value, arg1, arg2) => {
39
50
  // handle If/Else/ElseIf constructor
40
51
  // add them into condition child statements
41
52
  if (isIncluded(value, [If, Else, ElseIf])) {
42
- let args: [Signal | null, () => void] = _instanceof(arg1, Signal) ? [arg1, arg2] : [_null, arg1];
53
+ let args: [Signal | Signal[] | null, () => void] = (_instanceof(arg1, Signal) || isArray(arg1)) ? [arg1, arg2] : [_null, arg1];
43
54
  let statement = new value(...args);
44
- condition.statements = condition.statements ?? [];
45
- condition.statements?.push(statement);
55
+ condition.append(statement);
46
56
  }
47
57
  else {
48
58
  condition = _null;
@@ -5,13 +5,12 @@ import type { ConditionStatement } from "./ConditionStatement";
5
5
 
6
6
  export class Condition extends ProxyProto {
7
7
  static override [symbol_Statement] = true;
8
- statements: ConditionStatement[] | null = _null;
9
8
  declare layout: null;
10
9
  statement: ConditionStatement | null = _null;
11
10
 
12
11
  override build() {
13
12
  // run base build method with empty protos
14
- super.build(false);
13
+ super.build(false, false);
15
14
  // set condition matched proto
16
15
  this.validate()?.build();
17
16
  // update function for Signal subscribe
@@ -20,32 +19,36 @@ export class Condition extends ProxyProto {
20
19
  if (!matchProto?.builded) matchProto?.build();
21
20
  if (this.statement === matchProto) return;
22
21
  this.statement = matchProto ?? _null;
23
- forEach(this.statements, proto => proto !== matchProto && proto.removeNode())
22
+ forEach(this.protos, proto => !proto.visible && proto.removeNode());
24
23
  this.node?.replaceWith(...this.toDOM());
25
24
  this.parent?.mutate();
26
25
  }
27
26
  // build statements proto and subscribe expression signal
28
- forEach(this.statements, proto => {
29
- proto.exp$?.subscribe(update);
30
- proto.ondispose(() => {
31
- proto.exp$?.unsubscribe(update)
32
- });
27
+ forEach(this.protos, proto => {
28
+ forEach(proto.exps, exp$ => {
29
+ exp$?.subscribe(update);
30
+ proto.listen('dispose', () => {
31
+ exp$?.unsubscribe(update)
32
+ });
33
+ })
33
34
  })
34
35
  return this;
35
36
  }
36
37
 
38
+ override get protos(): Set<ConditionStatement> {
39
+ return super.protos as Set<ConditionStatement>
40
+ }
41
+
37
42
  override dispose(): void {
38
43
  super.dispose();
39
- forEach(this.statements, statement => statement.dispose());
40
44
  this.statement = _null;
41
- this.statements = _null;
42
45
  }
43
46
 
44
47
  validate() {
45
- this.clear();
46
- if (this.statements) for (let proto of this.statements) {
48
+ forEach(this.protos, proto => proto.visible = false);
49
+ for (let proto of this.protos) {
47
50
  if (proto.validate()) {
48
- this.append(proto);
51
+ proto.visible = true;
49
52
  return proto;
50
53
  }
51
54
  }
@@ -1,25 +1,25 @@
1
1
  import { symbol_Statement } from "@amateras/core";
2
2
  import { Proto } from "@amateras/core";
3
3
  import type { Signal } from "@amateras/signal";
4
- import { _null } from "@amateras/utils";
4
+ import { _null, toArray } from "@amateras/utils";
5
5
 
6
- export type ConditionLayout = (value: Signal<any> | null) => void;
6
+ export type ConditionLayout = (...value: Signal<any>[]) => void;
7
7
 
8
8
  export abstract class ConditionStatement extends Proto {
9
9
  static override [symbol_Statement] = true;
10
- exp$: Signal<any> | null;
11
- constructor(expression: Signal<any> | null, layout: ConditionLayout) {
12
- super(() => layout(this.exp$));
13
- this.exp$ = expression;
10
+ exps: Signal<any>[] | null;
11
+ constructor(expression: OrArray<Signal<any>> | null, layout: ConditionLayout) {
12
+ super(() => layout(...this.exps ?? []));
13
+ this.exps = expression ? toArray(expression) : _null;
14
14
  }
15
15
 
16
16
  override dispose(): void {
17
17
  super.dispose();
18
- this.exp$ = _null;
18
+ this.exps = _null;
19
19
  }
20
20
 
21
21
  validate() {
22
- if (!this.exp$) return true;
23
- return !!this.exp$.value;
22
+ if (!this.exps) return true;
23
+ return !this.exps.find(exp$ => !exp$.value)
24
24
  }
25
25
  }
@@ -8,7 +8,13 @@ declare global {
8
8
  export var Case: typeof _Case.Case
9
9
  export var Default: typeof _Default.Default
10
10
 
11
- export function $<T>(Match: typeof _Match.Match, expression: Signal<T>, layout: _Match.MatchLayout<T>): _Match.Match<T>
12
- export function $(Case: typeof _Case.Case, condition: any, layout: _Case.CaseLayout): _Case.Case;
13
- export function $(Default: typeof _Default.Default, layout: _Default.DefaultLayout): _Default.Default;
11
+ export namespace $ {
12
+ export interface Overload<I> {
13
+ match: [
14
+ input: [typeof _Match.Match, Signal],
15
+ output: I[1] extends Signal<infer T> ? _Match.Match<T> : never,
16
+ args: [layout: I[1] extends Signal<infer T> ? _Match.MatchLayout<T> : never]
17
+ ]
18
+ }
19
+ }
14
20
  }
@@ -51,7 +51,7 @@ export class Match<T = any> extends ProxyProto {
51
51
  // build cases proto and subscribe expression signal
52
52
  forEach(this.cases, proto => {
53
53
  this.exp$.subscribe(update);
54
- proto.ondispose(() => this.exp$.unsubscribe(update));
54
+ proto.listen('dispose', () => this.exp$.unsubscribe(update));
55
55
  })
56
56
  return this;
57
57
  }
@@ -6,7 +6,7 @@ import type { MetaConfig } from "./types";
6
6
 
7
7
  declare global {
8
8
  export namespace $ {
9
- function meta(config: MetaConfig): void
9
+ function meta(config: MetaConfig, parent?: Proto | null): void
10
10
 
11
11
  export namespace meta {
12
12
  function resolve(config: MetaConfig): void
@@ -22,11 +22,10 @@ declare module '@amateras/core' {
22
22
  }
23
23
 
24
24
  _Object_assign($, {
25
- meta(config: MetaConfig) {
25
+ meta(config: MetaConfig, parent = Proto.proto) {
26
26
  if (onclient()) return;
27
- let proto = Proto.proto;
28
- if (!proto) return;
29
- proto.global.meta = deepMerge(proto.global.meta ?? {}, config);
27
+ if (!parent) return;
28
+ parent.global.meta = deepMerge(parent.global.meta ?? {}, config);
30
29
  },
31
30
  })
32
31
 
@@ -5,7 +5,12 @@ import { _null, _Object_assign, isAsyncFunction, toURL } from "@amateras/utils";
5
5
 
6
6
  declare global {
7
7
  export namespace $ {
8
- export function fetch<T, R>(url: string | URL, options?: RequestInit & FetchOptions<T, R>): Promise<FetchResult<T, R>>
8
+ export function fetch<T, R>(url: string | URL, options?: RequestInit & FetchOptions<T, R>, proto?: Proto | null): Promise<FetchResult<T, R>>
9
+
10
+ export namespace fetch {
11
+ export let origin: string;
12
+ export let server: Bun.Server<undefined> | null
13
+ }
9
14
  }
10
15
  export var prefetch: {[key: string]: { expired: number, data: any }}
11
16
  }
@@ -13,7 +18,8 @@ declare global {
13
18
  declare module "@amateras/core" {
14
19
  export interface GlobalState {
15
20
  prefetch: {
16
- caches: {[key: string]: { expired: number, data: any }}
21
+ caches: {[key: string]: { expired: number, data: any }};
22
+ req: null | Request
17
23
  }
18
24
  }
19
25
  }
@@ -28,11 +34,12 @@ export type FetchResult<T, R> = {
28
34
  result: R;
29
35
  }
30
36
 
31
- GlobalState.assign({
37
+ GlobalState.assign(() => ({
32
38
  prefetch: {
33
- caches: {}
39
+ caches: {},
40
+ req: null
34
41
  }
35
- })
42
+ }))
36
43
 
37
44
  if (!globalThis.prefetch) globalThis.prefetch = {}
38
45
 
@@ -41,9 +48,8 @@ _Object_assign($, {
41
48
  // 保证每次全局渲染都在抓取完毕之后:将 Promise 添加到 global.prefetch.fetches 让根原型能确保所有 fetch 运行结束
42
49
  // 将已抓取的资料发送到客户端:从 record 函数回传的资料将会被记录在 global.prefetch.caches 当中,并且以抓取 URL 作为索引。
43
50
  // 客户端不会用到过时的资料:每个发送到客户端的资料缓存都附上了过期时间
44
- async fetch<T, R>(url: string | URL, options?: RequestInit & FetchOptions<T, R>) {
45
- url = toURL(url);
46
- let proto = Proto.proto;
51
+ async fetch<T, R>(url: string | URL, options?: RequestInit & FetchOptions<T, R>, proto = Proto.proto) {
52
+ url = toURL(url, $.fetch.origin);
47
53
  let cache = onclient() ? prefetch[url.href] : _null;
48
54
  let then = options?.then;
49
55
  let request = new Promise(async (resolve) => {
@@ -52,7 +58,17 @@ _Object_assign($, {
52
58
  resolve({record: cache.data, result});
53
59
  return ;
54
60
  }
55
- let response = await fetch(url, options);
61
+ let { origin, server } = $.fetch;
62
+ let cookies = proto?.global.prefetch.req?.headers.get('cookie') || '';
63
+ let response = url.origin === origin && server
64
+ ? await server.fetch(new Request(url, {
65
+ ...options,
66
+ headers: {
67
+ ...options?.headers,
68
+ 'Cookie': cookies
69
+ }
70
+ }))
71
+ : await fetch(url, options);
56
72
  let recordFn = options?.record;
57
73
  if (recordFn) {
58
74
  let record = isAsyncFunction(recordFn) ? await recordFn(response) : recordFn(response);
@@ -67,4 +83,9 @@ _Object_assign($, {
67
83
  if (onserver()) proto?.global.asyncTask(request)
68
84
  return request
69
85
  }
86
+ })
87
+
88
+ _Object_assign($.fetch, {
89
+ origin: onclient() ? location.origin : 'http://localhost',
90
+ server: null
70
91
  })