@praxisjs/runtime 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 (62) hide show
  1. package/CHANGELOG.md +28 -0
  2. package/dist/children.d.ts +3 -0
  3. package/dist/children.d.ts.map +1 -0
  4. package/dist/children.js +24 -0
  5. package/dist/children.js.map +1 -0
  6. package/dist/component.d.ts +5 -0
  7. package/dist/component.d.ts.map +1 -0
  8. package/dist/component.js +42 -0
  9. package/dist/component.js.map +1 -0
  10. package/dist/context.d.ts +4 -0
  11. package/dist/context.d.ts.map +1 -0
  12. package/dist/context.js +18 -0
  13. package/dist/context.js.map +1 -0
  14. package/dist/dom/constants.d.ts +5 -0
  15. package/dist/dom/constants.d.ts.map +1 -0
  16. package/dist/dom/constants.js +61 -0
  17. package/dist/dom/constants.js.map +1 -0
  18. package/dist/dom/create.d.ts +2 -0
  19. package/dist/dom/create.d.ts.map +1 -0
  20. package/dist/dom/create.js +7 -0
  21. package/dist/dom/create.js.map +1 -0
  22. package/dist/dom/events.d.ts +3 -0
  23. package/dist/dom/events.d.ts.map +1 -0
  24. package/dist/dom/events.js +5 -0
  25. package/dist/dom/events.js.map +1 -0
  26. package/dist/dom/props.d.ts +3 -0
  27. package/dist/dom/props.d.ts.map +1 -0
  28. package/dist/dom/props.js +72 -0
  29. package/dist/dom/props.js.map +1 -0
  30. package/dist/element.d.ts +3 -0
  31. package/dist/element.d.ts.map +1 -0
  32. package/dist/element.js +16 -0
  33. package/dist/element.js.map +1 -0
  34. package/dist/index.d.ts +14 -1
  35. package/dist/index.d.ts.map +1 -1
  36. package/dist/index.js +28 -1
  37. package/dist/index.js.map +1 -1
  38. package/dist/reactive.d.ts +3 -0
  39. package/dist/reactive.d.ts.map +1 -0
  40. package/dist/reactive.js +35 -0
  41. package/dist/reactive.js.map +1 -0
  42. package/dist/scope.d.ts +10 -0
  43. package/dist/scope.d.ts.map +1 -0
  44. package/dist/scope.js +20 -0
  45. package/dist/scope.js.map +1 -0
  46. package/package.json +4 -5
  47. package/src/children.ts +33 -0
  48. package/src/component.ts +60 -0
  49. package/src/context.ts +22 -0
  50. package/src/dom/constants.ts +63 -0
  51. package/src/dom/create.ts +7 -0
  52. package/src/dom/events.ts +11 -0
  53. package/src/dom/props.ts +79 -0
  54. package/src/element.ts +24 -0
  55. package/src/index.ts +36 -1
  56. package/src/reactive.ts +45 -0
  57. package/src/scope.ts +25 -0
  58. package/dist/renderer.d.ts +0 -3
  59. package/dist/renderer.d.ts.map +0 -1
  60. package/dist/renderer.js +0 -380
  61. package/dist/renderer.js.map +0 -1
  62. package/src/renderer.ts +0 -473
