rask-ui 0.1.1 → 0.2.1

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 (213) hide show
  1. package/README.md +923 -185
  2. package/dist/component.d.ts +17 -0
  3. package/dist/component.d.ts.map +1 -1
  4. package/dist/createAsync.d.ts +23 -0
  5. package/dist/createAsync.d.ts.map +1 -1
  6. package/dist/createAsync.js +23 -0
  7. package/dist/createContext.d.ts +26 -2
  8. package/dist/createContext.d.ts.map +1 -1
  9. package/dist/createContext.js +31 -5
  10. package/dist/createContext.test.d.ts +2 -0
  11. package/dist/createContext.test.d.ts.map +1 -0
  12. package/dist/createContext.test.js +136 -0
  13. package/dist/createMutation.d.ts +23 -0
  14. package/dist/createMutation.d.ts.map +1 -1
  15. package/dist/createMutation.js +23 -0
  16. package/dist/createQuery.d.ts +23 -0
  17. package/dist/createQuery.d.ts.map +1 -1
  18. package/dist/createQuery.js +23 -0
  19. package/dist/createRef.test.d.ts +2 -0
  20. package/dist/createRef.test.d.ts.map +1 -0
  21. package/dist/createRef.test.js +80 -0
  22. package/dist/createState.d.ts +24 -0
  23. package/dist/createState.d.ts.map +1 -1
  24. package/dist/createState.js +24 -0
  25. package/dist/createView.d.ts +54 -0
  26. package/dist/createView.d.ts.map +1 -0
  27. package/dist/createView.js +68 -0
  28. package/dist/createView.test.d.ts +2 -0
  29. package/dist/createView.test.d.ts.map +1 -0
  30. package/dist/createView.test.js +203 -0
  31. package/dist/error.d.ts.map +1 -1
  32. package/dist/error.js +4 -5
  33. package/dist/error.test.d.ts +2 -0
  34. package/dist/error.test.d.ts.map +1 -0
  35. package/dist/error.test.js +144 -0
  36. package/dist/index.d.ts +3 -2
  37. package/dist/index.d.ts.map +1 -1
  38. package/dist/index.js +3 -2
  39. package/dist/integration.test.d.ts +2 -0
  40. package/dist/integration.test.d.ts.map +1 -0
  41. package/dist/integration.test.js +155 -0
  42. package/dist/jsx-dev-runtime.d.ts +3 -3
  43. package/dist/jsx-dev-runtime.d.ts.map +1 -1
  44. package/dist/jsx-dev-runtime.js +2 -2
  45. package/dist/jsx-runtime.d.ts +1 -4
  46. package/dist/jsx-runtime.d.ts.map +1 -1
  47. package/dist/jsx-runtime.js +3 -14
  48. package/dist/observation.d.ts +1 -0
  49. package/dist/observation.d.ts.map +1 -1
  50. package/dist/observation.js +5 -0
  51. package/dist/test-setup.d.ts +3 -3
  52. package/dist/test-setup.d.ts.map +1 -1
  53. package/dist/test-setup.js +7 -8
  54. package/dist/tests/class.test.d.ts +2 -0
  55. package/dist/tests/class.test.d.ts.map +1 -0
  56. package/dist/tests/class.test.js +185 -0
  57. package/dist/tests/complex-rendering.test.d.ts +2 -0
  58. package/dist/tests/complex-rendering.test.d.ts.map +1 -0
  59. package/dist/tests/complex-rendering.test.js +400 -0
  60. package/dist/tests/component.cleanup.test.d.ts +2 -0
  61. package/dist/tests/component.cleanup.test.d.ts.map +1 -0
  62. package/dist/tests/component.cleanup.test.js +325 -0
  63. package/dist/tests/component.counter.test.d.ts +2 -0
  64. package/dist/tests/component.counter.test.d.ts.map +1 -0
  65. package/dist/tests/component.counter.test.js +124 -0
  66. package/dist/tests/component.interaction.test.d.ts +2 -0
  67. package/dist/tests/component.interaction.test.d.ts.map +1 -0
  68. package/dist/tests/component.interaction.test.js +73 -0
  69. package/dist/tests/component.props.test.d.ts +2 -0
  70. package/dist/tests/component.props.test.d.ts.map +1 -0
  71. package/dist/tests/component.props.test.js +334 -0
  72. package/dist/tests/component.return-types.test.d.ts +2 -0
  73. package/dist/tests/component.return-types.test.d.ts.map +1 -0
  74. package/dist/tests/component.return-types.test.js +357 -0
  75. package/dist/tests/component.state.test.d.ts +2 -0
  76. package/dist/tests/component.state.test.d.ts.map +1 -0
  77. package/dist/tests/component.state.test.js +135 -0
  78. package/dist/tests/component.test.d.ts +2 -0
  79. package/dist/tests/component.test.d.ts.map +1 -0
  80. package/dist/tests/component.test.js +63 -0
  81. package/dist/tests/createAsync.test.d.ts +2 -0
  82. package/dist/tests/createAsync.test.d.ts.map +1 -0
  83. package/dist/tests/createAsync.test.js +110 -0
  84. package/dist/tests/createContext.test.d.ts +2 -0
  85. package/dist/tests/createContext.test.d.ts.map +1 -0
  86. package/dist/tests/createContext.test.js +141 -0
  87. package/dist/tests/createMutation.test.d.ts +2 -0
  88. package/dist/tests/createMutation.test.d.ts.map +1 -0
  89. package/dist/tests/createMutation.test.js +168 -0
  90. package/dist/tests/createQuery.test.d.ts +2 -0
  91. package/dist/tests/createQuery.test.d.ts.map +1 -0
  92. package/dist/tests/createQuery.test.js +156 -0
  93. package/dist/tests/createRef.test.d.ts +2 -0
  94. package/dist/tests/createRef.test.d.ts.map +1 -0
  95. package/dist/tests/createRef.test.js +84 -0
  96. package/dist/tests/createState.test.d.ts +2 -0
  97. package/dist/tests/createState.test.d.ts.map +1 -0
  98. package/dist/tests/createState.test.js +103 -0
  99. package/dist/tests/createView.test.d.ts +2 -0
  100. package/dist/tests/createView.test.d.ts.map +1 -0
  101. package/dist/tests/createView.test.js +203 -0
  102. package/dist/tests/edge-cases.test.d.ts +2 -0
  103. package/dist/tests/edge-cases.test.d.ts.map +1 -0
  104. package/dist/tests/edge-cases.test.js +637 -0
  105. package/dist/tests/error-no-boundary.test.d.ts +2 -0
  106. package/dist/tests/error-no-boundary.test.d.ts.map +1 -0
  107. package/dist/tests/error-no-boundary.test.js +174 -0
  108. package/dist/tests/error.test.d.ts +2 -0
  109. package/dist/tests/error.test.d.ts.map +1 -0
  110. package/dist/tests/error.test.js +199 -0
  111. package/dist/tests/fragment.test.d.ts +2 -0
  112. package/dist/tests/fragment.test.d.ts.map +1 -0
  113. package/dist/tests/fragment.test.js +618 -0
  114. package/dist/tests/integration.test.d.ts +2 -0
  115. package/dist/tests/integration.test.d.ts.map +1 -0
  116. package/dist/tests/integration.test.js +192 -0
  117. package/dist/tests/keys.test.d.ts +2 -0
  118. package/dist/tests/keys.test.d.ts.map +1 -0
  119. package/dist/tests/keys.test.js +293 -0
  120. package/dist/tests/mount.test.d.ts +2 -0
  121. package/dist/tests/mount.test.d.ts.map +1 -0
  122. package/dist/tests/mount.test.js +91 -0
  123. package/dist/tests/observation.test.d.ts +2 -0
  124. package/dist/tests/observation.test.d.ts.map +1 -0
  125. package/dist/tests/observation.test.js +150 -0
  126. package/dist/tests/patch.test.d.ts +2 -0
  127. package/dist/tests/patch.test.d.ts.map +1 -0
  128. package/dist/tests/patch.test.js +498 -0
  129. package/dist/tests/patchChildren.test.d.ts +2 -0
  130. package/dist/tests/patchChildren.test.d.ts.map +1 -0
  131. package/dist/tests/patchChildren.test.js +387 -0
  132. package/dist/tests/primitives.test.d.ts +2 -0
  133. package/dist/tests/primitives.test.d.ts.map +1 -0
  134. package/dist/tests/primitives.test.js +132 -0
  135. package/dist/vdom/AbstractVNode.d.ts +22 -0
  136. package/dist/vdom/AbstractVNode.d.ts.map +1 -0
  137. package/dist/vdom/AbstractVNode.js +106 -0
  138. package/dist/vdom/ComponentVNode.d.ts +48 -0
  139. package/dist/vdom/ComponentVNode.d.ts.map +1 -0
  140. package/dist/vdom/ComponentVNode.js +209 -0
  141. package/dist/vdom/ElementVNode.d.ts +24 -0
  142. package/dist/vdom/ElementVNode.d.ts.map +1 -0
  143. package/dist/vdom/ElementVNode.js +126 -0
  144. package/dist/vdom/FragmentVNode.d.ts +13 -0
  145. package/dist/vdom/FragmentVNode.d.ts.map +1 -0
  146. package/dist/vdom/FragmentVNode.js +34 -0
  147. package/dist/vdom/RootVNode.d.ts +22 -0
  148. package/dist/vdom/RootVNode.d.ts.map +1 -0
  149. package/dist/vdom/RootVNode.js +55 -0
  150. package/dist/vdom/TextVNode.d.ts +11 -0
  151. package/dist/vdom/TextVNode.d.ts.map +1 -0
  152. package/dist/vdom/TextVNode.js +32 -0
  153. package/dist/vdom/class.test.d.ts +2 -0
  154. package/dist/vdom/class.test.d.ts.map +1 -0
  155. package/dist/vdom/class.test.js +143 -0
  156. package/dist/vdom/complex-rendering.test.d.ts +2 -0
  157. package/dist/vdom/complex-rendering.test.d.ts.map +1 -0
  158. package/dist/vdom/complex-rendering.test.js +400 -0
  159. package/dist/vdom/component.cleanup.test.d.ts +2 -0
  160. package/dist/vdom/component.cleanup.test.d.ts.map +1 -0
  161. package/dist/vdom/component.cleanup.test.js +323 -0
  162. package/dist/vdom/component.counter.test.d.ts +2 -0
  163. package/dist/vdom/component.counter.test.d.ts.map +1 -0
  164. package/dist/vdom/component.counter.test.js +124 -0
  165. package/dist/vdom/component.interaction.test.d.ts +2 -0
  166. package/dist/vdom/component.interaction.test.d.ts.map +1 -0
  167. package/dist/vdom/component.interaction.test.js +73 -0
  168. package/dist/vdom/component.props.test.d.ts +2 -0
  169. package/dist/vdom/component.props.test.d.ts.map +1 -0
  170. package/dist/vdom/component.props.test.js +88 -0
  171. package/dist/vdom/component.return-types.test.d.ts +2 -0
  172. package/dist/vdom/component.return-types.test.d.ts.map +1 -0
  173. package/dist/vdom/component.return-types.test.js +357 -0
  174. package/dist/vdom/component.state.test.d.ts +2 -0
  175. package/dist/vdom/component.state.test.d.ts.map +1 -0
  176. package/dist/vdom/component.state.test.js +129 -0
  177. package/dist/vdom/component.test.d.ts +2 -0
  178. package/dist/vdom/component.test.d.ts.map +1 -0
  179. package/dist/vdom/component.test.js +63 -0
  180. package/dist/vdom/dom-utils.d.ts +9 -0
  181. package/dist/vdom/dom-utils.d.ts.map +1 -0
  182. package/dist/vdom/dom-utils.js +84 -0
  183. package/dist/vdom/edge-cases.test.d.ts +2 -0
  184. package/dist/vdom/edge-cases.test.d.ts.map +1 -0
  185. package/dist/vdom/edge-cases.test.js +637 -0
  186. package/dist/vdom/fragment.test.d.ts +2 -0
  187. package/dist/vdom/fragment.test.d.ts.map +1 -0
  188. package/dist/vdom/fragment.test.js +618 -0
  189. package/dist/vdom/index.d.ts +10 -0
  190. package/dist/vdom/index.d.ts.map +1 -0
  191. package/dist/vdom/index.js +26 -0
  192. package/dist/vdom/keys.test.d.ts +2 -0
  193. package/dist/vdom/keys.test.d.ts.map +1 -0
  194. package/dist/vdom/keys.test.js +293 -0
  195. package/dist/vdom/mount.test.d.ts +2 -0
  196. package/dist/vdom/mount.test.d.ts.map +1 -0
  197. package/dist/vdom/mount.test.js +91 -0
  198. package/dist/vdom/patch.test.d.ts +2 -0
  199. package/dist/vdom/patch.test.d.ts.map +1 -0
  200. package/dist/vdom/patch.test.js +498 -0
  201. package/dist/vdom/patchChildren.test.d.ts +2 -0
  202. package/dist/vdom/patchChildren.test.d.ts.map +1 -0
  203. package/dist/vdom/patchChildren.test.js +392 -0
  204. package/dist/vdom/primitives.test.d.ts +2 -0
  205. package/dist/vdom/primitives.test.d.ts.map +1 -0
  206. package/dist/vdom/primitives.test.js +132 -0
  207. package/dist/vdom/types.d.ts +8 -0
  208. package/dist/vdom/types.d.ts.map +1 -0
  209. package/dist/vdom/types.js +1 -0
  210. package/dist/vdom/utils.d.ts +6 -0
  211. package/dist/vdom/utils.d.ts.map +1 -0
  212. package/dist/vdom/utils.js +63 -0
  213. package/package.json +4 -6
