yandel 1.1.2 → 1.2.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.
- package/README.md +8 -7
- package/dist/node.d.ts +1 -4
- package/dist/node.js +154 -92
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -199,10 +199,10 @@ function AppNotifications() {
|
|
|
199
199
|
|
|
200
200
|
#### Future node
|
|
201
201
|
|
|
202
|
-
Future nodes are `templates` (functions) and will be created at render time (as `components` and `text`). This applies for all type of functions that return a `ValidTemplateReturn`; also the generic tags like `div`, `span`, etc. are allowed
|
|
202
|
+
Future nodes are `templates` (functions) used as a child and will be created at render time (as `components` and `text`). This applies for all type of functions that return a `ValidTemplateReturn`; also the generic tags like `div`, `span`, etc. are allowed
|
|
203
203
|
|
|
204
204
|
```ts
|
|
205
|
-
import { div, main, nav } from "yandel"
|
|
205
|
+
import { div, main, nav } from "yandel";
|
|
206
206
|
|
|
207
207
|
function MyGridOfPhotos () {
|
|
208
208
|
return div(...);
|
|
@@ -566,7 +566,7 @@ function UserConsumerWithError() {
|
|
|
566
566
|
|
|
567
567
|
#### Future nodes
|
|
568
568
|
|
|
569
|
-
As explained in the section of [future nodes](#future-node), `components`, `text` and `future nodes` are created at render time
|
|
569
|
+
As explained in the section of [future nodes](#future-node), `components`, `text` and `future nodes` are created at render time. You have to understand how they are being created:
|
|
570
570
|
|
|
571
571
|
```ts
|
|
572
572
|
function UserConsumer() {
|
|
@@ -580,7 +580,8 @@ class App extends Component {
|
|
|
580
580
|
}
|
|
581
581
|
```
|
|
582
582
|
|
|
583
|
-
`
|
|
583
|
+
When a component is created, its `render` method will be called at render time. In this example, `UserConsumer` will be called before the `UserProvider` `render`
|
|
584
|
+
method at render time, so the context will be undefined.
|
|
584
585
|
|
|
585
586
|
```ts
|
|
586
587
|
public render(): ValidTemplateReturn {
|
|
@@ -591,11 +592,11 @@ public render(): ValidTemplateReturn {
|
|
|
591
592
|
}
|
|
592
593
|
```
|
|
593
594
|
|
|
594
|
-
Now, `UserConsumer` will be able to consume
|
|
595
|
+
Now, `UserConsumer` will be able to consume, because It will be created at render time right after the `UserProvider` `render` method, so it will be accesible.
|
|
595
596
|
|
|
596
597
|
### Ref
|
|
597
598
|
|
|
598
|
-
The `ref` prop allows you to have access to the
|
|
599
|
+
The `ref` prop allows you to have access to the tag's reference:
|
|
599
600
|
|
|
600
601
|
```ts
|
|
601
602
|
import { Component, ElementRef, Stores, input } from "yandel";
|
|
@@ -635,7 +636,7 @@ class WithKey extends Component {
|
|
|
635
636
|
|
|
636
637
|
## Usage
|
|
637
638
|
|
|
638
|
-
Define your
|
|
639
|
+
Define your UI: components, templates, nodes... then, use `createRoot.render` to start rendering and done!
|
|
639
640
|
|
|
640
641
|
```ts
|
|
641
642
|
import {
|
package/dist/node.d.ts
CHANGED
|
@@ -93,10 +93,7 @@ declare class TagNode<T extends HTMLTags | HTMLSVGTags = HTMLTags> extends Node
|
|
|
93
93
|
key: symbol | string | number | undefined;
|
|
94
94
|
index_length: number;
|
|
95
95
|
constructor(tag: T, props?: AnyProps<T>, children?: ValidNodeChild[]);
|
|
96
|
-
private _apply_dom_props;
|
|
97
96
|
private _diff_dom_props;
|
|
98
|
-
private _clear_dom_props;
|
|
99
|
-
private _clear_dom_events;
|
|
100
97
|
get dom(): AnyElement<T> | undefined;
|
|
101
98
|
get props(): AnyProps<T> | undefined;
|
|
102
99
|
get children(): ValidNodeChild[] | undefined;
|
|
@@ -105,7 +102,7 @@ declare class TagNode<T extends HTMLTags | HTMLSVGTags = HTMLTags> extends Node
|
|
|
105
102
|
clearChildren(): void;
|
|
106
103
|
clearDom(): void;
|
|
107
104
|
delete(): void;
|
|
108
|
-
switch(t: TagNode
|
|
105
|
+
switch(t: TagNode): boolean;
|
|
109
106
|
}
|
|
110
107
|
declare class ParentNode<T extends HTMLElement = HTMLElement> extends Node implements SwitchableNode<ParentNode> {
|
|
111
108
|
static is(t: any): t is ParentNode;
|
package/dist/node.js
CHANGED
|
@@ -76,7 +76,7 @@ class Stores {
|
|
|
76
76
|
this.#_stores = s;
|
|
77
77
|
}
|
|
78
78
|
}
|
|
79
|
-
class
|
|
79
|
+
class UpdateQueue {
|
|
80
80
|
static _update_scheduled = false;
|
|
81
81
|
static _queue = new Set();
|
|
82
82
|
static processNodeQueue() {
|
|
@@ -118,16 +118,14 @@ class ComponentUpdateQueue {
|
|
|
118
118
|
if (!this._update_scheduled)
|
|
119
119
|
this.notifyQueue();
|
|
120
120
|
}
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
}
|
|
130
|
-
});
|
|
121
|
+
static queueNodesDeletion(ns) {
|
|
122
|
+
queueMicrotask(() => {
|
|
123
|
+
for (const c of ns) {
|
|
124
|
+
if (c && c instanceof Node)
|
|
125
|
+
c.delete();
|
|
126
|
+
}
|
|
127
|
+
});
|
|
128
|
+
}
|
|
131
129
|
}
|
|
132
130
|
const ComponentInternalKey = Symbol("_$ci$_");
|
|
133
131
|
function _c_el(t) {
|
|
@@ -225,21 +223,10 @@ class ContentNode extends Node {
|
|
|
225
223
|
super.delete();
|
|
226
224
|
}
|
|
227
225
|
}
|
|
228
|
-
function
|
|
229
|
-
|
|
230
|
-
return t();
|
|
231
|
-
}
|
|
232
|
-
catch (error) {
|
|
233
|
-
throw new Error(`Template create error: ${error}`);
|
|
234
|
-
}
|
|
235
|
-
}
|
|
236
|
-
function _get_node_from_future(t) {
|
|
237
|
-
const n = create_template(t);
|
|
238
|
-
if (_is_fn(n))
|
|
239
|
-
return _get_node_from_future(n);
|
|
240
|
-
return n;
|
|
226
|
+
function _create_html_tag(tag) {
|
|
227
|
+
return SVG_TAGS.has(tag) ? _c_s(tag) : _c_el(tag);
|
|
241
228
|
}
|
|
242
|
-
function
|
|
229
|
+
function _apply_html_prop(dom, props, prop) {
|
|
243
230
|
const v = props[prop];
|
|
244
231
|
if (v == null)
|
|
245
232
|
return;
|
|
@@ -287,7 +274,7 @@ function _apply_dom_prop(dom, props, prop) {
|
|
|
287
274
|
else
|
|
288
275
|
dom.setAttribute(prop, v);
|
|
289
276
|
}
|
|
290
|
-
function
|
|
277
|
+
function _remove_html_prop(dom, props, prop) {
|
|
291
278
|
if (/^on[a-z]/.test(prop)) {
|
|
292
279
|
const ev_name = prop.slice(2);
|
|
293
280
|
dom.removeEventListener(ev_name, props[prop]);
|
|
@@ -295,11 +282,21 @@ function _remove_dom_prop(dom, props, prop) {
|
|
|
295
282
|
}
|
|
296
283
|
if (prop === "ref" && props.ref instanceof Stores) {
|
|
297
284
|
props.ref.stores = undefined;
|
|
298
|
-
return;
|
|
299
285
|
}
|
|
300
286
|
const v = props[prop];
|
|
301
|
-
if (v == null)
|
|
287
|
+
if (v == null) {
|
|
288
|
+
if (prop === "style")
|
|
289
|
+
return;
|
|
290
|
+
if (prop in dom) {
|
|
291
|
+
try {
|
|
292
|
+
dom[prop] = undefined;
|
|
293
|
+
}
|
|
294
|
+
catch (error) {
|
|
295
|
+
console.error("Remove prop error:", error);
|
|
296
|
+
}
|
|
297
|
+
}
|
|
302
298
|
return;
|
|
299
|
+
}
|
|
303
300
|
if (prop === "style" && typeof v === "object") {
|
|
304
301
|
try {
|
|
305
302
|
Object.entries(v).forEach(([cssProp]) => {
|
|
@@ -323,15 +320,15 @@ function _remove_dom_prop(dom, props, prop) {
|
|
|
323
320
|
switch (t) {
|
|
324
321
|
case "boolean": {
|
|
325
322
|
dom[prop] = false;
|
|
326
|
-
|
|
323
|
+
break;
|
|
327
324
|
}
|
|
328
325
|
case "string": {
|
|
329
326
|
dom[prop] = "";
|
|
330
|
-
|
|
327
|
+
break;
|
|
331
328
|
}
|
|
332
329
|
case "object": {
|
|
333
|
-
dom[prop] =
|
|
334
|
-
|
|
330
|
+
dom[prop] = undefined;
|
|
331
|
+
break;
|
|
335
332
|
}
|
|
336
333
|
}
|
|
337
334
|
}
|
|
@@ -339,6 +336,69 @@ function _remove_dom_prop(dom, props, prop) {
|
|
|
339
336
|
}
|
|
340
337
|
dom.removeAttribute(prop);
|
|
341
338
|
}
|
|
339
|
+
function _get_props_diff(a, b) {
|
|
340
|
+
const _to_remove = {};
|
|
341
|
+
const _to_apply = {};
|
|
342
|
+
for (const key in a) {
|
|
343
|
+
if (!b.hasOwnProperty(key)) {
|
|
344
|
+
_to_remove[key] = a[key];
|
|
345
|
+
}
|
|
346
|
+
}
|
|
347
|
+
for (const key in b) {
|
|
348
|
+
if (b[key] == null) {
|
|
349
|
+
_to_remove[key] = b[key];
|
|
350
|
+
}
|
|
351
|
+
else {
|
|
352
|
+
if (b[key] === a[key])
|
|
353
|
+
continue;
|
|
354
|
+
_to_apply[key] = b[key];
|
|
355
|
+
}
|
|
356
|
+
}
|
|
357
|
+
return {
|
|
358
|
+
apply: _to_apply,
|
|
359
|
+
remove: _to_remove,
|
|
360
|
+
};
|
|
361
|
+
}
|
|
362
|
+
// function _diff_html_props<T extends HTMLTags | HTMLSVGTags>(
|
|
363
|
+
// dom: AnyElement<T>,
|
|
364
|
+
// a: AnyProps<T>,
|
|
365
|
+
// b: AnyProps<T>
|
|
366
|
+
// ) {
|
|
367
|
+
// for (const prop of Object.keys(a)) {
|
|
368
|
+
// if (prop.startsWith("on")) {
|
|
369
|
+
// _remove_html_prop(dom, a, prop);
|
|
370
|
+
// if (prop in b) _apply_html_prop(dom, b, prop);
|
|
371
|
+
// } else if (!(prop in b)) {
|
|
372
|
+
// _remove_html_prop(dom, a, prop);
|
|
373
|
+
// } else _apply_html_prop(dom, b, prop);
|
|
374
|
+
// }
|
|
375
|
+
// }
|
|
376
|
+
function _clear_events(dom, props) {
|
|
377
|
+
for (const prop of Object.keys(props)) {
|
|
378
|
+
if (prop.startsWith("on")) {
|
|
379
|
+
if (typeof props[prop] !== "function")
|
|
380
|
+
continue;
|
|
381
|
+
const ev_name = prop.slice(2);
|
|
382
|
+
dom.removeEventListener(ev_name, props[prop]);
|
|
383
|
+
delete props[prop];
|
|
384
|
+
}
|
|
385
|
+
}
|
|
386
|
+
}
|
|
387
|
+
function _apply_props(dom, props) {
|
|
388
|
+
for (const prop of Object.keys(props)) {
|
|
389
|
+
_apply_html_prop(dom, props, prop);
|
|
390
|
+
}
|
|
391
|
+
}
|
|
392
|
+
function _clear_props(dom, props) {
|
|
393
|
+
for (const prop of Object.keys(props)) {
|
|
394
|
+
_remove_html_prop(dom, props, prop);
|
|
395
|
+
}
|
|
396
|
+
}
|
|
397
|
+
function _remove_style(dom, styles) {
|
|
398
|
+
for (const style of Object.keys(styles)) {
|
|
399
|
+
dom.style[style] = "";
|
|
400
|
+
}
|
|
401
|
+
}
|
|
342
402
|
class TagNode extends Node {
|
|
343
403
|
static is(t) {
|
|
344
404
|
return t instanceof TagNode;
|
|
@@ -359,51 +419,26 @@ class TagNode extends Node {
|
|
|
359
419
|
this._children = children;
|
|
360
420
|
this.needs_update = true;
|
|
361
421
|
}
|
|
362
|
-
_apply_dom_props() {
|
|
363
|
-
if (!this._props || !this._dom)
|
|
364
|
-
return;
|
|
365
|
-
for (const prop in this._props) {
|
|
366
|
-
_apply_dom_prop(this._dom, this._props, prop);
|
|
367
|
-
}
|
|
368
|
-
}
|
|
369
422
|
_diff_dom_props(nprops) {
|
|
370
|
-
if (
|
|
371
|
-
this._props
|
|
372
|
-
|
|
373
|
-
|
|
374
|
-
|
|
375
|
-
if (this._dom)
|
|
376
|
-
for (const prop in this._props) {
|
|
377
|
-
if (prop.startsWith("on")) {
|
|
378
|
-
_remove_dom_prop(this._dom, this._props, prop);
|
|
379
|
-
if (prop in nprops)
|
|
380
|
-
_apply_dom_prop(this._dom, nprops, prop);
|
|
381
|
-
}
|
|
382
|
-
else if (!(prop in nprops)) {
|
|
383
|
-
_remove_dom_prop(this._dom, this._props, prop);
|
|
423
|
+
if (this._dom) {
|
|
424
|
+
if (this._props) {
|
|
425
|
+
if (this._props.style) {
|
|
426
|
+
_remove_style(this._dom, this._props.style);
|
|
427
|
+
delete this._props.style;
|
|
384
428
|
}
|
|
385
|
-
|
|
386
|
-
|
|
387
|
-
|
|
388
|
-
|
|
389
|
-
|
|
390
|
-
_clear_dom_props() {
|
|
391
|
-
if (this._props && this._dom)
|
|
392
|
-
for (const prop in this._props) {
|
|
393
|
-
_remove_dom_prop(this._dom, this._props, prop);
|
|
429
|
+
_clear_events(this._dom, this._props);
|
|
430
|
+
const diff = _get_props_diff(this._props, nprops);
|
|
431
|
+
_clear_props(this._dom, diff.remove);
|
|
432
|
+
_apply_props(this._dom, diff.apply);
|
|
433
|
+
this._props = nprops;
|
|
394
434
|
}
|
|
395
|
-
|
|
396
|
-
|
|
397
|
-
|
|
398
|
-
|
|
399
|
-
const v = Reflect.get(this._props, prop);
|
|
400
|
-
if (!v)
|
|
401
|
-
continue;
|
|
402
|
-
if (prop.startsWith("on") && typeof v === "function") {
|
|
403
|
-
const ev_name = prop.slice(2);
|
|
404
|
-
this._dom.removeEventListener(ev_name, Reflect.get(this._props, prop));
|
|
405
|
-
}
|
|
435
|
+
else {
|
|
436
|
+
this._props = nprops;
|
|
437
|
+
_apply_props(this._dom, this._props);
|
|
438
|
+
return;
|
|
406
439
|
}
|
|
440
|
+
}
|
|
441
|
+
this._props = nprops;
|
|
407
442
|
}
|
|
408
443
|
get dom() {
|
|
409
444
|
return this._dom;
|
|
@@ -423,12 +458,16 @@ class TagNode extends Node {
|
|
|
423
458
|
if (this._deleted)
|
|
424
459
|
throw new Error("Attempt to create a deleted node");
|
|
425
460
|
if (this._dom) {
|
|
426
|
-
this.
|
|
427
|
-
|
|
428
|
-
|
|
461
|
+
if (this._props) {
|
|
462
|
+
_apply_props(this._dom, this._props);
|
|
463
|
+
this.needs_update = true;
|
|
464
|
+
return true;
|
|
465
|
+
}
|
|
466
|
+
return false;
|
|
429
467
|
}
|
|
430
|
-
this._dom = (
|
|
431
|
-
this.
|
|
468
|
+
this._dom = _create_html_tag(this.tag);
|
|
469
|
+
if (this._props)
|
|
470
|
+
_apply_props(this._dom, this._props);
|
|
432
471
|
this.needs_update = false;
|
|
433
472
|
return true;
|
|
434
473
|
}
|
|
@@ -436,7 +475,7 @@ class TagNode extends Node {
|
|
|
436
475
|
if (this._deleted)
|
|
437
476
|
return;
|
|
438
477
|
if (this._children?.length) {
|
|
439
|
-
queueNodesDeletion(this._children);
|
|
478
|
+
UpdateQueue.queueNodesDeletion(this._children);
|
|
440
479
|
delete this._children;
|
|
441
480
|
}
|
|
442
481
|
}
|
|
@@ -446,7 +485,8 @@ class TagNode extends Node {
|
|
|
446
485
|
return;
|
|
447
486
|
}
|
|
448
487
|
this.needs_update = true;
|
|
449
|
-
this.
|
|
488
|
+
if (this._props)
|
|
489
|
+
_clear_events(this._dom, this._props);
|
|
450
490
|
this._dom.remove();
|
|
451
491
|
delete this._dom;
|
|
452
492
|
}
|
|
@@ -464,10 +504,20 @@ class TagNode extends Node {
|
|
|
464
504
|
this.key = t.props?.key ?? t.key;
|
|
465
505
|
if (this.tag === t.tag) {
|
|
466
506
|
// Tag is the same
|
|
467
|
-
if (
|
|
468
|
-
this.
|
|
469
|
-
|
|
470
|
-
|
|
507
|
+
if (this._dom) {
|
|
508
|
+
if (this._props) {
|
|
509
|
+
if (t.props)
|
|
510
|
+
this._diff_dom_props(t.props);
|
|
511
|
+
else
|
|
512
|
+
_clear_props(this._dom, this._props);
|
|
513
|
+
}
|
|
514
|
+
else if (t.props) {
|
|
515
|
+
this._props = t.props;
|
|
516
|
+
_apply_props(this._dom, this._props);
|
|
517
|
+
}
|
|
518
|
+
}
|
|
519
|
+
else if (t.props)
|
|
520
|
+
this._props = t.props;
|
|
471
521
|
}
|
|
472
522
|
else {
|
|
473
523
|
// Tag has changed
|
|
@@ -515,7 +565,7 @@ class ParentNode extends Node {
|
|
|
515
565
|
clearChildren() {
|
|
516
566
|
this.child_length = 0;
|
|
517
567
|
if (this._children?.length) {
|
|
518
|
-
queueNodesDeletion(this._children);
|
|
568
|
+
UpdateQueue.queueNodesDeletion(this._children);
|
|
519
569
|
delete this._children;
|
|
520
570
|
}
|
|
521
571
|
}
|
|
@@ -542,6 +592,20 @@ class ParentNode extends Node {
|
|
|
542
592
|
return this.needs_update;
|
|
543
593
|
}
|
|
544
594
|
}
|
|
595
|
+
function _create_template(t) {
|
|
596
|
+
try {
|
|
597
|
+
return t();
|
|
598
|
+
}
|
|
599
|
+
catch (error) {
|
|
600
|
+
throw new Error(`Template create error: ${error}`);
|
|
601
|
+
}
|
|
602
|
+
}
|
|
603
|
+
function _get_node_from_future(t) {
|
|
604
|
+
const n = _create_template(t);
|
|
605
|
+
if (_is_fn(n))
|
|
606
|
+
return _get_node_from_future(n);
|
|
607
|
+
return n;
|
|
608
|
+
}
|
|
545
609
|
class ComponentNode extends Node {
|
|
546
610
|
static is(t) {
|
|
547
611
|
return t instanceof ComponentNode;
|
|
@@ -563,7 +627,7 @@ class ComponentNode extends Node {
|
|
|
563
627
|
throw new Error("Template has been deleted");
|
|
564
628
|
nextInternalTick(this._template);
|
|
565
629
|
try {
|
|
566
|
-
const child =
|
|
630
|
+
const child = _create_template(this._template.render.bind(this._template));
|
|
567
631
|
if (child == null) {
|
|
568
632
|
if (this.hasChildren) {
|
|
569
633
|
this.clearChildren();
|
|
@@ -603,7 +667,7 @@ class ComponentNode extends Node {
|
|
|
603
667
|
return;
|
|
604
668
|
this.child_length = 0;
|
|
605
669
|
if (this._children?.length) {
|
|
606
|
-
queueNodesDeletion(this._children);
|
|
670
|
+
UpdateQueue.queueNodesDeletion(this._children);
|
|
607
671
|
delete this._children;
|
|
608
672
|
}
|
|
609
673
|
}
|
|
@@ -619,10 +683,8 @@ class ComponentNode extends Node {
|
|
|
619
683
|
if (t.hasChildren) {
|
|
620
684
|
this.needs_update = diffChildren(this._children, t.children);
|
|
621
685
|
}
|
|
622
|
-
|
|
623
|
-
|
|
624
|
-
this.clearChildren();
|
|
625
|
-
}
|
|
686
|
+
this.needs_update = true;
|
|
687
|
+
this.clearChildren();
|
|
626
688
|
}
|
|
627
689
|
else if (t.hasChildren)
|
|
628
690
|
this._children = t.children;
|
|
@@ -686,7 +748,7 @@ class ComponentInternals {
|
|
|
686
748
|
: s;
|
|
687
749
|
Promise.resolve().then(() => {
|
|
688
750
|
if (this.#_n)
|
|
689
|
-
|
|
751
|
+
UpdateQueue.queueNodeUpdate(this.#_n);
|
|
690
752
|
});
|
|
691
753
|
}
|
|
692
754
|
addEffect(e) {
|