package/src/renderer.ts DELETED
@@ -1,473 +0,0 @@
1
- import {
2
- effect,
3
- createFunctionalContext,
4
- setFunctionalContext,
5
- } from "@praxisjs/core";
6
- import { initSlots } from "@praxisjs/decorators";
7
- import type {
8
- VNode,
9
- ChildrenInternal,
10
- ComponentInstance,
11
- } from "@praxisjs/shared";
12
-
13
- const EVENT_MAP: Record<string, string> = {
14
- onClick: "click",
15
- onDblClick: "dblclick",
16
- onChange: "change",
17
- onInput: "input",
18
- onSubmit: "submit",
19
- onReset: "reset",
20
- onKeyDown: "keydown",
21
- onKeyUp: "keyup",
22
- onKeyPress: "keypress",
23
- onFocus: "focus",
24
- onBlur: "blur",
25
- onMouseDown: "mousedown",
26
- onMouseUp: "mouseup",
27
- onMouseEnter: "mouseenter",
28
- onMouseLeave: "mouseleave",
29
- onMouseMove: "mousemove",
30
- onContextMenu: "contextmenu",
31
- onScroll: "scroll",
32
- onWheel: "wheel",
33
- onDragStart: "dragstart",
34
- onDragEnd: "dragend",
35
- onDragOver: "dragover",
36
- onDrop: "drop",
37
- onTouchStart: "touchstart",
38
- onTouchEnd: "touchend",
39
- onTouchMove: "touchmove",
40
- onAnimationEnd: "animationend",
41
- onTransitionEnd: "transitionend",
42
- };
43
-
44
- interface MountedNode {
45
- el: Node;
46
- vnode?: VNode;
47
- instance?: ComponentInstance;
48
- cleanups: Array<() => void>;
49
- children: MountedNode[];
50
- }
51
-
52
- const SVG_NAMESPACE = "http://www.w3.org/2000/svg";
53
- const SVG_TAGS = new Set([
54
- "svg",
55
- "path",
56
- "circle",
57
- "rect",
58
- "line",
59
- "polyline",
60
- "polygon",
61
- "ellipse",
62
- "text",
63
- "g",
64
- "defs",
65
- "use",
66
- "symbol",
67
- "marker",
68
- "clipPath",
69
- "mask",
70
- "pattern",
71
- "image",
72
- "linearGradient",
73
- "radialGradient",
74
- "stop",
75
- "filter",
76
- "feGaussianBlur",
77
- "tspan",
78
- "textPath",
79
- "foreignObject",
80
- ]);
81
-
82
- function applyProps(
83
- el: Element,
84
- props: Record<string, unknown>,
85
- cleanups: Array<() => void>,
86
- ) {
87
- for (const [key, value] of Object.entries(props)) {
88
- if (key === "key" || key === "children") continue;
89
-
90
- if (key in EVENT_MAP) {
91
- const eventName = EVENT_MAP[key];
92
- const eventListener = value as EventListener;
93
- el.addEventListener(eventName, eventListener);
94
- cleanups.push(() => {
95
- el.removeEventListener(eventName, eventListener);
96
- });
97
- continue;
98
- }
99
-
100
- if (key === "ref" && typeof value === "function") {
101
- (value as (el: Element) => void)(el);
102
- continue;
103
- }
104
-
105
- if (key === "className" || key === "class") {
106
- if (typeof value === "function") {
107
- cleanups.push(
108
- effect(() => {
109
- el.setAttribute("class", String((value as () => unknown)()));
110
- }),
111
- );
112
- } else {
113
- // eslint-disable-next-line @typescript-eslint/no-base-to-string
114
- el.setAttribute("class", String(value ?? ""));
115
- }
116
- continue;
117
- }
118
-
119
- if (key === "style") {
120
- if (typeof value === "function") {
121
- cleanups.push(
122
- effect(() => {
123
- const styleValue = (value as () => unknown)();
124
- if (typeof styleValue === "object" && styleValue !== null) {
125
- el.removeAttribute("style");
126
- Object.assign((el as HTMLElement).style, styleValue);
127
- } else {
128
- // eslint-disable-next-line @typescript-eslint/no-base-to-string
129
- el.setAttribute("style", String(styleValue ?? ""));
130
- }
131
- }),
132
- );
133
- } else if (typeof value === "object" && value !== null) {
134
- el.removeAttribute("style");
135
- Object.assign((el as HTMLElement).style, value);
136
- } else {
137
- // eslint-disable-next-line @typescript-eslint/no-base-to-string
138
- el.setAttribute("style", String(value ?? ""));
139
- }
140
- continue;
141
- }
142
-
143
- if (["checked", "value", "disabled", "selected"].includes(key)) {
144
- if (typeof value === "function") {
145
- cleanups.push(
146
- effect(() => {
147
- (el as unknown as Record<string, unknown>)[key] = (
148
- value as () => unknown
149
- )();
150
- }),
151
- );
152
- } else {
153
- (el as unknown as Record<string, unknown>)[key] = value;
154
- }
155
- continue;
156
- }
157
-
158
- if (typeof value === "function") {
159
- const attrKey = key === "htmlFor" ? "for" : key;
160
- cleanups.push(
161
- effect(() => {
162
- const attrValue = (value as () => unknown)();
163
- if (
164
- attrValue === false ||
165
- attrValue === null ||
166
- attrValue === undefined
167
- ) {
168
- el.removeAttribute(attrKey);
169
- } else if (attrValue === true) {
170
- el.setAttribute(attrKey, "");
171
- } else {
172
- // eslint-disable-next-line @typescript-eslint/no-base-to-string
173
- el.setAttribute(attrKey, String(attrValue));
174
- }
175
- }),
176
- );
177
- continue;
178
- }
179
-
180
- if (typeof value === "boolean") {
181
- if (value) {
182
- el.setAttribute(key, "");
183
- } else {
184
- el.removeAttribute(key);
185
- }
186
- continue;
187
- }
188
-
189
- if (value !== null && value !== undefined) {
190
- const attrKey = key === "htmlFor" ? "for" : key;
191
- // eslint-disable-next-line @typescript-eslint/no-base-to-string
192
- el.setAttribute(attrKey, String(value));
193
- }
194
- }
195
- }
196
-
197
- function renderChildren(
198
- children: ChildrenInternal,
199
- cleanups: Array<() => void>,
200
- ): Node[] {
201
- if (children === null || children === undefined || children === false) {
202
- return [document.createComment("empty")];
203
- }
204
-
205
- if (typeof children === "string" || typeof children === "number") {
206
- return [document.createTextNode(String(children))];
207
- }
208
-
209
- if (Array.isArray(children)) {
210
- return children.flatMap((child) => renderChildren(child, cleanups));
211
- }
212
-
213
- if (typeof children === "function") {
214
- const fn = children as () => ChildrenInternal;
215
-
216
- const anchor = document.createComment("reactive");
217
- let currentNodes: Node[] = [];
218
- let childCleanups: Array<() => void> = [];
219
- let initialized = false;
220
-
221
- cleanups.push(
222
- effect(() => {
223
- const result = fn();
224
-
225
- if (!initialized) {
226
- currentNodes = renderChildren(result, childCleanups);
227
- initialized = true;
228
- return;
229
- }
230
-
231
- childCleanups.forEach((c) => { c(); });
232
- childCleanups = [];
233
-
234
- const parent = anchor.parentNode;
235
- if (!parent) return;
236
-
237
- for (const node of currentNodes) {
238
- if (node.parentNode === parent) {
239
- parent.removeChild(node);
240
- }
241
- }
242
-
243
- const newNodes = renderChildren(result, childCleanups);
244
- newNodes.forEach((node) => parent.insertBefore(node, anchor));
245
- currentNodes = newNodes;
246
- }),
247
- );
248
-
249
- cleanups.push(() => {
250
- childCleanups.forEach((c) => { c(); });
251
- });
252
-
253
- return [...currentNodes, anchor];
254
- }
255
-
256
- if (typeof children === "object" && "type" in children) {
257
- const mounted = mountVNode(
258
- children,
259
- document.createComment("object"),
260
- cleanups,
261
- );
262
- return [mounted.el];
263
- }
264
-
265
- return [document.createTextNode(String(children))];
266
- }
267
-
268
- function mountVNode(
269
- vnode: VNode,
270
- _parent: Node,
271
- parentCleanups: Array<() => void>,
272
- ): MountedNode {
273
- const cleanups: Array<() => void> = [];
274
- const { type, props, children } = vnode;
275
-
276
- if (typeof type === "function" && "isComponent" in type) {
277
- const instance = new type({
278
- ...props,
279
- });
280
- if (children.length > 0) {
281
- initSlots(instance, children);
282
- }
283
- instance.onBeforeMount?.();
284
-
285
- const wrapper = document.createElement("div");
286
- wrapper.setAttribute("data-component", type.name || "AnonymousComponent");
287
-
288
- const isMemoized = instance._isMemorized;
289
-
290
- cleanups.push(
291
- effect(() => {
292
- if (instance._mounted) return;
293
-
294
- if (!isMemoized) {
295
- const resolvedProps: Record<string, unknown> = {};
296
- for (const [key, value] of Object.entries(props)) {
297
- if (key === "children" || key === "key") continue;
298
- if (typeof value === "function") {
299
- resolvedProps[key] = (value as () => unknown)();
300
- } else {
301
- resolvedProps[key] = value;
302
- }
303
- }
304
-
305
- if (!instance._stateDirty) {
306
- const arePropsEqual = instance._arePropsEqual;
307
- const lastProps = instance._lastResolvedProps;
308
-
309
- if (
310
- lastProps !== undefined &&
311
- arePropsEqual?.(lastProps, resolvedProps)
312
- ) {
313
- return;
314
- }
315
- }
316
-
317
- instance._lastResolvedProps = resolvedProps;
318
- }
319
-
320
- instance._stateDirty = false;
321
- instance._setProps({ ...props });
322
-
323
- let rerenderedVNode: VNode | null = null;
324
- try {
325
- rerenderedVNode = instance.render();
326
- } catch (e) {
327
- console.error("Error rendering component:", e);
328
- const error = e instanceof Error ? e : new Error(String(e));
329
- instance.onError?.(error);
330
- }
331
-
332
- wrapper.innerHTML = "";
333
-
334
- if (rerenderedVNode) {
335
- const childNode = mountVNode(rerenderedVNode, wrapper, cleanups);
336
- wrapper.appendChild(childNode.el);
337
- }
338
- }),
339
- );
340
-
341
- parentCleanups.push(() => {
342
- instance.onUnmount?.();
343
- instance._mounted = false;
344
- cleanups.forEach((fn) => {
345
- fn();
346
- });
347
- });
348
-
349
- queueMicrotask(() => {
350
- instance.onMount?.();
351
- instance._mounted = true;
352
- });
353
-
354
- return {
355
- el: wrapper,
356
- vnode,
357
- instance,
358
- cleanups,
359
- children: [],
360
- };
361
- }
362
-
363
- if (typeof type === "function") {
364
- const resolvedType = type as (
365
- props: Record<string, unknown> & { children?: ChildrenInternal[] },
366
- ) => VNode | null;
367
-
368
- const ctx = createFunctionalContext();
369
- setFunctionalContext(ctx);
370
-
371
- let resolvedVNode: VNode | null = null;
372
- try {
373
- resolvedVNode = resolvedType({ ...props, children });
374
- } catch (e) {
375
- const error = e instanceof Error ? e : new Error(String(e));
376
- ctx.onError.forEach((fn) => {
377
- fn(error);
378
- });
379
- }
380
-
381
- setFunctionalContext(null);
382
-
383
- ctx.onBeforeMount.forEach((fn) => {
384
- fn();
385
- });
386
-
387
- if (!resolvedVNode) {
388
- const commentNode = document.createComment(
389
- `<${type.name || "AnonymousComponent"} />`,
390
- );
391
- queueMicrotask(() => {
392
- ctx.onMount.forEach((fn) => {
393
- fn();
394
- });
395
- });
396
- cleanups.push(() => {
397
- ctx.onUnmount.forEach((fn) => {
398
- fn();
399
- });
400
- });
401
- parentCleanups.push(() => {
402
- cleanups.forEach((fn) => {
403
- fn();
404
- });
405
- });
406
- return {
407
- el: commentNode,
408
- cleanups,
409
- children: [],
410
- };
411
- }
412
-
413
- const childNode = mountVNode(resolvedVNode, _parent, cleanups);
414
- queueMicrotask(() => {
415
- ctx.onMount.forEach((fn) => {
416
- fn();
417
- });
418
- });
419
- cleanups.push(() => {
420
- ctx.onUnmount.forEach((fn) => {
421
- fn();
422
- });
423
- });
424
- parentCleanups.push(() => {
425
- cleanups.forEach((fn) => {
426
- fn();
427
- });
428
- });
429
- return {
430
- el: childNode.el,
431
- vnode,
432
- cleanups,
433
- children: [],
434
- };
435
- }
436
-
437
- const el = SVG_TAGS.has(type)
438
- ? document.createElementNS(SVG_NAMESPACE, type)
439
- : document.createElement(type);
440
- applyProps(el, props, cleanups);
441
-
442
- for (const child of children) {
443
- const childNodes = renderChildren(child, cleanups);
444
- childNodes.forEach((node) => el.appendChild(node));
445
- }
446
-
447
- parentCleanups.push(() => {
448
- cleanups.forEach((fn) => {
449
- fn();
450
- });
451
- });
452
-
453
- return {
454
- el,
455
- vnode,
456
- cleanups,
457
- children: [],
458
- };
459
- }
460
-
461
- export function render(vnode: VNode, container: HTMLElement) {
462
- const cleanups: Array<() => void> = [];
463
- container.innerHTML = "";
464
- const mounted = mountVNode(vnode, container, cleanups);
465
- container.appendChild(mounted.el);
466
-
467
- return () => {
468
- cleanups.forEach((fn) => {
469
- fn();
470
- });
471
- container.innerHTML = "";
472
- };
473
- }