@@ -0,0 +1,48 @@
1
+ import { Observer } from "../observation";
2
+ import { AbstractVNode } from "./AbstractVNode";
3
+ import { Props, VNode } from "./types";
4
+ export type ComponentChild = VNode | string | null | number | undefined | boolean;
5
+ export type ComponentChildren = ComponentChild | ComponentChild[];
6
+ /**
7
+ * Component function type. Components receive reactive props that should not be destructured.
8
+ *
9
+ * @warning **Do not destructure props!** Props are wrapped in a reactive proxy, and destructuring
10
+ * breaks reactivity. This is the same rule as Solid.js.
11
+ *
12
+ * @example
13
+ * // ❌ Bad - destructuring props loses reactivity
14
+ * function MyComponent({ count, name }) {
15
+ * return () => <div>{count} {name}</div>; // Won't update!
16
+ * }
17
+ *
18
+ * // ✅ Good - access props directly in render
19
+ * function MyComponent(props) {
20
+ * return () => <div>{props.count} {props.name}</div>; // Reactive!
21
+ * }
22
+ */
23
+ export type Component<P extends Props> = ((props: P) => () => ComponentChildren) | (() => () => ComponentChildren);
24
+ export type ComponentInstance = {
25
+ parent?: VNode;
26
+ contexts: Map<object, unknown> | null;
27
+ onMounts: Array<() => void>;
28
+ onCleanups: Array<() => void>;
29
+ observer: Observer;
30
+ reactiveProps: object;
31
+ error: unknown;
32
+ notifyError(error: unknown): void;
33
+ };
34
+ export declare function getCurrentComponent(): ComponentInstance;
35
+ export declare function onMount(cb: () => void): void;
36
+ export declare function onCleanup(cb: () => void): void;
37
+ export declare class ComponentVNode extends AbstractVNode {
38
+ component: Component<any>;
39
+ props: Props;
40
+ children: VNode[];
41
+ instance?: ComponentInstance;
42
+ constructor(component: Component<any>, props: Props, children: VNode[], key?: string);
43
+ rerender(): void;
44
+ mount(parent?: VNode): Node[];
45
+ patch(newNode: ComponentVNode): void;
46
+ unmount(): void;
47
+ }
48
+ //# sourceMappingURL=ComponentVNode.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"ComponentVNode.d.ts","sourceRoot":"","sources":["../../src/vdom/ComponentVNode.ts"],"names":[],"mappings":"AAAA,OAAO,EAAsB,QAAQ,EAAU,MAAM,gBAAgB,CAAC;AACtE,OAAO,EAAE,aAAa,EAAE,MAAM,iBAAiB,CAAC;AAGhD,OAAO,EAAE,KAAK,EAAE,KAAK,EAAE,MAAM,SAAS,CAAC;AAGvC,MAAM,MAAM,cAAc,GACtB,KAAK,GACL,MAAM,GACN,IAAI,GACJ,MAAM,GACN,SAAS,GACT,OAAO,CAAC;AACZ,MAAM,MAAM,iBAAiB,GAAG,cAAc,GAAG,cAAc,EAAE,CAAC;AAElE;;;;;;;;;;;;;;;;GAgBG;AACH,MAAM,MAAM,SAAS,CAAC,CAAC,SAAS,KAAK,IACjC,CAAC,CAAC,KAAK,EAAE,CAAC,KAAK,MAAM,iBAAiB,CAAC,GACvC,CAAC,MAAM,MAAM,iBAAiB,CAAC,CAAC;AAEpC,MAAM,MAAM,iBAAiB,GAAG;IAC9B,MAAM,CAAC,EAAE,KAAK,CAAC;IACf,QAAQ,EAAE,GAAG,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,IAAI,CAAC;IACtC,QAAQ,EAAE,KAAK,CAAC,MAAM,IAAI,CAAC,CAAC;IAC5B,UAAU,EAAE,KAAK,CAAC,MAAM,IAAI,CAAC,CAAC;IAC9B,QAAQ,EAAE,QAAQ,CAAC;IACnB,aAAa,EAAE,MAAM,CAAC;IACtB,KAAK,EAAE,OAAO,CAAC;IACf,WAAW,CAAC,KAAK,EAAE,OAAO,GAAG,IAAI,CAAC;CACnC,CAAC;AAKF,wBAAgB,mBAAmB,sBAYlC;AAED,wBAAgB,OAAO,CAAC,EAAE,EAAE,MAAM,IAAI,QAYrC;AAED,wBAAgB,SAAS,CAAC,EAAE,EAAE,MAAM,IAAI,QAYvC;AAED,qBAAa,cAAe,SAAQ,aAAa;IAC/C,SAAS,EAAE,SAAS,CAAC,GAAG,CAAC,CAAC;IAC1B,KAAK,EAAE,KAAK,CAAC;IAEb,QAAQ,EAAE,KAAK,EAAE,CAAM;IACvB,QAAQ,CAAC,EAAE,iBAAiB,CAAC;gBAE3B,SAAS,EAAE,SAAS,CAAC,GAAG,CAAC,EACzB,KAAK,EAAE,KAAK,EACZ,QAAQ,EAAE,KAAK,EAAE,EACjB,GAAG,CAAC,EAAE,MAAM;IAWd,QAAQ,IAAI,IAAI;IAGhB,KAAK,CAAC,MAAM,CAAC,EAAE,KAAK,GAAG,IAAI,EAAE;IAyH7B,KAAK,CAAC,OAAO,EAAE,cAAc;IAW7B,OAAO;CAcR"}
@@ -0,0 +1,209 @@
1
+ import { getCurrentObserver, Observer, Signal } from "../observation";
2
+ import { AbstractVNode } from "./AbstractVNode";
3
+ import { FragmentVNode } from "./FragmentVNode";
4
+ import { RootVNode } from "./RootVNode";
5
+ import { normalizeChildren } from "./utils";
6
+ import { currentRoot } from "./RootVNode";
7
+ import { ElementVNode } from "./ElementVNode";
8
+ export function getCurrentComponent() {
9
+ if (!currentRoot) {
10
+ throw new Error("No current root");
11
+ }
12
+ const currentComponent = currentRoot.componentStack[0];
13
+ if (!currentComponent) {
14
+ throw new Error("No current component");
15
+ }
16
+ return currentComponent;
17
+ }
18
+ export function onMount(cb) {
19
+ if (!currentRoot) {
20
+ throw new Error("Only use onCleanup in component setup");
21
+ }
22
+ const current = currentRoot.componentStack[0];
23
+ if (!current) {
24
+ throw new Error("Only use onCleanup in component setup");
25
+ }
26
+ current.onMounts.push(cb);
27
+ }
28
+ export function onCleanup(cb) {
29
+ if (!currentRoot) {
30
+ throw new Error("Only use onCleanup in component setup");
31
+ }
32
+ const current = currentRoot.componentStack[0];
33
+ if (!current) {
34
+ throw new Error("Only use onCleanup in component setup");
35
+ }
36
+ current.onCleanups.push(cb);
37
+ }
38
+ export class ComponentVNode extends AbstractVNode {
39
+ component;
40
+ props;
41
+ // These are the actual current children returned by component
42
+ children = [];
43
+ instance;
44
+ constructor(component, props, children, key) {
45
+ super();
46
+ this.component = component;
47
+ this.props = {
48
+ ...props,
49
+ children,
50
+ };
51
+ this.children = [];
52
+ this.key = key;
53
+ }
54
+ rerender() {
55
+ this.parent?.rerender();
56
+ }
57
+ mount(parent) {
58
+ this.parent = parent;
59
+ if (parent instanceof RootVNode) {
60
+ this.root = parent;
61
+ }
62
+ else {
63
+ this.root = parent?.root;
64
+ }
65
+ let errorSignal;
66
+ let error;
67
+ const executeRender = () => {
68
+ const stopObserving = instance.observer.observe();
69
+ let renderResult = new FragmentVNode([]);
70
+ try {
71
+ renderResult = render();
72
+ }
73
+ catch (error) {
74
+ instance.notifyError(error);
75
+ }
76
+ finally {
77
+ stopObserving();
78
+ }
79
+ return normalizeChildren(renderResult);
80
+ };
81
+ const instance = (this.instance = {
82
+ parent,
83
+ contexts: null,
84
+ onCleanups: [],
85
+ onMounts: [],
86
+ observer: new Observer(() => {
87
+ this.root?.setAsCurrent();
88
+ const newChildren = executeRender();
89
+ const prevChildren = this.children;
90
+ this.children = this.patchChildren(newChildren);
91
+ // Typically components return a single element, which does
92
+ // not require the parent to apply elements to the DOM again
93
+ const canSelfUpdate = prevChildren.length === 1 &&
94
+ this.children.length === 1 &&
95
+ prevChildren[0] instanceof ElementVNode &&
96
+ this.children[0] instanceof ElementVNode &&
97
+ this.canPatch(prevChildren[0], this.children[0]);
98
+ if (!canSelfUpdate) {
99
+ this.parent?.rerender();
100
+ }
101
+ this.root?.flushLifecycle();
102
+ this.root?.clearCurrent();
103
+ }),
104
+ reactiveProps: createReactiveProps(this.props),
105
+ get error() {
106
+ if (!errorSignal) {
107
+ errorSignal = new Signal();
108
+ }
109
+ const observer = getCurrentObserver();
110
+ if (observer) {
111
+ observer.subscribeSignal(errorSignal);
112
+ }
113
+ return error;
114
+ },
115
+ notifyError(childError) {
116
+ if (errorSignal) {
117
+ error = childError;
118
+ errorSignal.notify();
119
+ }
120
+ else if (instance.parent) {
121
+ let parent = instance.parent;
122
+ while (parent && !(parent instanceof ComponentVNode)) {
123
+ parent = parent?.parent;
124
+ }
125
+ // Only call notifyError if we found a ComponentVNode parent
126
+ if (parent instanceof ComponentVNode) {
127
+ parent.instance?.notifyError(childError);
128
+ }
129
+ // Otherwise there's no ErrorBoundary, so do nothing
130
+ }
131
+ else {
132
+ throw childError;
133
+ }
134
+ },
135
+ });
136
+ this.root?.setAsCurrent();
137
+ this.root?.pushComponent(instance);
138
+ const render = this.component(instance.reactiveProps);
139
+ // Validate that render is a function
140
+ if (typeof render !== "function") {
141
+ const initError = new Error(`Component must return a render function, but got ${render instanceof AbstractVNode ? "JSX" : typeof render}. ` +
142
+ `Components should return a function that returns JSX: \`return () => <div>...</div>\``);
143
+ console.error("Error initializing component:", initError);
144
+ error = initError;
145
+ instance.notifyError(initError);
146
+ this.root?.popComponent();
147
+ this.root?.clearCurrent();
148
+ return [];
149
+ }
150
+ this.children = executeRender();
151
+ this.root?.popComponent();
152
+ this.root?.clearCurrent();
153
+ const childElements = this.children
154
+ .map((child) => child.mount(this))
155
+ .flat();
156
+ // Queue onMount callbacks after children are mounted
157
+ // This ensures refs and other child lifecycle hooks run before parent onMount
158
+ instance.onMounts.forEach((cb) => {
159
+ this.root?.queueMount(cb);
160
+ });
161
+ return childElements;
162
+ }
163
+ patch(newNode) {
164
+ this.root?.setAsCurrent();
165
+ this.root?.pushComponent(this.instance);
166
+ for (const prop in newNode.props) {
167
+ this.instance.reactiveProps[prop] = newNode.props[prop];
168
+ }
169
+ this.root?.popComponent();
170
+ this.root?.clearCurrent();
171
+ }
172
+ unmount() {
173
+ this.instance.observer.dispose();
174
+ this.children.forEach((child) => child.unmount());
175
+ this.root?.queueUnmount(() => {
176
+ this.instance.onCleanups.forEach((cb) => {
177
+ try {
178
+ cb();
179
+ }
180
+ catch (error) {
181
+ // Log error but continue executing remaining cleanups
182
+ console.error("Error during cleanup:", error);
183
+ }
184
+ });
185
+ });
186
+ }
187
+ }
188
+ function createReactiveProps(props) {
189
+ const reactiveProps = {};
190
+ for (const prop in props) {
191
+ const signal = new Signal();
192
+ Object.defineProperty(reactiveProps, prop, {
193
+ get() {
194
+ const observer = getCurrentObserver();
195
+ if (observer) {
196
+ observer.subscribeSignal(signal);
197
+ }
198
+ return props[prop];
199
+ },
200
+ set(value) {
201
+ if (props[prop] !== value) {
202
+ props[prop] = value;
203
+ signal.notify();
204
+ }
205
+ },
206
+ });
207
+ }
208
+ return reactiveProps;
209
+ }
@@ -0,0 +1,24 @@
1
+ import { AbstractVNode } from "./AbstractVNode";
2
+ import { Props, VNode } from "./types";
3
+ export declare class ElementVNode extends AbstractVNode {
4
+ tag: string;
5
+ props: Props;
6
+ children: VNode[];
7
+ key?: string;
8
+ private ref?;
9
+ private eventListeners?;
10
+ constructor(tag: string, { ref, ...props }: Props, children: VNode[], key?: string);
11
+ rerender(): void;
12
+ mount(parent?: VNode): Node;
13
+ /**
14
+ * An ELEMENT patch goes through three operations
15
+ * - Patch or replace the element
16
+ * - Patch the children
17
+ */
18
+ patch(newNode: ElementVNode): void;
19
+ unmount(): void;
20
+ private setProp;
21
+ private patchProps;
22
+ private addEventListener;
23
+ }
24
+ //# sourceMappingURL=ElementVNode.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"ElementVNode.d.ts","sourceRoot":"","sources":["../../src/vdom/ElementVNode.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,aAAa,EAAE,MAAM,iBAAiB,CAAC;AAGhD,OAAO,EAAE,KAAK,EAAE,KAAK,EAAE,MAAM,SAAS,CAAC;AAUvC,qBAAa,YAAa,SAAQ,aAAa;IAC7C,GAAG,EAAE,MAAM,CAAC;IACZ,KAAK,EAAE,KAAK,CAAC;IACb,QAAQ,EAAE,KAAK,EAAE,CAAC;IAClB,GAAG,CAAC,EAAE,MAAM,CAAC;IACb,OAAO,CAAC,GAAG,CAAC,CAA0D;IACtE,OAAO,CAAC,cAAc,CAAC,CAA6B;gBAElD,GAAG,EAAE,MAAM,EACX,EAAE,GAAG,EAAE,GAAG,KAAK,EAAE,EAAE,KAAK,EACxB,QAAQ,EAAE,KAAK,EAAE,EACjB,GAAG,CAAC,EAAE,MAAM;IASd,QAAQ,IAAI,IAAI;IAOhB,KAAK,CAAC,MAAM,CAAC,EAAE,KAAK,GAAG,IAAI;IAkC3B;;;;OAIG;IACH,KAAK,CAAC,OAAO,EAAE,YAAY;IAW3B,OAAO;IAYP,OAAO,CAAC,OAAO,CAoCb;IACF,OAAO,CAAC,UAAU;IAGlB,OAAO,CAAC,gBAAgB;CAgBzB"}
@@ -0,0 +1,126 @@
1
+ import { AbstractVNode } from "./AbstractVNode";
2
+ import { RootVNode } from "./RootVNode";
3
+ import { isEventProp, setElementAttr, setElementProp, setElementStyle, setElementClass, } from "./dom-utils";
4
+ import { diffObjectKeys } from "./utils";
5
+ export class ElementVNode extends AbstractVNode {
6
+ tag;
7
+ props;
8
+ children;
9
+ key;
10
+ ref;
11
+ eventListeners;
12
+ constructor(tag, { ref, ...props }, children, key) {
13
+ super();
14
+ this.tag = tag;
15
+ this.props = props;
16
+ this.children = children;
17
+ this.key = key;
18
+ this.ref = ref;
19
+ }
20
+ rerender() {
21
+ const childrenElms = this.children
22
+ .map((child) => child.getElements())
23
+ .flat();
24
+ this.elm.replaceChildren(...childrenElms);
25
+ }
26
+ mount(parent) {
27
+ this.parent = parent;
28
+ if (parent instanceof RootVNode) {
29
+ this.root = parent;
30
+ }
31
+ else {
32
+ this.root = parent?.root;
33
+ }
34
+ const elm = (this.elm = document.createElement(this.tag));
35
+ for (const prop in this.props) {
36
+ this.setProp(prop, this.props[prop]);
37
+ }
38
+ if (this.ref) {
39
+ const ref = this.ref;
40
+ this.root?.queueMount(() => {
41
+ ref(elm);
42
+ });
43
+ }
44
+ this.children.forEach((child) => {
45
+ const childrenElms = child.mount(this);
46
+ if (Array.isArray(childrenElms)) {
47
+ childrenElms.forEach((node) => this.elm.appendChild(node));
48
+ }
49
+ else {
50
+ elm.appendChild(childrenElms);
51
+ }
52
+ });
53
+ return elm;
54
+ }
55
+ /**
56
+ * An ELEMENT patch goes through three operations
57
+ * - Patch or replace the element
58
+ * - Patch the children
59
+ */
60
+ patch(newNode) {
61
+ this.patchProps(newNode.props);
62
+ this.props = newNode.props;
63
+ this.children = this.patchChildren(newNode.children);
64
+ const childrenElms = this.children
65
+ .map((child) => child.getElements())
66
+ .flat();
67
+ this.elm.replaceChildren(...childrenElms);
68
+ }
69
+ unmount() {
70
+ this.children.forEach((child) => child.unmount());
71
+ this.root?.queueUnmount(() => {
72
+ if (this.eventListeners) {
73
+ for (const type in this.eventListeners) {
74
+ this.elm.removeEventListener(type, this.eventListeners[type]);
75
+ }
76
+ }
77
+ delete this.elm;
78
+ delete this.parent;
79
+ });
80
+ }
81
+ setProp = (prop, value) => {
82
+ const elm = this.getHTMLElement();
83
+ if (prop === "children") {
84
+ return;
85
+ }
86
+ if (prop === "class") {
87
+ setElementClass(elm, value);
88
+ return;
89
+ }
90
+ // Skip className if class is present (class takes precedence)
91
+ if (prop === "className" && "class" in this.props) {
92
+ return;
93
+ }
94
+ if (prop === "style") {
95
+ setElementStyle(elm, value);
96
+ return;
97
+ }
98
+ if (prop.startsWith("data-") || prop.startsWith("aria-")) {
99
+ setElementAttr(elm, prop, value);
100
+ return;
101
+ }
102
+ if (isEventProp(prop)) {
103
+ this.addEventListener(prop.slice(2).toLowerCase(), value);
104
+ return;
105
+ }
106
+ setElementProp(elm, prop, value);
107
+ };
108
+ patchProps(newProps) {
109
+ diffObjectKeys(this.props, newProps, this.setProp);
110
+ }
111
+ addEventListener(type, cb) {
112
+ if (!this.eventListeners) {
113
+ this.eventListeners = {};
114
+ }
115
+ if (this.eventListeners[type]) {
116
+ this.elm.removeEventListener(type, this.eventListeners[type]);
117
+ }
118
+ if (typeof cb === "function") {
119
+ this.elm.addEventListener(type, cb);
120
+ this.eventListeners[type] = cb;
121
+ }
122
+ else {
123
+ delete this.eventListeners[type];
124
+ }
125
+ }
126
+ }
@@ -0,0 +1,13 @@
1
+ import { AbstractVNode } from "./AbstractVNode";
2
+ import { VNode } from "./types";
3
+ export declare const Fragment: unique symbol;
4
+ export declare class FragmentVNode extends AbstractVNode {
5
+ children: VNode[];
6
+ key?: string;
7
+ constructor(children: VNode[], key?: string);
8
+ mount(parent?: VNode): Node[];
9
+ rerender(): void;
10
+ patch(newNode: FragmentVNode): void;
11
+ unmount(): void;
12
+ }
13
+ //# sourceMappingURL=FragmentVNode.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"FragmentVNode.d.ts","sourceRoot":"","sources":["../../src/vdom/FragmentVNode.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,aAAa,EAAE,MAAM,iBAAiB,CAAC;AAKhD,OAAO,EAAE,KAAK,EAAE,MAAM,SAAS,CAAC;AAEhC,eAAO,MAAM,QAAQ,eAAqB,CAAC;AAE3C,qBAAa,aAAc,SAAQ,aAAa;IAC9C,QAAQ,EAAE,KAAK,EAAE,CAAC;IAClB,GAAG,CAAC,EAAE,MAAM,CAAC;gBAED,QAAQ,EAAE,KAAK,EAAE,EAAE,GAAG,CAAC,EAAE,MAAM;IAK3C,KAAK,CAAC,MAAM,CAAC,EAAE,KAAK,GAAG,IAAI,EAAE;IAW7B,QAAQ,IAAI,IAAI;IAGhB,KAAK,CAAC,OAAO,EAAE,aAAa;IAG5B,OAAO;CAMR"}
@@ -0,0 +1,34 @@
1
+ import { AbstractVNode } from "./AbstractVNode";
2
+ import { RootVNode } from "./RootVNode";
3
+ export const Fragment = Symbol("Fragment");
4
+ export class FragmentVNode extends AbstractVNode {
5
+ children;
6
+ key;
7
+ constructor(children, key) {
8
+ super();
9
+ this.children = children;
10
+ this.key = key;
11
+ }
12
+ mount(parent) {
13
+ this.parent = parent;
14
+ if (parent instanceof RootVNode) {
15
+ this.root = parent;
16
+ }
17
+ else {
18
+ this.root = parent?.root;
19
+ }
20
+ return this.children.map((child) => child.mount(this)).flat();
21
+ }
22
+ rerender() {
23
+ this.parent?.rerender();
24
+ }
25
+ patch(newNode) {
26
+ this.children = this.patchChildren(newNode.children);
27
+ }
28
+ unmount() {
29
+ this.children.forEach((child) => child.unmount());
30
+ this.root?.queueUnmount(() => {
31
+ delete this.parent;
32
+ });
33
+ }
34
+ }
@@ -0,0 +1,22 @@
1
+ import { AbstractVNode } from "./AbstractVNode";
2
+ import { VNode } from "./types";
3
+ import { ComponentInstance } from "./ComponentVNode";
4
+ export declare let currentRoot: RootVNode | undefined;
5
+ export declare class RootVNode extends AbstractVNode {
6
+ children: VNode[];
7
+ componentStack: ComponentInstance[];
8
+ private lifecycleQueue;
9
+ constructor(rootNode: VNode, container: HTMLElement);
10
+ queueMount(cb: () => void): void;
11
+ queueUnmount(cb: () => void): void;
12
+ flushLifecycle(): void;
13
+ pushComponent(instance: ComponentInstance): void;
14
+ popComponent(): void;
15
+ setAsCurrent(): void;
16
+ clearCurrent(): void;
17
+ mount(): Node | Node[];
18
+ patch(): void;
19
+ rerender(): void;
20
+ unmount(): void;
21
+ }
22
+ //# sourceMappingURL=RootVNode.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"RootVNode.d.ts","sourceRoot":"","sources":["../../src/vdom/RootVNode.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,aAAa,EAAE,MAAM,iBAAiB,CAAC;AAChD,OAAO,EAAE,KAAK,EAAE,MAAM,SAAS,CAAC;AAChC,OAAO,EAAE,iBAAiB,EAAE,MAAM,kBAAkB,CAAC;AAIrD,eAAO,IAAI,WAAW,EAAE,SAAS,GAAG,SAAS,CAAC;AAE9C,qBAAa,SAAU,SAAQ,aAAa;IAC1C,QAAQ,EAAE,KAAK,EAAE,CAAC;IAClB,cAAc,EAAE,iBAAiB,EAAE,CAAM;IACzC,OAAO,CAAC,cAAc,CAGpB;gBAEU,QAAQ,EAAE,KAAK,EAAE,SAAS,EAAE,WAAW;IAMnD,UAAU,CAAC,EAAE,EAAE,MAAM,IAAI;IAIzB,YAAY,CAAC,EAAE,EAAE,MAAM,IAAI;IAI3B,cAAc;IAOd,aAAa,CAAC,QAAQ,EAAE,iBAAiB;IAIzC,YAAY;IAIZ,YAAY;IAIZ,YAAY;IAMZ,KAAK,IAAI,IAAI,GAAG,IAAI,EAAE;IAGtB,KAAK,IAAI,IAAI;IACb,QAAQ,IAAI,IAAI;IAShB,OAAO,IAAI,IAAI;CAChB"}
@@ -0,0 +1,55 @@
1
+ import { AbstractVNode } from "./AbstractVNode";
2
+ // Global reference to the currently executing root
3
+ // Safe because JS is single-threaded - only one render executes at a time
4
+ export let currentRoot;
5
+ export class RootVNode extends AbstractVNode {
6
+ children;
7
+ componentStack = [];
8
+ lifecycleQueue = {
9
+ toMount: [],
10
+ toUnmount: [],
11
+ };
12
+ constructor(rootNode, container) {
13
+ super();
14
+ this.elm = container;
15
+ this.children = [rootNode];
16
+ }
17
+ queueMount(cb) {
18
+ this.lifecycleQueue.toMount.push(cb);
19
+ }
20
+ queueUnmount(cb) {
21
+ this.lifecycleQueue.toUnmount.push(cb);
22
+ }
23
+ flushLifecycle() {
24
+ this.lifecycleQueue.toUnmount.forEach((cb) => cb());
25
+ this.lifecycleQueue.toUnmount = [];
26
+ this.lifecycleQueue.toMount.forEach((cb) => cb());
27
+ this.lifecycleQueue.toMount = [];
28
+ }
29
+ pushComponent(instance) {
30
+ this.componentStack.unshift(instance);
31
+ }
32
+ popComponent() {
33
+ this.componentStack.shift();
34
+ }
35
+ setAsCurrent() {
36
+ currentRoot = this;
37
+ }
38
+ clearCurrent() {
39
+ if (currentRoot === this) {
40
+ currentRoot = undefined;
41
+ }
42
+ }
43
+ mount() {
44
+ return this.children.map((childNode) => childNode.mount(this)).flat();
45
+ }
46
+ patch() { }
47
+ rerender() {
48
+ const childrenElms = this.children
49
+ .map((child) => child.getElements())
50
+ .flat();
51
+ this.elm.replaceChildren(...childrenElms);
52
+ this.flushLifecycle();
53
+ }
54
+ unmount() { }
55
+ }
@@ -0,0 +1,11 @@
1
+ import { AbstractVNode } from "./AbstractVNode";
2
+ import { VNode } from "./types";
3
+ export declare class TextVNode extends AbstractVNode {
4
+ text: string;
5
+ constructor(text: string);
6
+ mount(parent?: VNode): Node;
7
+ patch(newNode: TextVNode): void;
8
+ rerender(): void;
9
+ unmount(): void;
10
+ }
11
+ //# sourceMappingURL=TextVNode.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"TextVNode.d.ts","sourceRoot":"","sources":["../../src/vdom/TextVNode.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,aAAa,EAAE,MAAM,iBAAiB,CAAC;AAEhD,OAAO,EAAE,KAAK,EAAE,MAAM,SAAS,CAAC;AAEhC,qBAAa,SAAU,SAAQ,aAAa;IAC1C,IAAI,EAAE,MAAM,CAAC;gBACD,IAAI,EAAE,MAAM;IAIxB,KAAK,CAAC,MAAM,CAAC,EAAE,KAAK,GAAG,IAAI;IAe3B,KAAK,CAAC,OAAO,EAAE,SAAS;IAIxB,QAAQ,IAAI,IAAI;IAChB,OAAO;CAMR"}
@@ -0,0 +1,32 @@
1
+ import { AbstractVNode } from "./AbstractVNode";
2
+ import { RootVNode } from "./RootVNode";
3
+ export class TextVNode extends AbstractVNode {
4
+ text;
5
+ constructor(text) {
6
+ super();
7
+ this.text = text;
8
+ }
9
+ mount(parent) {
10
+ this.parent = parent;
11
+ if (parent instanceof RootVNode) {
12
+ this.root = parent;
13
+ }
14
+ else {
15
+ this.root = parent?.root;
16
+ }
17
+ const textNode = document.createTextNode(this.text);
18
+ this.elm = textNode;
19
+ return textNode;
20
+ }
21
+ patch(newNode) {
22
+ this.text = newNode.text;
23
+ this.elm.textContent = this.text;
24
+ }
25
+ rerender() { }
26
+ unmount() {
27
+ this.root?.queueUnmount(() => {
28
+ delete this.elm;
29
+ delete this.parent;
30
+ });
31
+ }
32
+ }
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=class.test.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"class.test.d.ts","sourceRoot":"","sources":["../../src/vdom/class.test.tsx"],"names":[],"mappings":""}