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
@@ -4,6 +4,8 @@ import './global';
4
4
  import { ElementProto } from './structure/ElementProto';
5
5
  import { Proto } from './structure/Proto';
6
6
  import { TextProto } from './structure/TextProto';
7
+ import type { ProxyProto } from '#structure/ProxyProto';
8
+ import type { NodeProto } from '#structure/NodeProto';
7
9
 
8
10
  function createProto(insert: boolean, ...args: any) {
9
11
  const prevProtoParent = Proto.proto;
@@ -37,9 +39,12 @@ function createProto(insert: boolean, ...args: any) {
37
39
 
38
40
  // Function Handler
39
41
  if (isFunction(arg1)) {
40
- let args: [any, any] = isFunction(arg2) ? [{}, arg2] : [arg2, arg3];
42
+ let args = arg2 ? [arg2, arg3] : [arg3];
43
+ if (_instanceof(arg1.prototype, ElementProto)) {
44
+ args = isFunction(arg2) ? [{}, arg2] : [arg2, arg3];
45
+ }
46
+ // Constructor Handler
41
47
  let target = new arg1(...args);
42
- // Widget Handler
43
48
  if (_instanceof(target, Proto)) {
44
49
  addProtoToParent(target);
45
50
  return target;
@@ -81,42 +86,96 @@ type ElementProtoArguments<C extends Constructor> =
81
86
  ? [layout?: ConstructorParameters<C>[1]] | [props: ConstructorParameters<C>[0], layout?: ConstructorParameters<C>[1]]
82
87
  : [props: ConstructorParameters<C>[0], layout?: ConstructorParameters<C>[1]]
83
88
 
84
- export function $<T extends ElementProto<any>, C extends Constructor<T>, R extends InstanceType<C>>(constructor: C, ...args: ElementProtoArguments<C>): R;
85
- export function $(template: TemplateStringsArray, ...args: any[]): Proto[];
86
- export function $(proto: Proto): Proto;
87
- export function $(args: any[]): Proto[];
88
- export function $<T extends keyof HTMLElementTagNameMap>(tagname: T, layout?: $.Layout<ElementProto<HTMLElementTagNameMap[T]>>): ElementProto;
89
- export function $<T extends string>(tagname: T, layout?: $.Layout<ElementProto>): ElementProto;
90
- export function $<T extends keyof HTMLElementTagNameMap>(tagname: T, attr: $.Props, layout?: $.Layout<ElementProto<HTMLElementTagNameMap[T]>>): ElementProto;
91
- export function $<T extends string>(tagname: T, attr: $.Props, layout?: $.Layout<ElementProto>): ElementProto;
89
+ export function $<K extends $.Overload[keyof $.Overload][0][0], T extends $.OverloadResolver<[K]>>(arg1: K, ...args: T[1]): T[0];
90
+ export function $<K extends $.Overload[keyof $.Overload][0][0], L extends $.Overload[keyof $.Overload][0][1], T extends $.OverloadResolver<[K, L]>>(arg1: K, arg2: L, ...args: T[1]): T[0];
92
91
  export function $(...args: any): any {
93
92
  return createProto(true, ...args);
94
93
  }
95
94
 
96
95
  interface Craft {
97
- <T extends ElementProto<any>, C extends Constructor<T>, R extends InstanceType<C>>(constructor: C, ...args: ElementProtoArguments<C>): R;
98
- (template: TemplateStringsArray, ...args: any[]): Proto[];
99
- (proto: Proto): Proto;
100
- (args: any[]): Proto[];
101
- <T extends keyof HTMLElementTagNameMap>(tagname: T, layout?: $.Layout<ElementProto<HTMLElementTagNameMap[T]>>): ElementProto;
102
- <T extends string>(tagname: T, layout?: $.Layout<ElementProto>): ElementProto;
103
- <T extends keyof HTMLElementTagNameMap>(tagname: T, attr: $.Props, layout?: $.Layout<ElementProto<HTMLElementTagNameMap[T]>>): ElementProto;
104
- <T extends string>(tagname: T, attr: $.Props, layout?: $.Layout<ElementProto>): ElementProto;
96
+ <K extends $.Overload[keyof $.Overload][0][0], T extends $.OverloadResolver<[K]>>(arg1: K, ...args: T[1]): T[0];
97
+ <K extends $.Overload[keyof $.Overload][0][0], L extends $.Overload[keyof $.Overload][0][1], T extends $.OverloadResolver<[K, L]>>(arg1: K, arg2: L, ...args: T[1]): T[0];
105
98
  }
106
99
 
107
100
  export namespace $ {
108
101
  /** Layout 是一个 Proto 模板函数,所有在此函数中运行 $ 函数所创建的 Proto 都会被加入到运行 Layout 的 Proto 中。 */
109
102
  export type Layout<E extends Proto = any> = (proto: E) => void;
110
103
  /** Props 是组件函数的参数,集合了该组件的自定义属性,以及组件 Layout 函数和元素属性的传递。 */
111
- export type Props<T = {}> = { [key: string]: any } & T & Partial<AttrMap>;
104
+ export type Props<T = {}, H = HTMLElement> = { [key: string]: any } & T & Partial<AttrMap<H>>;
112
105
 
113
106
  export type CraftMiddleware = (...args: any[]) => any;
114
107
  export type TextMiddleware = (value: any) => Proto | undefined;
115
108
  export type AttrMiddleware = (name: string, value: any, proto: ElementProto) => any;
109
+
110
+ export interface Overload<I extends any[] = any> {
111
+ proto: [
112
+ input: [Proto],
113
+ output: I[0] extends Constructor<infer P> ? P : I[0],
114
+ args: []
115
+ ]
116
+ template: [
117
+ input: [TemplateStringsArray],
118
+ output: Proto[],
119
+ args: any[]
120
+ ]
121
+ arr: [
122
+ input: [any[]],
123
+ output: Proto[],
124
+ args: []
125
+ ]
126
+ elementConstructor: [
127
+ input: [Constructor],
128
+ output: I[0] extends Constructor<infer E>
129
+ ? E extends ElementProto<any>
130
+ ? InstanceType<I[0]>
131
+ : never
132
+ : never,
133
+ args: I[0] extends Constructor<infer E>
134
+ ? E extends ElementProto<any>
135
+ ? ElementProtoArguments<I[0]>
136
+ : never
137
+ : never,
138
+ ],
139
+ proxyConstructor: [
140
+ input: [Constructor],
141
+ output: I[0] extends Constructor<infer P> ? P : I[0],
142
+ args: I[0] extends Constructor<infer E>
143
+ ? E extends ProxyProto
144
+ ? ConstructorParameters<I[0]>
145
+ : never
146
+ : never
147
+ ]
148
+ string: [
149
+ input: [string] | [keyof HTMLElementTagNameMap],
150
+ output: I[0] extends keyof HTMLElementTagNameMap ? ElementProto<HTMLElementTagNameMap[I[0]]> : ElementProto,
151
+ args: [
152
+ props?: I[0] extends keyof HTMLElementTagNameMap ? $.Props<{}, HTMLElementTagNameMap[I[0]]> : $.Props,
153
+ layout?:
154
+ | $.Layout<I[0] extends keyof HTMLElementTagNameMap ? ElementProto<HTMLElementTagNameMap[I[0]]> : ElementProto>]
155
+ | [layout?: $.Layout<I[0] extends keyof HTMLElementTagNameMap ? ElementProto<HTMLElementTagNameMap[I[0]]> : ElementProto>
156
+ ]
157
+ ]
158
+ }
116
159
 
117
- export interface AttrMap {}
160
+ export type OverloadResolver<K extends any[]> = {
161
+ [I in keyof Overload<K>]: Overload<K>[I] extends [infer Input, infer Output, infer Args]
162
+ ? K extends Input
163
+ ? [Output, Args]
164
+ : never
165
+ : never
166
+ }[keyof Overload<K>];
167
+
168
+ export interface AttrMap<T> extends ElementProtoOnEventMap<T> {}
169
+
170
+ type ElementProtoOnEventMap<T> = {
171
+ [key in keyof HTMLElementEventMap as string extends string ? `on${key}` : never]: string | ((event: HTMLElementEventMap[key] & { currentTarget: T }) => void)
172
+ }
118
173
 
119
- export interface ProtoEventMap {}
174
+ export interface ProtoEventMap<P extends Proto> {
175
+ dispose: [];
176
+ builded: [];
177
+ dom: [node: P extends NodeProto ? Exclude<P['node'], null> : Node]
178
+ }
120
179
 
121
180
  export const process = {
122
181
  craft: new Set<CraftMiddleware>(),
@@ -126,15 +185,11 @@ export namespace $ {
126
185
 
127
186
  export const craft: Craft = (...args: any) => createProto(false, ...args);
128
187
 
129
- export const dispose = (disposer: () => void) => {
130
- Proto.proto?.ondispose(disposer);
131
- }
132
-
133
- export const render = (proto: Proto, query: string) => {
188
+ export const render = (proto: Proto | Constructor<Proto>, query: string) => {
134
189
  // Disable render on server side
135
190
  if (onserver()) return;
136
-
137
- let nodes = $(proto).build().toDOM()
191
+
192
+ let nodes = $(proto as Proto).build().toDOM()
138
193
  document.querySelector(query)?.replaceChildren(...nodes);
139
194
  // if (!hmr(element, proto)) {
140
195
  // let nodes = proto.build().toDOM()
@@ -180,9 +235,9 @@ export namespace $ {
180
235
  return cases.get(condition)?.() ?? cases.get(symbol_default)?.();
181
236
  }
182
237
 
183
- export const async = (fn: () => Promise<void>) => {
184
- Proto.proto?.global.asyncTask(fn());
185
- }
238
+ export const tuple = <T extends any[]>(...args: T) => args;
239
+
240
+ export const async = <T>(fn: (parent: Proto | null) => Promise<T>): Promise<T> => Proto.proto?.global.asyncTask(fn(Proto.proto)) ?? fn(Proto.proto);
186
241
 
187
242
  export const stylesheet = onclient() ? new CSSStyleSheet() : _null;
188
243
  export const styleMap = new Map<Constructor<ElementProto>, Set<string>>();
@@ -58,10 +58,10 @@ function diff(parentElement: HTMLElement, newProto: Proto, oldProto?: Proto, pre
58
58
  if (!_instanceof(oldProto, NodeProto) || !_instanceof(newProto, NodeProto)) return;
59
59
  newProto.node = oldProto.node;
60
60
  oldProto.dispose();
61
- if (newProto.modifiers) forEach(newProto.modifiers, mod => {
62
- if (!newProto.node) throw 'diff:transferNode:newProto.node==null'
63
- mod(newProto.node)
64
- })
61
+ // if (newProto.modifiers) forEach(newProto.modifiers, mod => {
62
+ // if (!newProto.node) throw 'diff:transferNode:newProto.node==null'
63
+ // mod(newProto.node)
64
+ // })
65
65
  }
66
66
 
67
67
  if (!oldProto) {
@@ -1,4 +1,4 @@
1
- import { _Array_from, _null, _Object_assign, _Object_entries, forEach, isNull, isUndefined, map } from "@amateras/utils";
1
+ import { _Array_from, _null, _Object_assign, _Object_entries, forEach, isFunction, isNull, isUndefined, map } from "@amateras/utils";
2
2
  import { NodeProto } from "./NodeProto";
3
3
 
4
4
  const SELF_CLOSING_TAGNAMES = ['img', 'hr', 'br', 'input', 'link', 'meta'];
@@ -9,7 +9,7 @@ export class ElementProto<H extends HTMLElement = HTMLElement> extends NodeProto
9
9
  declare layout: $.Layout | null;
10
10
  #innerHTML = '';
11
11
  private __props__: $.Props | null
12
- constructor(tagname: string, props: $.Props | null, layout?: $.Layout | null) {
12
+ constructor(tagname: string, props: $.Props<any, any> | null, layout?: $.Layout | null) {
13
13
  super(() => layout?.(this));
14
14
  this.tagname = tagname;
15
15
  this.__props__ = props;
@@ -32,6 +32,13 @@ export class ElementProto<H extends HTMLElement = HTMLElement> extends NodeProto
32
32
  props({ ...props }: $.Props) {
33
33
  let {class: className, ...propsFiltered} = props;
34
34
  if (className) this.addClass(...className.split(' '));
35
+ // handle event listener in props
36
+ forEach(_Object_entries(propsFiltered), ([name, value]) => {
37
+ if (name.startsWith('on') && isFunction(value)) {
38
+ this.on(name.replace('on', ''), value);
39
+ delete propsFiltered[name];
40
+ }
41
+ })
35
42
  this.attrProcess(propsFiltered);
36
43
  }
37
44
 
@@ -40,19 +47,19 @@ export class ElementProto<H extends HTMLElement = HTMLElement> extends NodeProto
40
47
  on(type: string, listener: (event: Event & { currentTarget: H }) => void, options?: boolean | AddEventListenerOptions) {
41
48
  let setListener = (node: Node) => {
42
49
  node.addEventListener(type, listener as any, options)
43
- this.ondispose(() => this.node?.removeEventListener(type, listener as any))
50
+ this.listen('dispose', () => this.node?.removeEventListener(type, listener as any))
44
51
  }
45
52
  if (this.node) setListener(this.node);
46
- else this.ondom(setListener);
53
+ else this.listen('dom', setListener);
47
54
  }
48
55
 
49
56
  override toString(): string {
50
- return this.parseHTML()
57
+ return this.parseHTML();
51
58
  }
52
59
 
53
60
  parseHTML(options?: { children?: string, attr?: string }) {
54
61
  let tagname = this.tagname;
55
- let childrenHTML = options?.children ?? (this.#innerHTML || map(this.protos, proto => `${proto}`).join(''));
62
+ let childrenHTML = options?.children ?? (this.#innerHTML || map(this.protos, proto => proto.visible ? `${proto}` : '').join(''));
56
63
  let attr = options?.attr ?? map(_Object_entries(this.#attr), ([key, value]) => value.length ? `${key}="${value}"` : key).join(' ');
57
64
  let attrText = attr.length ? ' ' + attr : '';
58
65
  if (SELF_CLOSING_TAGNAMES.includes(tagname)) return `<${tagname}${attrText} />`;
@@ -60,16 +67,31 @@ export class ElementProto<H extends HTMLElement = HTMLElement> extends NodeProto
60
67
  }
61
68
 
62
69
  override toDOM(children = true): H[] {
63
- if (this.node) return [this.node];
64
- let element = document.createElement(this.tagname) as H;
70
+ let element = this.node ?? document.createElement(this.tagname) as H;
65
71
  this.node = element;
66
- if (this.#innerHTML) this.node.innerHTML = this.#innerHTML;
67
- else if (children) element.append(...map(this.protos, proto => proto.toDOM(children)).flat());
72
+ if (this.#innerHTML && this.node.innerHTML !== this.#innerHTML) this.node.innerHTML = this.#innerHTML;
73
+ else if (children) this.DOMProcess();
68
74
  forEach(_Object_entries(this.#attr), ([key, value]) => element.setAttribute(key, value));
69
- forEach(this.modifiers, process => process(element));
75
+ this.dispatch('dom', [this.node])
70
76
  return [element];
71
77
  }
72
78
 
79
+ private DOMProcess() {
80
+ let thisNode = this.node;
81
+ if (thisNode) {
82
+ let nodes = map(_Array_from(this.protos).filter(proto => proto.visible), proto => proto.toDOM()).flat();
83
+ let nextNode: Node | null = _null;
84
+ forEach(nodes, (node, i) => {
85
+ let currentNode = thisNode.childNodes[i];
86
+ if (currentNode !== node) {
87
+ if (!nodes.includes(currentNode as any)) nextNode = currentNode ?? _null;
88
+ else nextNode = thisNode.childNodes[i + 1] ?? _null;
89
+ thisNode.insertBefore(node, nextNode);
90
+ }
91
+ })
92
+ }
93
+ }
94
+
73
95
  private attrProcess(attrObj: $.Props) {
74
96
  forEach(_Object_entries(attrObj), ([key, value]) => {
75
97
  for (let process of $.process.attr) {
@@ -1,26 +1,35 @@
1
1
  import { _null, _Object_assign, forEach } from "@amateras/utils";
2
- import type { Proto } from "./Proto";
2
+ import { Proto } from "./Proto";
3
+
4
+ export type GlobalStateInitial = (global: GlobalState) => object | void;
3
5
 
4
6
  export class GlobalState {
5
7
  static disposers = new Set<(global: GlobalState) => void>()
6
8
  promises = new Set<Promise<any>>();
7
9
  root: Proto;
10
+ static initials = new Set<GlobalStateInitial>();
8
11
  constructor(root: Proto) {
12
+ forEach(GlobalState.initials, initial => {
13
+ const obj = initial(this);
14
+ if (obj) _Object_assign(this, obj);
15
+ })
9
16
  this.root = root;
10
- root.ondispose(() => this.dispose());
17
+ root.listen('dispose', () => this.dispose());
11
18
  }
19
+
12
20
  dispose() {
13
21
  this.promises.clear();
14
22
  this.root = _null as any;
15
23
  forEach(GlobalState.disposers, disposer => disposer(this));
16
24
  }
17
25
 
18
- static assign(obj: object) {
19
- _Object_assign(GlobalState.prototype, obj);
26
+ static assign(initial: GlobalStateInitial) {
27
+ this.initials.add(initial);
20
28
  }
21
29
 
22
30
  asyncTask(promise: Promise<any>) {
23
31
  this.promises.add(promise);
24
32
  promise.finally(() => this.promises.delete(promise));
33
+ return promise;
25
34
  }
26
35
  }
@@ -4,20 +4,18 @@ import { onclient } from "#env";
4
4
 
5
5
  export class NodeProto<N extends Node & ChildNode = Node & ChildNode> extends Proto {
6
6
  node: null | N = _null;
7
- modifiers: null | ((node: N) => void)[] = _null
8
7
  constructor(layout?: $.Layout) {
9
8
  super(layout);
10
9
  }
11
10
 
11
+ /**@deprecated - use `Proto.listen` on 'dom' event instead */
12
12
  ondom(callback: (node: N) => void) {
13
- this.modifiers = this.modifiers ?? [];
14
- this.modifiers.push(callback);
13
+ this.listen('dom', callback as any)
15
14
  }
16
15
 
17
16
  override dispose(): void {
18
17
  super.dispose();
19
18
  this.node = _null;
20
- this.modifiers = _null
21
19
  }
22
20
 
23
21
  inDOM() {
@@ -8,8 +8,6 @@ export abstract class Proto {
8
8
  static proto: Proto | null = _null;
9
9
  static [symbol_ProtoType] = 'Proto';
10
10
  static [symbol_Statement] = false;
11
- static disposer: ((proto: Proto) => void)[] = []
12
- private disposers: Function[] | null = _null;
13
11
  layout: $.Layout | null;
14
12
  readonly parent: Proto | null = _null;
15
13
  global: GlobalState = Proto.proto?.global ?? new GlobalState(this);
@@ -17,7 +15,8 @@ export abstract class Proto {
17
15
  firstProto: Proto | null = _null;
18
16
  lastProto: Proto | null = _null;
19
17
  builded = false;
20
- listeners: { [key: string]: Set<(src: Proto) => void> } | null = _null;
18
+ visible = true;
19
+ listeners: { [key: string]: Set<(...args: any[]) => void> } | null = _null;
21
20
  /**
22
21
  * @virtual This property is phantom types, declare the return type of {@link Proto.children}
23
22
  * @deprecated
@@ -28,20 +27,18 @@ export abstract class Proto {
28
27
  }
29
28
 
30
29
  dispose() {
31
- forEach(Proto.disposer, disposer => disposer(this));
32
- forEach(this.disposers, disposer => disposer());
30
+ this.dispatch('dispose', [this])
33
31
  forEach(this.protos, proto => proto.dispose());
34
32
  this.global = _null as any;
35
33
  this.sibling = _null;
36
34
  this.firstProto = _null;
37
35
  this.lastProto = _null;
38
- this.disposers = _null;
39
36
  (this as Mutable<this>).parent = _null;
40
37
  this.layout = _null;
41
38
  }
42
39
 
43
40
  get children(): this['__child__'][] {
44
- return map(this.protos, proto => {
41
+ return map(Array.from(this.protos).filter(proto => proto.visible), proto => {
45
42
  //@ts-ignore
46
43
  if (proto.constructor[symbol_Statement])
47
44
  return proto.children
@@ -105,6 +102,10 @@ export abstract class Proto {
105
102
  proto.global = this.global;
106
103
  }
107
104
 
105
+ remove() {
106
+ this.parent?.removeProtos(this);
107
+ }
108
+
108
109
  removeProtos(...protos: Proto[]) {
109
110
  let protoSet = this.protos;
110
111
  forEach(protos, proto => {
@@ -116,11 +117,12 @@ export abstract class Proto {
116
117
  }
117
118
 
118
119
  private processProtos(...protos: Proto[]) {
119
- let prevProto: null | Proto = null;
120
+ let prevProto: null | Proto = _null;
120
121
  if (protos.length)
121
122
  forEach(protos, (proto, i) => {
122
123
  if (i === 0) this.firstProto = proto;
123
124
  if (prevProto) prevProto.sibling = proto;
125
+ proto.sibling = _null;
124
126
  prevProto = proto;
125
127
  (proto as Mutable<Proto>).parent = this;
126
128
  })
@@ -129,28 +131,30 @@ export abstract class Proto {
129
131
  this.lastProto = prevProto;
130
132
  }
131
133
 
132
- build(cascading = true): this {
133
- this.clear(true);
134
+ build(cascading = true, clear = true): this {
135
+ if (clear) this.clear(true);
134
136
  $.context(Proto, this, () => this.layout?.(this));
135
137
  this.builded = true;
136
138
  if (cascading) forEach(this.protos, proto => {
137
139
  proto.build()
138
140
  });
139
- this.dispatch('builded', this);
141
+ this.dispatch('builded', [this]);
140
142
  return this
141
143
  }
142
144
 
143
145
  toString(): string {
144
- return map(this.protos, proto => `${proto}`).join('')
146
+ return map(_Array_from(this.protos).filter(proto => proto.visible), proto => `${proto}`).join('')
145
147
  }
146
148
 
147
149
  toDOM(children = true): Node[] {
148
- return children ? map(this.protos, proto => proto.toDOM(children)).flat() : [];
150
+ return children ? map(_Array_from(this.protos).filter(proto => proto.visible), proto => proto.toDOM(children)).flat() : [];
149
151
  }
150
152
 
153
+ /**
154
+ * @deprecated use Proto.listen('dispose') instead
155
+ */
151
156
  ondispose(disposer: () => void) {
152
- this.disposers = this.disposers ?? [];
153
- this.disposers.push(disposer);
157
+ this.listen('dispose', disposer);
154
158
  }
155
159
 
156
160
  removeNode() {
@@ -169,10 +173,10 @@ export abstract class Proto {
169
173
  return _null;
170
174
  }
171
175
 
172
- findBelow(filter: (proto: Proto) => boolean | void): Proto | null {
176
+ findBelow<T extends Proto>(filter: (proto: Proto) => boolean | void): T | null {
173
177
  for (let proto of this.protos) {
174
- if (filter(proto)) return proto;
175
- let nested = proto.findBelow(filter);
178
+ if (filter(proto)) return proto as T;
179
+ let nested = proto.findBelow<T>(filter);
176
180
  if (nested) return nested;
177
181
  }
178
182
  return _null;
@@ -196,14 +200,24 @@ export abstract class Proto {
196
200
  get text(): string {
197
201
  return this.children.map(proto => proto.text).join('')
198
202
  }
199
-
200
- dispatch(type: string, src: Proto, options?: {bubbles?: boolean}) {
201
- if (options?.bubbles) this.parent?.dispatch(type, src, options);
203
+
204
+ dispatch<K extends keyof $.ProtoEventMap<this>>(type: K, args: $.ProtoEventMap<this>[K], options?: {bubbles?: boolean}): boolean
205
+ dispatch(type: string, args: any[], options?: {bubbles?: boolean}): boolean
206
+ dispatch(type: string, args: any[], options?: {bubbles?: boolean}): boolean {
202
207
  let handlerSet = this.listeners?.[type];
203
- forEach(handlerSet, handle => handle(src));
208
+ let preventDefault = false;
209
+ if (options?.bubbles) {
210
+ let prevent = this.parent?.dispatch(type, args, options);
211
+ if (!preventDefault) preventDefault = prevent ?? false;
212
+ }
213
+ forEach(handlerSet, handle => {
214
+ let prevent = handle(...args) ?? false;
215
+ if (!preventDefault) preventDefault = prevent;
216
+ });
217
+ return preventDefault;
204
218
  }
205
219
 
206
- listen<K extends keyof $.ProtoEventMap>(type: K, handle: (...args: $.ProtoEventMap[K]) => void): void;
220
+ listen<K extends keyof $.ProtoEventMap<this>>(type: K, handle: (...args: $.ProtoEventMap<this>[K]) => void): void;
207
221
  listen(type: string, handle: (src: Proto) => void): void;
208
222
  listen(type: string, handle: (...args: any) => void) {
209
223
  let listeners = this.listeners ?? {};
@@ -1,4 +1,3 @@
1
- import { forEach } from "@amateras/utils";
2
1
  import { NodeProto } from "./NodeProto";
3
2
 
4
3
  export class TextProto extends NodeProto<Text> {
@@ -25,7 +24,7 @@ export class TextProto extends NodeProto<Text> {
25
24
  if (this.node) return [this.node];
26
25
  let node = new Text(this.#content);
27
26
  this.node = node;
28
- forEach(this.modifiers, mod => mod(node));
27
+ this.dispatch('dom', [this.node])
29
28
  return [node];
30
29
  }
31
30
 
@@ -1,6 +1,6 @@
1
1
  # amateras/css
2
2
 
3
- ## Usage
3
+ ## Import
4
4
  ```ts
5
5
  import 'amateras';
6
6
  import 'amateras/css';
@@ -21,23 +21,27 @@ const style = $.css({
21
21
  }
22
22
  })
23
23
 
24
- $(document.body).css(style).content([
25
- $('p').content('Text with blue color.')
26
- ]);
24
+ $('div', { css: style }, $$ => {
25
+ $('p', $$ => $`Change the size of window to see the color changes.`)
26
+ })
27
27
  ```
28
28
 
29
- ## Define Style in Method Chain
29
+ ## Define Style in Layout
30
+ There are two way to define CSS rule of proto.
30
31
  ```ts
31
- $(document.body).content([
32
- $('h1').css({ color: 'red' }).content('This is a title with red color!')
33
- ])
32
+ $('div', $$ => {
33
+ $$.css({
34
+ padding: '1rem 2rem'
35
+ })
36
+ $('h1', { css: { color: 'red' } }, $$ => $`This is a title with red color!`)
37
+ })
34
38
  ```
35
39
 
36
40
  ## Define Variables
37
41
 
38
42
  ### Single Variable
39
43
  ```ts
40
- const largeText = $.css.variables('1.2rem');
44
+ const largeText = $.css.variable('1.2rem');
41
45
 
42
46
  $.css({
43
47
  fontSize: largeText
@@ -46,7 +50,9 @@ $.css({
46
50
 
47
51
  ### Variable Group
48
52
  ```ts
49
- const text = $.css.variables({
53
+ import 'amateras/css/variable';
54
+
55
+ const text = $.css.variable({
50
56
  small: '0.8rem',
51
57
  medium: '1rem',
52
58
  large: '1.2rem'
@@ -59,6 +65,8 @@ $.css({
59
65
 
60
66
  ## Define Keyframes
61
67
  ```ts
68
+ import 'amateras/css/keyframes';
69
+
62
70
  const keyframes = $.css.keyframes({
63
71
  fadeIn: {
64
72
  '0%': { opacity: 0 },
@@ -101,11 +109,6 @@ $.CSS({
101
109
  }
102
110
  },
103
111
  })
104
-
105
- $(document.body).content([
106
- $('h1').class('title').content('A red color title.')
107
- ])
108
-
109
112
  ```
110
113
 
111
114
  ### CSS Colors Preset
@@ -26,9 +26,8 @@ declare global {
26
26
 
27
27
  export type CSSPropertyMap = {
28
28
  [key: string]:
29
- [$.CSSPropertySyntax, string | number, boolean]
30
- | [$.CSSPropertySyntax, string | number]
31
- | ['*']
29
+ [syntax: $.CSSPropertySyntax, initialValue?: string | number, inherits?: boolean]
30
+ | [syntax: '*']
32
31
  }
33
32
  }
34
33
  }
@@ -25,7 +25,7 @@ declare global {
25
25
  export function rules(proto: Proto): $CSSRule[];
26
26
  }
27
27
 
28
- export interface AttrMap {
28
+ export interface AttrMap<T> {
29
29
  css: OrArray<$.CSSMap | $CSSRule>;
30
30
  }
31
31
  }
@@ -18,4 +18,8 @@ export class $CSSProperty extends $CSS {
18
18
  toString() {
19
19
  return `var(${this.name})`
20
20
  }
21
+
22
+ declare(value?: $.CSSValue) {
23
+ return {[this.name]: `${value ?? this.initialValue}`}
24
+ }
21
25
  }
@@ -24,7 +24,7 @@ export class $CSSVariable<V = string> extends $CSS {
24
24
  return `var(${this.name}, ${value})`
25
25
  }
26
26
 
27
- declare(value?: V | $CSSVariable) {
27
+ declare(value?: $.CSSValue | $CSSVariable) {
28
28
  return {[this.name]: `${value ?? this.value}`}
29
29
  }
30
30
 
@@ -85,6 +85,7 @@ export interface $CSSDeclarationMap {
85
85
  content: string;
86
86
  counterIncrement: string;
87
87
  counterReset: string;
88
+ container: string;
88
89
  containerName: string;
89
90
  containerType: 'inline-size' | 'size' | 'normal' | 'scroll-state';
90
91
  cursor: 'auto' | 'default' | 'none' | 'context-menu' | 'help' | 'pointer' | 'progress' | 'wait' | 'cell' | 'crosshair' | 'text' | 'vertical-text' | 'alias' | 'copy' | 'move' | 'no-drop' | 'not-allowed' | 'e-resize' | 'n-resize' | 'ne-resize' | 'nw-resize' | 's-resize' | 'se-resize' | 'sw-resize' | 'w-resize' | 'ew-resize' | 'ns-resize' | 'nesw-resize' | 'nwse-resize' | 'col-resize' | 'row-resize' | 'all-scroll' | 'zoom-in' | 'zoom-out' | 'grab' | 'grabbing';
@@ -234,6 +235,9 @@ export interface $CSSDeclarationMap {
234
235
  scrollMarginInline: string;
235
236
  scrollMarginInlineStart: string;
236
237
  scrollMarginInlineEnd: string;
238
+ scrollbarWidth: 'thin' | 'none' | 'auto'
239
+ scrollbarGutter: OptionalStrings<['stable', 'both-edges']>
240
+ scrollbarColor: 'auto';
237
241
  shapeRendering: 'auto' | 'optimizeSpeed' | 'crispEdges' | 'geometricPrecision';
238
242
  stopColor: string;
239
243
  stopOpacity: string;
@@ -270,6 +274,7 @@ export interface $CSSDeclarationMap {
270
274
  textTransform: 'none' | 'capitalize' | 'uppercase' | 'lowercase';
271
275
  textUnderlineOffset: string;
272
276
  textUnderlinePosition: 'auto' | 'under' | 'left' | 'right';
277
+ textWrap: 'auto' | 'wrap' | 'nowrap' | 'balance' | 'pretty' | 'stable'
273
278
  top: string;
274
279
  touchAction: 'auto' | 'none' | 'pan-x' | 'pan-y' | 'manipulation';
275
280
  transform: string;