native-document 1.0.94 → 1.0.98
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/{src/devtools/hrm → devtools}/ComponentRegistry.js +2 -2
- package/devtools/index.js +8 -0
- package/{src/devtools/plugin.js → devtools/plugin/dev-tools-plugin.js} +2 -2
- package/{src/devtools/hrm/nd-vite-hot-reload.js → devtools/transformers/nd-vite-devtools.js} +16 -6
- package/devtools/transformers/src/transformComponentForHrm.js +74 -0
- package/devtools/transformers/src/transformJsFile.js +9 -0
- package/devtools/transformers/src/utils.js +79 -0
- package/devtools/widget/Widget.js +48 -0
- package/devtools/widget/widget.css +81 -0
- package/devtools/widget.js +23 -0
- package/dist/native-document.components.min.js +2441 -1191
- package/dist/native-document.dev.js +2710 -1359
- package/dist/native-document.dev.js.map +1 -1
- package/dist/native-document.devtools.min.js +1 -1
- package/dist/native-document.min.js +1 -1
- package/docs/cache.md +1 -1
- package/docs/core-concepts.md +1 -1
- package/docs/native-document-element.md +51 -15
- package/docs/observables.md +310 -306
- package/docs/state-management.md +198 -193
- package/index.def.js +762 -26
- package/package.json +1 -1
- package/readme.md +1 -1
- package/src/core/data/ObservableChecker.js +2 -0
- package/src/core/data/ObservableItem.js +97 -0
- package/src/core/data/ObservableObject.js +182 -0
- package/src/core/data/Store.js +364 -34
- package/src/core/data/observable-helpers/object.js +2 -166
- package/src/core/elements/anchor.js +28 -20
- package/src/core/elements/control/for-each.js +1 -1
- package/src/core/utils/formatters.js +91 -0
- package/src/core/utils/localstorage.js +57 -0
- package/src/core/utils/validator.js +0 -2
- package/src/core/wrappers/DocumentObserver.js +102 -31
- package/src/core/wrappers/ElementCreator.js +5 -0
- package/src/core/wrappers/NDElement.js +32 -1
- package/src/core/wrappers/prototypes/nd-element.transition.extensions.js +83 -0
- package/src/devtools.js +9 -0
- package/src/fetch/NativeFetch.js +5 -2
- package/types/elements.d.ts +580 -2
- package/types/nd-element.d.ts +6 -0
- package/types/observable.d.ts +71 -15
- package/types/plugins-manager.d.ts +1 -1
- package/types/store.d.ts +33 -6
- package/hrm.js +0 -7
- package/src/devtools/app/App.js +0 -66
- package/src/devtools/app/app.css +0 -0
- package/src/devtools/hrm/transformComponent.js +0 -129
- package/src/devtools/index.js +0 -18
- package/src/devtools/widget/DevToolsWidget.js +0 -26
- /package/{src/devtools/hrm → devtools/transformers/templates}/hrm.hook.template.js +0 -0
- /package/{src/devtools/hrm → devtools/transformers/templates}/hrm.orbservable.hook.template.js +0 -0
|
@@ -374,59 +374,6 @@ var NativeComponents = (function (exports) {
|
|
|
374
374
|
}
|
|
375
375
|
var DebugManager = DebugManager$1;
|
|
376
376
|
|
|
377
|
-
const MemoryManager = (function() {
|
|
378
|
-
|
|
379
|
-
let $nextObserverId = 0;
|
|
380
|
-
const $observables = new Map();
|
|
381
|
-
|
|
382
|
-
return {
|
|
383
|
-
/**
|
|
384
|
-
* Register an observable and return an id.
|
|
385
|
-
*
|
|
386
|
-
* @param {ObservableItem} observable
|
|
387
|
-
* @param {Function} getListeners
|
|
388
|
-
* @returns {number}
|
|
389
|
-
*/
|
|
390
|
-
register(observable) {
|
|
391
|
-
const id = ++$nextObserverId;
|
|
392
|
-
$observables.set(id, new WeakRef(observable));
|
|
393
|
-
return id;
|
|
394
|
-
},
|
|
395
|
-
unregister(id) {
|
|
396
|
-
$observables.delete(id);
|
|
397
|
-
},
|
|
398
|
-
getObservableById(id) {
|
|
399
|
-
return $observables.get(id)?.deref();
|
|
400
|
-
},
|
|
401
|
-
cleanup() {
|
|
402
|
-
for (const [_, weakObservableRef] of $observables) {
|
|
403
|
-
const observable = weakObservableRef.deref();
|
|
404
|
-
if (observable) {
|
|
405
|
-
observable.cleanup();
|
|
406
|
-
}
|
|
407
|
-
}
|
|
408
|
-
$observables.clear();
|
|
409
|
-
},
|
|
410
|
-
/**
|
|
411
|
-
* Clean observables that are not referenced anymore.
|
|
412
|
-
* @param {number} threshold
|
|
413
|
-
*/
|
|
414
|
-
cleanObservables(threshold) {
|
|
415
|
-
if($observables.size < threshold) return;
|
|
416
|
-
let cleanedCount = 0;
|
|
417
|
-
for (const [id, weakObservableRef] of $observables) {
|
|
418
|
-
if (!weakObservableRef.deref()) {
|
|
419
|
-
$observables.delete(id);
|
|
420
|
-
cleanedCount++;
|
|
421
|
-
}
|
|
422
|
-
}
|
|
423
|
-
if (cleanedCount > 0) {
|
|
424
|
-
DebugManager.log('Memory Auto Clean', `🧹 Cleaned ${cleanedCount} orphaned observables`);
|
|
425
|
-
}
|
|
426
|
-
}
|
|
427
|
-
};
|
|
428
|
-
}());
|
|
429
|
-
|
|
430
377
|
/**
|
|
431
378
|
*
|
|
432
379
|
* @param {ObservableItem} $observable
|
|
@@ -519,1191 +466,1993 @@ var NativeComponents = (function (exports) {
|
|
|
519
466
|
return this.observable.cleanup();
|
|
520
467
|
};
|
|
521
468
|
|
|
522
|
-
|
|
523
|
-
|
|
524
|
-
|
|
525
|
-
|
|
526
|
-
|
|
527
|
-
|
|
528
|
-
|
|
529
|
-
const ObservableWhen = function(observer, value) {
|
|
530
|
-
this.$target = value;
|
|
531
|
-
this.$observer = observer;
|
|
532
|
-
};
|
|
533
|
-
|
|
534
|
-
ObservableWhen.prototype.__$isObservableWhen = true;
|
|
469
|
+
const DocumentObserver = {
|
|
470
|
+
mounted: new WeakMap(),
|
|
471
|
+
beforeUnmount: new WeakMap(),
|
|
472
|
+
mountedSupposedSize: 0,
|
|
473
|
+
unmounted: new WeakMap(),
|
|
474
|
+
unmountedSupposedSize: 0,
|
|
475
|
+
observer: null,
|
|
535
476
|
|
|
536
|
-
|
|
537
|
-
|
|
538
|
-
|
|
539
|
-
|
|
540
|
-
|
|
541
|
-
|
|
542
|
-
|
|
543
|
-
|
|
544
|
-
|
|
545
|
-
|
|
546
|
-
|
|
547
|
-
|
|
548
|
-
|
|
477
|
+
executeMountedCallback(node) {
|
|
478
|
+
const data = DocumentObserver.mounted.get(node);
|
|
479
|
+
if(!data) {
|
|
480
|
+
return;
|
|
481
|
+
}
|
|
482
|
+
data.inDom = true;
|
|
483
|
+
if(!data.mounted) {
|
|
484
|
+
return;
|
|
485
|
+
}
|
|
486
|
+
if(Array.isArray(data.mounted)) {
|
|
487
|
+
for(const cb of data.mounted) {
|
|
488
|
+
cb(node);
|
|
489
|
+
}
|
|
490
|
+
return;
|
|
491
|
+
}
|
|
492
|
+
data.mounted(node);
|
|
493
|
+
},
|
|
549
494
|
|
|
550
|
-
|
|
551
|
-
|
|
552
|
-
|
|
553
|
-
|
|
554
|
-
|
|
555
|
-
|
|
556
|
-
|
|
557
|
-
|
|
495
|
+
executeUnmountedCallback(node) {
|
|
496
|
+
const data = DocumentObserver.unmounted.get(node);
|
|
497
|
+
if(!data) {
|
|
498
|
+
return;
|
|
499
|
+
}
|
|
500
|
+
data.inDom = false;
|
|
501
|
+
if(!data.unmounted) {
|
|
502
|
+
return;
|
|
503
|
+
}
|
|
558
504
|
|
|
559
|
-
|
|
560
|
-
|
|
561
|
-
|
|
562
|
-
|
|
563
|
-
|
|
564
|
-
|
|
565
|
-
|
|
505
|
+
let shouldRemove = false;
|
|
506
|
+
if(Array.isArray(data.unmounted)) {
|
|
507
|
+
for(const cb of data.unmounted) {
|
|
508
|
+
if(cb(node) === true) {
|
|
509
|
+
shouldRemove = true;
|
|
510
|
+
}
|
|
511
|
+
}
|
|
512
|
+
} else {
|
|
513
|
+
shouldRemove = data.unmounted(node) === true;
|
|
514
|
+
}
|
|
566
515
|
|
|
567
|
-
|
|
568
|
-
|
|
569
|
-
|
|
570
|
-
|
|
571
|
-
|
|
572
|
-
*/
|
|
573
|
-
ObservableWhen.prototype.isActive = ObservableWhen.prototype.val;
|
|
516
|
+
if(shouldRemove) {
|
|
517
|
+
data.disconnect();
|
|
518
|
+
node.nd?.remove();
|
|
519
|
+
}
|
|
520
|
+
},
|
|
574
521
|
|
|
575
|
-
|
|
576
|
-
|
|
577
|
-
|
|
578
|
-
|
|
579
|
-
|
|
580
|
-
|
|
581
|
-
|
|
582
|
-
|
|
583
|
-
|
|
584
|
-
|
|
585
|
-
|
|
586
|
-
|
|
587
|
-
|
|
588
|
-
|
|
589
|
-
const debounce = function(fn, delay, options = {}) {
|
|
590
|
-
let timer = null;
|
|
591
|
-
let lastArgs = null;
|
|
522
|
+
checkMutation: function(mutationsList) {
|
|
523
|
+
for(const mutation of mutationsList) {
|
|
524
|
+
if(DocumentObserver.mountedSupposedSize > 0) {
|
|
525
|
+
for(const node of mutation.addedNodes) {
|
|
526
|
+
DocumentObserver.executeMountedCallback(node);
|
|
527
|
+
if(!node.querySelectorAll) {
|
|
528
|
+
continue;
|
|
529
|
+
}
|
|
530
|
+
const children = node.querySelectorAll('[data--nd-mounted]');
|
|
531
|
+
for(const child of children) {
|
|
532
|
+
DocumentObserver.executeMountedCallback(child);
|
|
533
|
+
}
|
|
534
|
+
}
|
|
535
|
+
}
|
|
592
536
|
|
|
593
|
-
|
|
594
|
-
|
|
595
|
-
|
|
596
|
-
|
|
537
|
+
if (DocumentObserver.unmountedSupposedSize > 0) {
|
|
538
|
+
for (const node of mutation.removedNodes) {
|
|
539
|
+
DocumentObserver.executeUnmountedCallback(node);
|
|
540
|
+
if(!node.querySelectorAll) {
|
|
541
|
+
continue;
|
|
542
|
+
}
|
|
543
|
+
const children = node.querySelectorAll('[data--nd-unmounted]');
|
|
544
|
+
for(const child of children) {
|
|
545
|
+
DocumentObserver.executeUnmountedCallback(child);
|
|
546
|
+
}
|
|
547
|
+
}
|
|
548
|
+
}
|
|
597
549
|
}
|
|
598
|
-
|
|
550
|
+
},
|
|
599
551
|
|
|
600
|
-
|
|
601
|
-
|
|
602
|
-
|
|
603
|
-
|
|
604
|
-
|
|
552
|
+
/**
|
|
553
|
+
* @param {HTMLElement} element
|
|
554
|
+
* @param {boolean} inDom
|
|
555
|
+
* @returns {{ disconnect: Function, mounted: Function, unmounted: Function, off: Function }}
|
|
556
|
+
*/
|
|
557
|
+
watch: function(element, inDom = false) {
|
|
558
|
+
let mountedRegistered = false;
|
|
559
|
+
let unmountedRegistered = false;
|
|
605
560
|
|
|
606
|
-
|
|
607
|
-
|
|
608
|
-
|
|
609
|
-
|
|
610
|
-
|
|
561
|
+
let data = {
|
|
562
|
+
inDom,
|
|
563
|
+
mounted: null,
|
|
564
|
+
unmounted: null,
|
|
565
|
+
disconnect: () => {
|
|
566
|
+
if (mountedRegistered) {
|
|
567
|
+
DocumentObserver.mounted.delete(element);
|
|
568
|
+
DocumentObserver.mountedSupposedSize--;
|
|
569
|
+
}
|
|
570
|
+
if (unmountedRegistered) {
|
|
571
|
+
DocumentObserver.unmounted.delete(element);
|
|
572
|
+
DocumentObserver.unmountedSupposedSize--;
|
|
573
|
+
}
|
|
574
|
+
data = null;
|
|
575
|
+
}
|
|
576
|
+
};
|
|
611
577
|
|
|
612
|
-
|
|
613
|
-
|
|
614
|
-
|
|
615
|
-
|
|
616
|
-
|
|
617
|
-
|
|
578
|
+
const addListener = (type, callback) => {
|
|
579
|
+
if (!data[type]) {
|
|
580
|
+
data[type] = callback;
|
|
581
|
+
return;
|
|
582
|
+
}
|
|
583
|
+
if (!Array.isArray(data[type])) {
|
|
584
|
+
data[type] = [data[type], callback];
|
|
585
|
+
return;
|
|
586
|
+
}
|
|
587
|
+
data[type].push(callback);
|
|
588
|
+
};
|
|
618
589
|
|
|
619
|
-
|
|
620
|
-
|
|
621
|
-
|
|
622
|
-
|
|
623
|
-
|
|
590
|
+
const removeListener = (type, callback) => {
|
|
591
|
+
if(!data?.[type]) {
|
|
592
|
+
return;
|
|
593
|
+
}
|
|
594
|
+
if(Array.isArray(data[type])) {
|
|
595
|
+
const index = data[type].indexOf(callback);
|
|
596
|
+
if(index > -1) {
|
|
597
|
+
data[type].splice(index, 1);
|
|
598
|
+
}
|
|
599
|
+
if(data[type].length === 1) {
|
|
600
|
+
data[type] = data[type][0];
|
|
601
|
+
}
|
|
602
|
+
if(data[type].length === 0) {
|
|
603
|
+
data[type] = null;
|
|
604
|
+
}
|
|
605
|
+
return;
|
|
606
|
+
}
|
|
607
|
+
data[type] = null;
|
|
608
|
+
};
|
|
624
609
|
|
|
625
|
-
|
|
626
|
-
|
|
627
|
-
return new Date(value.getTime());
|
|
628
|
-
}
|
|
610
|
+
return {
|
|
611
|
+
disconnect: () => data?.disconnect(),
|
|
629
612
|
|
|
630
|
-
|
|
631
|
-
|
|
632
|
-
|
|
633
|
-
|
|
613
|
+
mounted: (callback) => {
|
|
614
|
+
addListener('mounted', callback);
|
|
615
|
+
DocumentObserver.mounted.set(element, data);
|
|
616
|
+
if (!mountedRegistered) {
|
|
617
|
+
DocumentObserver.mountedSupposedSize++;
|
|
618
|
+
mountedRegistered = true;
|
|
619
|
+
}
|
|
620
|
+
},
|
|
634
621
|
|
|
635
|
-
|
|
636
|
-
|
|
637
|
-
|
|
638
|
-
|
|
639
|
-
|
|
622
|
+
unmounted: (callback) => {
|
|
623
|
+
addListener('unmounted', callback);
|
|
624
|
+
DocumentObserver.unmounted.set(element, data);
|
|
625
|
+
if (!unmountedRegistered) {
|
|
626
|
+
DocumentObserver.unmountedSupposedSize++;
|
|
627
|
+
unmountedRegistered = true;
|
|
628
|
+
}
|
|
629
|
+
},
|
|
640
630
|
|
|
641
|
-
|
|
642
|
-
|
|
643
|
-
|
|
644
|
-
|
|
645
|
-
cloned[key] = deepClone(value[key]);
|
|
646
|
-
}
|
|
631
|
+
off: (type, callback) => {
|
|
632
|
+
removeListener(type, callback);
|
|
633
|
+
}
|
|
634
|
+
};
|
|
647
635
|
}
|
|
648
|
-
return cloned;
|
|
649
636
|
};
|
|
650
637
|
|
|
651
|
-
|
|
652
|
-
|
|
653
|
-
|
|
654
|
-
|
|
655
|
-
|
|
656
|
-
*/
|
|
657
|
-
function ObservableItem(value, configs = null) {
|
|
658
|
-
value = Validator.isObservable(value) ? value.val() : value;
|
|
659
|
-
|
|
660
|
-
this.$previousValue = null;
|
|
661
|
-
this.$currentValue = value;
|
|
662
|
-
|
|
663
|
-
this.$firstListener = null;
|
|
664
|
-
this.$listeners = null;
|
|
665
|
-
this.$watchers = null;
|
|
666
|
-
|
|
667
|
-
this.$memoryId = null;
|
|
638
|
+
DocumentObserver.observer = new MutationObserver(DocumentObserver.checkMutation);
|
|
639
|
+
DocumentObserver.observer.observe(document.body, {
|
|
640
|
+
childList: true,
|
|
641
|
+
subtree: true,
|
|
642
|
+
});
|
|
668
643
|
|
|
669
|
-
|
|
670
|
-
|
|
671
|
-
|
|
672
|
-
this.$initialValue = Validator.isObject(value) ? deepClone(value) : value;
|
|
673
|
-
}
|
|
674
|
-
}
|
|
644
|
+
function NDElement(element) {
|
|
645
|
+
this.$element = element;
|
|
646
|
+
this.$observer = null;
|
|
675
647
|
}
|
|
676
648
|
|
|
677
|
-
|
|
678
|
-
get() {
|
|
679
|
-
return this.$currentValue;
|
|
680
|
-
},
|
|
681
|
-
set(value) {
|
|
682
|
-
this.set(value);
|
|
683
|
-
},
|
|
684
|
-
configurable: true,
|
|
685
|
-
});
|
|
649
|
+
NDElement.prototype.__$isNDElement = true;
|
|
686
650
|
|
|
687
|
-
|
|
688
|
-
|
|
651
|
+
NDElement.prototype.valueOf = function() {
|
|
652
|
+
return this.$element;
|
|
653
|
+
};
|
|
689
654
|
|
|
690
|
-
|
|
691
|
-
|
|
692
|
-
* The interceptor can modify the value or return undefined to use the original value.
|
|
693
|
-
*
|
|
694
|
-
* @param {(value) => any} callback - Interceptor function that receives (newValue, currentValue) and returns the transformed value or undefined
|
|
695
|
-
* @returns {ObservableItem} The observable instance for chaining
|
|
696
|
-
* @example
|
|
697
|
-
* const count = Observable(0);
|
|
698
|
-
* count.intercept((newVal, oldVal) => Math.max(0, newVal)); // Prevent negative values
|
|
699
|
-
*/
|
|
700
|
-
ObservableItem.prototype.intercept = function(callback) {
|
|
701
|
-
this.$interceptor = callback;
|
|
702
|
-
this.set = this.$setWithInterceptor;
|
|
655
|
+
NDElement.prototype.ref = function(target, name) {
|
|
656
|
+
target[name] = this.$element;
|
|
703
657
|
return this;
|
|
704
658
|
};
|
|
705
659
|
|
|
706
|
-
|
|
707
|
-
|
|
660
|
+
NDElement.prototype.refSelf = function(target, name) {
|
|
661
|
+
target[name] = this;
|
|
662
|
+
return this;
|
|
708
663
|
};
|
|
709
664
|
|
|
710
|
-
|
|
711
|
-
|
|
712
|
-
|
|
713
|
-
|
|
714
|
-
|
|
715
|
-
|
|
716
|
-
|
|
665
|
+
NDElement.prototype.unmountChildren = function() {
|
|
666
|
+
let element = this.$element;
|
|
667
|
+
for(let i = 0, length = element.children.length; i < length; i++) {
|
|
668
|
+
let elementChildren = element.children[i];
|
|
669
|
+
if(!elementChildren.$ndProx) {
|
|
670
|
+
elementChildren.nd?.remove();
|
|
671
|
+
}
|
|
672
|
+
elementChildren = null;
|
|
717
673
|
}
|
|
674
|
+
element = null;
|
|
675
|
+
return this;
|
|
718
676
|
};
|
|
719
677
|
|
|
720
|
-
|
|
721
|
-
|
|
722
|
-
|
|
723
|
-
|
|
678
|
+
NDElement.prototype.remove = function() {
|
|
679
|
+
let element = this.$element;
|
|
680
|
+
element.nd.unmountChildren();
|
|
681
|
+
element.$ndProx = null;
|
|
682
|
+
delete element.nd?.on?.prevent;
|
|
683
|
+
delete element.nd?.on;
|
|
684
|
+
delete element.nd;
|
|
685
|
+
element = null;
|
|
686
|
+
return this;
|
|
687
|
+
};
|
|
724
688
|
|
|
725
|
-
|
|
726
|
-
|
|
727
|
-
|
|
728
|
-
|
|
689
|
+
NDElement.prototype.lifecycle = function(states) {
|
|
690
|
+
this.$observer = this.$observer || DocumentObserver.watch(this.$element);
|
|
691
|
+
|
|
692
|
+
if(states.mounted) {
|
|
693
|
+
this.$element.setAttribute('data--nd-mounted', '1');
|
|
694
|
+
this.$observer.mounted(states.mounted);
|
|
729
695
|
}
|
|
730
|
-
if(
|
|
731
|
-
|
|
696
|
+
if(states.unmounted) {
|
|
697
|
+
this.$element.setAttribute('data--nd-unmounted', '1');
|
|
698
|
+
this.$observer.unmounted(states.unmounted);
|
|
732
699
|
}
|
|
700
|
+
return this;
|
|
733
701
|
};
|
|
734
702
|
|
|
735
|
-
|
|
736
|
-
this.
|
|
737
|
-
this.triggerListeners(operations);
|
|
703
|
+
NDElement.prototype.mounted = function(callback) {
|
|
704
|
+
return this.lifecycle({ mounted: callback });
|
|
738
705
|
};
|
|
739
706
|
|
|
740
|
-
|
|
741
|
-
this.
|
|
742
|
-
this.triggerFirstListener(operations);
|
|
707
|
+
NDElement.prototype.unmounted = function(callback) {
|
|
708
|
+
return this.lifecycle({ unmounted: callback });
|
|
743
709
|
};
|
|
744
710
|
|
|
745
|
-
|
|
746
|
-
|
|
747
|
-
if(this.$watchers?.size && this.$listeners?.length) {
|
|
748
|
-
this.trigger = (this.$listeners.length === 1) ? this.triggerWatchersAndFirstListener : this.triggerAll;
|
|
749
|
-
return;
|
|
750
|
-
}
|
|
751
|
-
if(this.$listeners?.length) {
|
|
752
|
-
if(this.$listeners.length === 1) {
|
|
753
|
-
this.$firstListener = this.$listeners[0];
|
|
754
|
-
this.trigger = this.triggerFirstListener;
|
|
755
|
-
}
|
|
756
|
-
else {
|
|
757
|
-
this.trigger = this.triggerListeners;
|
|
758
|
-
}
|
|
759
|
-
return;
|
|
760
|
-
}
|
|
761
|
-
if(this.$watchers?.size) {
|
|
762
|
-
this.trigger = this.triggerWatchers;
|
|
763
|
-
return;
|
|
764
|
-
}
|
|
765
|
-
this.trigger = noneTrigger;
|
|
766
|
-
};
|
|
767
|
-
ObservableItem.prototype.trigger = noneTrigger;
|
|
711
|
+
NDElement.prototype.beforeUnmount = function(id, callback) {
|
|
712
|
+
const el = this.$element;
|
|
768
713
|
|
|
769
|
-
|
|
770
|
-
|
|
771
|
-
|
|
772
|
-
return;
|
|
773
|
-
}
|
|
774
|
-
this.$previousValue = this.$currentValue;
|
|
775
|
-
this.$currentValue = newValue;
|
|
776
|
-
this.trigger();
|
|
777
|
-
this.$previousValue = null;
|
|
778
|
-
};
|
|
714
|
+
if(!DocumentObserver.beforeUnmount.has(el)) {
|
|
715
|
+
DocumentObserver.beforeUnmount.set(el, new Map());
|
|
716
|
+
const originalRemove = el.remove.bind(el);
|
|
779
717
|
|
|
780
|
-
|
|
781
|
-
* @param {*} data
|
|
782
|
-
*/
|
|
783
|
-
ObservableItem.prototype.$setWithInterceptor = function(data) {
|
|
784
|
-
let newValue = (typeof data === 'function') ? data(this.$currentValue) : data;
|
|
785
|
-
const result = this.$interceptor(newValue, this.$currentValue);
|
|
718
|
+
let $isUnmounting = false;
|
|
786
719
|
|
|
787
|
-
|
|
788
|
-
|
|
720
|
+
el.remove = async () => {
|
|
721
|
+
if($isUnmounting) {
|
|
722
|
+
return;
|
|
723
|
+
}
|
|
724
|
+
$isUnmounting = true;
|
|
725
|
+
|
|
726
|
+
try {
|
|
727
|
+
const callbacks = DocumentObserver.beforeUnmount.get(el);
|
|
728
|
+
for (const cb of callbacks.values()) {
|
|
729
|
+
await cb.call(this, el);
|
|
730
|
+
}
|
|
731
|
+
} finally {
|
|
732
|
+
originalRemove();
|
|
733
|
+
$isUnmounting = false;
|
|
734
|
+
}
|
|
735
|
+
};
|
|
789
736
|
}
|
|
790
737
|
|
|
791
|
-
|
|
738
|
+
DocumentObserver.beforeUnmount.get(el).set(id, callback);
|
|
739
|
+
return this;
|
|
792
740
|
};
|
|
793
741
|
|
|
794
|
-
|
|
795
|
-
|
|
796
|
-
*/
|
|
797
|
-
ObservableItem.prototype.$basicSet = function(data) {
|
|
798
|
-
let newValue = (typeof data === 'function') ? data(this.$currentValue) : data;
|
|
799
|
-
this.$updateWithNewValue(newValue);
|
|
742
|
+
NDElement.prototype.htmlElement = function() {
|
|
743
|
+
return this.$element;
|
|
800
744
|
};
|
|
801
745
|
|
|
802
|
-
|
|
803
|
-
|
|
804
|
-
ObservableItem.prototype.val = function() {
|
|
805
|
-
return this.$currentValue;
|
|
806
|
-
};
|
|
746
|
+
NDElement.prototype.node = NDElement.prototype.htmlElement;
|
|
807
747
|
|
|
808
|
-
|
|
809
|
-
this.$
|
|
810
|
-
|
|
811
|
-
|
|
812
|
-
if(
|
|
813
|
-
|
|
814
|
-
|
|
815
|
-
|
|
816
|
-
}
|
|
817
|
-
}
|
|
748
|
+
NDElement.prototype.shadow = function(mode, style = null) {
|
|
749
|
+
const $element = this.$element;
|
|
750
|
+
const children = Array.from($element.childNodes);
|
|
751
|
+
const shadowRoot = $element.attachShadow({ mode });
|
|
752
|
+
if(style) {
|
|
753
|
+
const styleNode = document.createElement("style");
|
|
754
|
+
styleNode.textContent = style;
|
|
755
|
+
shadowRoot.appendChild(styleNode);
|
|
818
756
|
}
|
|
819
|
-
|
|
820
|
-
|
|
821
|
-
|
|
822
|
-
|
|
757
|
+
$element.append = shadowRoot.append.bind(shadowRoot);
|
|
758
|
+
$element.appendChild = shadowRoot.appendChild.bind(shadowRoot);
|
|
759
|
+
shadowRoot.append(...children);
|
|
760
|
+
|
|
761
|
+
return this;
|
|
823
762
|
};
|
|
824
763
|
|
|
825
|
-
|
|
826
|
-
|
|
827
|
-
* Useful for disposing resources, removing event listeners, or other cleanup tasks.
|
|
828
|
-
*
|
|
829
|
-
* @param {Function} callback - Cleanup function to execute on observable disposal
|
|
830
|
-
* @example
|
|
831
|
-
* const obs = Observable(0);
|
|
832
|
-
* obs.onCleanup(() => console.log('Cleaned up!'));
|
|
833
|
-
* obs.cleanup(); // Logs: "Cleaned up!"
|
|
834
|
-
*/
|
|
835
|
-
ObservableItem.prototype.onCleanup = function(callback) {
|
|
836
|
-
this.$cleanupListeners = this.$cleanupListeners ?? [];
|
|
837
|
-
this.$cleanupListeners.push(callback);
|
|
764
|
+
NDElement.prototype.openShadow = function(style = null) {
|
|
765
|
+
return this.shadow('open', style);
|
|
838
766
|
};
|
|
839
767
|
|
|
840
|
-
|
|
841
|
-
|
|
842
|
-
for (let i = 0; i < this.$cleanupListeners.length; i++) {
|
|
843
|
-
this.$cleanupListeners[i]();
|
|
844
|
-
}
|
|
845
|
-
this.$cleanupListeners = null;
|
|
846
|
-
}
|
|
847
|
-
MemoryManager.unregister(this.$memoryId);
|
|
848
|
-
this.disconnectAll();
|
|
849
|
-
delete this.$value;
|
|
768
|
+
NDElement.prototype.closedShadow = function(style = null) {
|
|
769
|
+
return this.shadow('closed', style);
|
|
850
770
|
};
|
|
851
771
|
|
|
852
772
|
/**
|
|
773
|
+
* Attaches a template binding to the element by hydrating it with the specified method.
|
|
853
774
|
*
|
|
854
|
-
* @param {
|
|
855
|
-
* @
|
|
775
|
+
* @param {string} methodName - Name of the hydration method to call
|
|
776
|
+
* @param {BindingHydrator} bindingHydrator - Template binding with $hydrate method
|
|
777
|
+
* @returns {HTMLElement} The underlying HTML element
|
|
778
|
+
* @example
|
|
779
|
+
* const onClick = $binder.attach((event, data) => console.log(data));
|
|
780
|
+
* element.nd.attach('onClick', onClick);
|
|
856
781
|
*/
|
|
857
|
-
|
|
858
|
-
|
|
859
|
-
|
|
860
|
-
this.$listeners.push(callback);
|
|
861
|
-
this.assocTrigger();
|
|
782
|
+
NDElement.prototype.attach = function(methodName, bindingHydrator) {
|
|
783
|
+
bindingHydrator.$hydrate(this.$element, methodName);
|
|
784
|
+
return this.$element;
|
|
862
785
|
};
|
|
863
786
|
|
|
864
787
|
/**
|
|
865
|
-
*
|
|
866
|
-
*
|
|
788
|
+
* Extends the current NDElement instance with custom methods.
|
|
789
|
+
* Methods are bound to the instance and available for chaining.
|
|
867
790
|
*
|
|
868
|
-
* @param {
|
|
869
|
-
* @
|
|
791
|
+
* @param {Object} methods - Object containing method definitions
|
|
792
|
+
* @returns {this} The NDElement instance with added methods for chaining
|
|
870
793
|
* @example
|
|
871
|
-
*
|
|
872
|
-
*
|
|
873
|
-
*
|
|
794
|
+
* element.nd.with({
|
|
795
|
+
* highlight() {
|
|
796
|
+
* this.$element.style.background = 'yellow';
|
|
797
|
+
* return this;
|
|
798
|
+
* }
|
|
799
|
+
* }).highlight().onClick(() => console.log('Clicked'));
|
|
874
800
|
*/
|
|
875
|
-
|
|
876
|
-
|
|
801
|
+
NDElement.prototype.with = function(methods) {
|
|
802
|
+
if (!methods || typeof methods !== 'object') {
|
|
803
|
+
throw new NativeDocumentError('extend() requires an object of methods');
|
|
804
|
+
}
|
|
877
805
|
|
|
878
|
-
|
|
806
|
+
for (const name in methods) {
|
|
807
|
+
const method = methods[name];
|
|
879
808
|
|
|
880
|
-
|
|
881
|
-
|
|
882
|
-
|
|
809
|
+
if (typeof method !== 'function') {
|
|
810
|
+
console.warn(`⚠️ extends(): "${name}" is not a function, skipping`);
|
|
811
|
+
continue;
|
|
812
|
+
}
|
|
883
813
|
|
|
884
|
-
|
|
885
|
-
watchValueList = callback;
|
|
886
|
-
this.$watchers.set(value, callback);
|
|
887
|
-
} else if(!Validator.isArray(watchValueList.list)) {
|
|
888
|
-
watchValueList = [watchValueList, callback];
|
|
889
|
-
callback = (value) => {
|
|
890
|
-
for(let i = 0, length = watchValueList.length; i < length; i++) {
|
|
891
|
-
watchValueList[i](value);
|
|
892
|
-
}
|
|
893
|
-
};
|
|
894
|
-
callback.list = watchValueList;
|
|
895
|
-
this.$watchers.set(value, callback);
|
|
896
|
-
} else {
|
|
897
|
-
watchValueList.list.push(callback);
|
|
814
|
+
this[name] = method.bind(this);
|
|
898
815
|
}
|
|
899
816
|
|
|
900
|
-
this
|
|
817
|
+
return this;
|
|
901
818
|
};
|
|
902
819
|
|
|
903
820
|
/**
|
|
904
|
-
*
|
|
821
|
+
* Extends the NDElement prototype with new methods available to all NDElement instances.
|
|
822
|
+
* Use this to add global methods to all NDElements.
|
|
905
823
|
*
|
|
906
|
-
* @param {
|
|
907
|
-
* @
|
|
824
|
+
* @param {Object} methods - Object containing method definitions to add to prototype
|
|
825
|
+
* @returns {typeof NDElement} The NDElement constructor
|
|
826
|
+
* @throws {NativeDocumentError} If methods is not an object or contains non-function values
|
|
908
827
|
* @example
|
|
909
|
-
*
|
|
910
|
-
*
|
|
911
|
-
*
|
|
912
|
-
*
|
|
913
|
-
*
|
|
828
|
+
* NDElement.extend({
|
|
829
|
+
* fadeIn() {
|
|
830
|
+
* this.$element.style.opacity = '1';
|
|
831
|
+
* return this;
|
|
832
|
+
* }
|
|
833
|
+
* });
|
|
834
|
+
* // Now all NDElements have .fadeIn() method
|
|
835
|
+
* Div().nd.fadeIn();
|
|
914
836
|
*/
|
|
915
|
-
|
|
916
|
-
if(!
|
|
917
|
-
|
|
918
|
-
const watchValueList = this.$watchers.get(value);
|
|
919
|
-
if(!watchValueList) return;
|
|
920
|
-
|
|
921
|
-
if(!callback || !Array.isArray(watchValueList.list)) {
|
|
922
|
-
this.$watchers?.delete(value);
|
|
923
|
-
this.assocTrigger();
|
|
924
|
-
return;
|
|
925
|
-
}
|
|
926
|
-
const index = watchValueList.indexOf(callback);
|
|
927
|
-
watchValueList?.splice(index, 1);
|
|
928
|
-
if(watchValueList.length === 1) {
|
|
929
|
-
this.$watchers.set(value, watchValueList[0]);
|
|
837
|
+
NDElement.extend = function(methods) {
|
|
838
|
+
if (!methods || typeof methods !== 'object') {
|
|
839
|
+
throw new NativeDocumentError('NDElement.extend() requires an object of methods');
|
|
930
840
|
}
|
|
931
|
-
|
|
932
|
-
|
|
841
|
+
|
|
842
|
+
if (Array.isArray(methods)) {
|
|
843
|
+
throw new NativeDocumentError('NDElement.extend() requires an object, not an array');
|
|
933
844
|
}
|
|
934
|
-
this.assocTrigger();
|
|
935
|
-
};
|
|
936
845
|
|
|
937
|
-
|
|
938
|
-
|
|
939
|
-
|
|
940
|
-
|
|
941
|
-
|
|
942
|
-
* @example
|
|
943
|
-
* const status = Observable('loading');
|
|
944
|
-
* status.once('ready', (val) => console.log('Ready!'));
|
|
945
|
-
* status.once(val => val === 'error', (val) => console.log('Error occurred'));
|
|
946
|
-
*/
|
|
947
|
-
ObservableItem.prototype.once = function(predicate, callback) {
|
|
948
|
-
const fn = typeof predicate === 'function' ? predicate : (v) => v === predicate;
|
|
846
|
+
const protectedMethods = new Set([
|
|
847
|
+
'constructor', 'valueOf', '$element', '$observer',
|
|
848
|
+
'ref', 'remove', 'cleanup', 'with', 'extend', 'attach',
|
|
849
|
+
'lifecycle', 'mounted', 'unmounted', 'unmountChildren'
|
|
850
|
+
]);
|
|
949
851
|
|
|
950
|
-
const
|
|
951
|
-
if (
|
|
952
|
-
|
|
953
|
-
callback(val);
|
|
852
|
+
for (const name in methods) {
|
|
853
|
+
if (!Object.hasOwn(methods, name)) {
|
|
854
|
+
continue;
|
|
954
855
|
}
|
|
955
|
-
};
|
|
956
|
-
this.subscribe(handler);
|
|
957
|
-
};
|
|
958
856
|
|
|
959
|
-
|
|
960
|
-
* Unsubscribe from an observable.
|
|
961
|
-
* @param {Function} callback
|
|
962
|
-
*/
|
|
963
|
-
ObservableItem.prototype.unsubscribe = function(callback) {
|
|
964
|
-
if(!this.$listeners) return;
|
|
965
|
-
const index = this.$listeners.indexOf(callback);
|
|
966
|
-
if (index > -1) {
|
|
967
|
-
this.$listeners.splice(index, 1);
|
|
968
|
-
}
|
|
969
|
-
this.assocTrigger();
|
|
970
|
-
};
|
|
857
|
+
const method = methods[name];
|
|
971
858
|
|
|
972
|
-
|
|
973
|
-
|
|
974
|
-
|
|
975
|
-
|
|
976
|
-
*/
|
|
977
|
-
ObservableItem.prototype.check = function(callback) {
|
|
978
|
-
return new ObservableChecker(this, callback)
|
|
979
|
-
};
|
|
859
|
+
if (typeof method !== 'function') {
|
|
860
|
+
DebugManager.warn('NDElement.extend', `"${name}" is not a function, skipping`);
|
|
861
|
+
continue;
|
|
862
|
+
}
|
|
980
863
|
|
|
981
|
-
|
|
982
|
-
|
|
983
|
-
|
|
984
|
-
|
|
985
|
-
* @param {string|number} key - Property key to retrieve
|
|
986
|
-
* @returns {*} The value of the property, unwrapped if it's an observable
|
|
987
|
-
* @example
|
|
988
|
-
* const user = Observable({ name: 'John', age: Observable(25) });
|
|
989
|
-
* user.get('name'); // 'John'
|
|
990
|
-
* user.get('age'); // 25 (unwrapped from observable)
|
|
991
|
-
*/
|
|
992
|
-
ObservableItem.prototype.get = function(key) {
|
|
993
|
-
const item = this.$currentValue[key];
|
|
994
|
-
return Validator.isObservable(item) ? item.val() : item;
|
|
995
|
-
};
|
|
864
|
+
if (protectedMethods.has(name)) {
|
|
865
|
+
DebugManager.error('NDElement.extend', `Cannot override protected method "${name}"`);
|
|
866
|
+
throw new NativeDocumentError(`Cannot override protected method "${name}"`);
|
|
867
|
+
}
|
|
996
868
|
|
|
997
|
-
|
|
998
|
-
|
|
999
|
-
|
|
1000
|
-
*
|
|
1001
|
-
* @param {*} value - The value to compare against
|
|
1002
|
-
* @returns {ObservableWhen} An ObservableWhen instance that tracks when the observable equals the value
|
|
1003
|
-
* @example
|
|
1004
|
-
* const status = Observable('idle');
|
|
1005
|
-
* const isLoading = status.when('loading');
|
|
1006
|
-
* isLoading.subscribe(active => console.log('Loading:', active));
|
|
1007
|
-
* status.set('loading'); // Logs: "Loading: true"
|
|
1008
|
-
*/
|
|
1009
|
-
ObservableItem.prototype.when = function(value) {
|
|
1010
|
-
return new ObservableWhen(this, value);
|
|
1011
|
-
};
|
|
869
|
+
if (NDElement.prototype[name]) {
|
|
870
|
+
DebugManager.warn('NDElement.extend', `Overwriting existing prototype method "${name}"`);
|
|
871
|
+
}
|
|
1012
872
|
|
|
1013
|
-
|
|
1014
|
-
* Compares the observable's current value with another value or observable.
|
|
1015
|
-
*
|
|
1016
|
-
* @param {*|ObservableItem} other - Value or observable to compare against
|
|
1017
|
-
* @returns {boolean} True if values are equal
|
|
1018
|
-
* @example
|
|
1019
|
-
* const a = Observable(5);
|
|
1020
|
-
* const b = Observable(5);
|
|
1021
|
-
* a.equals(5); // true
|
|
1022
|
-
* a.equals(b); // true
|
|
1023
|
-
* a.equals(10); // false
|
|
1024
|
-
*/
|
|
1025
|
-
ObservableItem.prototype.equals = function(other) {
|
|
1026
|
-
if(Validator.isObservable(other)) {
|
|
1027
|
-
return this.$currentValue === other.$currentValue;
|
|
873
|
+
NDElement.prototype[name] = method;
|
|
1028
874
|
}
|
|
1029
|
-
return this.$currentValue === other;
|
|
1030
|
-
};
|
|
1031
|
-
|
|
1032
|
-
/**
|
|
1033
|
-
* Converts the observable's current value to a boolean.
|
|
1034
|
-
*
|
|
1035
|
-
* @returns {boolean} The boolean representation of the current value
|
|
1036
|
-
* @example
|
|
1037
|
-
* const count = Observable(0);
|
|
1038
|
-
* count.toBool(); // false
|
|
1039
|
-
* count.set(5);
|
|
1040
|
-
* count.toBool(); // true
|
|
1041
|
-
*/
|
|
1042
|
-
ObservableItem.prototype.toBool = function() {
|
|
1043
|
-
return !!this.$currentValue;
|
|
1044
|
-
};
|
|
1045
875
|
|
|
1046
|
-
|
|
1047
|
-
* Toggles the boolean value of the observable (false becomes true, true becomes false).
|
|
1048
|
-
*
|
|
1049
|
-
* @example
|
|
1050
|
-
* const isOpen = Observable(false);
|
|
1051
|
-
* isOpen.toggle(); // Now true
|
|
1052
|
-
* isOpen.toggle(); // Now false
|
|
1053
|
-
*/
|
|
1054
|
-
ObservableItem.prototype.toggle = function() {
|
|
1055
|
-
this.set(!this.$currentValue);
|
|
876
|
+
return NDElement;
|
|
1056
877
|
};
|
|
1057
878
|
|
|
1058
|
-
|
|
1059
|
-
|
|
1060
|
-
|
|
1061
|
-
|
|
1062
|
-
|
|
1063
|
-
* const count = Observable(0, { reset: true });
|
|
1064
|
-
* count.set(10);
|
|
1065
|
-
* count.reset(); // Back to 0
|
|
1066
|
-
*/
|
|
1067
|
-
ObservableItem.prototype.reset = function() {
|
|
1068
|
-
if(!this.configs?.reset) {
|
|
1069
|
-
return;
|
|
1070
|
-
}
|
|
1071
|
-
const resetValue = (Validator.isObject(this.$initialValue))
|
|
1072
|
-
? deepClone(this.$initialValue, (observable) => {
|
|
1073
|
-
observable.reset();
|
|
1074
|
-
})
|
|
1075
|
-
: this.$initialValue;
|
|
1076
|
-
this.set(resetValue);
|
|
879
|
+
const COMMON_NODE_TYPES = {
|
|
880
|
+
ELEMENT: 1,
|
|
881
|
+
TEXT: 3,
|
|
882
|
+
COMMENT: 8,
|
|
883
|
+
DOCUMENT_FRAGMENT: 11
|
|
1077
884
|
};
|
|
1078
885
|
|
|
1079
|
-
|
|
1080
|
-
|
|
1081
|
-
|
|
1082
|
-
|
|
1083
|
-
|
|
1084
|
-
|
|
1085
|
-
|
|
1086
|
-
|
|
1087
|
-
|
|
1088
|
-
|
|
1089
|
-
|
|
1090
|
-
|
|
1091
|
-
|
|
1092
|
-
|
|
1093
|
-
|
|
1094
|
-
|
|
1095
|
-
|
|
1096
|
-
|
|
1097
|
-
|
|
1098
|
-
|
|
1099
|
-
|
|
1100
|
-
|
|
1101
|
-
|
|
1102
|
-
|
|
1103
|
-
|
|
1104
|
-
|
|
1105
|
-
|
|
1106
|
-
|
|
1107
|
-
|
|
886
|
+
const Validator = {
|
|
887
|
+
isObservable(value) {
|
|
888
|
+
return value?.__$isObservable;
|
|
889
|
+
},
|
|
890
|
+
isTemplateBinding(value) {
|
|
891
|
+
return value?.__$isTemplateBinding;
|
|
892
|
+
},
|
|
893
|
+
isObservableWhenResult(value) {
|
|
894
|
+
return value && (value.__$isObservableWhen || (typeof value === 'object' && '$target' in value && '$observer' in value));
|
|
895
|
+
},
|
|
896
|
+
isArrayObservable(value) {
|
|
897
|
+
return value?.__$isObservableArray;
|
|
898
|
+
},
|
|
899
|
+
isProxy(value) {
|
|
900
|
+
return value?.__isProxy__
|
|
901
|
+
},
|
|
902
|
+
isObservableOrProxy(value) {
|
|
903
|
+
return Validator.isObservable(value) || Validator.isProxy(value);
|
|
904
|
+
},
|
|
905
|
+
isAnchor(value) {
|
|
906
|
+
return value?.__Anchor__
|
|
907
|
+
},
|
|
908
|
+
isObservableChecker(value) {
|
|
909
|
+
return value?.__$isObservableChecker || value instanceof ObservableChecker;
|
|
910
|
+
},
|
|
911
|
+
isArray(value) {
|
|
912
|
+
return Array.isArray(value);
|
|
913
|
+
},
|
|
914
|
+
isString(value) {
|
|
915
|
+
return typeof value === 'string';
|
|
916
|
+
},
|
|
917
|
+
isNumber(value) {
|
|
918
|
+
return typeof value === 'number';
|
|
919
|
+
},
|
|
920
|
+
isBoolean(value) {
|
|
921
|
+
return typeof value === 'boolean';
|
|
922
|
+
},
|
|
923
|
+
isFunction(value) {
|
|
924
|
+
return typeof value === 'function';
|
|
925
|
+
},
|
|
926
|
+
isAsyncFunction(value) {
|
|
927
|
+
return typeof value === 'function' && value.constructor.name === 'AsyncFunction';
|
|
928
|
+
},
|
|
929
|
+
isObject(value) {
|
|
930
|
+
return typeof value === 'object' && value !== null;
|
|
931
|
+
},
|
|
932
|
+
isJson(value) {
|
|
933
|
+
return !(typeof value !== 'object' || value === null || Array.isArray(value) || value.constructor.name !== 'Object')
|
|
934
|
+
},
|
|
935
|
+
isElement(value) {
|
|
936
|
+
return value && (
|
|
937
|
+
value.nodeType === COMMON_NODE_TYPES.ELEMENT ||
|
|
938
|
+
value.nodeType === COMMON_NODE_TYPES.TEXT ||
|
|
939
|
+
value.nodeType === COMMON_NODE_TYPES.DOCUMENT_FRAGMENT ||
|
|
940
|
+
value.nodeType === COMMON_NODE_TYPES.COMMENT
|
|
941
|
+
);
|
|
942
|
+
},
|
|
943
|
+
isFragment(value) {
|
|
944
|
+
return value?.nodeType === COMMON_NODE_TYPES.DOCUMENT_FRAGMENT;
|
|
945
|
+
},
|
|
946
|
+
isStringOrObservable(value) {
|
|
947
|
+
return this.isString(value) || this.isObservable(value);
|
|
948
|
+
},
|
|
949
|
+
isValidChild(child) {
|
|
950
|
+
return child === null ||
|
|
951
|
+
this.isElement(child) ||
|
|
952
|
+
this.isObservable(child) ||
|
|
953
|
+
this.isNDElement(child) ||
|
|
954
|
+
['string', 'number', 'boolean'].includes(typeof child);
|
|
955
|
+
},
|
|
956
|
+
isNDElement(child) {
|
|
957
|
+
return child?.__$isNDElement || child instanceof NDElement;
|
|
958
|
+
},
|
|
959
|
+
isValidChildren(children) {
|
|
960
|
+
if (!Array.isArray(children)) {
|
|
961
|
+
children = [children];
|
|
1108
962
|
}
|
|
1109
|
-
|
|
1110
|
-
|
|
963
|
+
|
|
964
|
+
const invalid = children.filter(child => !this.isValidChild(child));
|
|
965
|
+
return invalid.length === 0;
|
|
1111
966
|
},
|
|
1112
|
-
|
|
1113
|
-
|
|
1114
|
-
|
|
1115
|
-
return;
|
|
967
|
+
validateChildren(children) {
|
|
968
|
+
if (!Array.isArray(children)) {
|
|
969
|
+
children = [children];
|
|
1116
970
|
}
|
|
1117
971
|
|
|
1118
|
-
|
|
1119
|
-
if(
|
|
1120
|
-
|
|
1121
|
-
node.nd?.remove();
|
|
972
|
+
const invalid = children.filter(child => !this.isValidChild(child));
|
|
973
|
+
if (invalid.length > 0) {
|
|
974
|
+
throw new NativeDocumentError(`Invalid children detected: ${invalid.map(i => typeof i).join(', ')}`);
|
|
1122
975
|
}
|
|
1123
|
-
},
|
|
1124
|
-
checkMutation: function(mutationsList) {
|
|
1125
|
-
for(const mutation of mutationsList) {
|
|
1126
|
-
if(DocumentObserver.mountedSupposedSize > 0 ) {
|
|
1127
|
-
for(const node of mutation.addedNodes) {
|
|
1128
|
-
DocumentObserver.executeMountedCallback(node);
|
|
1129
|
-
if(!node.querySelectorAll) {
|
|
1130
|
-
return;
|
|
1131
|
-
}
|
|
1132
|
-
const children = node.querySelectorAll('[data--nd-mounted]');
|
|
1133
|
-
if(!children.length) {
|
|
1134
|
-
return;
|
|
1135
|
-
}
|
|
1136
|
-
for(const node of children) {
|
|
1137
|
-
DocumentObserver.executeMountedCallback(node);
|
|
1138
|
-
}
|
|
1139
|
-
}
|
|
1140
|
-
}
|
|
1141
976
|
|
|
1142
|
-
|
|
1143
|
-
|
|
1144
|
-
|
|
1145
|
-
|
|
1146
|
-
|
|
1147
|
-
|
|
1148
|
-
|
|
1149
|
-
|
|
1150
|
-
|
|
1151
|
-
|
|
1152
|
-
for(const node of children) {
|
|
1153
|
-
DocumentObserver.executeUnmountedCallback(node);
|
|
1154
|
-
}
|
|
1155
|
-
}
|
|
1156
|
-
}
|
|
977
|
+
return children;
|
|
978
|
+
},
|
|
979
|
+
/**
|
|
980
|
+
* Check if the data contains observables.
|
|
981
|
+
* @param {Array|Object} data
|
|
982
|
+
* @returns {boolean}
|
|
983
|
+
*/
|
|
984
|
+
containsObservables(data) {
|
|
985
|
+
if(!data) {
|
|
986
|
+
return false;
|
|
1157
987
|
}
|
|
988
|
+
return Validator.isObject(data)
|
|
989
|
+
&& Object.values(data).some(value => Validator.isObservable(value));
|
|
1158
990
|
},
|
|
1159
991
|
/**
|
|
1160
|
-
*
|
|
1161
|
-
* @param {
|
|
1162
|
-
* @
|
|
1163
|
-
* @returns {{watch: (function(): Map<any, any>), disconnect: (function(): boolean), mounted: (function(*): Set<any>), unmounted: (function(*): Set<any>)}}
|
|
992
|
+
* Check if the data contains an observable reference.
|
|
993
|
+
* @param {string} data
|
|
994
|
+
* @returns {boolean}
|
|
1164
995
|
*/
|
|
1165
|
-
|
|
1166
|
-
|
|
1167
|
-
|
|
1168
|
-
|
|
1169
|
-
|
|
1170
|
-
|
|
1171
|
-
|
|
1172
|
-
DocumentObserver.unmounted.delete(element);
|
|
1173
|
-
DocumentObserver.mountedSupposedSize--;
|
|
1174
|
-
DocumentObserver.unmountedSupposedSize--;
|
|
1175
|
-
data = null;
|
|
1176
|
-
}
|
|
1177
|
-
};
|
|
996
|
+
containsObservableReference(data) {
|
|
997
|
+
if(!data || typeof data !== 'string') {
|
|
998
|
+
return false;
|
|
999
|
+
}
|
|
1000
|
+
return /\{\{#ObItem::\([0-9]+\)\}\}/.test(data);
|
|
1001
|
+
},
|
|
1002
|
+
validateAttributes(attributes) {},
|
|
1178
1003
|
|
|
1179
|
-
|
|
1180
|
-
|
|
1181
|
-
|
|
1182
|
-
|
|
1183
|
-
DocumentObserver.mounted.set(element, data);
|
|
1184
|
-
DocumentObserver.mountedSupposedSize++;
|
|
1185
|
-
},
|
|
1186
|
-
unmounted: (callback) => {
|
|
1187
|
-
data.unmounted = callback;
|
|
1188
|
-
DocumentObserver.unmounted.set(element, data);
|
|
1189
|
-
DocumentObserver.unmountedSupposedSize++;
|
|
1190
|
-
}
|
|
1191
|
-
};
|
|
1004
|
+
validateEventCallback(callback) {
|
|
1005
|
+
if (typeof callback !== 'function') {
|
|
1006
|
+
throw new NativeDocumentError('Event callback must be a function');
|
|
1007
|
+
}
|
|
1192
1008
|
}
|
|
1193
1009
|
};
|
|
1194
1010
|
|
|
1195
|
-
|
|
1196
|
-
|
|
1197
|
-
|
|
1198
|
-
subtree: true,
|
|
1199
|
-
});
|
|
1200
|
-
|
|
1201
|
-
function NDElement(element) {
|
|
1202
|
-
this.$element = element;
|
|
1203
|
-
this.$observer = null;
|
|
1204
|
-
}
|
|
1011
|
+
function Anchor(name, isUniqueChild = false) {
|
|
1012
|
+
const anchorFragment = document.createDocumentFragment();
|
|
1013
|
+
anchorFragment.__Anchor__ = true;
|
|
1205
1014
|
|
|
1206
|
-
|
|
1015
|
+
const anchorStart = document.createComment('Anchor Start : '+name);
|
|
1016
|
+
const anchorEnd = document.createComment('/ Anchor End '+name);
|
|
1207
1017
|
|
|
1208
|
-
|
|
1209
|
-
|
|
1210
|
-
};
|
|
1018
|
+
anchorFragment.appendChild(anchorStart);
|
|
1019
|
+
anchorFragment.appendChild(anchorEnd);
|
|
1211
1020
|
|
|
1212
|
-
|
|
1213
|
-
|
|
1214
|
-
|
|
1215
|
-
};
|
|
1021
|
+
anchorFragment.nativeInsertBefore = anchorFragment.insertBefore;
|
|
1022
|
+
anchorFragment.nativeAppendChild = anchorFragment.appendChild;
|
|
1023
|
+
anchorFragment.nativeAppend = anchorFragment.append;
|
|
1216
1024
|
|
|
1217
|
-
|
|
1218
|
-
target[name] = this;
|
|
1219
|
-
return this;
|
|
1220
|
-
};
|
|
1025
|
+
const isParentUniqueChild = (parent) => (isUniqueChild || (parent.firstChild === anchorStart && parent.lastChild === anchorEnd));
|
|
1221
1026
|
|
|
1222
|
-
|
|
1223
|
-
|
|
1224
|
-
|
|
1225
|
-
|
|
1226
|
-
|
|
1227
|
-
elementChildren.nd?.remove();
|
|
1027
|
+
const insertBefore = function(parent, child, target) {
|
|
1028
|
+
const childElement = Validator.isElement(child) ? child : ElementCreator.getChild(child);
|
|
1029
|
+
if(parent === anchorFragment) {
|
|
1030
|
+
parent.nativeInsertBefore(childElement, target);
|
|
1031
|
+
return;
|
|
1228
1032
|
}
|
|
1229
|
-
|
|
1230
|
-
|
|
1231
|
-
|
|
1232
|
-
|
|
1233
|
-
|
|
1033
|
+
if(isParentUniqueChild(parent) && target === anchorEnd) {
|
|
1034
|
+
parent.append(childElement, target);
|
|
1035
|
+
return;
|
|
1036
|
+
}
|
|
1037
|
+
parent.insertBefore(childElement, target);
|
|
1038
|
+
};
|
|
1234
1039
|
|
|
1235
|
-
|
|
1236
|
-
|
|
1237
|
-
|
|
1238
|
-
|
|
1239
|
-
|
|
1240
|
-
|
|
1241
|
-
|
|
1242
|
-
|
|
1243
|
-
|
|
1244
|
-
};
|
|
1040
|
+
anchorFragment.appendElement = function(child, before = null) {
|
|
1041
|
+
const parentNode = anchorStart.parentNode;
|
|
1042
|
+
const targetBefore = before || anchorEnd;
|
|
1043
|
+
if(parentNode === anchorFragment) {
|
|
1044
|
+
parentNode.nativeInsertBefore(child, targetBefore);
|
|
1045
|
+
return;
|
|
1046
|
+
}
|
|
1047
|
+
parentNode?.insertBefore(child, targetBefore);
|
|
1048
|
+
};
|
|
1245
1049
|
|
|
1246
|
-
|
|
1247
|
-
|
|
1050
|
+
anchorFragment.appendChild = function(child, before = null) {
|
|
1051
|
+
const parent = anchorEnd.parentNode;
|
|
1052
|
+
if(!parent) {
|
|
1053
|
+
DebugManager.error('Anchor', 'Anchor : parent not found', child);
|
|
1054
|
+
return;
|
|
1055
|
+
}
|
|
1056
|
+
before = before ?? anchorEnd;
|
|
1057
|
+
insertBefore(parent, child, before);
|
|
1058
|
+
};
|
|
1248
1059
|
|
|
1249
|
-
|
|
1250
|
-
|
|
1251
|
-
|
|
1252
|
-
}
|
|
1253
|
-
if(states.unmounted) {
|
|
1254
|
-
this.$element.setAttribute('data--nd-unmounted', '1');
|
|
1255
|
-
this.$observer.unmounted(states.unmounted);
|
|
1256
|
-
}
|
|
1257
|
-
return this;
|
|
1258
|
-
};
|
|
1060
|
+
anchorFragment.append = function(...args ) {
|
|
1061
|
+
return anchorFragment.appendChild(args);
|
|
1062
|
+
};
|
|
1259
1063
|
|
|
1260
|
-
|
|
1261
|
-
|
|
1262
|
-
|
|
1064
|
+
anchorFragment.removeChildren = async function() {
|
|
1065
|
+
const parent = anchorEnd.parentNode;
|
|
1066
|
+
if(parent === anchorFragment) {
|
|
1067
|
+
return;
|
|
1068
|
+
}
|
|
1069
|
+
// if(isParentUniqueChild(parent)) {
|
|
1070
|
+
// parent.replaceChildren(anchorStart, anchorEnd);
|
|
1071
|
+
// return;
|
|
1072
|
+
// }
|
|
1263
1073
|
|
|
1264
|
-
|
|
1265
|
-
|
|
1266
|
-
|
|
1074
|
+
let itemToRemove = anchorStart.nextSibling, tempItem;
|
|
1075
|
+
const removes = [];
|
|
1076
|
+
while(itemToRemove && itemToRemove !== anchorEnd) {
|
|
1077
|
+
tempItem = itemToRemove.nextSibling;
|
|
1078
|
+
removes.push(itemToRemove.remove());
|
|
1079
|
+
itemToRemove = tempItem;
|
|
1080
|
+
}
|
|
1081
|
+
await Promise.all(removes);
|
|
1082
|
+
};
|
|
1267
1083
|
|
|
1268
|
-
|
|
1269
|
-
|
|
1270
|
-
|
|
1084
|
+
anchorFragment.remove = async function() {
|
|
1085
|
+
const parent = anchorEnd.parentNode;
|
|
1086
|
+
if(parent === anchorFragment) {
|
|
1087
|
+
return;
|
|
1088
|
+
}
|
|
1089
|
+
let itemToRemove = anchorStart.nextSibling, tempItem;
|
|
1090
|
+
const allItemToRemove = [];
|
|
1091
|
+
const removes = [];
|
|
1092
|
+
while(itemToRemove && itemToRemove !== anchorEnd) {
|
|
1093
|
+
tempItem = itemToRemove.nextSibling;
|
|
1094
|
+
allItemToRemove.push(itemToRemove);
|
|
1095
|
+
removes.push(itemToRemove.remove());
|
|
1096
|
+
itemToRemove = tempItem;
|
|
1097
|
+
}
|
|
1098
|
+
await Promise.all(removes);
|
|
1099
|
+
anchorFragment.nativeAppend(...allItemToRemove);
|
|
1100
|
+
};
|
|
1271
1101
|
|
|
1272
|
-
|
|
1102
|
+
anchorFragment.removeWithAnchors = async function() {
|
|
1103
|
+
await anchorFragment.removeChildren();
|
|
1104
|
+
anchorStart.remove();
|
|
1105
|
+
anchorEnd.remove();
|
|
1106
|
+
};
|
|
1273
1107
|
|
|
1274
|
-
|
|
1275
|
-
|
|
1276
|
-
|
|
1277
|
-
|
|
1278
|
-
|
|
1279
|
-
|
|
1280
|
-
|
|
1281
|
-
|
|
1282
|
-
|
|
1283
|
-
|
|
1284
|
-
|
|
1285
|
-
|
|
1108
|
+
anchorFragment.replaceContent = async function(child) {
|
|
1109
|
+
const childElement = Validator.isElement(child) ? child : ElementCreator.getChild(child);
|
|
1110
|
+
const parent = anchorEnd.parentNode;
|
|
1111
|
+
if(!parent) {
|
|
1112
|
+
return;
|
|
1113
|
+
}
|
|
1114
|
+
// if(isParentUniqueChild(parent)) {
|
|
1115
|
+
// parent.replaceChildren(anchorStart, childElement, anchorEnd);
|
|
1116
|
+
// return;
|
|
1117
|
+
// }
|
|
1118
|
+
await anchorFragment.removeChildren();
|
|
1119
|
+
parent.insertBefore(childElement, anchorEnd);
|
|
1120
|
+
};
|
|
1286
1121
|
|
|
1287
|
-
|
|
1288
|
-
};
|
|
1122
|
+
anchorFragment.setContent = anchorFragment.replaceContent;
|
|
1289
1123
|
|
|
1290
|
-
|
|
1291
|
-
|
|
1292
|
-
|
|
1124
|
+
anchorFragment.insertBefore = function(child, anchor = null) {
|
|
1125
|
+
anchorFragment.appendChild(child, anchor);
|
|
1126
|
+
};
|
|
1293
1127
|
|
|
1294
|
-
|
|
1295
|
-
|
|
1296
|
-
|
|
1128
|
+
|
|
1129
|
+
anchorFragment.endElement = function() {
|
|
1130
|
+
return anchorEnd;
|
|
1131
|
+
};
|
|
1132
|
+
|
|
1133
|
+
anchorFragment.startElement = function() {
|
|
1134
|
+
return anchorStart;
|
|
1135
|
+
};
|
|
1136
|
+
anchorFragment.restore = function() {
|
|
1137
|
+
anchorFragment.appendChild(anchorFragment);
|
|
1138
|
+
};
|
|
1139
|
+
anchorFragment.clear = anchorFragment.remove;
|
|
1140
|
+
anchorFragment.detach = anchorFragment.remove;
|
|
1141
|
+
|
|
1142
|
+
anchorFragment.getByIndex = function(index) {
|
|
1143
|
+
let currentNode = anchorStart;
|
|
1144
|
+
for(let i = 0; i <= index; i++) {
|
|
1145
|
+
if(!currentNode.nextSibling) {
|
|
1146
|
+
return null;
|
|
1147
|
+
}
|
|
1148
|
+
currentNode = currentNode.nextSibling;
|
|
1149
|
+
}
|
|
1150
|
+
return currentNode !== anchorStart ? currentNode : null;
|
|
1151
|
+
};
|
|
1152
|
+
|
|
1153
|
+
return anchorFragment;
|
|
1154
|
+
}
|
|
1155
|
+
DocumentFragment.prototype.setAttribute = () => {};
|
|
1156
|
+
|
|
1157
|
+
const BOOLEAN_ATTRIBUTES = new Set([
|
|
1158
|
+
'checked',
|
|
1159
|
+
'selected',
|
|
1160
|
+
'disabled',
|
|
1161
|
+
'readonly',
|
|
1162
|
+
'required',
|
|
1163
|
+
'autofocus',
|
|
1164
|
+
'multiple',
|
|
1165
|
+
'autocomplete',
|
|
1166
|
+
'hidden',
|
|
1167
|
+
'contenteditable',
|
|
1168
|
+
'spellcheck',
|
|
1169
|
+
'translate',
|
|
1170
|
+
'draggable',
|
|
1171
|
+
'async',
|
|
1172
|
+
'defer',
|
|
1173
|
+
'autoplay',
|
|
1174
|
+
'controls',
|
|
1175
|
+
'loop',
|
|
1176
|
+
'muted',
|
|
1177
|
+
'download',
|
|
1178
|
+
'reversed',
|
|
1179
|
+
'open',
|
|
1180
|
+
'default',
|
|
1181
|
+
'formnovalidate',
|
|
1182
|
+
'novalidate',
|
|
1183
|
+
'scoped',
|
|
1184
|
+
'itemscope',
|
|
1185
|
+
'allowfullscreen',
|
|
1186
|
+
'allowpaymentrequest',
|
|
1187
|
+
'playsinline'
|
|
1188
|
+
]);
|
|
1189
|
+
|
|
1190
|
+
const MemoryManager = (function() {
|
|
1191
|
+
|
|
1192
|
+
let $nextObserverId = 0;
|
|
1193
|
+
const $observables = new Map();
|
|
1194
|
+
|
|
1195
|
+
return {
|
|
1196
|
+
/**
|
|
1197
|
+
* Register an observable and return an id.
|
|
1198
|
+
*
|
|
1199
|
+
* @param {ObservableItem} observable
|
|
1200
|
+
* @param {Function} getListeners
|
|
1201
|
+
* @returns {number}
|
|
1202
|
+
*/
|
|
1203
|
+
register(observable) {
|
|
1204
|
+
const id = ++$nextObserverId;
|
|
1205
|
+
$observables.set(id, new WeakRef(observable));
|
|
1206
|
+
return id;
|
|
1207
|
+
},
|
|
1208
|
+
unregister(id) {
|
|
1209
|
+
$observables.delete(id);
|
|
1210
|
+
},
|
|
1211
|
+
getObservableById(id) {
|
|
1212
|
+
return $observables.get(id)?.deref();
|
|
1213
|
+
},
|
|
1214
|
+
cleanup() {
|
|
1215
|
+
for (const [_, weakObservableRef] of $observables) {
|
|
1216
|
+
const observable = weakObservableRef.deref();
|
|
1217
|
+
if (observable) {
|
|
1218
|
+
observable.cleanup();
|
|
1219
|
+
}
|
|
1220
|
+
}
|
|
1221
|
+
$observables.clear();
|
|
1222
|
+
},
|
|
1223
|
+
/**
|
|
1224
|
+
* Clean observables that are not referenced anymore.
|
|
1225
|
+
* @param {number} threshold
|
|
1226
|
+
*/
|
|
1227
|
+
cleanObservables(threshold) {
|
|
1228
|
+
if($observables.size < threshold) return;
|
|
1229
|
+
let cleanedCount = 0;
|
|
1230
|
+
for (const [id, weakObservableRef] of $observables) {
|
|
1231
|
+
if (!weakObservableRef.deref()) {
|
|
1232
|
+
$observables.delete(id);
|
|
1233
|
+
cleanedCount++;
|
|
1234
|
+
}
|
|
1235
|
+
}
|
|
1236
|
+
if (cleanedCount > 0) {
|
|
1237
|
+
DebugManager.log('Memory Auto Clean', `🧹 Cleaned ${cleanedCount} orphaned observables`);
|
|
1238
|
+
}
|
|
1239
|
+
}
|
|
1240
|
+
};
|
|
1241
|
+
}());
|
|
1297
1242
|
|
|
1298
1243
|
/**
|
|
1299
|
-
*
|
|
1244
|
+
* Creates an ObservableWhen that tracks whether an observable equals a specific value.
|
|
1300
1245
|
*
|
|
1301
|
-
* @param {
|
|
1302
|
-
* @param {
|
|
1303
|
-
* @
|
|
1304
|
-
* @example
|
|
1305
|
-
* const onClick = $binder.attach((event, data) => console.log(data));
|
|
1306
|
-
* element.nd.attach('onClick', onClick);
|
|
1246
|
+
* @param {ObservableItem} observer - The observable to watch
|
|
1247
|
+
* @param {*} value - The value to compare against
|
|
1248
|
+
* @class ObservableWhen
|
|
1307
1249
|
*/
|
|
1308
|
-
|
|
1309
|
-
|
|
1310
|
-
|
|
1250
|
+
const ObservableWhen = function(observer, value) {
|
|
1251
|
+
this.$target = value;
|
|
1252
|
+
this.$observer = observer;
|
|
1311
1253
|
};
|
|
1312
1254
|
|
|
1255
|
+
ObservableWhen.prototype.__$isObservableWhen = true;
|
|
1256
|
+
|
|
1313
1257
|
/**
|
|
1314
|
-
*
|
|
1315
|
-
* Methods are bound to the instance and available for chaining.
|
|
1258
|
+
* Subscribes to changes in the match status (true when observable equals target value).
|
|
1316
1259
|
*
|
|
1317
|
-
* @param {
|
|
1318
|
-
* @returns {
|
|
1260
|
+
* @param {Function} callback - Function called with boolean indicating if values match
|
|
1261
|
+
* @returns {Function} Unsubscribe function
|
|
1319
1262
|
* @example
|
|
1320
|
-
*
|
|
1321
|
-
*
|
|
1322
|
-
*
|
|
1323
|
-
* return this;
|
|
1324
|
-
* }
|
|
1325
|
-
* }).highlight().onClick(() => console.log('Clicked'));
|
|
1263
|
+
* const status = Observable('idle');
|
|
1264
|
+
* const isLoading = status.when('loading');
|
|
1265
|
+
* isLoading.subscribe(active => console.log('Loading:', active));
|
|
1326
1266
|
*/
|
|
1327
|
-
|
|
1328
|
-
|
|
1329
|
-
throw new NativeDocumentError('extend() requires an object of methods');
|
|
1330
|
-
}
|
|
1331
|
-
|
|
1332
|
-
for (const name in methods) {
|
|
1333
|
-
const method = methods[name];
|
|
1334
|
-
|
|
1335
|
-
if (typeof method !== 'function') {
|
|
1336
|
-
console.warn(`⚠️ extends(): "${name}" is not a function, skipping`);
|
|
1337
|
-
continue;
|
|
1338
|
-
}
|
|
1339
|
-
|
|
1340
|
-
this[name] = method.bind(this);
|
|
1341
|
-
}
|
|
1342
|
-
|
|
1343
|
-
return this;
|
|
1267
|
+
ObservableWhen.prototype.subscribe = function(callback) {
|
|
1268
|
+
return this.$observer.on(this.$target, callback);
|
|
1344
1269
|
};
|
|
1345
1270
|
|
|
1346
1271
|
/**
|
|
1347
|
-
*
|
|
1348
|
-
* Use this to add global methods to all NDElements.
|
|
1272
|
+
* Returns true if the observable's current value equals the target value.
|
|
1349
1273
|
*
|
|
1350
|
-
* @
|
|
1351
|
-
* @returns {typeof NDElement} The NDElement constructor
|
|
1352
|
-
* @throws {NativeDocumentError} If methods is not an object or contains non-function values
|
|
1353
|
-
* @example
|
|
1354
|
-
* NDElement.extend({
|
|
1355
|
-
* fadeIn() {
|
|
1356
|
-
* this.$element.style.opacity = '1';
|
|
1357
|
-
* return this;
|
|
1358
|
-
* }
|
|
1359
|
-
* });
|
|
1360
|
-
* // Now all NDElements have .fadeIn() method
|
|
1361
|
-
* Div().nd.fadeIn();
|
|
1274
|
+
* @returns {boolean} True if observable value matches target value
|
|
1362
1275
|
*/
|
|
1363
|
-
|
|
1364
|
-
|
|
1365
|
-
|
|
1366
|
-
}
|
|
1276
|
+
ObservableWhen.prototype.val = function() {
|
|
1277
|
+
return this.$observer.$currentValue === this.$target;
|
|
1278
|
+
};
|
|
1367
1279
|
|
|
1368
|
-
|
|
1369
|
-
|
|
1370
|
-
|
|
1280
|
+
/**
|
|
1281
|
+
* Returns true if the observable's current value equals the target value.
|
|
1282
|
+
* Alias for val().
|
|
1283
|
+
*
|
|
1284
|
+
* @returns {boolean} True if observable value matches target value
|
|
1285
|
+
*/
|
|
1286
|
+
ObservableWhen.prototype.isMatch = ObservableWhen.prototype.val;
|
|
1371
1287
|
|
|
1372
|
-
|
|
1373
|
-
|
|
1374
|
-
|
|
1375
|
-
|
|
1376
|
-
|
|
1288
|
+
/**
|
|
1289
|
+
* Returns true if the observable's current value equals the target value.
|
|
1290
|
+
* Alias for val().
|
|
1291
|
+
*
|
|
1292
|
+
* @returns {boolean} True if observable value matches target value
|
|
1293
|
+
*/
|
|
1294
|
+
ObservableWhen.prototype.isActive = ObservableWhen.prototype.val;
|
|
1377
1295
|
|
|
1378
|
-
|
|
1379
|
-
|
|
1380
|
-
|
|
1296
|
+
const invoke = function(fn, args, context) {
|
|
1297
|
+
if(context) {
|
|
1298
|
+
fn.apply(context, args);
|
|
1299
|
+
} else {
|
|
1300
|
+
fn(...args);
|
|
1301
|
+
}
|
|
1302
|
+
};
|
|
1303
|
+
/**
|
|
1304
|
+
*
|
|
1305
|
+
* @param {Function} fn
|
|
1306
|
+
* @param {number} delay
|
|
1307
|
+
* @param {{leading?:Boolean, trailing?:Boolean, debounce?:Boolean, check: Function}}options
|
|
1308
|
+
* @returns {(function(...[*]): void)|*}
|
|
1309
|
+
*/
|
|
1310
|
+
const debounce = function(fn, delay, options = {}) {
|
|
1311
|
+
let timer = null;
|
|
1312
|
+
let lastArgs = null;
|
|
1313
|
+
|
|
1314
|
+
return function(...args) {
|
|
1315
|
+
const context = options.context === true ? this : null;
|
|
1316
|
+
if(options.check) {
|
|
1317
|
+
options.check(...args);
|
|
1381
1318
|
}
|
|
1319
|
+
lastArgs = args;
|
|
1382
1320
|
|
|
1383
|
-
|
|
1321
|
+
// debounce mode: reset the timer for each call
|
|
1322
|
+
clearTimeout(timer);
|
|
1323
|
+
timer = setTimeout(() => invoke(fn, lastArgs, context), delay);
|
|
1324
|
+
}
|
|
1325
|
+
};
|
|
1384
1326
|
|
|
1385
|
-
|
|
1386
|
-
|
|
1387
|
-
|
|
1388
|
-
|
|
1327
|
+
const nextTick = function(fn) {
|
|
1328
|
+
let pending = false;
|
|
1329
|
+
return function(...args) {
|
|
1330
|
+
if (pending) return;
|
|
1331
|
+
pending = true;
|
|
1389
1332
|
|
|
1390
|
-
|
|
1391
|
-
|
|
1392
|
-
|
|
1393
|
-
}
|
|
1333
|
+
Promise.resolve().then(() => {
|
|
1334
|
+
fn.apply(this, args);
|
|
1335
|
+
pending = false;
|
|
1336
|
+
});
|
|
1337
|
+
};
|
|
1338
|
+
};
|
|
1394
1339
|
|
|
1395
|
-
|
|
1396
|
-
|
|
1340
|
+
const deepClone = (value, onObservableFound) => {
|
|
1341
|
+
try {
|
|
1342
|
+
if(window.structuredClone !== undefined) {
|
|
1343
|
+
return window.structuredClone(value);
|
|
1397
1344
|
}
|
|
1345
|
+
} catch (e){}
|
|
1398
1346
|
|
|
1399
|
-
|
|
1347
|
+
if (value === null || typeof value !== 'object') {
|
|
1348
|
+
return value;
|
|
1400
1349
|
}
|
|
1401
1350
|
|
|
1402
|
-
|
|
1403
|
-
|
|
1351
|
+
// Dates
|
|
1352
|
+
if (value instanceof Date) {
|
|
1353
|
+
return new Date(value.getTime());
|
|
1354
|
+
}
|
|
1404
1355
|
|
|
1405
|
-
|
|
1406
|
-
|
|
1407
|
-
|
|
1408
|
-
|
|
1409
|
-
|
|
1356
|
+
// Arrays
|
|
1357
|
+
if (Array.isArray(value)) {
|
|
1358
|
+
return value.map(item => deepClone(item));
|
|
1359
|
+
}
|
|
1360
|
+
|
|
1361
|
+
// Observables - keep the référence
|
|
1362
|
+
if (Validator.isObservable(value)) {
|
|
1363
|
+
onObservableFound && onObservableFound(value);
|
|
1364
|
+
return value;
|
|
1365
|
+
}
|
|
1366
|
+
|
|
1367
|
+
// Objects
|
|
1368
|
+
const cloned = {};
|
|
1369
|
+
for (const key in value) {
|
|
1370
|
+
if (Object.hasOwn(value, key)) {
|
|
1371
|
+
cloned[key] = deepClone(value[key]);
|
|
1372
|
+
}
|
|
1373
|
+
}
|
|
1374
|
+
return cloned;
|
|
1410
1375
|
};
|
|
1411
1376
|
|
|
1412
|
-
const
|
|
1413
|
-
|
|
1414
|
-
|
|
1415
|
-
|
|
1416
|
-
|
|
1417
|
-
|
|
1418
|
-
|
|
1419
|
-
|
|
1420
|
-
return value && (value.__$isObservableWhen || (typeof value === 'object' && '$target' in value && '$observer' in value));
|
|
1421
|
-
},
|
|
1422
|
-
isArrayObservable(value) {
|
|
1423
|
-
return value?.__$isObservableArray;
|
|
1424
|
-
},
|
|
1425
|
-
isProxy(value) {
|
|
1426
|
-
return value?.__isProxy__
|
|
1427
|
-
},
|
|
1428
|
-
isObservableOrProxy(value) {
|
|
1429
|
-
return Validator.isObservable(value) || Validator.isProxy(value);
|
|
1430
|
-
},
|
|
1431
|
-
isAnchor(value) {
|
|
1432
|
-
return value?.__Anchor__
|
|
1433
|
-
},
|
|
1434
|
-
isObservableChecker(value) {
|
|
1435
|
-
return value?.__$isObservableChecker || value instanceof ObservableChecker;
|
|
1436
|
-
},
|
|
1437
|
-
isArray(value) {
|
|
1438
|
-
return Array.isArray(value);
|
|
1439
|
-
},
|
|
1440
|
-
isString(value) {
|
|
1441
|
-
return typeof value === 'string';
|
|
1442
|
-
},
|
|
1443
|
-
isNumber(value) {
|
|
1444
|
-
return typeof value === 'number';
|
|
1445
|
-
},
|
|
1446
|
-
isBoolean(value) {
|
|
1447
|
-
return typeof value === 'boolean';
|
|
1448
|
-
},
|
|
1449
|
-
isFunction(value) {
|
|
1450
|
-
return typeof value === 'function';
|
|
1451
|
-
},
|
|
1452
|
-
isAsyncFunction(value) {
|
|
1453
|
-
return typeof value === 'function' && value.constructor.name === 'AsyncFunction';
|
|
1377
|
+
const LocalStorage$1 = {
|
|
1378
|
+
getJson(key) {
|
|
1379
|
+
let value = localStorage.getItem(key);
|
|
1380
|
+
try {
|
|
1381
|
+
return JSON.parse(value);
|
|
1382
|
+
} catch (e) {
|
|
1383
|
+
throw new NativeDocumentError('invalid_json:'+key);
|
|
1384
|
+
}
|
|
1454
1385
|
},
|
|
1455
|
-
|
|
1456
|
-
return
|
|
1386
|
+
getNumber(key) {
|
|
1387
|
+
return Number(this.get(key));
|
|
1457
1388
|
},
|
|
1458
|
-
|
|
1459
|
-
|
|
1389
|
+
getBool(key) {
|
|
1390
|
+
const value = this.get(key);
|
|
1391
|
+
return value === 'true' || value === '1';
|
|
1460
1392
|
},
|
|
1461
|
-
|
|
1462
|
-
|
|
1463
|
-
value.nodeType === COMMON_NODE_TYPES.ELEMENT ||
|
|
1464
|
-
value.nodeType === COMMON_NODE_TYPES.TEXT ||
|
|
1465
|
-
value.nodeType === COMMON_NODE_TYPES.DOCUMENT_FRAGMENT ||
|
|
1466
|
-
value.nodeType === COMMON_NODE_TYPES.COMMENT
|
|
1467
|
-
);
|
|
1393
|
+
setJson(key, value) {
|
|
1394
|
+
localStorage.setItem(key, JSON.stringify(value));
|
|
1468
1395
|
},
|
|
1469
|
-
|
|
1470
|
-
|
|
1396
|
+
setBool(key, value) {
|
|
1397
|
+
localStorage.setItem(key, value ? 'true' : 'false');
|
|
1471
1398
|
},
|
|
1472
|
-
|
|
1473
|
-
return
|
|
1399
|
+
get(key, defaultValue = null) {
|
|
1400
|
+
return localStorage.getItem(key) || defaultValue;
|
|
1474
1401
|
},
|
|
1475
|
-
|
|
1476
|
-
return
|
|
1477
|
-
this.isElement(child) ||
|
|
1478
|
-
this.isObservable(child) ||
|
|
1479
|
-
this.isNDElement(child) ||
|
|
1480
|
-
['string', 'number', 'boolean'].includes(typeof child);
|
|
1402
|
+
set(key, value) {
|
|
1403
|
+
return localStorage.setItem(key, value);
|
|
1481
1404
|
},
|
|
1482
|
-
|
|
1483
|
-
|
|
1405
|
+
remove(key) {
|
|
1406
|
+
localStorage.removeItem(key);
|
|
1484
1407
|
},
|
|
1485
|
-
|
|
1486
|
-
|
|
1487
|
-
|
|
1488
|
-
|
|
1408
|
+
has(key) {
|
|
1409
|
+
return localStorage.getItem(key) != null;
|
|
1410
|
+
}
|
|
1411
|
+
};
|
|
1489
1412
|
|
|
1490
|
-
|
|
1491
|
-
|
|
1492
|
-
|
|
1493
|
-
|
|
1494
|
-
|
|
1495
|
-
|
|
1496
|
-
|
|
1413
|
+
const $getFromStorage$1 = (key, value) => {
|
|
1414
|
+
if(!LocalStorage$1.has(key)) {
|
|
1415
|
+
return value;
|
|
1416
|
+
}
|
|
1417
|
+
switch (typeof value) {
|
|
1418
|
+
case 'object': return LocalStorage$1.getJson(key) ?? value;
|
|
1419
|
+
case 'boolean': return LocalStorage$1.getBool(key) ?? value;
|
|
1420
|
+
case 'number': return LocalStorage$1.getNumber(key) ?? value;
|
|
1421
|
+
default: return LocalStorage$1.get(key, value) ?? value;
|
|
1422
|
+
}
|
|
1423
|
+
};
|
|
1497
1424
|
|
|
1498
|
-
|
|
1499
|
-
|
|
1500
|
-
|
|
1501
|
-
|
|
1425
|
+
const $saveToStorage$1 = (value) => {
|
|
1426
|
+
switch (typeof value) {
|
|
1427
|
+
case 'object': return LocalStorage$1.setJson;
|
|
1428
|
+
case 'boolean': return LocalStorage$1.setBool;
|
|
1429
|
+
default: return LocalStorage$1.set;
|
|
1430
|
+
}
|
|
1431
|
+
};
|
|
1432
|
+
|
|
1433
|
+
const StoreFactory = function() {
|
|
1434
|
+
|
|
1435
|
+
const $stores = new Map();
|
|
1436
|
+
const $followersCache = new Map();
|
|
1502
1437
|
|
|
1503
|
-
return children;
|
|
1504
|
-
},
|
|
1505
1438
|
/**
|
|
1506
|
-
*
|
|
1507
|
-
* @param {Array|Object} data
|
|
1508
|
-
* @returns {boolean}
|
|
1439
|
+
* Internal helper — retrieves a store entry or throws if not found.
|
|
1509
1440
|
*/
|
|
1510
|
-
|
|
1511
|
-
|
|
1512
|
-
|
|
1441
|
+
const $getStoreOrThrow = (method, name) => {
|
|
1442
|
+
const item = $stores.get(name);
|
|
1443
|
+
if (!item) {
|
|
1444
|
+
DebugManager.error('Store', `Store.${method}('${name}') : store not found. Did you call Store.create('${name}') first?`);
|
|
1445
|
+
throw new NativeDocumentError(
|
|
1446
|
+
`Store.${method}('${name}') : store not found.`
|
|
1447
|
+
);
|
|
1513
1448
|
}
|
|
1514
|
-
return
|
|
1515
|
-
|
|
1516
|
-
|
|
1449
|
+
return item;
|
|
1450
|
+
};
|
|
1451
|
+
|
|
1517
1452
|
/**
|
|
1518
|
-
*
|
|
1519
|
-
* @param {string} data
|
|
1520
|
-
* @returns {boolean}
|
|
1453
|
+
* Internal helper — blocks write operations on a read-only observer.
|
|
1521
1454
|
*/
|
|
1522
|
-
|
|
1523
|
-
|
|
1524
|
-
|
|
1525
|
-
|
|
1526
|
-
|
|
1527
|
-
|
|
1528
|
-
|
|
1455
|
+
const $applyReadOnly = (observer, name, context) => {
|
|
1456
|
+
const readOnlyError = (method) => () => {
|
|
1457
|
+
DebugManager.error('Store', `Store.${context}('${name}') is read-only. '${method}()' is not allowed.`);
|
|
1458
|
+
throw new NativeDocumentError(
|
|
1459
|
+
`Store.${context}('${name}') is read-only.`
|
|
1460
|
+
);
|
|
1461
|
+
};
|
|
1462
|
+
observer.set = readOnlyError('set');
|
|
1463
|
+
observer.toggle = readOnlyError('toggle');
|
|
1464
|
+
observer.reset = readOnlyError('reset');
|
|
1465
|
+
};
|
|
1529
1466
|
|
|
1530
|
-
|
|
1531
|
-
if
|
|
1532
|
-
|
|
1467
|
+
const $createObservable = (value, options = {}) => {
|
|
1468
|
+
if(Array.isArray(value)) {
|
|
1469
|
+
return Observable$1.array(value, options);
|
|
1533
1470
|
}
|
|
1534
|
-
|
|
1535
|
-
|
|
1471
|
+
if(typeof value === 'object') {
|
|
1472
|
+
return Observable$1.object(value, options);
|
|
1473
|
+
}
|
|
1474
|
+
return Observable$1(value, options);
|
|
1475
|
+
};
|
|
1536
1476
|
|
|
1537
|
-
|
|
1538
|
-
|
|
1539
|
-
|
|
1477
|
+
const $api = {
|
|
1478
|
+
/**
|
|
1479
|
+
* Create a new state and return the observer.
|
|
1480
|
+
* Throws if a store with the same name already exists.
|
|
1481
|
+
*
|
|
1482
|
+
* @param {string} name
|
|
1483
|
+
* @param {*} value
|
|
1484
|
+
* @returns {ObservableItem}
|
|
1485
|
+
*/
|
|
1486
|
+
create(name, value) {
|
|
1487
|
+
if ($stores.has(name)) {
|
|
1488
|
+
DebugManager.warn('Store', `Store.create('${name}') : a store with this name already exists. Use Store.get('${name}') to retrieve it.`);
|
|
1489
|
+
throw new NativeDocumentError(
|
|
1490
|
+
`Store.create('${name}') : a store with this name already exists.`
|
|
1491
|
+
);
|
|
1492
|
+
}
|
|
1493
|
+
const observer = $createObservable(value);
|
|
1494
|
+
$stores.set(name, { observer, subscribers: new Set(), resettable: false, composed: false });
|
|
1495
|
+
return observer;
|
|
1496
|
+
},
|
|
1540
1497
|
|
|
1541
|
-
|
|
1542
|
-
|
|
1498
|
+
/**
|
|
1499
|
+
* Create a new resettable state and return the observer.
|
|
1500
|
+
* The store can be reset to its initial value via Store.reset(name).
|
|
1501
|
+
* Throws if a store with the same name already exists.
|
|
1502
|
+
*
|
|
1503
|
+
* @param {string} name
|
|
1504
|
+
* @param {*} value
|
|
1505
|
+
* @returns {ObservableItem}
|
|
1506
|
+
*/
|
|
1507
|
+
createResettable(name, value) {
|
|
1508
|
+
if ($stores.has(name)) {
|
|
1509
|
+
DebugManager.warn('Store', `Store.createResettable('${name}') : a store with this name already exists.`);
|
|
1510
|
+
throw new NativeDocumentError(
|
|
1511
|
+
`Store.createResettable('${name}') : a store with this name already exists.`
|
|
1512
|
+
);
|
|
1513
|
+
}
|
|
1514
|
+
const observer = $createObservable(value, { reset: true });
|
|
1515
|
+
$stores.set(name, { observer, subscribers: new Set(), resettable: true, composed: false });
|
|
1516
|
+
return observer;
|
|
1517
|
+
},
|
|
1543
1518
|
|
|
1544
|
-
|
|
1545
|
-
|
|
1519
|
+
/**
|
|
1520
|
+
* Create a computed store derived from other stores.
|
|
1521
|
+
* The value is automatically recalculated when any dependency changes.
|
|
1522
|
+
* This store is read-only — Store.use() and Store.set() will throw.
|
|
1523
|
+
* Throws if a store with the same name already exists.
|
|
1524
|
+
*
|
|
1525
|
+
* @param {string} name
|
|
1526
|
+
* @param {() => *} computation - Function that returns the computed value
|
|
1527
|
+
* @param {string[]} dependencies - Names of the stores to watch
|
|
1528
|
+
* @returns {ObservableItem}
|
|
1529
|
+
*
|
|
1530
|
+
* @example
|
|
1531
|
+
* Store.create('products', [{ id: 1, price: 10 }]);
|
|
1532
|
+
* Store.create('cart', [{ productId: 1, quantity: 2 }]);
|
|
1533
|
+
*
|
|
1534
|
+
* Store.createComposed('total', () => {
|
|
1535
|
+
* const products = Store.get('products').val();
|
|
1536
|
+
* const cart = Store.get('cart').val();
|
|
1537
|
+
* return cart.reduce((sum, item) => {
|
|
1538
|
+
* const product = products.find(p => p.id === item.productId);
|
|
1539
|
+
* return sum + (product.price * item.quantity);
|
|
1540
|
+
* }, 0);
|
|
1541
|
+
* }, ['products', 'cart']);
|
|
1542
|
+
*/
|
|
1543
|
+
createComposed(name, computation, dependencies) {
|
|
1544
|
+
if ($stores.has(name)) {
|
|
1545
|
+
DebugManager.warn('Store', `Store.createComposed('${name}') : a store with this name already exists.`);
|
|
1546
|
+
throw new NativeDocumentError(
|
|
1547
|
+
`Store.createComposed('${name}') : a store with this name already exists.`
|
|
1548
|
+
);
|
|
1549
|
+
}
|
|
1550
|
+
if (typeof computation !== 'function') {
|
|
1551
|
+
throw new NativeDocumentError(
|
|
1552
|
+
`Store.createComposed('${name}') : computation must be a function.`
|
|
1553
|
+
);
|
|
1554
|
+
}
|
|
1555
|
+
if (!Array.isArray(dependencies) || dependencies.length === 0) {
|
|
1556
|
+
throw new NativeDocumentError(
|
|
1557
|
+
`Store.createComposed('${name}') : dependencies must be a non-empty array of store names.`
|
|
1558
|
+
);
|
|
1559
|
+
}
|
|
1546
1560
|
|
|
1547
|
-
|
|
1548
|
-
|
|
1561
|
+
// Resolve dependency observers
|
|
1562
|
+
const depObservers = dependencies.map(depName => {
|
|
1563
|
+
if(typeof depName !== 'string') {
|
|
1564
|
+
return depName;
|
|
1565
|
+
}
|
|
1566
|
+
const depItem = $stores.get(depName);
|
|
1567
|
+
if (!depItem) {
|
|
1568
|
+
DebugManager.error('Store', `Store.createComposed('${name}') : dependency '${depName}' not found. Create it first.`);
|
|
1569
|
+
throw new NativeDocumentError(
|
|
1570
|
+
`Store.createComposed('${name}') : dependency store '${depName}' not found.`
|
|
1571
|
+
);
|
|
1572
|
+
}
|
|
1573
|
+
return depItem.observer;
|
|
1574
|
+
});
|
|
1549
1575
|
|
|
1550
|
-
|
|
1576
|
+
// Create computed observable from dependency observers
|
|
1577
|
+
const observer = Observable$1.computed(computation, depObservers);
|
|
1551
1578
|
|
|
1552
|
-
|
|
1553
|
-
|
|
1554
|
-
|
|
1555
|
-
parent.nativeInsertBefore(childElement, target);
|
|
1556
|
-
return;
|
|
1557
|
-
}
|
|
1558
|
-
if(isParentUniqueChild(parent) && target === anchorEnd) {
|
|
1559
|
-
parent.append(childElement, target);
|
|
1560
|
-
return;
|
|
1561
|
-
}
|
|
1562
|
-
parent.insertBefore(childElement, target);
|
|
1563
|
-
};
|
|
1579
|
+
$stores.set(name, { observer, subscribers: new Set(), resettable: false, composed: true });
|
|
1580
|
+
return observer;
|
|
1581
|
+
},
|
|
1564
1582
|
|
|
1565
|
-
|
|
1566
|
-
|
|
1567
|
-
|
|
1568
|
-
|
|
1569
|
-
|
|
1570
|
-
|
|
1583
|
+
/**
|
|
1584
|
+
* Returns true if a store with the given name exists.
|
|
1585
|
+
*
|
|
1586
|
+
* @param {string} name
|
|
1587
|
+
* @returns {boolean}
|
|
1588
|
+
*/
|
|
1589
|
+
has(name) {
|
|
1590
|
+
return $stores.has(name);
|
|
1591
|
+
},
|
|
1592
|
+
|
|
1593
|
+
/**
|
|
1594
|
+
* Resets a resettable store to its initial value and notifies all subscribers.
|
|
1595
|
+
* Throws if the store was not created with createResettable().
|
|
1596
|
+
*
|
|
1597
|
+
* @param {string} name
|
|
1598
|
+
*/
|
|
1599
|
+
reset(name) {
|
|
1600
|
+
const item = $getStoreOrThrow('reset', name);
|
|
1601
|
+
if (item.composed) {
|
|
1602
|
+
DebugManager.error('Store', `Store.reset('${name}') : composed stores cannot be reset. Their value is derived from dependencies.`);
|
|
1603
|
+
throw new NativeDocumentError(
|
|
1604
|
+
`Store.reset('${name}') : composed stores cannot be reset.`
|
|
1605
|
+
);
|
|
1606
|
+
}
|
|
1607
|
+
if (!item.resettable) {
|
|
1608
|
+
DebugManager.error('Store', `Store.reset('${name}') : this store is not resettable. Use Store.createResettable('${name}', value) instead of Store.create().`);
|
|
1609
|
+
throw new NativeDocumentError(
|
|
1610
|
+
`Store.reset('${name}') : this store is not resettable. Use Store.createResettable('${name}', value) instead of Store.create().`
|
|
1611
|
+
);
|
|
1612
|
+
}
|
|
1613
|
+
item.observer.reset();
|
|
1614
|
+
},
|
|
1615
|
+
|
|
1616
|
+
/**
|
|
1617
|
+
* Returns a two-way synchronized follower of the store.
|
|
1618
|
+
* Writing to the follower propagates the value back to the store and all its subscribers.
|
|
1619
|
+
* Throws if called on a composed store — use Store.follow() instead.
|
|
1620
|
+
* Call follower.destroy() or follower.dispose() to unsubscribe.
|
|
1621
|
+
*
|
|
1622
|
+
* @param {string} name
|
|
1623
|
+
* @returns {ObservableItem}
|
|
1624
|
+
*/
|
|
1625
|
+
use(name) {
|
|
1626
|
+
const item = $getStoreOrThrow('use', name);
|
|
1627
|
+
|
|
1628
|
+
if (item.composed) {
|
|
1629
|
+
DebugManager.error('Store', `Store.use('${name}') : composed stores are read-only. Use Store.follow('${name}') instead.`);
|
|
1630
|
+
throw new NativeDocumentError(
|
|
1631
|
+
`Store.use('${name}') : composed stores are read-only. Use Store.follow('${name}') instead.`
|
|
1632
|
+
);
|
|
1633
|
+
}
|
|
1634
|
+
|
|
1635
|
+
const { observer: originalObserver, subscribers } = item;
|
|
1636
|
+
const observerFollower = $createObservable(originalObserver.val());
|
|
1637
|
+
|
|
1638
|
+
const onStoreChange = value => observerFollower.set(value);
|
|
1639
|
+
const onFollowerChange = value => originalObserver.set(value);
|
|
1640
|
+
|
|
1641
|
+
originalObserver.subscribe(onStoreChange);
|
|
1642
|
+
observerFollower.subscribe(onFollowerChange);
|
|
1643
|
+
|
|
1644
|
+
observerFollower.destroy = () => {
|
|
1645
|
+
originalObserver.unsubscribe(onStoreChange);
|
|
1646
|
+
observerFollower.unsubscribe(onFollowerChange);
|
|
1647
|
+
subscribers.delete(observerFollower);
|
|
1648
|
+
observerFollower.cleanup();
|
|
1649
|
+
};
|
|
1650
|
+
observerFollower.dispose = observerFollower.destroy;
|
|
1651
|
+
|
|
1652
|
+
subscribers.add(observerFollower);
|
|
1653
|
+
return observerFollower;
|
|
1654
|
+
},
|
|
1655
|
+
|
|
1656
|
+
/**
|
|
1657
|
+
* Returns a read-only follower of the store.
|
|
1658
|
+
* The follower reflects store changes but cannot write back to the store.
|
|
1659
|
+
* Any attempt to call .set(), .toggle() or .reset() will throw.
|
|
1660
|
+
* Call follower.destroy() or follower.dispose() to unsubscribe.
|
|
1661
|
+
*
|
|
1662
|
+
* @param {string} name
|
|
1663
|
+
* @returns {ObservableItem}
|
|
1664
|
+
*/
|
|
1665
|
+
follow(name) {
|
|
1666
|
+
const { observer: originalObserver, subscribers } = $getStoreOrThrow('follow', name);
|
|
1667
|
+
const observerFollower = $createObservable(originalObserver.val());
|
|
1668
|
+
|
|
1669
|
+
const onStoreChange = value => observerFollower.set(value);
|
|
1670
|
+
originalObserver.subscribe(onStoreChange);
|
|
1671
|
+
|
|
1672
|
+
$applyReadOnly(observerFollower, name, 'follow');
|
|
1673
|
+
|
|
1674
|
+
observerFollower.destroy = () => {
|
|
1675
|
+
originalObserver.unsubscribe(onStoreChange);
|
|
1676
|
+
subscribers.delete(observerFollower);
|
|
1677
|
+
observerFollower.cleanup();
|
|
1678
|
+
};
|
|
1679
|
+
observerFollower.dispose = observerFollower.destroy;
|
|
1680
|
+
|
|
1681
|
+
subscribers.add(observerFollower);
|
|
1682
|
+
return observerFollower;
|
|
1683
|
+
},
|
|
1684
|
+
|
|
1685
|
+
/**
|
|
1686
|
+
* Returns the raw store observer directly (no follower, no cleanup contract).
|
|
1687
|
+
* Use this for direct read access when you don't need to unsubscribe.
|
|
1688
|
+
* WARNING : mutations on this observer impact all subscribers immediately.
|
|
1689
|
+
*
|
|
1690
|
+
* @param {string} name
|
|
1691
|
+
* @returns {ObservableItem|null}
|
|
1692
|
+
*/
|
|
1693
|
+
get(name) {
|
|
1694
|
+
const item = $stores.get(name);
|
|
1695
|
+
if (!item) {
|
|
1696
|
+
DebugManager.warn('Store', `Store.get('${name}') : store not found.`);
|
|
1697
|
+
return null;
|
|
1698
|
+
}
|
|
1699
|
+
return item.observer;
|
|
1700
|
+
},
|
|
1701
|
+
|
|
1702
|
+
/**
|
|
1703
|
+
* @param {string} name
|
|
1704
|
+
* @returns {{ observer: ObservableItem, subscribers: Set } | null}
|
|
1705
|
+
*/
|
|
1706
|
+
getWithSubscribers(name) {
|
|
1707
|
+
return $stores.get(name) ?? null;
|
|
1708
|
+
},
|
|
1709
|
+
|
|
1710
|
+
/**
|
|
1711
|
+
* Destroys a store : cleans up the observer, destroys all followers, and removes the entry.
|
|
1712
|
+
*
|
|
1713
|
+
* @param {string} name
|
|
1714
|
+
*/
|
|
1715
|
+
delete(name) {
|
|
1716
|
+
const item = $stores.get(name);
|
|
1717
|
+
if (!item) {
|
|
1718
|
+
DebugManager.warn('Store', `Store.delete('${name}') : store not found, nothing to delete.`);
|
|
1719
|
+
return;
|
|
1720
|
+
}
|
|
1721
|
+
item.subscribers.forEach(follower => follower.destroy());
|
|
1722
|
+
item.subscribers.clear();
|
|
1723
|
+
item.observer.cleanup();
|
|
1724
|
+
$stores.delete(name);
|
|
1725
|
+
},
|
|
1726
|
+
/**
|
|
1727
|
+
* Creates an isolated store group with its own state namespace.
|
|
1728
|
+
* Each group is a fully independent StoreFactory instance —
|
|
1729
|
+
* no key conflicts, no shared state with the parent store.
|
|
1730
|
+
*
|
|
1731
|
+
* @param {string | ((group: ReturnType<typeof StoreFactory>) => void)} name - Group name for debugging, or setup callback if no name is provided
|
|
1732
|
+
* @param {((group: ReturnType<typeof StoreFactory>) => void)} [callback] - Setup function receiving the isolated store instance
|
|
1733
|
+
* @returns {ReturnType<typeof StoreFactory>}
|
|
1734
|
+
*
|
|
1735
|
+
* @example
|
|
1736
|
+
* // With name (recommended)
|
|
1737
|
+
* const EventStore = Store.group('events', (group) => {
|
|
1738
|
+
* group.create('catalog', []);
|
|
1739
|
+
* group.create('filters', { category: null, date: null });
|
|
1740
|
+
* group.createResettable('selected', null);
|
|
1741
|
+
* group.createComposed('filtered', () => {
|
|
1742
|
+
* const catalog = EventStore.get('catalog').val();
|
|
1743
|
+
* const filters = EventStore.get('filters').val();
|
|
1744
|
+
* return catalog.filter(event => {
|
|
1745
|
+
* if (filters.category && event.category !== filters.category) return false;
|
|
1746
|
+
* return true;
|
|
1747
|
+
* });
|
|
1748
|
+
* }, ['catalog', 'filters']);
|
|
1749
|
+
* });
|
|
1750
|
+
*
|
|
1751
|
+
* // Without name
|
|
1752
|
+
* const CartStore = Store.group((group) => {
|
|
1753
|
+
* group.create('items', []);
|
|
1754
|
+
* });
|
|
1755
|
+
*
|
|
1756
|
+
* // Usage
|
|
1757
|
+
* EventStore.use('catalog'); // two-way follower
|
|
1758
|
+
* EventStore.follow('filtered'); // read-only follower
|
|
1759
|
+
* EventStore.get('filters'); // raw observable
|
|
1760
|
+
*
|
|
1761
|
+
* // Cross-group composed
|
|
1762
|
+
* const OrderStore = Store.group('orders', (group) => {
|
|
1763
|
+
* group.createComposed('summary', () => {
|
|
1764
|
+
* const items = CartStore.get('items').val();
|
|
1765
|
+
* const events = EventStore.get('catalog').val();
|
|
1766
|
+
* return { items, events };
|
|
1767
|
+
* }, [CartStore.get('items'), EventStore.get('catalog')]);
|
|
1768
|
+
* });
|
|
1769
|
+
*/
|
|
1770
|
+
group(name, callback) {
|
|
1771
|
+
if (typeof name === 'function') {
|
|
1772
|
+
callback = name;
|
|
1773
|
+
name = 'anonymous';
|
|
1774
|
+
}
|
|
1775
|
+
const store = StoreFactory();
|
|
1776
|
+
callback && callback(store);
|
|
1777
|
+
return store;
|
|
1778
|
+
},
|
|
1779
|
+
createPersistent(name, value, localstorage_key) {
|
|
1780
|
+
localstorage_key = localstorage_key || name;
|
|
1781
|
+
const observer = this.create(name, $getFromStorage$1(localstorage_key, value));
|
|
1782
|
+
const saver = $saveToStorage$1(value);
|
|
1783
|
+
|
|
1784
|
+
observer.subscribe((val) => saver(localstorage_key, val));
|
|
1785
|
+
return observer;
|
|
1786
|
+
},
|
|
1787
|
+
createPersistentResettable(name, value, localstorage_key) {
|
|
1788
|
+
localstorage_key = localstorage_key || name;
|
|
1789
|
+
const observer = this.createResettable(name, $getFromStorage$1(localstorage_key, value));
|
|
1790
|
+
const saver = $saveToStorage$1(value);
|
|
1791
|
+
observer.subscribe((val) => saver(localstorage_key, val));
|
|
1792
|
+
|
|
1793
|
+
const originalReset = observer.reset.bind(observer);
|
|
1794
|
+
observer.reset = () => {
|
|
1795
|
+
LocalStorage$1.remove(localstorage_key);
|
|
1796
|
+
originalReset();
|
|
1797
|
+
};
|
|
1798
|
+
|
|
1799
|
+
return observer;
|
|
1571
1800
|
}
|
|
1572
|
-
parentNode?.insertBefore(child, targetBefore);
|
|
1573
1801
|
};
|
|
1574
1802
|
|
|
1575
|
-
|
|
1576
|
-
|
|
1577
|
-
|
|
1578
|
-
|
|
1579
|
-
|
|
1803
|
+
|
|
1804
|
+
return new Proxy($api, {
|
|
1805
|
+
get(target, prop) {
|
|
1806
|
+
if (typeof prop === 'symbol' || prop.startsWith('$') || prop in target) {
|
|
1807
|
+
return target[prop];
|
|
1808
|
+
}
|
|
1809
|
+
if (target.has(prop)) {
|
|
1810
|
+
if ($followersCache.has(prop)) {
|
|
1811
|
+
return $followersCache.get(prop);
|
|
1812
|
+
}
|
|
1813
|
+
const follower = target.follow(prop);
|
|
1814
|
+
$followersCache.set(prop, follower);
|
|
1815
|
+
return follower;
|
|
1816
|
+
}
|
|
1817
|
+
return undefined;
|
|
1818
|
+
},
|
|
1819
|
+
set(target, prop, value) {
|
|
1820
|
+
DebugManager.error('Store', `Forbidden: You cannot overwrite the store key '${String(prop)}'. Use .use('${String(prop)}').set(value) instead.`);
|
|
1821
|
+
throw new NativeDocumentError(`Store structure is immutable. Use .set() on the observable.`);
|
|
1822
|
+
},
|
|
1823
|
+
deleteProperty(target, prop) {
|
|
1824
|
+
throw new NativeDocumentError(`Store keys cannot be deleted.`);
|
|
1580
1825
|
}
|
|
1581
|
-
|
|
1582
|
-
|
|
1583
|
-
|
|
1584
|
-
|
|
1585
|
-
|
|
1826
|
+
});
|
|
1827
|
+
};
|
|
1828
|
+
|
|
1829
|
+
const Store = StoreFactory();
|
|
1830
|
+
|
|
1831
|
+
Store.create('locale', 'fr');
|
|
1832
|
+
|
|
1833
|
+
const $parseDateParts = (value, locale) => {
|
|
1834
|
+
const d = new Date(value);
|
|
1835
|
+
return {
|
|
1836
|
+
d,
|
|
1837
|
+
parts: new Intl.DateTimeFormat(locale, {
|
|
1838
|
+
year: 'numeric',
|
|
1839
|
+
month: 'long',
|
|
1840
|
+
day: '2-digit',
|
|
1841
|
+
hour: '2-digit',
|
|
1842
|
+
minute: '2-digit',
|
|
1843
|
+
second: '2-digit',
|
|
1844
|
+
}).formatToParts(d).reduce((acc, { type, value }) => {
|
|
1845
|
+
acc[type] = value;
|
|
1846
|
+
return acc;
|
|
1847
|
+
}, {})
|
|
1586
1848
|
};
|
|
1849
|
+
};
|
|
1587
1850
|
|
|
1588
|
-
|
|
1589
|
-
|
|
1590
|
-
|
|
1591
|
-
|
|
1592
|
-
|
|
1593
|
-
|
|
1594
|
-
|
|
1595
|
-
|
|
1851
|
+
const $applyDatePattern = (pattern, d, parts) => {
|
|
1852
|
+
const pad = n => String(n).padStart(2, '0');
|
|
1853
|
+
return pattern
|
|
1854
|
+
.replace('YYYY', parts.year)
|
|
1855
|
+
.replace('YY', parts.year.slice(-2))
|
|
1856
|
+
.replace('MMMM', parts.month)
|
|
1857
|
+
.replace('MMM', parts.month.slice(0, 3))
|
|
1858
|
+
.replace('MM', pad(d.getMonth() + 1))
|
|
1859
|
+
.replace('DD', pad(d.getDate()))
|
|
1860
|
+
.replace('D', d.getDate())
|
|
1861
|
+
.replace('HH', parts.hour)
|
|
1862
|
+
.replace('mm', parts.minute)
|
|
1863
|
+
.replace('ss', parts.second);
|
|
1864
|
+
};
|
|
1865
|
+
|
|
1866
|
+
const Formatters = {
|
|
1867
|
+
|
|
1868
|
+
currency: (value, locale, { currency = 'XOF', notation, minimumFractionDigits, maximumFractionDigits } = {}) =>
|
|
1869
|
+
new Intl.NumberFormat(locale, {
|
|
1870
|
+
style: 'currency',
|
|
1871
|
+
currency,
|
|
1872
|
+
notation,
|
|
1873
|
+
minimumFractionDigits,
|
|
1874
|
+
maximumFractionDigits
|
|
1875
|
+
}).format(value),
|
|
1876
|
+
|
|
1877
|
+
number: (value, locale, { notation, minimumFractionDigits, maximumFractionDigits } = {}) =>
|
|
1878
|
+
new Intl.NumberFormat(locale, {
|
|
1879
|
+
notation,
|
|
1880
|
+
minimumFractionDigits,
|
|
1881
|
+
maximumFractionDigits
|
|
1882
|
+
}).format(value),
|
|
1883
|
+
|
|
1884
|
+
percent: (value, locale, { decimals = 1 } = {}) =>
|
|
1885
|
+
new Intl.NumberFormat(locale, {
|
|
1886
|
+
style: 'percent',
|
|
1887
|
+
maximumFractionDigits: decimals
|
|
1888
|
+
}).format(value),
|
|
1889
|
+
|
|
1890
|
+
date: (value, locale, { format, dateStyle = 'long' } = {}) => {
|
|
1891
|
+
if (format) {
|
|
1892
|
+
const { d, parts } = $parseDateParts(value, locale);
|
|
1893
|
+
return $applyDatePattern(format, d, parts);
|
|
1596
1894
|
}
|
|
1895
|
+
return new Intl.DateTimeFormat(locale, { dateStyle }).format(new Date(value));
|
|
1896
|
+
},
|
|
1597
1897
|
|
|
1598
|
-
|
|
1599
|
-
|
|
1600
|
-
|
|
1601
|
-
|
|
1602
|
-
fragment.append(itemToRemove);
|
|
1603
|
-
itemToRemove = tempItem;
|
|
1898
|
+
time: (value, locale, { format, hour = '2-digit', minute = '2-digit', second } = {}) => {
|
|
1899
|
+
if (format) {
|
|
1900
|
+
const { d, parts } = $parseDateParts(value, locale);
|
|
1901
|
+
return $applyDatePattern(format, d, parts);
|
|
1604
1902
|
}
|
|
1605
|
-
|
|
1606
|
-
}
|
|
1607
|
-
|
|
1608
|
-
|
|
1609
|
-
if(
|
|
1610
|
-
|
|
1903
|
+
return new Intl.DateTimeFormat(locale, { hour, minute, second }).format(new Date(value));
|
|
1904
|
+
},
|
|
1905
|
+
|
|
1906
|
+
datetime: (value, locale, { format, dateStyle = 'long', hour = '2-digit', minute = '2-digit', second } = {}) => {
|
|
1907
|
+
if (format) {
|
|
1908
|
+
const { d, parts } = $parseDateParts(value, locale);
|
|
1909
|
+
return $applyDatePattern(format, d, parts);
|
|
1611
1910
|
}
|
|
1612
|
-
|
|
1613
|
-
|
|
1614
|
-
|
|
1615
|
-
|
|
1616
|
-
|
|
1911
|
+
return new Intl.DateTimeFormat(locale, { dateStyle, hour, minute, second }).format(new Date(value));
|
|
1912
|
+
},
|
|
1913
|
+
|
|
1914
|
+
relative: (value, locale, { unit = 'day', numeric = 'auto' } = {}) => {
|
|
1915
|
+
const diff = Math.round((value - Date.now()) / (1000 * 60 * 60 * 24));
|
|
1916
|
+
return new Intl.RelativeTimeFormat(locale, { numeric }).format(diff, unit);
|
|
1917
|
+
},
|
|
1918
|
+
|
|
1919
|
+
plural: (value, locale, { singular, plural } = {}) => {
|
|
1920
|
+
const rule = new Intl.PluralRules(locale).select(value);
|
|
1921
|
+
return `${value} ${rule === 'one' ? singular : plural}`;
|
|
1922
|
+
},
|
|
1923
|
+
};
|
|
1924
|
+
|
|
1925
|
+
/**
|
|
1926
|
+
*
|
|
1927
|
+
* @param {*} value
|
|
1928
|
+
* @param {{ propagation: boolean, reset: boolean} | null} configs
|
|
1929
|
+
* @class ObservableItem
|
|
1930
|
+
*/
|
|
1931
|
+
function ObservableItem(value, configs = null) {
|
|
1932
|
+
value = Validator.isObservable(value) ? value.val() : value;
|
|
1933
|
+
|
|
1934
|
+
this.$previousValue = null;
|
|
1935
|
+
this.$currentValue = value;
|
|
1936
|
+
|
|
1937
|
+
this.$firstListener = null;
|
|
1938
|
+
this.$listeners = null;
|
|
1939
|
+
this.$watchers = null;
|
|
1940
|
+
|
|
1941
|
+
this.$memoryId = null;
|
|
1942
|
+
|
|
1943
|
+
if(configs) {
|
|
1944
|
+
this.configs = configs;
|
|
1945
|
+
if(configs.reset) {
|
|
1946
|
+
this.$initialValue = Validator.isObject(value) ? deepClone(value) : value;
|
|
1617
1947
|
}
|
|
1618
|
-
}
|
|
1948
|
+
}
|
|
1949
|
+
}
|
|
1619
1950
|
|
|
1620
|
-
|
|
1621
|
-
|
|
1622
|
-
|
|
1623
|
-
|
|
1624
|
-
|
|
1951
|
+
Object.defineProperty(ObservableItem.prototype, '$value', {
|
|
1952
|
+
get() {
|
|
1953
|
+
return this.$currentValue;
|
|
1954
|
+
},
|
|
1955
|
+
set(value) {
|
|
1956
|
+
this.set(value);
|
|
1957
|
+
},
|
|
1958
|
+
configurable: true,
|
|
1959
|
+
});
|
|
1960
|
+
|
|
1961
|
+
ObservableItem.prototype.__$isObservable = true;
|
|
1962
|
+
const noneTrigger = function() {};
|
|
1963
|
+
|
|
1964
|
+
/**
|
|
1965
|
+
* Intercepts and transforms values before they are set on the observable.
|
|
1966
|
+
* The interceptor can modify the value or return undefined to use the original value.
|
|
1967
|
+
*
|
|
1968
|
+
* @param {(value) => any} callback - Interceptor function that receives (newValue, currentValue) and returns the transformed value or undefined
|
|
1969
|
+
* @returns {ObservableItem} The observable instance for chaining
|
|
1970
|
+
* @example
|
|
1971
|
+
* const count = Observable(0);
|
|
1972
|
+
* count.intercept((newVal, oldVal) => Math.max(0, newVal)); // Prevent negative values
|
|
1973
|
+
*/
|
|
1974
|
+
ObservableItem.prototype.intercept = function(callback) {
|
|
1975
|
+
this.$interceptor = callback;
|
|
1976
|
+
this.set = this.$setWithInterceptor;
|
|
1977
|
+
return this;
|
|
1978
|
+
};
|
|
1979
|
+
|
|
1980
|
+
ObservableItem.prototype.triggerFirstListener = function(operations) {
|
|
1981
|
+
this.$firstListener(this.$currentValue, this.$previousValue, operations);
|
|
1982
|
+
};
|
|
1983
|
+
|
|
1984
|
+
ObservableItem.prototype.triggerListeners = function(operations) {
|
|
1985
|
+
const $listeners = this.$listeners;
|
|
1986
|
+
const $previousValue = this.$previousValue;
|
|
1987
|
+
const $currentValue = this.$currentValue;
|
|
1988
|
+
|
|
1989
|
+
for(let i = 0, length = $listeners.length; i < length; i++) {
|
|
1990
|
+
$listeners[i]($currentValue, $previousValue, operations);
|
|
1991
|
+
}
|
|
1992
|
+
};
|
|
1993
|
+
|
|
1994
|
+
ObservableItem.prototype.triggerWatchers = function(operations) {
|
|
1995
|
+
const $watchers = this.$watchers;
|
|
1996
|
+
const $previousValue = this.$previousValue;
|
|
1997
|
+
const $currentValue = this.$currentValue;
|
|
1998
|
+
|
|
1999
|
+
const $currentValueCallbacks = $watchers.get($currentValue);
|
|
2000
|
+
const $previousValueCallbacks = $watchers.get($previousValue);
|
|
2001
|
+
if($currentValueCallbacks) {
|
|
2002
|
+
$currentValueCallbacks(true, $previousValue, operations);
|
|
2003
|
+
}
|
|
2004
|
+
if($previousValueCallbacks) {
|
|
2005
|
+
$previousValueCallbacks(false, $currentValue, operations);
|
|
2006
|
+
}
|
|
2007
|
+
};
|
|
2008
|
+
|
|
2009
|
+
ObservableItem.prototype.triggerAll = function(operations) {
|
|
2010
|
+
this.triggerWatchers(operations);
|
|
2011
|
+
this.triggerListeners(operations);
|
|
2012
|
+
};
|
|
2013
|
+
|
|
2014
|
+
ObservableItem.prototype.triggerWatchersAndFirstListener = function(operations) {
|
|
2015
|
+
this.triggerWatchers(operations);
|
|
2016
|
+
this.triggerFirstListener(operations);
|
|
2017
|
+
};
|
|
2018
|
+
|
|
2019
|
+
ObservableItem.prototype.assocTrigger = function() {
|
|
2020
|
+
this.$firstListener = null;
|
|
2021
|
+
if(this.$watchers?.size && this.$listeners?.length) {
|
|
2022
|
+
this.trigger = (this.$listeners.length === 1) ? this.triggerWatchersAndFirstListener : this.triggerAll;
|
|
2023
|
+
return;
|
|
2024
|
+
}
|
|
2025
|
+
if(this.$listeners?.length) {
|
|
2026
|
+
if(this.$listeners.length === 1) {
|
|
2027
|
+
this.$firstListener = this.$listeners[0];
|
|
2028
|
+
this.trigger = this.triggerFirstListener;
|
|
2029
|
+
}
|
|
2030
|
+
else {
|
|
2031
|
+
this.trigger = this.triggerListeners;
|
|
2032
|
+
}
|
|
2033
|
+
return;
|
|
2034
|
+
}
|
|
2035
|
+
if(this.$watchers?.size) {
|
|
2036
|
+
this.trigger = this.triggerWatchers;
|
|
2037
|
+
return;
|
|
2038
|
+
}
|
|
2039
|
+
this.trigger = noneTrigger;
|
|
2040
|
+
};
|
|
2041
|
+
ObservableItem.prototype.trigger = noneTrigger;
|
|
2042
|
+
|
|
2043
|
+
ObservableItem.prototype.$updateWithNewValue = function(newValue) {
|
|
2044
|
+
newValue = newValue?.__$isObservable ? newValue.val() : newValue;
|
|
2045
|
+
if(this.$currentValue === newValue) {
|
|
2046
|
+
return;
|
|
2047
|
+
}
|
|
2048
|
+
this.$previousValue = this.$currentValue;
|
|
2049
|
+
this.$currentValue = newValue;
|
|
2050
|
+
this.trigger();
|
|
2051
|
+
this.$previousValue = null;
|
|
2052
|
+
};
|
|
2053
|
+
|
|
2054
|
+
/**
|
|
2055
|
+
* @param {*} data
|
|
2056
|
+
*/
|
|
2057
|
+
ObservableItem.prototype.$setWithInterceptor = function(data) {
|
|
2058
|
+
let newValue = (typeof data === 'function') ? data(this.$currentValue) : data;
|
|
2059
|
+
const result = this.$interceptor(newValue, this.$currentValue);
|
|
2060
|
+
|
|
2061
|
+
if (result !== undefined) {
|
|
2062
|
+
newValue = result;
|
|
2063
|
+
}
|
|
2064
|
+
|
|
2065
|
+
this.$updateWithNewValue(newValue);
|
|
2066
|
+
};
|
|
2067
|
+
|
|
2068
|
+
/**
|
|
2069
|
+
* @param {*} data
|
|
2070
|
+
*/
|
|
2071
|
+
ObservableItem.prototype.$basicSet = function(data) {
|
|
2072
|
+
let newValue = (typeof data === 'function') ? data(this.$currentValue) : data;
|
|
2073
|
+
this.$updateWithNewValue(newValue);
|
|
2074
|
+
};
|
|
2075
|
+
|
|
2076
|
+
ObservableItem.prototype.set = ObservableItem.prototype.$basicSet;
|
|
2077
|
+
|
|
2078
|
+
ObservableItem.prototype.val = function() {
|
|
2079
|
+
return this.$currentValue;
|
|
2080
|
+
};
|
|
2081
|
+
|
|
2082
|
+
ObservableItem.prototype.disconnectAll = function() {
|
|
2083
|
+
this.$listeners?.splice(0);
|
|
2084
|
+
this.$previousValue = null;
|
|
2085
|
+
this.$currentValue = null;
|
|
2086
|
+
if(this.$watchers) {
|
|
2087
|
+
for (const [_, watchValueList] of this.$watchers) {
|
|
2088
|
+
if(Validator.isArray(watchValueList)) {
|
|
2089
|
+
watchValueList.splice(0);
|
|
2090
|
+
}
|
|
2091
|
+
}
|
|
2092
|
+
}
|
|
2093
|
+
this.$watchers?.clear();
|
|
2094
|
+
this.$listeners = null;
|
|
2095
|
+
this.$watchers = null;
|
|
2096
|
+
this.trigger = noneTrigger;
|
|
2097
|
+
};
|
|
2098
|
+
|
|
2099
|
+
/**
|
|
2100
|
+
* Registers a cleanup callback that will be executed when the observable is cleaned up.
|
|
2101
|
+
* Useful for disposing resources, removing event listeners, or other cleanup tasks.
|
|
2102
|
+
*
|
|
2103
|
+
* @param {Function} callback - Cleanup function to execute on observable disposal
|
|
2104
|
+
* @example
|
|
2105
|
+
* const obs = Observable(0);
|
|
2106
|
+
* obs.onCleanup(() => console.log('Cleaned up!'));
|
|
2107
|
+
* obs.cleanup(); // Logs: "Cleaned up!"
|
|
2108
|
+
*/
|
|
2109
|
+
ObservableItem.prototype.onCleanup = function(callback) {
|
|
2110
|
+
this.$cleanupListeners = this.$cleanupListeners ?? [];
|
|
2111
|
+
this.$cleanupListeners.push(callback);
|
|
2112
|
+
};
|
|
2113
|
+
|
|
2114
|
+
ObservableItem.prototype.cleanup = function() {
|
|
2115
|
+
if (this.$cleanupListeners) {
|
|
2116
|
+
for (let i = 0; i < this.$cleanupListeners.length; i++) {
|
|
2117
|
+
this.$cleanupListeners[i]();
|
|
2118
|
+
}
|
|
2119
|
+
this.$cleanupListeners = null;
|
|
2120
|
+
}
|
|
2121
|
+
MemoryManager.unregister(this.$memoryId);
|
|
2122
|
+
this.disconnectAll();
|
|
2123
|
+
delete this.$value;
|
|
2124
|
+
};
|
|
2125
|
+
|
|
2126
|
+
/**
|
|
2127
|
+
*
|
|
2128
|
+
* @param {Function} callback
|
|
2129
|
+
* @returns {(function(): void)}
|
|
2130
|
+
*/
|
|
2131
|
+
ObservableItem.prototype.subscribe = function(callback) {
|
|
2132
|
+
this.$listeners = this.$listeners ?? [];
|
|
2133
|
+
|
|
2134
|
+
this.$listeners.push(callback);
|
|
2135
|
+
this.assocTrigger();
|
|
2136
|
+
};
|
|
2137
|
+
|
|
2138
|
+
/**
|
|
2139
|
+
* Watches for a specific value and executes callback when the observable equals that value.
|
|
2140
|
+
* Creates a watcher that only triggers when the observable changes to the specified value.
|
|
2141
|
+
*
|
|
2142
|
+
* @param {*} value - The value to watch for
|
|
2143
|
+
* @param {(value) => void|ObservableItem} callback - Callback function or observable to set when value matches
|
|
2144
|
+
* @example
|
|
2145
|
+
* const status = Observable('idle');
|
|
2146
|
+
* status.on('loading', () => console.log('Started loading'));
|
|
2147
|
+
* status.on('error', isError); // Set another observable
|
|
2148
|
+
*/
|
|
2149
|
+
ObservableItem.prototype.on = function(value, callback) {
|
|
2150
|
+
this.$watchers = this.$watchers ?? new Map();
|
|
2151
|
+
|
|
2152
|
+
let watchValueList = this.$watchers.get(value);
|
|
2153
|
+
|
|
2154
|
+
if(callback.__$isObservable) {
|
|
2155
|
+
callback = callback.set.bind(callback);
|
|
2156
|
+
}
|
|
2157
|
+
|
|
2158
|
+
if(!watchValueList) {
|
|
2159
|
+
watchValueList = callback;
|
|
2160
|
+
this.$watchers.set(value, callback);
|
|
2161
|
+
} else if(!Validator.isArray(watchValueList.list)) {
|
|
2162
|
+
watchValueList = [watchValueList, callback];
|
|
2163
|
+
callback = (value) => {
|
|
2164
|
+
for(let i = 0, length = watchValueList.length; i < length; i++) {
|
|
2165
|
+
watchValueList[i](value);
|
|
2166
|
+
}
|
|
2167
|
+
};
|
|
2168
|
+
callback.list = watchValueList;
|
|
2169
|
+
this.$watchers.set(value, callback);
|
|
2170
|
+
} else {
|
|
2171
|
+
watchValueList.list.push(callback);
|
|
2172
|
+
}
|
|
2173
|
+
|
|
2174
|
+
this.assocTrigger();
|
|
2175
|
+
};
|
|
2176
|
+
|
|
2177
|
+
/**
|
|
2178
|
+
* Removes a watcher for a specific value. If no callback is provided, removes all watchers for that value.
|
|
2179
|
+
*
|
|
2180
|
+
* @param {*} value - The value to stop watching
|
|
2181
|
+
* @param {Function} [callback] - Specific callback to remove. If omitted, removes all watchers for this value
|
|
2182
|
+
* @example
|
|
2183
|
+
* const status = Observable('idle');
|
|
2184
|
+
* const handler = () => console.log('Loading');
|
|
2185
|
+
* status.on('loading', handler);
|
|
2186
|
+
* status.off('loading', handler); // Remove specific handler
|
|
2187
|
+
* status.off('loading'); // Remove all handlers for 'loading'
|
|
2188
|
+
*/
|
|
2189
|
+
ObservableItem.prototype.off = function(value, callback) {
|
|
2190
|
+
if(!this.$watchers) return;
|
|
2191
|
+
|
|
2192
|
+
const watchValueList = this.$watchers.get(value);
|
|
2193
|
+
if(!watchValueList) return;
|
|
2194
|
+
|
|
2195
|
+
if(!callback || !Array.isArray(watchValueList.list)) {
|
|
2196
|
+
this.$watchers?.delete(value);
|
|
2197
|
+
this.assocTrigger();
|
|
2198
|
+
return;
|
|
2199
|
+
}
|
|
2200
|
+
const index = watchValueList.indexOf(callback);
|
|
2201
|
+
watchValueList?.splice(index, 1);
|
|
2202
|
+
if(watchValueList.length === 1) {
|
|
2203
|
+
this.$watchers.set(value, watchValueList[0]);
|
|
2204
|
+
}
|
|
2205
|
+
else if(watchValueList.length === 0) {
|
|
2206
|
+
this.$watchers?.delete(value);
|
|
2207
|
+
}
|
|
2208
|
+
this.assocTrigger();
|
|
2209
|
+
};
|
|
2210
|
+
|
|
2211
|
+
/**
|
|
2212
|
+
* Subscribes to the observable but automatically unsubscribes after the first time the predicate matches.
|
|
2213
|
+
*
|
|
2214
|
+
* @param {(value) => Boolean|any} predicate - Value to match or function that returns true when condition is met
|
|
2215
|
+
* @param {(value) => void} callback - Callback to execute when predicate matches, receives the matched value
|
|
2216
|
+
* @example
|
|
2217
|
+
* const status = Observable('loading');
|
|
2218
|
+
* status.once('ready', (val) => console.log('Ready!'));
|
|
2219
|
+
* status.once(val => val === 'error', (val) => console.log('Error occurred'));
|
|
2220
|
+
*/
|
|
2221
|
+
ObservableItem.prototype.once = function(predicate, callback) {
|
|
2222
|
+
const fn = typeof predicate === 'function' ? predicate : (v) => v === predicate;
|
|
2223
|
+
|
|
2224
|
+
const handler = (val) => {
|
|
2225
|
+
if (fn(val)) {
|
|
2226
|
+
this.unsubscribe(handler);
|
|
2227
|
+
callback(val);
|
|
2228
|
+
}
|
|
2229
|
+
};
|
|
2230
|
+
this.subscribe(handler);
|
|
2231
|
+
};
|
|
2232
|
+
|
|
2233
|
+
/**
|
|
2234
|
+
* Unsubscribe from an observable.
|
|
2235
|
+
* @param {Function} callback
|
|
2236
|
+
*/
|
|
2237
|
+
ObservableItem.prototype.unsubscribe = function(callback) {
|
|
2238
|
+
if(!this.$listeners) return;
|
|
2239
|
+
const index = this.$listeners.indexOf(callback);
|
|
2240
|
+
if (index > -1) {
|
|
2241
|
+
this.$listeners.splice(index, 1);
|
|
2242
|
+
}
|
|
2243
|
+
this.assocTrigger();
|
|
2244
|
+
};
|
|
2245
|
+
|
|
2246
|
+
/**
|
|
2247
|
+
* Create an Observable checker instance
|
|
2248
|
+
* @param callback
|
|
2249
|
+
* @returns {ObservableChecker}
|
|
2250
|
+
*/
|
|
2251
|
+
ObservableItem.prototype.check = function(callback) {
|
|
2252
|
+
return new ObservableChecker(this, callback)
|
|
2253
|
+
};
|
|
2254
|
+
|
|
2255
|
+
ObservableItem.prototype.transform = ObservableItem.prototype.check;
|
|
2256
|
+
ObservableItem.prototype.pluck = ObservableItem.prototype.check;
|
|
2257
|
+
ObservableItem.prototype.is = ObservableItem.prototype.check;
|
|
2258
|
+
ObservableItem.prototype.select = ObservableItem.prototype.check;
|
|
2259
|
+
|
|
2260
|
+
/**
|
|
2261
|
+
* Gets a property value from the observable's current value.
|
|
2262
|
+
* If the property is an observable, returns its value.
|
|
2263
|
+
*
|
|
2264
|
+
* @param {string|number} key - Property key to retrieve
|
|
2265
|
+
* @returns {*} The value of the property, unwrapped if it's an observable
|
|
2266
|
+
* @example
|
|
2267
|
+
* const user = Observable({ name: 'John', age: Observable(25) });
|
|
2268
|
+
* user.get('name'); // 'John'
|
|
2269
|
+
* user.get('age'); // 25 (unwrapped from observable)
|
|
2270
|
+
*/
|
|
2271
|
+
ObservableItem.prototype.get = function(key) {
|
|
2272
|
+
const item = this.$currentValue[key];
|
|
2273
|
+
return Validator.isObservable(item) ? item.val() : item;
|
|
2274
|
+
};
|
|
2275
|
+
|
|
2276
|
+
/**
|
|
2277
|
+
* Creates an ObservableWhen that represents whether the observable equals a specific value.
|
|
2278
|
+
* Returns an object that can be subscribed to and will emit true/false.
|
|
2279
|
+
*
|
|
2280
|
+
* @param {*} value - The value to compare against
|
|
2281
|
+
* @returns {ObservableWhen} An ObservableWhen instance that tracks when the observable equals the value
|
|
2282
|
+
* @example
|
|
2283
|
+
* const status = Observable('idle');
|
|
2284
|
+
* const isLoading = status.when('loading');
|
|
2285
|
+
* isLoading.subscribe(active => console.log('Loading:', active));
|
|
2286
|
+
* status.set('loading'); // Logs: "Loading: true"
|
|
2287
|
+
*/
|
|
2288
|
+
ObservableItem.prototype.when = function(value) {
|
|
2289
|
+
return new ObservableWhen(this, value);
|
|
2290
|
+
};
|
|
2291
|
+
|
|
2292
|
+
/**
|
|
2293
|
+
* Compares the observable's current value with another value or observable.
|
|
2294
|
+
*
|
|
2295
|
+
* @param {*|ObservableItem} other - Value or observable to compare against
|
|
2296
|
+
* @returns {boolean} True if values are equal
|
|
2297
|
+
* @example
|
|
2298
|
+
* const a = Observable(5);
|
|
2299
|
+
* const b = Observable(5);
|
|
2300
|
+
* a.equals(5); // true
|
|
2301
|
+
* a.equals(b); // true
|
|
2302
|
+
* a.equals(10); // false
|
|
2303
|
+
*/
|
|
2304
|
+
ObservableItem.prototype.equals = function(other) {
|
|
2305
|
+
if(Validator.isObservable(other)) {
|
|
2306
|
+
return this.$currentValue === other.$currentValue;
|
|
2307
|
+
}
|
|
2308
|
+
return this.$currentValue === other;
|
|
2309
|
+
};
|
|
2310
|
+
|
|
2311
|
+
/**
|
|
2312
|
+
* Converts the observable's current value to a boolean.
|
|
2313
|
+
*
|
|
2314
|
+
* @returns {boolean} The boolean representation of the current value
|
|
2315
|
+
* @example
|
|
2316
|
+
* const count = Observable(0);
|
|
2317
|
+
* count.toBool(); // false
|
|
2318
|
+
* count.set(5);
|
|
2319
|
+
* count.toBool(); // true
|
|
2320
|
+
*/
|
|
2321
|
+
ObservableItem.prototype.toBool = function() {
|
|
2322
|
+
return !!this.$currentValue;
|
|
2323
|
+
};
|
|
1625
2324
|
|
|
1626
|
-
|
|
1627
|
-
|
|
1628
|
-
|
|
1629
|
-
|
|
1630
|
-
|
|
1631
|
-
|
|
1632
|
-
|
|
1633
|
-
|
|
1634
|
-
|
|
1635
|
-
|
|
1636
|
-
|
|
1637
|
-
parent.insertBefore(childElement, anchorEnd);
|
|
1638
|
-
};
|
|
2325
|
+
/**
|
|
2326
|
+
* Toggles the boolean value of the observable (false becomes true, true becomes false).
|
|
2327
|
+
*
|
|
2328
|
+
* @example
|
|
2329
|
+
* const isOpen = Observable(false);
|
|
2330
|
+
* isOpen.toggle(); // Now true
|
|
2331
|
+
* isOpen.toggle(); // Now false
|
|
2332
|
+
*/
|
|
2333
|
+
ObservableItem.prototype.toggle = function() {
|
|
2334
|
+
this.set(!this.$currentValue);
|
|
2335
|
+
};
|
|
1639
2336
|
|
|
1640
|
-
|
|
2337
|
+
/**
|
|
2338
|
+
* Resets the observable to its initial value.
|
|
2339
|
+
* Only works if the observable was created with { reset: true } config.
|
|
2340
|
+
*
|
|
2341
|
+
* @example
|
|
2342
|
+
* const count = Observable(0, { reset: true });
|
|
2343
|
+
* count.set(10);
|
|
2344
|
+
* count.reset(); // Back to 0
|
|
2345
|
+
*/
|
|
2346
|
+
ObservableItem.prototype.reset = function() {
|
|
2347
|
+
if(!this.configs?.reset) {
|
|
2348
|
+
return;
|
|
2349
|
+
}
|
|
2350
|
+
const resetValue = (Validator.isObject(this.$initialValue))
|
|
2351
|
+
? deepClone(this.$initialValue, (observable) => {
|
|
2352
|
+
observable.reset();
|
|
2353
|
+
})
|
|
2354
|
+
: this.$initialValue;
|
|
2355
|
+
this.set(resetValue);
|
|
2356
|
+
};
|
|
1641
2357
|
|
|
1642
|
-
|
|
1643
|
-
|
|
1644
|
-
|
|
2358
|
+
/**
|
|
2359
|
+
* Returns a string representation of the observable's current value.
|
|
2360
|
+
*
|
|
2361
|
+
* @returns {string} String representation of the current value
|
|
2362
|
+
*/
|
|
2363
|
+
ObservableItem.prototype.toString = function() {
|
|
2364
|
+
return String(this.$currentValue);
|
|
2365
|
+
};
|
|
1645
2366
|
|
|
2367
|
+
/**
|
|
2368
|
+
* Returns the primitive value of the observable (its current value).
|
|
2369
|
+
* Called automatically in type coercion contexts.
|
|
2370
|
+
*
|
|
2371
|
+
* @returns {*} The current value of the observable
|
|
2372
|
+
*/
|
|
2373
|
+
ObservableItem.prototype.valueOf = function() {
|
|
2374
|
+
return this.$currentValue;
|
|
2375
|
+
};
|
|
1646
2376
|
|
|
1647
|
-
anchorFragment.endElement = function() {
|
|
1648
|
-
return anchorEnd;
|
|
1649
|
-
};
|
|
1650
2377
|
|
|
1651
|
-
|
|
1652
|
-
|
|
1653
|
-
|
|
1654
|
-
|
|
1655
|
-
|
|
1656
|
-
|
|
1657
|
-
|
|
1658
|
-
|
|
2378
|
+
/**
|
|
2379
|
+
* Creates a derived observable that formats the current value using Intl.
|
|
2380
|
+
* Automatically reacts to both value changes and locale changes (Store.__nd.locale).
|
|
2381
|
+
*
|
|
2382
|
+
* @param {string | Function} type - Format type or custom formatter function
|
|
2383
|
+
* @param {Object} [options={}] - Options passed to the formatter
|
|
2384
|
+
* @returns {ObservableItem<string>}
|
|
2385
|
+
*
|
|
2386
|
+
* @example
|
|
2387
|
+
* // Currency
|
|
2388
|
+
* price.format('currency') // "15 000 FCFA"
|
|
2389
|
+
* price.format('currency', { currency: 'EUR' }) // "15 000,00 €"
|
|
2390
|
+
* price.format('currency', { notation: 'compact' }) // "15 K FCFA"
|
|
2391
|
+
*
|
|
2392
|
+
* // Number
|
|
2393
|
+
* count.format('number') // "15 000"
|
|
2394
|
+
*
|
|
2395
|
+
* // Percent
|
|
2396
|
+
* rate.format('percent') // "15,0 %"
|
|
2397
|
+
* rate.format('percent', { decimals: 2 }) // "15,00 %"
|
|
2398
|
+
*
|
|
2399
|
+
* // Date
|
|
2400
|
+
* date.format('date') // "3 mars 2026"
|
|
2401
|
+
* date.format('date', { dateStyle: 'full' }) // "mardi 3 mars 2026"
|
|
2402
|
+
* date.format('date', { format: 'DD/MM/YYYY' }) // "03/03/2026"
|
|
2403
|
+
* date.format('date', { format: 'DD MMM YYYY' }) // "03 mar 2026"
|
|
2404
|
+
* date.format('date', { format: 'DD MMMM YYYY' }) // "03 mars 2026"
|
|
2405
|
+
*
|
|
2406
|
+
* // Time
|
|
2407
|
+
* date.format('time') // "20:30"
|
|
2408
|
+
* date.format('time', { second: '2-digit' }) // "20:30:00"
|
|
2409
|
+
* date.format('time', { format: 'HH:mm:ss' }) // "20:30:00"
|
|
2410
|
+
*
|
|
2411
|
+
* // Datetime
|
|
2412
|
+
* date.format('datetime') // "3 mars 2026, 20:30"
|
|
2413
|
+
* date.format('datetime', { dateStyle: 'full' }) // "mardi 3 mars 2026, 20:30"
|
|
2414
|
+
* date.format('datetime', { format: 'DD/MM/YYYY HH:mm' }) // "03/03/2026 20:30"
|
|
2415
|
+
*
|
|
2416
|
+
* // Relative
|
|
2417
|
+
* date.format('relative') // "dans 11 jours"
|
|
2418
|
+
* date.format('relative', { unit: 'month' }) // "dans 1 mois"
|
|
2419
|
+
*
|
|
2420
|
+
* // Plural
|
|
2421
|
+
* count.format('plural', { singular: 'billet', plural: 'billets' }) // "3 billets"
|
|
2422
|
+
*
|
|
2423
|
+
* // Custom formatter
|
|
2424
|
+
* price.format(value => `${value.toLocaleString()} FCFA`)
|
|
2425
|
+
*
|
|
2426
|
+
* // Reacts to locale changes automatically
|
|
2427
|
+
* Store.setLocale('en-US');
|
|
2428
|
+
*/
|
|
2429
|
+
ObservableItem.prototype.format = function(type, options = {}) {
|
|
2430
|
+
const self = this;
|
|
1659
2431
|
|
|
1660
|
-
|
|
1661
|
-
|
|
1662
|
-
|
|
1663
|
-
if(!currentNode.nextSibling) {
|
|
1664
|
-
return null;
|
|
1665
|
-
}
|
|
1666
|
-
currentNode = currentNode.nextSibling;
|
|
1667
|
-
}
|
|
1668
|
-
return currentNode !== anchorStart ? currentNode : null;
|
|
1669
|
-
};
|
|
2432
|
+
if (typeof type === 'function') {
|
|
2433
|
+
return new ObservableChecker(self, type);
|
|
2434
|
+
}
|
|
1670
2435
|
|
|
1671
|
-
|
|
1672
|
-
|
|
1673
|
-
DocumentFragment.prototype.setAttribute = () => {};
|
|
2436
|
+
const formatter = Formatters[type];
|
|
2437
|
+
const localeObservable = Store.follow('locale');
|
|
1674
2438
|
|
|
1675
|
-
|
|
1676
|
-
|
|
1677
|
-
|
|
1678
|
-
|
|
1679
|
-
|
|
1680
|
-
|
|
1681
|
-
|
|
1682
|
-
|
|
1683
|
-
|
|
1684
|
-
|
|
1685
|
-
|
|
1686
|
-
|
|
1687
|
-
|
|
1688
|
-
|
|
1689
|
-
|
|
1690
|
-
|
|
1691
|
-
|
|
1692
|
-
'controls',
|
|
1693
|
-
'loop',
|
|
1694
|
-
'muted',
|
|
1695
|
-
'download',
|
|
1696
|
-
'reversed',
|
|
1697
|
-
'open',
|
|
1698
|
-
'default',
|
|
1699
|
-
'formnovalidate',
|
|
1700
|
-
'novalidate',
|
|
1701
|
-
'scoped',
|
|
1702
|
-
'itemscope',
|
|
1703
|
-
'allowfullscreen',
|
|
1704
|
-
'allowpaymentrequest',
|
|
1705
|
-
'playsinline'
|
|
1706
|
-
]);
|
|
2439
|
+
return Observable$1.computed(() => formatter(self.val(), localeObservable.val(), options),
|
|
2440
|
+
[self, localeObservable]
|
|
2441
|
+
);
|
|
2442
|
+
};
|
|
2443
|
+
|
|
2444
|
+
ObservableItem.prototype.persist = function(key, options = {}) {
|
|
2445
|
+
let value = $getFromStorage$1(key, this.$currentValue);
|
|
2446
|
+
if(options.get) {
|
|
2447
|
+
value = options.get(value);
|
|
2448
|
+
}
|
|
2449
|
+
this.set(value);
|
|
2450
|
+
const saver = $saveToStorage$1(this.$currentValue);
|
|
2451
|
+
this.subscribe((newValue) => {
|
|
2452
|
+
saver(key, options.set ? options.set(newValue) : newValue);
|
|
2453
|
+
});
|
|
2454
|
+
return this;
|
|
2455
|
+
};
|
|
1707
2456
|
|
|
1708
2457
|
/**
|
|
1709
2458
|
*
|
|
@@ -1712,17 +2461,17 @@ var NativeComponents = (function (exports) {
|
|
|
1712
2461
|
* @returns {ObservableItem}
|
|
1713
2462
|
* @constructor
|
|
1714
2463
|
*/
|
|
1715
|
-
function Observable(value, configs = null) {
|
|
2464
|
+
function Observable$1(value, configs = null) {
|
|
1716
2465
|
return new ObservableItem(value, configs);
|
|
1717
2466
|
}
|
|
1718
2467
|
|
|
1719
|
-
const $$1 = Observable;
|
|
2468
|
+
const $$1 = Observable$1;
|
|
1720
2469
|
|
|
1721
2470
|
/**
|
|
1722
2471
|
*
|
|
1723
2472
|
* @param {string} propertyName
|
|
1724
2473
|
*/
|
|
1725
|
-
Observable.useValueProperty = function(propertyName = 'value') {
|
|
2474
|
+
Observable$1.useValueProperty = function(propertyName = 'value') {
|
|
1726
2475
|
Object.defineProperty(ObservableItem.prototype, propertyName, {
|
|
1727
2476
|
get() {
|
|
1728
2477
|
return this.$currentValue;
|
|
@@ -1740,7 +2489,7 @@ var NativeComponents = (function (exports) {
|
|
|
1740
2489
|
* @param id
|
|
1741
2490
|
* @returns {ObservableItem|null}
|
|
1742
2491
|
*/
|
|
1743
|
-
Observable.getById = function(id) {
|
|
2492
|
+
Observable$1.getById = function(id) {
|
|
1744
2493
|
const item = MemoryManager.getObservableById(parseInt(id));
|
|
1745
2494
|
if(!item) {
|
|
1746
2495
|
throw new NativeDocumentError('Observable.getById : No observable found with id ' + id);
|
|
@@ -1752,7 +2501,7 @@ var NativeComponents = (function (exports) {
|
|
|
1752
2501
|
*
|
|
1753
2502
|
* @param {ObservableItem} observable
|
|
1754
2503
|
*/
|
|
1755
|
-
Observable.cleanup = function(observable) {
|
|
2504
|
+
Observable$1.cleanup = function(observable) {
|
|
1756
2505
|
observable.cleanup();
|
|
1757
2506
|
};
|
|
1758
2507
|
|
|
@@ -1761,7 +2510,7 @@ var NativeComponents = (function (exports) {
|
|
|
1761
2510
|
* @param {Boolean} enable
|
|
1762
2511
|
* @param {{interval:Boolean, threshold:number}} options
|
|
1763
2512
|
*/
|
|
1764
|
-
Observable.autoCleanup = function(enable = false, options = {}) {
|
|
2513
|
+
Observable$1.autoCleanup = function(enable = false, options = {}) {
|
|
1765
2514
|
if(!enable) {
|
|
1766
2515
|
return;
|
|
1767
2516
|
}
|
|
@@ -1953,6 +2702,88 @@ var NativeComponents = (function (exports) {
|
|
|
1953
2702
|
return ElementCreator.getChild(child());
|
|
1954
2703
|
};
|
|
1955
2704
|
|
|
2705
|
+
/**
|
|
2706
|
+
* @param {HTMLElement} el
|
|
2707
|
+
* @param {number} timeout
|
|
2708
|
+
*/
|
|
2709
|
+
const waitForVisualEnd = (el, timeout = 1000) => {
|
|
2710
|
+
return new Promise((resolve) => {
|
|
2711
|
+
let isResolved = false;
|
|
2712
|
+
|
|
2713
|
+
const cleanupAndResolve = (e) => {
|
|
2714
|
+
if (e && e.target !== el) return;
|
|
2715
|
+
if (isResolved) return;
|
|
2716
|
+
|
|
2717
|
+
isResolved = true;
|
|
2718
|
+
el.removeEventListener('transitionend', cleanupAndResolve);
|
|
2719
|
+
el.removeEventListener('animationend', cleanupAndResolve);
|
|
2720
|
+
clearTimeout(timer);
|
|
2721
|
+
resolve();
|
|
2722
|
+
};
|
|
2723
|
+
|
|
2724
|
+
el.addEventListener('transitionend', cleanupAndResolve);
|
|
2725
|
+
el.addEventListener('animationend', cleanupAndResolve);
|
|
2726
|
+
|
|
2727
|
+
const timer = setTimeout(cleanupAndResolve, timeout);
|
|
2728
|
+
|
|
2729
|
+
const style = window.getComputedStyle(el);
|
|
2730
|
+
const hasTransition = style.transitionDuration !== '0s';
|
|
2731
|
+
const hasAnimation = style.animationDuration !== '0s';
|
|
2732
|
+
|
|
2733
|
+
if (!hasTransition && !hasAnimation) {
|
|
2734
|
+
cleanupAndResolve();
|
|
2735
|
+
}
|
|
2736
|
+
});
|
|
2737
|
+
};
|
|
2738
|
+
|
|
2739
|
+
NDElement.prototype.transitionOut = function(transitionName) {
|
|
2740
|
+
const exitClass = transitionName + '-exit';
|
|
2741
|
+
this.beforeUnmount('transition-exit', async function() {
|
|
2742
|
+
this.$element.classes.add(exitClass);
|
|
2743
|
+
await waitForVisualEnd(this.$element);
|
|
2744
|
+
this.$element.classes.remove(exitClass);
|
|
2745
|
+
});
|
|
2746
|
+
return this;
|
|
2747
|
+
};
|
|
2748
|
+
|
|
2749
|
+
NDElement.prototype.transitionIn = function(transitionName) {
|
|
2750
|
+
const startClass = transitionName + '-enter-from';
|
|
2751
|
+
const endClass = transitionName + '-enter-to';
|
|
2752
|
+
|
|
2753
|
+
this.$element.classes.add(startClass);
|
|
2754
|
+
|
|
2755
|
+
this.mounted(() => {
|
|
2756
|
+
requestAnimationFrame(() => {
|
|
2757
|
+
requestAnimationFrame(() => {
|
|
2758
|
+
this.$element.classes.remove(startClass);
|
|
2759
|
+
this.$element.classes.add(endClass);
|
|
2760
|
+
|
|
2761
|
+
waitForVisualEnd(this.$element).then(() => {
|
|
2762
|
+
this.$element.classes.remove(endClass);
|
|
2763
|
+
});
|
|
2764
|
+
});
|
|
2765
|
+
});
|
|
2766
|
+
});
|
|
2767
|
+
return this;
|
|
2768
|
+
};
|
|
2769
|
+
|
|
2770
|
+
|
|
2771
|
+
NDElement.prototype.transition = function (transitionName) {
|
|
2772
|
+
this.transitionIn(transitionName);
|
|
2773
|
+
this.transitionOut(transitionName);
|
|
2774
|
+
return this;
|
|
2775
|
+
};
|
|
2776
|
+
|
|
2777
|
+
NDElement.prototype.animate = function(animationName) {
|
|
2778
|
+
this.$element.classes.add(animationName);
|
|
2779
|
+
|
|
2780
|
+
waitForVisualEnd(this.$element).then(() => {
|
|
2781
|
+
this.$element.classes.remove(animationName);
|
|
2782
|
+
});
|
|
2783
|
+
|
|
2784
|
+
return this;
|
|
2785
|
+
};
|
|
2786
|
+
|
|
1956
2787
|
String.prototype.handleNdAttribute = function(element, attributeName) {
|
|
1957
2788
|
element.setAttribute(attributeName, this);
|
|
1958
2789
|
};
|
|
@@ -2050,6 +2881,10 @@ var NativeComponents = (function (exports) {
|
|
|
2050
2881
|
parent.appendChild(child);
|
|
2051
2882
|
}
|
|
2052
2883
|
},
|
|
2884
|
+
async safeRemove(element) {
|
|
2885
|
+
await element.remove();
|
|
2886
|
+
|
|
2887
|
+
},
|
|
2053
2888
|
getChild(child) {
|
|
2054
2889
|
if(child == null) {
|
|
2055
2890
|
return null;
|
|
@@ -2390,10 +3225,10 @@ var NativeComponents = (function (exports) {
|
|
|
2390
3225
|
/**
|
|
2391
3226
|
*
|
|
2392
3227
|
* @param {string} name
|
|
2393
|
-
* @param {?Function} customWrapper
|
|
3228
|
+
* @param {?Function=} customWrapper
|
|
2394
3229
|
* @returns {Function}
|
|
2395
3230
|
*/
|
|
2396
|
-
function HtmlElementWrapper(name, customWrapper) {
|
|
3231
|
+
function HtmlElementWrapper(name, customWrapper = null) {
|
|
2397
3232
|
return createHtmlElement.bind(null, name, customWrapper);
|
|
2398
3233
|
}
|
|
2399
3234
|
|
|
@@ -2433,7 +3268,7 @@ var NativeComponents = (function (exports) {
|
|
|
2433
3268
|
String.prototype.use = function(args) {
|
|
2434
3269
|
const value = this;
|
|
2435
3270
|
|
|
2436
|
-
return Observable.computed(() => {
|
|
3271
|
+
return Observable$1.computed(() => {
|
|
2437
3272
|
return value.replace(/\$\{(.*?)}/g, (match, key) => {
|
|
2438
3273
|
const data = args[key];
|
|
2439
3274
|
if(Validator.isObservable(data)) {
|
|
@@ -2453,7 +3288,7 @@ var NativeComponents = (function (exports) {
|
|
|
2453
3288
|
return value;
|
|
2454
3289
|
}
|
|
2455
3290
|
const [_, id] = value.match(/\{\{#ObItem::\(([0-9]+)\)\}\}/);
|
|
2456
|
-
return Observable.getById(id);
|
|
3291
|
+
return Observable$1.getById(id);
|
|
2457
3292
|
});
|
|
2458
3293
|
};
|
|
2459
3294
|
|
|
@@ -2726,7 +3561,7 @@ var NativeComponents = (function (exports) {
|
|
|
2726
3561
|
}
|
|
2727
3562
|
}
|
|
2728
3563
|
|
|
2729
|
-
const viewArray = Observable.array();
|
|
3564
|
+
const viewArray = Observable$1.array();
|
|
2730
3565
|
|
|
2731
3566
|
const filters = Object.entries(filterCallbacks);
|
|
2732
3567
|
const updateView = () => {
|
|
@@ -2812,7 +3647,7 @@ var NativeComponents = (function (exports) {
|
|
|
2812
3647
|
* items.push(4); // Triggers update
|
|
2813
3648
|
* items.subscribe((arr) => console.log(arr));
|
|
2814
3649
|
*/
|
|
2815
|
-
Observable.array = function(target = [], configs = null) {
|
|
3650
|
+
Observable$1.array = function(target = [], configs = null) {
|
|
2816
3651
|
return new ObservableArray(target, configs);
|
|
2817
3652
|
};
|
|
2818
3653
|
|
|
@@ -2821,8 +3656,8 @@ var NativeComponents = (function (exports) {
|
|
|
2821
3656
|
* @param {Function} callback
|
|
2822
3657
|
* @returns {Function}
|
|
2823
3658
|
*/
|
|
2824
|
-
Observable.batch = function(callback) {
|
|
2825
|
-
const $observer = Observable(0);
|
|
3659
|
+
Observable$1.batch = function(callback) {
|
|
3660
|
+
const $observer = Observable$1(0);
|
|
2826
3661
|
const batch = function() {
|
|
2827
3662
|
if(Validator.isAsyncFunction(callback)) {
|
|
2828
3663
|
return (callback(...arguments)).then(() => {
|
|
@@ -2836,10 +3671,71 @@ var NativeComponents = (function (exports) {
|
|
|
2836
3671
|
return batch;
|
|
2837
3672
|
};
|
|
2838
3673
|
|
|
2839
|
-
const
|
|
3674
|
+
const ObservableObject = function(target, configs) {
|
|
3675
|
+
ObservableItem.call(this, target);
|
|
3676
|
+
this.$observables = {};
|
|
3677
|
+
this.configs = configs;
|
|
3678
|
+
|
|
3679
|
+
this.$load(target);
|
|
3680
|
+
|
|
3681
|
+
for(const name in target) {
|
|
3682
|
+
if(!Object.hasOwn(this, name)) {
|
|
3683
|
+
Object.defineProperty(this, name, {
|
|
3684
|
+
get: () => this.$observables[name],
|
|
3685
|
+
set: (value) => this.$observables[name].set(value)
|
|
3686
|
+
});
|
|
3687
|
+
}
|
|
3688
|
+
}
|
|
3689
|
+
|
|
3690
|
+
};
|
|
3691
|
+
|
|
3692
|
+
ObservableObject.prototype = Object.create(ObservableItem.prototype);
|
|
3693
|
+
|
|
3694
|
+
Object.defineProperty(ObservableObject, '$value', {
|
|
3695
|
+
get() {
|
|
3696
|
+
return this.val();
|
|
3697
|
+
},
|
|
3698
|
+
set(value) {
|
|
3699
|
+
this.set(value);
|
|
3700
|
+
}
|
|
3701
|
+
});
|
|
3702
|
+
|
|
3703
|
+
ObservableObject.prototype.__$isObservableObject = true;
|
|
3704
|
+
ObservableObject.prototype.__isProxy__ = true;
|
|
3705
|
+
|
|
3706
|
+
ObservableObject.prototype.$load = function(initialValue) {
|
|
3707
|
+
const configs = this.configs;
|
|
3708
|
+
for(const key in initialValue) {
|
|
3709
|
+
const itemValue = initialValue[key];
|
|
3710
|
+
if(Array.isArray(itemValue)) {
|
|
3711
|
+
if(configs?.deep !== false) {
|
|
3712
|
+
const mappedItemValue = itemValue.map(item => {
|
|
3713
|
+
if(Validator.isJson(item)) {
|
|
3714
|
+
return Observable$1.json(item, configs);
|
|
3715
|
+
}
|
|
3716
|
+
if(Validator.isArray(item)) {
|
|
3717
|
+
return Observable$1.array(item, configs);
|
|
3718
|
+
}
|
|
3719
|
+
return Observable$1(item, configs);
|
|
3720
|
+
});
|
|
3721
|
+
this.$observables[key] = Observable$1.array(mappedItemValue, configs);
|
|
3722
|
+
continue;
|
|
3723
|
+
}
|
|
3724
|
+
this.$observables[key] = Observable$1.array(itemValue, configs);
|
|
3725
|
+
continue;
|
|
3726
|
+
}
|
|
3727
|
+
if(Validator.isObservable(itemValue) || Validator.isProxy(itemValue)) {
|
|
3728
|
+
this.$observables[key] = itemValue;
|
|
3729
|
+
continue;
|
|
3730
|
+
}
|
|
3731
|
+
this.$observables[key] = Observable$1(itemValue, configs);
|
|
3732
|
+
}
|
|
3733
|
+
};
|
|
3734
|
+
|
|
3735
|
+
ObservableObject.prototype.val = function() {
|
|
2840
3736
|
const result = {};
|
|
2841
|
-
for(const key in
|
|
2842
|
-
const dataItem =
|
|
3737
|
+
for(const key in this.$observables) {
|
|
3738
|
+
const dataItem = this.$observables[key];
|
|
2843
3739
|
if(Validator.isObservable(dataItem)) {
|
|
2844
3740
|
let value = dataItem.val();
|
|
2845
3741
|
if(Array.isArray(value)) {
|
|
@@ -2862,9 +3758,10 @@ var NativeComponents = (function (exports) {
|
|
|
2862
3758
|
}
|
|
2863
3759
|
return result;
|
|
2864
3760
|
};
|
|
3761
|
+
ObservableObject.prototype.$val = ObservableObject.prototype.val;
|
|
2865
3762
|
|
|
2866
|
-
|
|
2867
|
-
const item =
|
|
3763
|
+
ObservableObject.prototype.get = function(property) {
|
|
3764
|
+
const item = this.$observables[property];
|
|
2868
3765
|
if(Validator.isObservable(item)) {
|
|
2869
3766
|
return item.val();
|
|
2870
3767
|
}
|
|
@@ -2873,100 +3770,87 @@ var NativeComponents = (function (exports) {
|
|
|
2873
3770
|
}
|
|
2874
3771
|
return item;
|
|
2875
3772
|
};
|
|
3773
|
+
ObservableObject.prototype.$get = ObservableObject.prototype.get;
|
|
2876
3774
|
|
|
2877
|
-
|
|
2878
|
-
|
|
2879
|
-
|
|
2880
|
-
|
|
2881
|
-
|
|
2882
|
-
|
|
2883
|
-
|
|
2884
|
-
|
|
2885
|
-
|
|
2886
|
-
|
|
2887
|
-
|
|
2888
|
-
|
|
2889
|
-
|
|
2890
|
-
|
|
2891
|
-
|
|
2892
|
-
|
|
2893
|
-
|
|
2894
|
-
|
|
2895
|
-
|
|
2896
|
-
* user.name = 'Jane X'
|
|
2897
|
-
* user.age.subscribe(val => console.log('Age:', val));
|
|
2898
|
-
*/
|
|
2899
|
-
Observable.init = function(initialValue, configs = null) {
|
|
2900
|
-
const data = {};
|
|
2901
|
-
for(const key in initialValue) {
|
|
2902
|
-
const itemValue = initialValue[key];
|
|
2903
|
-
if(Array.isArray(itemValue)) {
|
|
2904
|
-
if(configs?.deep !== false) {
|
|
2905
|
-
const mappedItemValue = itemValue.map(item => {
|
|
2906
|
-
if(Validator.isJson(item)) {
|
|
2907
|
-
return Observable.json(item, configs);
|
|
2908
|
-
}
|
|
2909
|
-
if(Validator.isArray(item)) {
|
|
2910
|
-
return Observable.array(item, configs);
|
|
3775
|
+
ObservableObject.prototype.set = function(newData) {
|
|
3776
|
+
const data = Validator.isProxy(newData) ? newData.$value : newData;
|
|
3777
|
+
const configs = this.configs;
|
|
3778
|
+
|
|
3779
|
+
for(const key in data) {
|
|
3780
|
+
const targetItem = this.$observables[key];
|
|
3781
|
+
const newValueOrigin = newData[key];
|
|
3782
|
+
const newValue = data[key];
|
|
3783
|
+
|
|
3784
|
+
if(Validator.isObservable(targetItem)) {
|
|
3785
|
+
if(!Validator.isArray(newValue)) {
|
|
3786
|
+
targetItem.set(newValue);
|
|
3787
|
+
continue;
|
|
3788
|
+
}
|
|
3789
|
+
const firstElementFromOriginalValue = newValueOrigin.at(0);
|
|
3790
|
+
if(Validator.isObservable(firstElementFromOriginalValue) || Validator.isProxy(firstElementFromOriginalValue)) {
|
|
3791
|
+
const newValues = newValue.map(item => {
|
|
3792
|
+
if(Validator.isProxy(firstElementFromOriginalValue)) {
|
|
3793
|
+
return Observable$1.init(item, configs);
|
|
2911
3794
|
}
|
|
2912
|
-
return Observable(item, configs);
|
|
3795
|
+
return Observable$1(item, configs);
|
|
2913
3796
|
});
|
|
2914
|
-
|
|
3797
|
+
targetItem.set(newValues);
|
|
2915
3798
|
continue;
|
|
2916
3799
|
}
|
|
2917
|
-
|
|
3800
|
+
targetItem.set([...newValue]);
|
|
2918
3801
|
continue;
|
|
2919
3802
|
}
|
|
2920
|
-
if(Validator.
|
|
2921
|
-
|
|
3803
|
+
if(Validator.isProxy(targetItem)) {
|
|
3804
|
+
targetItem.update(newValue);
|
|
2922
3805
|
continue;
|
|
2923
3806
|
}
|
|
2924
|
-
|
|
3807
|
+
this[key] = newValue;
|
|
2925
3808
|
}
|
|
3809
|
+
};
|
|
3810
|
+
ObservableObject.prototype.$set = ObservableObject.prototype.set;
|
|
3811
|
+
ObservableObject.prototype.$updateWith = ObservableObject.prototype.set;
|
|
2926
3812
|
|
|
2927
|
-
|
|
2928
|
-
|
|
2929
|
-
|
|
2930
|
-
|
|
2931
|
-
}
|
|
2932
|
-
};
|
|
3813
|
+
ObservableObject.prototype.observables = function() {
|
|
3814
|
+
return Object.values(this.$observables);
|
|
3815
|
+
};
|
|
3816
|
+
ObservableObject.prototype.$observables = ObservableObject.prototype.observables;
|
|
2933
3817
|
|
|
2934
|
-
|
|
3818
|
+
ObservableObject.prototype.keys = function() {
|
|
3819
|
+
return Object.keys(this.$observables);
|
|
3820
|
+
};
|
|
3821
|
+
ObservableObject.prototype.$keys = ObservableObject.prototype.keys;
|
|
3822
|
+
ObservableObject.prototype.clone = function() {
|
|
3823
|
+
return Observable$1.init(this.val(), this.configs);
|
|
3824
|
+
};
|
|
3825
|
+
ObservableObject.prototype.$clone = ObservableObject.prototype.clone;
|
|
3826
|
+
ObservableObject.prototype.reset = function() {
|
|
3827
|
+
for(const key in this.$observables) {
|
|
3828
|
+
this.$observables[key].reset();
|
|
3829
|
+
}
|
|
3830
|
+
};
|
|
3831
|
+
ObservableObject.prototype.originalSubscribe = ObservableObject.prototype.subscribe;
|
|
3832
|
+
ObservableObject.prototype.subscribe = function(callback) {
|
|
3833
|
+
const observables = this.observables();
|
|
3834
|
+
const updatedValue = nextTick(() => {
|
|
3835
|
+
this.$currentValue = this.val();
|
|
3836
|
+
this.trigger();
|
|
3837
|
+
});
|
|
2935
3838
|
|
|
2936
|
-
|
|
3839
|
+
this.originalSubscribe(callback);
|
|
2937
3840
|
|
|
2938
|
-
|
|
2939
|
-
|
|
2940
|
-
|
|
3841
|
+
for(let i = 0, length = observables.length; i < length; i++) {
|
|
3842
|
+
const observable = observables[i];
|
|
3843
|
+
observable.subscribe(updatedValue);
|
|
3844
|
+
}
|
|
3845
|
+
};
|
|
3846
|
+
ObservableObject.prototype.configs = function() {
|
|
3847
|
+
return this.configs;
|
|
3848
|
+
};
|
|
2941
3849
|
|
|
2942
|
-
|
|
2943
|
-
|
|
2944
|
-
const proxy = new Proxy(data, {
|
|
2945
|
-
get(target, property) {
|
|
2946
|
-
if(property === '__isProxy__') { return true; }
|
|
2947
|
-
if(property === '$value') { return $val() }
|
|
2948
|
-
if(property === 'get' || property === '$get') { return $get; }
|
|
2949
|
-
if(property === 'val' || property === '$val') { return $val; }
|
|
2950
|
-
if(property === 'set' || property === '$set' || property === '$updateWith') { return $updateWith; }
|
|
2951
|
-
if(property === 'observables' || property === '$observables') { return Object.values(target); }
|
|
2952
|
-
if(property === 'keys'|| property === '$keys') { return Object.keys(initialValue); }
|
|
2953
|
-
if(property === 'clone' || property === '$clone') { return $clone; }
|
|
2954
|
-
if(property === 'reset') { return $reset; }
|
|
2955
|
-
if(property === 'configs') { return configs; }
|
|
2956
|
-
return target[property];
|
|
2957
|
-
},
|
|
2958
|
-
set(target, prop, newValue) {
|
|
2959
|
-
if(target[prop] !== undefined) {
|
|
2960
|
-
Validator.isObservable(newValue)
|
|
2961
|
-
? target[prop].set(newValue.val())
|
|
2962
|
-
: target[prop].set(newValue);
|
|
2963
|
-
return true;
|
|
2964
|
-
}
|
|
2965
|
-
return true;
|
|
2966
|
-
}
|
|
2967
|
-
});
|
|
3850
|
+
ObservableObject.prototype.update = ObservableObject.prototype.set;
|
|
2968
3851
|
|
|
2969
|
-
|
|
3852
|
+
Observable$1.init = function(initialValue, configs = null) {
|
|
3853
|
+
return new ObservableObject(initialValue, configs)
|
|
2970
3854
|
};
|
|
2971
3855
|
|
|
2972
3856
|
/**
|
|
@@ -2974,8 +3858,8 @@ var NativeComponents = (function (exports) {
|
|
|
2974
3858
|
* @param {any[]} data
|
|
2975
3859
|
* @return Proxy[]
|
|
2976
3860
|
*/
|
|
2977
|
-
Observable.arrayOfObject = function(data) {
|
|
2978
|
-
return data.map(item => Observable.object(item));
|
|
3861
|
+
Observable$1.arrayOfObject = function(data) {
|
|
3862
|
+
return data.map(item => Observable$1.object(item));
|
|
2979
3863
|
};
|
|
2980
3864
|
|
|
2981
3865
|
/**
|
|
@@ -2983,7 +3867,7 @@ var NativeComponents = (function (exports) {
|
|
|
2983
3867
|
* @param {ObservableItem|Object<ObservableItem>} data
|
|
2984
3868
|
* @returns {{}|*|null}
|
|
2985
3869
|
*/
|
|
2986
|
-
Observable.value = function(data) {
|
|
3870
|
+
Observable$1.value = function(data) {
|
|
2987
3871
|
if(Validator.isObservable(data)) {
|
|
2988
3872
|
return data.val();
|
|
2989
3873
|
}
|
|
@@ -2994,52 +3878,15 @@ var NativeComponents = (function (exports) {
|
|
|
2994
3878
|
const result = [];
|
|
2995
3879
|
for(let i = 0, length = data.length; i < length; i++) {
|
|
2996
3880
|
const item = data[i];
|
|
2997
|
-
result.push(Observable.value(item));
|
|
3881
|
+
result.push(Observable$1.value(item));
|
|
2998
3882
|
}
|
|
2999
3883
|
return result;
|
|
3000
3884
|
}
|
|
3001
3885
|
return data;
|
|
3002
3886
|
};
|
|
3003
3887
|
|
|
3004
|
-
|
|
3005
|
-
Observable.
|
|
3006
|
-
const data = Validator.isProxy(newData) ? newData.$value : newData;
|
|
3007
|
-
const configs = $target.configs;
|
|
3008
|
-
|
|
3009
|
-
for(const key in data) {
|
|
3010
|
-
const targetItem = $target[key];
|
|
3011
|
-
const newValueOrigin = newData[key];
|
|
3012
|
-
const newValue = data[key];
|
|
3013
|
-
|
|
3014
|
-
if(Validator.isObservable(targetItem)) {
|
|
3015
|
-
if(Validator.isArray(newValue)) {
|
|
3016
|
-
const firstElementFromOriginalValue = newValueOrigin.at(0);
|
|
3017
|
-
if(Validator.isObservable(firstElementFromOriginalValue) || Validator.isProxy(firstElementFromOriginalValue)) {
|
|
3018
|
-
const newValues = newValue.map(item => {
|
|
3019
|
-
if(Validator.isProxy(firstElementFromOriginalValue)) {
|
|
3020
|
-
return Observable.init(item, configs);
|
|
3021
|
-
}
|
|
3022
|
-
return Observable(item, configs);
|
|
3023
|
-
});
|
|
3024
|
-
targetItem.set(newValues);
|
|
3025
|
-
continue;
|
|
3026
|
-
}
|
|
3027
|
-
targetItem.set([...newValue]);
|
|
3028
|
-
continue;
|
|
3029
|
-
}
|
|
3030
|
-
targetItem.set(newValue);
|
|
3031
|
-
continue;
|
|
3032
|
-
}
|
|
3033
|
-
if(Validator.isProxy(targetItem)) {
|
|
3034
|
-
Observable.update(targetItem, newValue);
|
|
3035
|
-
continue;
|
|
3036
|
-
}
|
|
3037
|
-
$target[key] = newValue;
|
|
3038
|
-
}
|
|
3039
|
-
};
|
|
3040
|
-
|
|
3041
|
-
Observable.object = Observable.init;
|
|
3042
|
-
Observable.json = Observable.init;
|
|
3888
|
+
Observable$1.object = Observable$1.init;
|
|
3889
|
+
Observable$1.json = Observable$1.init;
|
|
3043
3890
|
|
|
3044
3891
|
/**
|
|
3045
3892
|
* Creates a computed observable that automatically updates when its dependencies change.
|
|
@@ -3060,7 +3907,7 @@ var NativeComponents = (function (exports) {
|
|
|
3060
3907
|
* const batch = Observable.batch(() => { ... });
|
|
3061
3908
|
* const computed = Observable.computed(() => { ... }, batch);
|
|
3062
3909
|
*/
|
|
3063
|
-
Observable.computed = function(callback, dependencies = []) {
|
|
3910
|
+
Observable$1.computed = function(callback, dependencies = []) {
|
|
3064
3911
|
const initialValue = callback();
|
|
3065
3912
|
const observable = new ObservableItem(initialValue);
|
|
3066
3913
|
const updatedValue = nextTick(() => observable.set(callback()));
|
|
@@ -3086,40 +3933,196 @@ var NativeComponents = (function (exports) {
|
|
|
3086
3933
|
return observable;
|
|
3087
3934
|
};
|
|
3088
3935
|
|
|
3936
|
+
/**
|
|
3937
|
+
* Creates a `<div>` element.
|
|
3938
|
+
* @type {function(GlobalAttributes=, NdChild|NdChild[]=): HTMLDivElement}
|
|
3939
|
+
*/
|
|
3089
3940
|
HtmlElementWrapper('div');
|
|
3941
|
+
|
|
3942
|
+
/**
|
|
3943
|
+
* Creates a `<span>` element.
|
|
3944
|
+
* @type {function(GlobalAttributes=, NdChild|NdChild[]=): HTMLSpanElement}
|
|
3945
|
+
*/
|
|
3090
3946
|
HtmlElementWrapper('span');
|
|
3947
|
+
|
|
3948
|
+
/**
|
|
3949
|
+
* Creates a `<label>` element.
|
|
3950
|
+
* @type {function(LabelAttributes=, NdChild|NdChild[]=): HTMLLabelElement}
|
|
3951
|
+
*/
|
|
3091
3952
|
HtmlElementWrapper('label');
|
|
3953
|
+
|
|
3954
|
+
/**
|
|
3955
|
+
* Creates a `<p>` element.
|
|
3956
|
+
* @type {function(GlobalAttributes=, NdChild|NdChild[]=): HTMLParagraphElement}
|
|
3957
|
+
*/
|
|
3092
3958
|
HtmlElementWrapper('p');
|
|
3959
|
+
|
|
3960
|
+
/**
|
|
3961
|
+
* Creates a `<strong>` element.
|
|
3962
|
+
* @type {function(GlobalAttributes=, NdChild|NdChild[]=): HTMLElement}
|
|
3963
|
+
*/
|
|
3093
3964
|
HtmlElementWrapper('strong');
|
|
3965
|
+
|
|
3966
|
+
/**
|
|
3967
|
+
* Creates a `<h1>` element.
|
|
3968
|
+
* @type {function(GlobalAttributes=, NdChild|NdChild[]=): HTMLHeadingElement}
|
|
3969
|
+
*/
|
|
3094
3970
|
HtmlElementWrapper('h1');
|
|
3971
|
+
|
|
3972
|
+
/**
|
|
3973
|
+
* Creates a `<h2>` element.
|
|
3974
|
+
* @type {function(GlobalAttributes=, NdChild|NdChild[]=): HTMLHeadingElement}
|
|
3975
|
+
*/
|
|
3095
3976
|
HtmlElementWrapper('h2');
|
|
3977
|
+
|
|
3978
|
+
/**
|
|
3979
|
+
* Creates a `<h3>` element.
|
|
3980
|
+
* @type {function(GlobalAttributes=, NdChild|NdChild[]=): HTMLHeadingElement}
|
|
3981
|
+
*/
|
|
3096
3982
|
HtmlElementWrapper('h3');
|
|
3983
|
+
|
|
3984
|
+
/**
|
|
3985
|
+
* Creates a `<h4>` element.
|
|
3986
|
+
* @type {function(GlobalAttributes=, NdChild|NdChild[]=): HTMLHeadingElement}
|
|
3987
|
+
*/
|
|
3097
3988
|
HtmlElementWrapper('h4');
|
|
3989
|
+
|
|
3990
|
+
/**
|
|
3991
|
+
* Creates a `<h5>` element.
|
|
3992
|
+
* @type {function(GlobalAttributes=, NdChild|NdChild[]=): HTMLHeadingElement}
|
|
3993
|
+
*/
|
|
3098
3994
|
HtmlElementWrapper('h5');
|
|
3995
|
+
|
|
3996
|
+
/**
|
|
3997
|
+
* Creates a `<h6>` element.
|
|
3998
|
+
* @type {function(GlobalAttributes=, NdChild|NdChild[]=): HTMLHeadingElement}
|
|
3999
|
+
*/
|
|
3099
4000
|
HtmlElementWrapper('h6');
|
|
3100
4001
|
|
|
4002
|
+
/**
|
|
4003
|
+
* Creates a `<br>` element.
|
|
4004
|
+
* @type {function(GlobalAttributes=): HTMLBRElement}
|
|
4005
|
+
*/
|
|
3101
4006
|
HtmlElementWrapper('br');
|
|
3102
4007
|
|
|
4008
|
+
/**
|
|
4009
|
+
* Creates an `<a>` element.
|
|
4010
|
+
* @type {function(AnchorAttributes=, NdChild|NdChild[]=): HTMLAnchorElement}
|
|
4011
|
+
*/
|
|
3103
4012
|
HtmlElementWrapper('a');
|
|
4013
|
+
|
|
4014
|
+
/**
|
|
4015
|
+
* Creates a `<pre>` element.
|
|
4016
|
+
* @type {function(GlobalAttributes=, NdChild|NdChild[]=): HTMLPreElement}
|
|
4017
|
+
*/
|
|
3104
4018
|
HtmlElementWrapper('pre');
|
|
4019
|
+
|
|
4020
|
+
/**
|
|
4021
|
+
* Creates a `<code>` element.
|
|
4022
|
+
* @type {function(GlobalAttributes=, NdChild|NdChild[]=): HTMLElement}
|
|
4023
|
+
*/
|
|
3105
4024
|
HtmlElementWrapper('code');
|
|
4025
|
+
|
|
4026
|
+
/**
|
|
4027
|
+
* Creates a `<blockquote>` element.
|
|
4028
|
+
* @type {function(GlobalAttributes & { cite?: string }=, NdChild|NdChild[]=): HTMLQuoteElement}
|
|
4029
|
+
*/
|
|
3106
4030
|
HtmlElementWrapper('blockquote');
|
|
4031
|
+
|
|
4032
|
+
/**
|
|
4033
|
+
* Creates an `<hr>` element.
|
|
4034
|
+
* @type {function(GlobalAttributes=): HTMLHRElement}
|
|
4035
|
+
*/
|
|
3107
4036
|
HtmlElementWrapper('hr');
|
|
4037
|
+
|
|
4038
|
+
/**
|
|
4039
|
+
* Creates an `<em>` element.
|
|
4040
|
+
* @type {function(GlobalAttributes=, NdChild|NdChild[]=): HTMLElement}
|
|
4041
|
+
*/
|
|
3108
4042
|
HtmlElementWrapper('em');
|
|
4043
|
+
|
|
4044
|
+
/**
|
|
4045
|
+
* Creates a `<small>` element.
|
|
4046
|
+
* @type {function(GlobalAttributes=, NdChild|NdChild[]=): HTMLElement}
|
|
4047
|
+
*/
|
|
3109
4048
|
HtmlElementWrapper('small');
|
|
4049
|
+
|
|
4050
|
+
/**
|
|
4051
|
+
* Creates a `<mark>` element.
|
|
4052
|
+
* @type {function(GlobalAttributes=, NdChild|NdChild[]=): HTMLElement}
|
|
4053
|
+
*/
|
|
3110
4054
|
HtmlElementWrapper('mark');
|
|
4055
|
+
|
|
4056
|
+
/**
|
|
4057
|
+
* Creates a `<del>` element.
|
|
4058
|
+
* @type {function(ModAttributes=, NdChild|NdChild[]=): HTMLModElement}
|
|
4059
|
+
*/
|
|
3111
4060
|
HtmlElementWrapper('del');
|
|
4061
|
+
|
|
4062
|
+
/**
|
|
4063
|
+
* Creates an `<ins>` element.
|
|
4064
|
+
* @type {function(ModAttributes=, NdChild|NdChild[]=): HTMLModElement}
|
|
4065
|
+
*/
|
|
3112
4066
|
HtmlElementWrapper('ins');
|
|
4067
|
+
|
|
4068
|
+
/**
|
|
4069
|
+
* Creates a `<sub>` element.
|
|
4070
|
+
* @type {function(GlobalAttributes=, NdChild|NdChild[]=): HTMLElement}
|
|
4071
|
+
*/
|
|
3113
4072
|
HtmlElementWrapper('sub');
|
|
4073
|
+
|
|
4074
|
+
/**
|
|
4075
|
+
* Creates a `<sup>` element.
|
|
4076
|
+
* @type {function(GlobalAttributes=, NdChild|NdChild[]=): HTMLElement}
|
|
4077
|
+
*/
|
|
3114
4078
|
HtmlElementWrapper('sup');
|
|
4079
|
+
|
|
4080
|
+
/**
|
|
4081
|
+
* Creates an `<abbr>` element.
|
|
4082
|
+
* @type {function(GlobalAttributes=, NdChild|NdChild[]=): HTMLElement}
|
|
4083
|
+
*/
|
|
3115
4084
|
HtmlElementWrapper('abbr');
|
|
4085
|
+
|
|
4086
|
+
/**
|
|
4087
|
+
* Creates a `<cite>` element.
|
|
4088
|
+
* @type {function(GlobalAttributes=, NdChild|NdChild[]=): HTMLElement}
|
|
4089
|
+
*/
|
|
3116
4090
|
HtmlElementWrapper('cite');
|
|
4091
|
+
|
|
4092
|
+
/**
|
|
4093
|
+
* Creates a `<q>` element.
|
|
4094
|
+
* @type {function(GlobalAttributes & { cite?: string }=, NdChild|NdChild[]=): HTMLQuoteElement}
|
|
4095
|
+
*/
|
|
3117
4096
|
HtmlElementWrapper('q');
|
|
3118
4097
|
|
|
4098
|
+
/**
|
|
4099
|
+
* Creates a `<dl>` element.
|
|
4100
|
+
* @type {function(GlobalAttributes=, NdChild|NdChild[]=): HTMLDListElement}
|
|
4101
|
+
*/
|
|
3119
4102
|
HtmlElementWrapper('dl');
|
|
4103
|
+
|
|
4104
|
+
/**
|
|
4105
|
+
* Creates a `<dt>` element.
|
|
4106
|
+
* @type {function(GlobalAttributes=, NdChild|NdChild[]=): HTMLElement}
|
|
4107
|
+
*/
|
|
3120
4108
|
HtmlElementWrapper('dt');
|
|
4109
|
+
|
|
4110
|
+
/**
|
|
4111
|
+
* Creates a `<dd>` element.
|
|
4112
|
+
* @type {function(GlobalAttributes=, NdChild|NdChild[]=): HTMLElement}
|
|
4113
|
+
*/
|
|
3121
4114
|
HtmlElementWrapper('dd');
|
|
3122
4115
|
|
|
4116
|
+
/**
|
|
4117
|
+
* Creates a `<form>` element.
|
|
4118
|
+
* Extended with fluent methods: `.submit()`, `.post()`, `.get()`, `.multipartFormData()`.
|
|
4119
|
+
* @type {function(FormAttributes=, NdChild|NdChild[]=): HTMLFormElement & {
|
|
4120
|
+
* submit: (actionOrFn: string | ((e: SubmitEvent) => void)) => HTMLFormElement,
|
|
4121
|
+
* post: (action: string) => HTMLFormElement,
|
|
4122
|
+
* get: (action: string) => HTMLFormElement,
|
|
4123
|
+
* multipartFormData: () => HTMLFormElement,
|
|
4124
|
+
* }}
|
|
4125
|
+
*/
|
|
3123
4126
|
HtmlElementWrapper('form', function(el) {
|
|
3124
4127
|
|
|
3125
4128
|
el.submit = function(action) {
|
|
@@ -3149,70 +4152,317 @@ var NativeComponents = (function (exports) {
|
|
|
3149
4152
|
return el;
|
|
3150
4153
|
});
|
|
3151
4154
|
|
|
4155
|
+
/**
|
|
4156
|
+
* Creates an `<input>` element.
|
|
4157
|
+
* @type {function(InputAttributes=): HTMLInputElement}
|
|
4158
|
+
*/
|
|
3152
4159
|
HtmlElementWrapper('input');
|
|
3153
4160
|
|
|
4161
|
+
/**
|
|
4162
|
+
* Creates a `<textarea>` element.
|
|
4163
|
+
* @type {function(TextAreaAttributes=, NdChild|NdChild[]=): HTMLTextAreaElement}
|
|
4164
|
+
*/
|
|
3154
4165
|
HtmlElementWrapper('textarea');
|
|
3155
4166
|
|
|
4167
|
+
/**
|
|
4168
|
+
* Creates a `<select>` element.
|
|
4169
|
+
* @type {function(SelectAttributes=, NdChild|NdChild[]=): HTMLSelectElement}
|
|
4170
|
+
*/
|
|
3156
4171
|
HtmlElementWrapper('select');
|
|
3157
|
-
|
|
4172
|
+
|
|
4173
|
+
/**
|
|
4174
|
+
* Creates a `<fieldset>` element.
|
|
4175
|
+
* @type {function(GlobalAttributes & { disabled?: Observable<boolean>|boolean }=, NdChild|NdChild[]=): HTMLFieldSetElement}
|
|
4176
|
+
*/
|
|
4177
|
+
HtmlElementWrapper('fieldset');
|
|
4178
|
+
|
|
4179
|
+
/**
|
|
4180
|
+
* Creates an `<option>` element.
|
|
4181
|
+
* @type {function(OptionAttributes=, NdChild|NdChild[]=): HTMLOptionElement}
|
|
4182
|
+
*/
|
|
3158
4183
|
HtmlElementWrapper('option');
|
|
4184
|
+
|
|
4185
|
+
/**
|
|
4186
|
+
* Creates a `<legend>` element.
|
|
4187
|
+
* @type {function(GlobalAttributes=, NdChild|NdChild[]=): HTMLLegendElement}
|
|
4188
|
+
*/
|
|
3159
4189
|
HtmlElementWrapper('legend');
|
|
4190
|
+
|
|
4191
|
+
/**
|
|
4192
|
+
* Creates a `<datalist>` element.
|
|
4193
|
+
* @type {function(GlobalAttributes=, NdChild|NdChild[]=): HTMLDataListElement}
|
|
4194
|
+
*/
|
|
3160
4195
|
HtmlElementWrapper('datalist');
|
|
4196
|
+
|
|
4197
|
+
/**
|
|
4198
|
+
* Creates an `<output>` element.
|
|
4199
|
+
* @type {function(OutputAttributes=, NdChild|NdChild[]=): HTMLOutputElement}
|
|
4200
|
+
*/
|
|
3161
4201
|
HtmlElementWrapper('output');
|
|
4202
|
+
|
|
4203
|
+
/**
|
|
4204
|
+
* Creates a `<progress>` element.
|
|
4205
|
+
* @type {function(ProgressAttributes=, NdChild|NdChild[]=): HTMLProgressElement}
|
|
4206
|
+
*/
|
|
3162
4207
|
HtmlElementWrapper('progress');
|
|
3163
|
-
HtmlElementWrapper('meter');
|
|
3164
4208
|
|
|
4209
|
+
/**
|
|
4210
|
+
* Creates a `<meter>` element.
|
|
4211
|
+
* @type {function(MeterAttributes=, NdChild|NdChild[]=): HTMLMeterElement}
|
|
4212
|
+
*/
|
|
4213
|
+
HtmlElementWrapper('meter');
|
|
3165
4214
|
|
|
4215
|
+
/**
|
|
4216
|
+
* Creates a `<button>` element.
|
|
4217
|
+
* @type {function(ButtonAttributes=, NdChild|NdChild[]=): HTMLButtonElement}
|
|
4218
|
+
*/
|
|
3166
4219
|
const Button$1 = HtmlElementWrapper('button');
|
|
3167
4220
|
|
|
4221
|
+
/**
|
|
4222
|
+
* Creates a `<main>` element.
|
|
4223
|
+
* @type {function(GlobalAttributes=, NdChild|NdChild[]=): HTMLElement}
|
|
4224
|
+
*/
|
|
3168
4225
|
HtmlElementWrapper('main');
|
|
4226
|
+
|
|
4227
|
+
/**
|
|
4228
|
+
* Creates a `<section>` element.
|
|
4229
|
+
* @type {function(GlobalAttributes=, NdChild|NdChild[]=): HTMLElement}
|
|
4230
|
+
*/
|
|
3169
4231
|
HtmlElementWrapper('section');
|
|
4232
|
+
|
|
4233
|
+
/**
|
|
4234
|
+
* Creates an `<article>` element.
|
|
4235
|
+
* @type {function(GlobalAttributes=, NdChild|NdChild[]=): HTMLElement}
|
|
4236
|
+
*/
|
|
3170
4237
|
HtmlElementWrapper('article');
|
|
4238
|
+
|
|
4239
|
+
/**
|
|
4240
|
+
* Creates an `<aside>` element.
|
|
4241
|
+
* @type {function(GlobalAttributes=, NdChild|NdChild[]=): HTMLElement}
|
|
4242
|
+
*/
|
|
3171
4243
|
HtmlElementWrapper('aside');
|
|
4244
|
+
|
|
4245
|
+
/**
|
|
4246
|
+
* Creates a `<nav>` element.
|
|
4247
|
+
* @type {function(GlobalAttributes=, NdChild|NdChild[]=): HTMLElement}
|
|
4248
|
+
*/
|
|
3172
4249
|
HtmlElementWrapper('nav');
|
|
4250
|
+
|
|
4251
|
+
/**
|
|
4252
|
+
* Creates a `<figure>` element.
|
|
4253
|
+
* @type {function(GlobalAttributes=, NdChild|NdChild[]=): HTMLElement}
|
|
4254
|
+
*/
|
|
3173
4255
|
HtmlElementWrapper('figure');
|
|
4256
|
+
|
|
4257
|
+
/**
|
|
4258
|
+
* Creates a `<figcaption>` element.
|
|
4259
|
+
* @type {function(GlobalAttributes=, NdChild|NdChild[]=): HTMLElement}
|
|
4260
|
+
*/
|
|
3174
4261
|
HtmlElementWrapper('figcaption');
|
|
3175
4262
|
|
|
4263
|
+
/**
|
|
4264
|
+
* Creates a `<header>` element.
|
|
4265
|
+
* @type {function(GlobalAttributes=, NdChild|NdChild[]=): HTMLElement}
|
|
4266
|
+
*/
|
|
3176
4267
|
HtmlElementWrapper('header');
|
|
4268
|
+
|
|
4269
|
+
/**
|
|
4270
|
+
* Creates a `<footer>` element.
|
|
4271
|
+
* @type {function(GlobalAttributes=, NdChild|NdChild[]=): HTMLElement}
|
|
4272
|
+
*/
|
|
3177
4273
|
HtmlElementWrapper('footer');
|
|
3178
4274
|
|
|
4275
|
+
/**
|
|
4276
|
+
* Creates an `<img>` element.
|
|
4277
|
+
* @type {function(ImgAttributes=): HTMLImageElement}
|
|
4278
|
+
*/
|
|
3179
4279
|
HtmlElementWrapper('img');
|
|
3180
4280
|
|
|
4281
|
+
/**
|
|
4282
|
+
* Creates a `<details>` element.
|
|
4283
|
+
* @type {function(DetailsAttributes=, NdChild|NdChild[]=): HTMLDetailsElement}
|
|
4284
|
+
*/
|
|
3181
4285
|
HtmlElementWrapper('details');
|
|
4286
|
+
|
|
4287
|
+
/**
|
|
4288
|
+
* Creates a `<summary>` element.
|
|
4289
|
+
* @type {function(GlobalAttributes=, NdChild|NdChild[]=): HTMLElement}
|
|
4290
|
+
*/
|
|
3182
4291
|
HtmlElementWrapper('summary');
|
|
4292
|
+
|
|
4293
|
+
/**
|
|
4294
|
+
* Creates a `<dialog>` element.
|
|
4295
|
+
* @type {function(DialogAttributes=, NdChild|NdChild[]=): HTMLDialogElement}
|
|
4296
|
+
*/
|
|
3183
4297
|
HtmlElementWrapper('dialog');
|
|
4298
|
+
|
|
4299
|
+
/**
|
|
4300
|
+
* Creates a `<menu>` element.
|
|
4301
|
+
* @type {function(GlobalAttributes=, NdChild|NdChild[]=): HTMLMenuElement}
|
|
4302
|
+
*/
|
|
3184
4303
|
HtmlElementWrapper('menu');
|
|
3185
4304
|
|
|
4305
|
+
/**
|
|
4306
|
+
* Creates an `<ol>` element.
|
|
4307
|
+
* @type {function(OlAttributes=, NdChild|NdChild[]=): HTMLOListElement}
|
|
4308
|
+
*/
|
|
3186
4309
|
HtmlElementWrapper('ol');
|
|
4310
|
+
|
|
4311
|
+
/**
|
|
4312
|
+
* Creates a `<ul>` element.
|
|
4313
|
+
* @type {function(GlobalAttributes=, NdChild|NdChild[]=): HTMLUListElement}
|
|
4314
|
+
*/
|
|
3187
4315
|
HtmlElementWrapper('ul');
|
|
4316
|
+
|
|
4317
|
+
/**
|
|
4318
|
+
* Creates a `<li>` element.
|
|
4319
|
+
* @type {function(GlobalAttributes & { value?: number }=, NdChild|NdChild[]=): HTMLLIElement}
|
|
4320
|
+
*/
|
|
3188
4321
|
HtmlElementWrapper('li');
|
|
3189
4322
|
|
|
4323
|
+
/**
|
|
4324
|
+
* Creates an `<audio>` element.
|
|
4325
|
+
* @type {function(AudioAttributes=, NdChild|NdChild[]=): HTMLAudioElement}
|
|
4326
|
+
*/
|
|
3190
4327
|
HtmlElementWrapper('audio');
|
|
4328
|
+
|
|
4329
|
+
/**
|
|
4330
|
+
* Creates a `<video>` element.
|
|
4331
|
+
* @type {function(VideoAttributes=, NdChild|NdChild[]=): HTMLVideoElement}
|
|
4332
|
+
*/
|
|
3191
4333
|
HtmlElementWrapper('video');
|
|
4334
|
+
|
|
4335
|
+
/**
|
|
4336
|
+
* Creates a `<source>` element.
|
|
4337
|
+
* @type {function(SourceAttributes=): HTMLSourceElement}
|
|
4338
|
+
*/
|
|
3192
4339
|
HtmlElementWrapper('source');
|
|
4340
|
+
|
|
4341
|
+
/**
|
|
4342
|
+
* Creates a `<track>` element.
|
|
4343
|
+
* @type {function(TrackAttributes=): HTMLTrackElement}
|
|
4344
|
+
*/
|
|
3193
4345
|
HtmlElementWrapper('track');
|
|
4346
|
+
|
|
4347
|
+
/**
|
|
4348
|
+
* Creates a `<canvas>` element.
|
|
4349
|
+
* @type {function(CanvasAttributes=, NdChild|NdChild[]=): HTMLCanvasElement}
|
|
4350
|
+
*/
|
|
3194
4351
|
HtmlElementWrapper('canvas');
|
|
4352
|
+
|
|
4353
|
+
/**
|
|
4354
|
+
* Creates an `<svg>` element.
|
|
4355
|
+
* @type {function(SvgAttributes=, NdChild|NdChild[]=): SVGSVGElement}
|
|
4356
|
+
*/
|
|
3195
4357
|
HtmlElementWrapper('svg');
|
|
3196
4358
|
|
|
4359
|
+
/**
|
|
4360
|
+
* Creates a `<time>` element.
|
|
4361
|
+
* @type {function(TimeAttributes=, NdChild|NdChild[]=): HTMLTimeElement}
|
|
4362
|
+
*/
|
|
3197
4363
|
HtmlElementWrapper('time');
|
|
4364
|
+
|
|
4365
|
+
/**
|
|
4366
|
+
* Creates a `<data>` element.
|
|
4367
|
+
* @type {function(GlobalAttributes & { value?: Observable<string>|string }=, NdChild|NdChild[]=): HTMLDataElement}
|
|
4368
|
+
*/
|
|
3198
4369
|
HtmlElementWrapper('data');
|
|
4370
|
+
|
|
4371
|
+
/**
|
|
4372
|
+
* Creates an `<address>` element.
|
|
4373
|
+
* @type {function(GlobalAttributes=, NdChild|NdChild[]=): HTMLElement}
|
|
4374
|
+
*/
|
|
3199
4375
|
HtmlElementWrapper('address');
|
|
4376
|
+
|
|
4377
|
+
/**
|
|
4378
|
+
* Creates a `<kbd>` element.
|
|
4379
|
+
* @type {function(GlobalAttributes=, NdChild|NdChild[]=): HTMLElement}
|
|
4380
|
+
*/
|
|
3200
4381
|
HtmlElementWrapper('kbd');
|
|
4382
|
+
|
|
4383
|
+
/**
|
|
4384
|
+
* Creates a `<samp>` element.
|
|
4385
|
+
* @type {function(GlobalAttributes=, NdChild|NdChild[]=): HTMLElement}
|
|
4386
|
+
*/
|
|
3201
4387
|
HtmlElementWrapper('samp');
|
|
4388
|
+
|
|
4389
|
+
/**
|
|
4390
|
+
* Creates a `<var>` element.
|
|
4391
|
+
* @type {function(GlobalAttributes=, NdChild|NdChild[]=): HTMLElement}
|
|
4392
|
+
*/
|
|
3202
4393
|
HtmlElementWrapper('var');
|
|
4394
|
+
|
|
4395
|
+
/**
|
|
4396
|
+
* Creates a `<wbr>` element.
|
|
4397
|
+
* @type {function(GlobalAttributes=): HTMLElement}
|
|
4398
|
+
*/
|
|
3203
4399
|
HtmlElementWrapper('wbr');
|
|
3204
4400
|
|
|
4401
|
+
/**
|
|
4402
|
+
* Creates a `<caption>` element.
|
|
4403
|
+
* @type {function(GlobalAttributes=, NdChild|NdChild[]=): HTMLTableCaptionElement}
|
|
4404
|
+
*/
|
|
3205
4405
|
HtmlElementWrapper('caption');
|
|
4406
|
+
|
|
4407
|
+
/**
|
|
4408
|
+
* Creates a `<table>` element.
|
|
4409
|
+
* @type {function(GlobalAttributes=, NdChild|NdChild[]=): HTMLTableElement}
|
|
4410
|
+
*/
|
|
3206
4411
|
const Table = HtmlElementWrapper('table');
|
|
4412
|
+
|
|
4413
|
+
/**
|
|
4414
|
+
* Creates a `<thead>` element.
|
|
4415
|
+
* @type {function(GlobalAttributes=, NdChild|NdChild[]=): HTMLTableSectionElement}
|
|
4416
|
+
*/
|
|
3207
4417
|
HtmlElementWrapper('thead');
|
|
4418
|
+
|
|
4419
|
+
/**
|
|
4420
|
+
* Creates a `<tfoot>` element.
|
|
4421
|
+
* @type {function(GlobalAttributes=, NdChild|NdChild[]=): HTMLTableSectionElement}
|
|
4422
|
+
*/
|
|
3208
4423
|
HtmlElementWrapper('tfoot');
|
|
4424
|
+
|
|
4425
|
+
/**
|
|
4426
|
+
* Creates a `<tbody>` element.
|
|
4427
|
+
* @type {function(GlobalAttributes=, NdChild|NdChild[]=): HTMLTableSectionElement}
|
|
4428
|
+
*/
|
|
3209
4429
|
HtmlElementWrapper('tbody');
|
|
4430
|
+
|
|
4431
|
+
/**
|
|
4432
|
+
* Creates a `<tr>` element.
|
|
4433
|
+
* @type {function(GlobalAttributes=, NdChild|NdChild[]=): HTMLTableRowElement}
|
|
4434
|
+
*/
|
|
3210
4435
|
const Tr = HtmlElementWrapper('tr');
|
|
4436
|
+
|
|
4437
|
+
/**
|
|
4438
|
+
* Alias for {@link Tr}.
|
|
4439
|
+
* @type {typeof Tr}
|
|
4440
|
+
*/
|
|
3211
4441
|
const TRow = Tr;
|
|
4442
|
+
|
|
4443
|
+
/**
|
|
4444
|
+
* Creates a `<th>` element.
|
|
4445
|
+
* @type {function(ThAttributes=, NdChild|NdChild[]=): HTMLTableCellElement}
|
|
4446
|
+
*/
|
|
3212
4447
|
const Th = HtmlElementWrapper('th');
|
|
4448
|
+
|
|
4449
|
+
/**
|
|
4450
|
+
* Alias for {@link Th}.
|
|
4451
|
+
* @type {typeof Th}
|
|
4452
|
+
*/
|
|
3213
4453
|
const THeadCell$1 = Th;
|
|
4454
|
+
|
|
4455
|
+
/**
|
|
4456
|
+
* Creates a `<td>` element.
|
|
4457
|
+
* @type {function(TdAttributes=, NdChild|NdChild[]=): HTMLTableCellElement}
|
|
4458
|
+
*/
|
|
3214
4459
|
HtmlElementWrapper('td');
|
|
3215
4460
|
|
|
4461
|
+
/**
|
|
4462
|
+
* Creates an empty `DocumentFragment` wrapper.
|
|
4463
|
+
* Useful for grouping elements without adding a DOM node.
|
|
4464
|
+
* @type {function(GlobalAttributes=, NdChild|NdChild[]=): DocumentFragment}
|
|
4465
|
+
*/
|
|
3216
4466
|
HtmlElementWrapper('');
|
|
3217
4467
|
|
|
3218
4468
|
/**
|
|
@@ -6326,10 +7576,10 @@ var NativeComponents = (function (exports) {
|
|
|
6326
7576
|
this.$element = null;
|
|
6327
7577
|
this.$configs = configs;
|
|
6328
7578
|
this.$fields = new Map();
|
|
6329
|
-
this.$submitting = Observable(false);
|
|
6330
|
-
this.$errors = Observable(null);
|
|
6331
|
-
this.$isDirty = Observable(false);
|
|
6332
|
-
this.$isValid = Observable(false);
|
|
7579
|
+
this.$submitting = Observable$1(false);
|
|
7580
|
+
this.$errors = Observable$1(null);
|
|
7581
|
+
this.$isDirty = Observable$1(false);
|
|
7582
|
+
this.$isValid = Observable$1(false);
|
|
6333
7583
|
}
|
|
6334
7584
|
|
|
6335
7585
|
FormControl.defaultLayoutTemplate = null;
|
|
@@ -7650,7 +8900,7 @@ var NativeComponents = (function (exports) {
|
|
|
7650
8900
|
defaultItem: null,
|
|
7651
8901
|
value: (config?.data && Validator.isObservable(config.data))
|
|
7652
8902
|
? config.data
|
|
7653
|
-
: Observable.array(config?.data || []),
|
|
8903
|
+
: Observable$1.array(config?.data || []),
|
|
7654
8904
|
rules: null,
|
|
7655
8905
|
layout: null,
|
|
7656
8906
|
template: null,
|
|
@@ -7747,8 +8997,8 @@ var NativeComponents = (function (exports) {
|
|
|
7747
8997
|
defaultItemData = Validator.isObservable(defaultItemData)
|
|
7748
8998
|
? defaultItemData
|
|
7749
8999
|
: Validator.isObject(defaultItemData)
|
|
7750
|
-
? Observable.init(defaultItemData)
|
|
7751
|
-
: Observable(defaultItemData);
|
|
9000
|
+
? Observable$1.init(defaultItemData)
|
|
9001
|
+
: Observable$1(defaultItemData);
|
|
7752
9002
|
|
|
7753
9003
|
this.$items = this.$items || new WeakMap();
|
|
7754
9004
|
|
|
@@ -10230,9 +11480,9 @@ var NativeComponents = (function (exports) {
|
|
|
10230
11480
|
...configs
|
|
10231
11481
|
});
|
|
10232
11482
|
|
|
10233
|
-
this.$currentPage = Observable(1);
|
|
10234
|
-
this.$selectedRows = Observable.array();
|
|
10235
|
-
this.$expandedRows = Observable.array();
|
|
11483
|
+
this.$currentPage = Observable$1(1);
|
|
11484
|
+
this.$selectedRows = Observable$1.array();
|
|
11485
|
+
this.$expandedRows = Observable$1.array();
|
|
10236
11486
|
}
|
|
10237
11487
|
|
|
10238
11488
|
DataTable.defaultToolbarTemplate = null;
|