native-document 1.0.95 → 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 +1922 -1277
- package/dist/native-document.dev.js +1985 -1401
- 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/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/utils/formatters.js +91 -0
- package/src/core/utils/localstorage.js +57 -0
- package/src/core/utils/validator.js +0 -2
- package/src/devtools.js +9 -0
- package/src/fetch/NativeFetch.js +5 -2
- 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
|
@@ -363,69 +363,16 @@ var NativeComponents = (function (exports) {
|
|
|
363
363
|
// });
|
|
364
364
|
};
|
|
365
365
|
|
|
366
|
-
let DebugManager = {};
|
|
366
|
+
let DebugManager$1 = {};
|
|
367
367
|
{
|
|
368
|
-
DebugManager = {
|
|
368
|
+
DebugManager$1 = {
|
|
369
369
|
log() {},
|
|
370
370
|
warn() {},
|
|
371
371
|
error() {},
|
|
372
372
|
disable() {}
|
|
373
373
|
};
|
|
374
374
|
}
|
|
375
|
-
var DebugManager
|
|
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$1.log('Memory Auto Clean', `🧹 Cleaned ${cleanedCount} orphaned observables`);
|
|
425
|
-
}
|
|
426
|
-
}
|
|
427
|
-
};
|
|
428
|
-
}());
|
|
375
|
+
var DebugManager = DebugManager$1;
|
|
429
376
|
|
|
430
377
|
/**
|
|
431
378
|
*
|
|
@@ -519,1307 +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;
|
|
535
|
-
|
|
536
|
-
/**
|
|
537
|
-
* Subscribes to changes in the match status (true when observable equals target value).
|
|
538
|
-
*
|
|
539
|
-
* @param {Function} callback - Function called with boolean indicating if values match
|
|
540
|
-
* @returns {Function} Unsubscribe function
|
|
541
|
-
* @example
|
|
542
|
-
* const status = Observable('idle');
|
|
543
|
-
* const isLoading = status.when('loading');
|
|
544
|
-
* isLoading.subscribe(active => console.log('Loading:', active));
|
|
545
|
-
*/
|
|
546
|
-
ObservableWhen.prototype.subscribe = function(callback) {
|
|
547
|
-
return this.$observer.on(this.$target, callback);
|
|
548
|
-
};
|
|
549
|
-
|
|
550
|
-
/**
|
|
551
|
-
* Returns true if the observable's current value equals the target value.
|
|
552
|
-
*
|
|
553
|
-
* @returns {boolean} True if observable value matches target value
|
|
554
|
-
*/
|
|
555
|
-
ObservableWhen.prototype.val = function() {
|
|
556
|
-
return this.$observer.$currentValue === this.$target;
|
|
557
|
-
};
|
|
558
|
-
|
|
559
|
-
/**
|
|
560
|
-
* Returns true if the observable's current value equals the target value.
|
|
561
|
-
* Alias for val().
|
|
562
|
-
*
|
|
563
|
-
* @returns {boolean} True if observable value matches target value
|
|
564
|
-
*/
|
|
565
|
-
ObservableWhen.prototype.isMatch = ObservableWhen.prototype.val;
|
|
566
|
-
|
|
567
|
-
/**
|
|
568
|
-
* Returns true if the observable's current value equals the target value.
|
|
569
|
-
* Alias for val().
|
|
570
|
-
*
|
|
571
|
-
* @returns {boolean} True if observable value matches target value
|
|
572
|
-
*/
|
|
573
|
-
ObservableWhen.prototype.isActive = ObservableWhen.prototype.val;
|
|
469
|
+
const DocumentObserver = {
|
|
470
|
+
mounted: new WeakMap(),
|
|
471
|
+
beforeUnmount: new WeakMap(),
|
|
472
|
+
mountedSupposedSize: 0,
|
|
473
|
+
unmounted: new WeakMap(),
|
|
474
|
+
unmountedSupposedSize: 0,
|
|
475
|
+
observer: null,
|
|
574
476
|
|
|
575
|
-
|
|
576
|
-
|
|
577
|
-
|
|
578
|
-
|
|
579
|
-
|
|
580
|
-
|
|
581
|
-
|
|
582
|
-
|
|
583
|
-
|
|
584
|
-
|
|
585
|
-
|
|
586
|
-
|
|
587
|
-
|
|
588
|
-
|
|
589
|
-
|
|
590
|
-
|
|
591
|
-
|
|
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
|
+
},
|
|
592
494
|
|
|
593
|
-
|
|
594
|
-
const
|
|
595
|
-
if(
|
|
596
|
-
|
|
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;
|
|
597
503
|
}
|
|
598
|
-
lastArgs = args;
|
|
599
504
|
|
|
600
|
-
|
|
601
|
-
|
|
602
|
-
|
|
603
|
-
|
|
604
|
-
|
|
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
|
+
}
|
|
605
515
|
|
|
606
|
-
|
|
607
|
-
|
|
608
|
-
|
|
609
|
-
|
|
610
|
-
|
|
516
|
+
if(shouldRemove) {
|
|
517
|
+
data.disconnect();
|
|
518
|
+
node.nd?.remove();
|
|
519
|
+
}
|
|
520
|
+
},
|
|
611
521
|
|
|
612
|
-
|
|
613
|
-
|
|
614
|
-
|
|
615
|
-
|
|
616
|
-
|
|
617
|
-
|
|
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
|
+
}
|
|
618
536
|
|
|
619
|
-
|
|
620
|
-
|
|
621
|
-
|
|
622
|
-
|
|
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
|
+
}
|
|
623
549
|
}
|
|
624
|
-
}
|
|
550
|
+
},
|
|
625
551
|
|
|
626
|
-
|
|
627
|
-
|
|
628
|
-
|
|
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;
|
|
629
560
|
|
|
630
|
-
|
|
631
|
-
|
|
632
|
-
|
|
633
|
-
|
|
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
|
+
};
|
|
634
577
|
|
|
635
|
-
|
|
636
|
-
|
|
637
|
-
|
|
638
|
-
|
|
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
|
+
};
|
|
639
589
|
|
|
640
|
-
|
|
641
|
-
|
|
642
|
-
|
|
643
|
-
|
|
644
|
-
|
|
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
|
+
};
|
|
645
609
|
|
|
646
|
-
|
|
647
|
-
|
|
648
|
-
for (const key in value) {
|
|
649
|
-
if (Object.hasOwn(value, key)) {
|
|
650
|
-
cloned[key] = deepClone(value[key]);
|
|
651
|
-
}
|
|
652
|
-
}
|
|
653
|
-
return cloned;
|
|
654
|
-
};
|
|
610
|
+
return {
|
|
611
|
+
disconnect: () => data?.disconnect(),
|
|
655
612
|
|
|
656
|
-
|
|
657
|
-
|
|
658
|
-
|
|
659
|
-
|
|
660
|
-
|
|
661
|
-
|
|
662
|
-
|
|
663
|
-
|
|
613
|
+
mounted: (callback) => {
|
|
614
|
+
addListener('mounted', callback);
|
|
615
|
+
DocumentObserver.mounted.set(element, data);
|
|
616
|
+
if (!mountedRegistered) {
|
|
617
|
+
DocumentObserver.mountedSupposedSize++;
|
|
618
|
+
mountedRegistered = true;
|
|
619
|
+
}
|
|
620
|
+
},
|
|
664
621
|
|
|
665
|
-
|
|
666
|
-
|
|
622
|
+
unmounted: (callback) => {
|
|
623
|
+
addListener('unmounted', callback);
|
|
624
|
+
DocumentObserver.unmounted.set(element, data);
|
|
625
|
+
if (!unmountedRegistered) {
|
|
626
|
+
DocumentObserver.unmountedSupposedSize++;
|
|
627
|
+
unmountedRegistered = true;
|
|
628
|
+
}
|
|
629
|
+
},
|
|
667
630
|
|
|
668
|
-
|
|
669
|
-
|
|
670
|
-
|
|
631
|
+
off: (type, callback) => {
|
|
632
|
+
removeListener(type, callback);
|
|
633
|
+
}
|
|
634
|
+
};
|
|
635
|
+
}
|
|
636
|
+
};
|
|
671
637
|
|
|
672
|
-
|
|
638
|
+
DocumentObserver.observer = new MutationObserver(DocumentObserver.checkMutation);
|
|
639
|
+
DocumentObserver.observer.observe(document.body, {
|
|
640
|
+
childList: true,
|
|
641
|
+
subtree: true,
|
|
642
|
+
});
|
|
673
643
|
|
|
674
|
-
|
|
675
|
-
|
|
676
|
-
|
|
677
|
-
this.$initialValue = Validator.isObject(value) ? deepClone(value) : value;
|
|
678
|
-
}
|
|
679
|
-
}
|
|
644
|
+
function NDElement(element) {
|
|
645
|
+
this.$element = element;
|
|
646
|
+
this.$observer = null;
|
|
680
647
|
}
|
|
681
648
|
|
|
682
|
-
|
|
683
|
-
get() {
|
|
684
|
-
return this.$currentValue;
|
|
685
|
-
},
|
|
686
|
-
set(value) {
|
|
687
|
-
this.set(value);
|
|
688
|
-
},
|
|
689
|
-
configurable: true,
|
|
690
|
-
});
|
|
649
|
+
NDElement.prototype.__$isNDElement = true;
|
|
691
650
|
|
|
692
|
-
|
|
693
|
-
|
|
651
|
+
NDElement.prototype.valueOf = function() {
|
|
652
|
+
return this.$element;
|
|
653
|
+
};
|
|
694
654
|
|
|
695
|
-
|
|
696
|
-
|
|
697
|
-
* The interceptor can modify the value or return undefined to use the original value.
|
|
698
|
-
*
|
|
699
|
-
* @param {(value) => any} callback - Interceptor function that receives (newValue, currentValue) and returns the transformed value or undefined
|
|
700
|
-
* @returns {ObservableItem} The observable instance for chaining
|
|
701
|
-
* @example
|
|
702
|
-
* const count = Observable(0);
|
|
703
|
-
* count.intercept((newVal, oldVal) => Math.max(0, newVal)); // Prevent negative values
|
|
704
|
-
*/
|
|
705
|
-
ObservableItem.prototype.intercept = function(callback) {
|
|
706
|
-
this.$interceptor = callback;
|
|
707
|
-
this.set = this.$setWithInterceptor;
|
|
655
|
+
NDElement.prototype.ref = function(target, name) {
|
|
656
|
+
target[name] = this.$element;
|
|
708
657
|
return this;
|
|
709
658
|
};
|
|
710
659
|
|
|
711
|
-
|
|
712
|
-
|
|
660
|
+
NDElement.prototype.refSelf = function(target, name) {
|
|
661
|
+
target[name] = this;
|
|
662
|
+
return this;
|
|
713
663
|
};
|
|
714
664
|
|
|
715
|
-
|
|
716
|
-
|
|
717
|
-
|
|
718
|
-
|
|
719
|
-
|
|
720
|
-
|
|
721
|
-
|
|
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;
|
|
722
673
|
}
|
|
674
|
+
element = null;
|
|
675
|
+
return this;
|
|
723
676
|
};
|
|
724
677
|
|
|
725
|
-
|
|
726
|
-
|
|
727
|
-
|
|
728
|
-
|
|
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
|
+
};
|
|
729
688
|
|
|
730
|
-
|
|
731
|
-
|
|
732
|
-
|
|
733
|
-
|
|
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);
|
|
734
695
|
}
|
|
735
|
-
if(
|
|
736
|
-
|
|
696
|
+
if(states.unmounted) {
|
|
697
|
+
this.$element.setAttribute('data--nd-unmounted', '1');
|
|
698
|
+
this.$observer.unmounted(states.unmounted);
|
|
737
699
|
}
|
|
700
|
+
return this;
|
|
738
701
|
};
|
|
739
702
|
|
|
740
|
-
|
|
741
|
-
this.
|
|
742
|
-
this.triggerListeners(operations);
|
|
703
|
+
NDElement.prototype.mounted = function(callback) {
|
|
704
|
+
return this.lifecycle({ mounted: callback });
|
|
743
705
|
};
|
|
744
706
|
|
|
745
|
-
|
|
746
|
-
this.
|
|
747
|
-
this.triggerFirstListener(operations);
|
|
707
|
+
NDElement.prototype.unmounted = function(callback) {
|
|
708
|
+
return this.lifecycle({ unmounted: callback });
|
|
748
709
|
};
|
|
749
710
|
|
|
750
|
-
|
|
751
|
-
|
|
752
|
-
if(this.$watchers?.size && this.$listeners?.length) {
|
|
753
|
-
this.trigger = (this.$listeners.length === 1) ? this.triggerWatchersAndFirstListener : this.triggerAll;
|
|
754
|
-
return;
|
|
755
|
-
}
|
|
756
|
-
if(this.$listeners?.length) {
|
|
757
|
-
if(this.$listeners.length === 1) {
|
|
758
|
-
this.$firstListener = this.$listeners[0];
|
|
759
|
-
this.trigger = this.triggerFirstListener;
|
|
760
|
-
}
|
|
761
|
-
else {
|
|
762
|
-
this.trigger = this.triggerListeners;
|
|
763
|
-
}
|
|
764
|
-
return;
|
|
765
|
-
}
|
|
766
|
-
if(this.$watchers?.size) {
|
|
767
|
-
this.trigger = this.triggerWatchers;
|
|
768
|
-
return;
|
|
769
|
-
}
|
|
770
|
-
this.trigger = noneTrigger;
|
|
771
|
-
};
|
|
772
|
-
ObservableItem.prototype.trigger = noneTrigger;
|
|
711
|
+
NDElement.prototype.beforeUnmount = function(id, callback) {
|
|
712
|
+
const el = this.$element;
|
|
773
713
|
|
|
774
|
-
|
|
775
|
-
|
|
776
|
-
|
|
777
|
-
return;
|
|
778
|
-
}
|
|
779
|
-
this.$previousValue = this.$currentValue;
|
|
780
|
-
this.$currentValue = newValue;
|
|
781
|
-
this.trigger();
|
|
782
|
-
this.$previousValue = null;
|
|
783
|
-
};
|
|
714
|
+
if(!DocumentObserver.beforeUnmount.has(el)) {
|
|
715
|
+
DocumentObserver.beforeUnmount.set(el, new Map());
|
|
716
|
+
const originalRemove = el.remove.bind(el);
|
|
784
717
|
|
|
785
|
-
|
|
786
|
-
* @param {*} data
|
|
787
|
-
*/
|
|
788
|
-
ObservableItem.prototype.$setWithInterceptor = function(data) {
|
|
789
|
-
let newValue = (typeof data === 'function') ? data(this.$currentValue) : data;
|
|
790
|
-
const result = this.$interceptor(newValue, this.$currentValue);
|
|
718
|
+
let $isUnmounting = false;
|
|
791
719
|
|
|
792
|
-
|
|
793
|
-
|
|
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
|
+
};
|
|
794
736
|
}
|
|
795
737
|
|
|
796
|
-
|
|
738
|
+
DocumentObserver.beforeUnmount.get(el).set(id, callback);
|
|
739
|
+
return this;
|
|
797
740
|
};
|
|
798
741
|
|
|
799
|
-
|
|
800
|
-
|
|
801
|
-
*/
|
|
802
|
-
ObservableItem.prototype.$basicSet = function(data) {
|
|
803
|
-
let newValue = (typeof data === 'function') ? data(this.$currentValue) : data;
|
|
804
|
-
this.$updateWithNewValue(newValue);
|
|
742
|
+
NDElement.prototype.htmlElement = function() {
|
|
743
|
+
return this.$element;
|
|
805
744
|
};
|
|
806
745
|
|
|
807
|
-
|
|
808
|
-
|
|
809
|
-
ObservableItem.prototype.val = function() {
|
|
810
|
-
return this.$currentValue;
|
|
811
|
-
};
|
|
746
|
+
NDElement.prototype.node = NDElement.prototype.htmlElement;
|
|
812
747
|
|
|
813
|
-
|
|
814
|
-
this.$
|
|
815
|
-
|
|
816
|
-
|
|
817
|
-
if(
|
|
818
|
-
|
|
819
|
-
|
|
820
|
-
|
|
821
|
-
}
|
|
822
|
-
}
|
|
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);
|
|
823
756
|
}
|
|
824
|
-
|
|
825
|
-
|
|
826
|
-
|
|
827
|
-
|
|
757
|
+
$element.append = shadowRoot.append.bind(shadowRoot);
|
|
758
|
+
$element.appendChild = shadowRoot.appendChild.bind(shadowRoot);
|
|
759
|
+
shadowRoot.append(...children);
|
|
760
|
+
|
|
761
|
+
return this;
|
|
828
762
|
};
|
|
829
763
|
|
|
830
|
-
|
|
831
|
-
|
|
832
|
-
* Useful for disposing resources, removing event listeners, or other cleanup tasks.
|
|
833
|
-
*
|
|
834
|
-
* @param {Function} callback - Cleanup function to execute on observable disposal
|
|
835
|
-
* @example
|
|
836
|
-
* const obs = Observable(0);
|
|
837
|
-
* obs.onCleanup(() => console.log('Cleaned up!'));
|
|
838
|
-
* obs.cleanup(); // Logs: "Cleaned up!"
|
|
839
|
-
*/
|
|
840
|
-
ObservableItem.prototype.onCleanup = function(callback) {
|
|
841
|
-
this.$cleanupListeners = this.$cleanupListeners ?? [];
|
|
842
|
-
this.$cleanupListeners.push(callback);
|
|
764
|
+
NDElement.prototype.openShadow = function(style = null) {
|
|
765
|
+
return this.shadow('open', style);
|
|
843
766
|
};
|
|
844
767
|
|
|
845
|
-
|
|
846
|
-
|
|
847
|
-
for (let i = 0; i < this.$cleanupListeners.length; i++) {
|
|
848
|
-
this.$cleanupListeners[i]();
|
|
849
|
-
}
|
|
850
|
-
this.$cleanupListeners = null;
|
|
851
|
-
}
|
|
852
|
-
MemoryManager.unregister(this.$memoryId);
|
|
853
|
-
this.disconnectAll();
|
|
854
|
-
delete this.$value;
|
|
768
|
+
NDElement.prototype.closedShadow = function(style = null) {
|
|
769
|
+
return this.shadow('closed', style);
|
|
855
770
|
};
|
|
856
771
|
|
|
857
772
|
/**
|
|
773
|
+
* Attaches a template binding to the element by hydrating it with the specified method.
|
|
858
774
|
*
|
|
859
|
-
* @param {
|
|
860
|
-
* @
|
|
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);
|
|
861
781
|
*/
|
|
862
|
-
|
|
863
|
-
|
|
864
|
-
|
|
865
|
-
this.$listeners.push(callback);
|
|
866
|
-
this.assocTrigger();
|
|
782
|
+
NDElement.prototype.attach = function(methodName, bindingHydrator) {
|
|
783
|
+
bindingHydrator.$hydrate(this.$element, methodName);
|
|
784
|
+
return this.$element;
|
|
867
785
|
};
|
|
868
786
|
|
|
869
787
|
/**
|
|
870
|
-
*
|
|
871
|
-
*
|
|
788
|
+
* Extends the current NDElement instance with custom methods.
|
|
789
|
+
* Methods are bound to the instance and available for chaining.
|
|
872
790
|
*
|
|
873
|
-
* @param {
|
|
874
|
-
* @
|
|
791
|
+
* @param {Object} methods - Object containing method definitions
|
|
792
|
+
* @returns {this} The NDElement instance with added methods for chaining
|
|
875
793
|
* @example
|
|
876
|
-
*
|
|
877
|
-
*
|
|
878
|
-
*
|
|
794
|
+
* element.nd.with({
|
|
795
|
+
* highlight() {
|
|
796
|
+
* this.$element.style.background = 'yellow';
|
|
797
|
+
* return this;
|
|
798
|
+
* }
|
|
799
|
+
* }).highlight().onClick(() => console.log('Clicked'));
|
|
879
800
|
*/
|
|
880
|
-
|
|
881
|
-
|
|
801
|
+
NDElement.prototype.with = function(methods) {
|
|
802
|
+
if (!methods || typeof methods !== 'object') {
|
|
803
|
+
throw new NativeDocumentError('extend() requires an object of methods');
|
|
804
|
+
}
|
|
882
805
|
|
|
883
|
-
|
|
806
|
+
for (const name in methods) {
|
|
807
|
+
const method = methods[name];
|
|
884
808
|
|
|
885
|
-
|
|
886
|
-
|
|
887
|
-
|
|
809
|
+
if (typeof method !== 'function') {
|
|
810
|
+
console.warn(`⚠️ extends(): "${name}" is not a function, skipping`);
|
|
811
|
+
continue;
|
|
812
|
+
}
|
|
888
813
|
|
|
889
|
-
|
|
890
|
-
watchValueList = callback;
|
|
891
|
-
this.$watchers.set(value, callback);
|
|
892
|
-
} else if(!Validator.isArray(watchValueList.list)) {
|
|
893
|
-
watchValueList = [watchValueList, callback];
|
|
894
|
-
callback = (value) => {
|
|
895
|
-
for(let i = 0, length = watchValueList.length; i < length; i++) {
|
|
896
|
-
watchValueList[i](value);
|
|
897
|
-
}
|
|
898
|
-
};
|
|
899
|
-
callback.list = watchValueList;
|
|
900
|
-
this.$watchers.set(value, callback);
|
|
901
|
-
} else {
|
|
902
|
-
watchValueList.list.push(callback);
|
|
814
|
+
this[name] = method.bind(this);
|
|
903
815
|
}
|
|
904
816
|
|
|
905
|
-
this
|
|
817
|
+
return this;
|
|
906
818
|
};
|
|
907
819
|
|
|
908
820
|
/**
|
|
909
|
-
*
|
|
821
|
+
* Extends the NDElement prototype with new methods available to all NDElement instances.
|
|
822
|
+
* Use this to add global methods to all NDElements.
|
|
910
823
|
*
|
|
911
|
-
* @param {
|
|
912
|
-
* @
|
|
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
|
|
913
827
|
* @example
|
|
914
|
-
*
|
|
915
|
-
*
|
|
916
|
-
*
|
|
917
|
-
*
|
|
918
|
-
*
|
|
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();
|
|
919
836
|
*/
|
|
920
|
-
|
|
921
|
-
if(!
|
|
922
|
-
|
|
923
|
-
const watchValueList = this.$watchers.get(value);
|
|
924
|
-
if(!watchValueList) return;
|
|
925
|
-
|
|
926
|
-
if(!callback || !Array.isArray(watchValueList.list)) {
|
|
927
|
-
this.$watchers?.delete(value);
|
|
928
|
-
this.assocTrigger();
|
|
929
|
-
return;
|
|
930
|
-
}
|
|
931
|
-
const index = watchValueList.indexOf(callback);
|
|
932
|
-
watchValueList?.splice(index, 1);
|
|
933
|
-
if(watchValueList.length === 1) {
|
|
934
|
-
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');
|
|
935
840
|
}
|
|
936
|
-
|
|
937
|
-
|
|
841
|
+
|
|
842
|
+
if (Array.isArray(methods)) {
|
|
843
|
+
throw new NativeDocumentError('NDElement.extend() requires an object, not an array');
|
|
938
844
|
}
|
|
939
|
-
this.assocTrigger();
|
|
940
|
-
};
|
|
941
845
|
|
|
942
|
-
|
|
943
|
-
|
|
944
|
-
|
|
945
|
-
|
|
946
|
-
|
|
947
|
-
* @example
|
|
948
|
-
* const status = Observable('loading');
|
|
949
|
-
* status.once('ready', (val) => console.log('Ready!'));
|
|
950
|
-
* status.once(val => val === 'error', (val) => console.log('Error occurred'));
|
|
951
|
-
*/
|
|
952
|
-
ObservableItem.prototype.once = function(predicate, callback) {
|
|
953
|
-
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
|
+
]);
|
|
954
851
|
|
|
955
|
-
const
|
|
956
|
-
if (
|
|
957
|
-
|
|
958
|
-
callback(val);
|
|
852
|
+
for (const name in methods) {
|
|
853
|
+
if (!Object.hasOwn(methods, name)) {
|
|
854
|
+
continue;
|
|
959
855
|
}
|
|
960
|
-
};
|
|
961
|
-
this.subscribe(handler);
|
|
962
|
-
};
|
|
963
856
|
|
|
964
|
-
|
|
965
|
-
|
|
966
|
-
|
|
967
|
-
|
|
968
|
-
|
|
969
|
-
|
|
970
|
-
|
|
971
|
-
|
|
972
|
-
|
|
857
|
+
const method = methods[name];
|
|
858
|
+
|
|
859
|
+
if (typeof method !== 'function') {
|
|
860
|
+
DebugManager.warn('NDElement.extend', `"${name}" is not a function, skipping`);
|
|
861
|
+
continue;
|
|
862
|
+
}
|
|
863
|
+
|
|
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
|
+
}
|
|
868
|
+
|
|
869
|
+
if (NDElement.prototype[name]) {
|
|
870
|
+
DebugManager.warn('NDElement.extend', `Overwriting existing prototype method "${name}"`);
|
|
871
|
+
}
|
|
872
|
+
|
|
873
|
+
NDElement.prototype[name] = method;
|
|
973
874
|
}
|
|
974
|
-
|
|
875
|
+
|
|
876
|
+
return NDElement;
|
|
975
877
|
};
|
|
976
878
|
|
|
977
|
-
|
|
978
|
-
|
|
979
|
-
|
|
980
|
-
|
|
981
|
-
|
|
982
|
-
ObservableItem.prototype.check = function(callback) {
|
|
983
|
-
return new ObservableChecker(this, callback)
|
|
879
|
+
const COMMON_NODE_TYPES = {
|
|
880
|
+
ELEMENT: 1,
|
|
881
|
+
TEXT: 3,
|
|
882
|
+
COMMENT: 8,
|
|
883
|
+
DOCUMENT_FRAGMENT: 11
|
|
984
884
|
};
|
|
985
885
|
|
|
986
|
-
|
|
987
|
-
|
|
988
|
-
|
|
989
|
-
|
|
990
|
-
|
|
991
|
-
|
|
992
|
-
|
|
993
|
-
|
|
994
|
-
|
|
995
|
-
|
|
996
|
-
|
|
997
|
-
|
|
998
|
-
|
|
999
|
-
|
|
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];
|
|
962
|
+
}
|
|
963
|
+
|
|
964
|
+
const invalid = children.filter(child => !this.isValidChild(child));
|
|
965
|
+
return invalid.length === 0;
|
|
966
|
+
},
|
|
967
|
+
validateChildren(children) {
|
|
968
|
+
if (!Array.isArray(children)) {
|
|
969
|
+
children = [children];
|
|
970
|
+
}
|
|
971
|
+
|
|
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(', ')}`);
|
|
975
|
+
}
|
|
976
|
+
|
|
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;
|
|
987
|
+
}
|
|
988
|
+
return Validator.isObject(data)
|
|
989
|
+
&& Object.values(data).some(value => Validator.isObservable(value));
|
|
990
|
+
},
|
|
991
|
+
/**
|
|
992
|
+
* Check if the data contains an observable reference.
|
|
993
|
+
* @param {string} data
|
|
994
|
+
* @returns {boolean}
|
|
995
|
+
*/
|
|
996
|
+
containsObservableReference(data) {
|
|
997
|
+
if(!data || typeof data !== 'string') {
|
|
998
|
+
return false;
|
|
999
|
+
}
|
|
1000
|
+
return /\{\{#ObItem::\([0-9]+\)\}\}/.test(data);
|
|
1001
|
+
},
|
|
1002
|
+
validateAttributes(attributes) {},
|
|
1003
|
+
|
|
1004
|
+
validateEventCallback(callback) {
|
|
1005
|
+
if (typeof callback !== 'function') {
|
|
1006
|
+
throw new NativeDocumentError('Event callback must be a function');
|
|
1007
|
+
}
|
|
1008
|
+
}
|
|
1000
1009
|
};
|
|
1001
1010
|
|
|
1011
|
+
function Anchor(name, isUniqueChild = false) {
|
|
1012
|
+
const anchorFragment = document.createDocumentFragment();
|
|
1013
|
+
anchorFragment.__Anchor__ = true;
|
|
1014
|
+
|
|
1015
|
+
const anchorStart = document.createComment('Anchor Start : '+name);
|
|
1016
|
+
const anchorEnd = document.createComment('/ Anchor End '+name);
|
|
1017
|
+
|
|
1018
|
+
anchorFragment.appendChild(anchorStart);
|
|
1019
|
+
anchorFragment.appendChild(anchorEnd);
|
|
1020
|
+
|
|
1021
|
+
anchorFragment.nativeInsertBefore = anchorFragment.insertBefore;
|
|
1022
|
+
anchorFragment.nativeAppendChild = anchorFragment.appendChild;
|
|
1023
|
+
anchorFragment.nativeAppend = anchorFragment.append;
|
|
1024
|
+
|
|
1025
|
+
const isParentUniqueChild = (parent) => (isUniqueChild || (parent.firstChild === anchorStart && parent.lastChild === anchorEnd));
|
|
1026
|
+
|
|
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;
|
|
1032
|
+
}
|
|
1033
|
+
if(isParentUniqueChild(parent) && target === anchorEnd) {
|
|
1034
|
+
parent.append(childElement, target);
|
|
1035
|
+
return;
|
|
1036
|
+
}
|
|
1037
|
+
parent.insertBefore(childElement, target);
|
|
1038
|
+
};
|
|
1039
|
+
|
|
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
|
+
};
|
|
1049
|
+
|
|
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
|
+
};
|
|
1059
|
+
|
|
1060
|
+
anchorFragment.append = function(...args ) {
|
|
1061
|
+
return anchorFragment.appendChild(args);
|
|
1062
|
+
};
|
|
1063
|
+
|
|
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
|
+
// }
|
|
1073
|
+
|
|
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
|
+
};
|
|
1083
|
+
|
|
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
|
+
};
|
|
1101
|
+
|
|
1102
|
+
anchorFragment.removeWithAnchors = async function() {
|
|
1103
|
+
await anchorFragment.removeChildren();
|
|
1104
|
+
anchorStart.remove();
|
|
1105
|
+
anchorEnd.remove();
|
|
1106
|
+
};
|
|
1107
|
+
|
|
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
|
+
};
|
|
1121
|
+
|
|
1122
|
+
anchorFragment.setContent = anchorFragment.replaceContent;
|
|
1123
|
+
|
|
1124
|
+
anchorFragment.insertBefore = function(child, anchor = null) {
|
|
1125
|
+
anchorFragment.appendChild(child, anchor);
|
|
1126
|
+
};
|
|
1127
|
+
|
|
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
|
+
}());
|
|
1242
|
+
|
|
1002
1243
|
/**
|
|
1003
|
-
* Creates an ObservableWhen that
|
|
1004
|
-
* Returns an object that can be subscribed to and will emit true/false.
|
|
1244
|
+
* Creates an ObservableWhen that tracks whether an observable equals a specific value.
|
|
1005
1245
|
*
|
|
1246
|
+
* @param {ObservableItem} observer - The observable to watch
|
|
1006
1247
|
* @param {*} value - The value to compare against
|
|
1007
|
-
* @
|
|
1248
|
+
* @class ObservableWhen
|
|
1249
|
+
*/
|
|
1250
|
+
const ObservableWhen = function(observer, value) {
|
|
1251
|
+
this.$target = value;
|
|
1252
|
+
this.$observer = observer;
|
|
1253
|
+
};
|
|
1254
|
+
|
|
1255
|
+
ObservableWhen.prototype.__$isObservableWhen = true;
|
|
1256
|
+
|
|
1257
|
+
/**
|
|
1258
|
+
* Subscribes to changes in the match status (true when observable equals target value).
|
|
1259
|
+
*
|
|
1260
|
+
* @param {Function} callback - Function called with boolean indicating if values match
|
|
1261
|
+
* @returns {Function} Unsubscribe function
|
|
1008
1262
|
* @example
|
|
1009
1263
|
* const status = Observable('idle');
|
|
1010
1264
|
* const isLoading = status.when('loading');
|
|
1011
1265
|
* isLoading.subscribe(active => console.log('Loading:', active));
|
|
1012
|
-
* status.set('loading'); // Logs: "Loading: true"
|
|
1013
1266
|
*/
|
|
1014
|
-
|
|
1015
|
-
return
|
|
1267
|
+
ObservableWhen.prototype.subscribe = function(callback) {
|
|
1268
|
+
return this.$observer.on(this.$target, callback);
|
|
1016
1269
|
};
|
|
1017
1270
|
|
|
1018
1271
|
/**
|
|
1019
|
-
*
|
|
1272
|
+
* Returns true if the observable's current value equals the target value.
|
|
1020
1273
|
*
|
|
1021
|
-
* @
|
|
1022
|
-
* @returns {boolean} True if values are equal
|
|
1023
|
-
* @example
|
|
1024
|
-
* const a = Observable(5);
|
|
1025
|
-
* const b = Observable(5);
|
|
1026
|
-
* a.equals(5); // true
|
|
1027
|
-
* a.equals(b); // true
|
|
1028
|
-
* a.equals(10); // false
|
|
1274
|
+
* @returns {boolean} True if observable value matches target value
|
|
1029
1275
|
*/
|
|
1030
|
-
|
|
1031
|
-
|
|
1032
|
-
return this.$currentValue === other.$currentValue;
|
|
1033
|
-
}
|
|
1034
|
-
return this.$currentValue === other;
|
|
1276
|
+
ObservableWhen.prototype.val = function() {
|
|
1277
|
+
return this.$observer.$currentValue === this.$target;
|
|
1035
1278
|
};
|
|
1036
1279
|
|
|
1037
1280
|
/**
|
|
1038
|
-
*
|
|
1281
|
+
* Returns true if the observable's current value equals the target value.
|
|
1282
|
+
* Alias for val().
|
|
1039
1283
|
*
|
|
1040
|
-
* @returns {boolean}
|
|
1041
|
-
* @example
|
|
1042
|
-
* const count = Observable(0);
|
|
1043
|
-
* count.toBool(); // false
|
|
1044
|
-
* count.set(5);
|
|
1045
|
-
* count.toBool(); // true
|
|
1284
|
+
* @returns {boolean} True if observable value matches target value
|
|
1046
1285
|
*/
|
|
1047
|
-
|
|
1048
|
-
|
|
1286
|
+
ObservableWhen.prototype.isMatch = ObservableWhen.prototype.val;
|
|
1287
|
+
|
|
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;
|
|
1295
|
+
|
|
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);
|
|
1318
|
+
}
|
|
1319
|
+
lastArgs = args;
|
|
1320
|
+
|
|
1321
|
+
// debounce mode: reset the timer for each call
|
|
1322
|
+
clearTimeout(timer);
|
|
1323
|
+
timer = setTimeout(() => invoke(fn, lastArgs, context), delay);
|
|
1324
|
+
}
|
|
1325
|
+
};
|
|
1326
|
+
|
|
1327
|
+
const nextTick = function(fn) {
|
|
1328
|
+
let pending = false;
|
|
1329
|
+
return function(...args) {
|
|
1330
|
+
if (pending) return;
|
|
1331
|
+
pending = true;
|
|
1332
|
+
|
|
1333
|
+
Promise.resolve().then(() => {
|
|
1334
|
+
fn.apply(this, args);
|
|
1335
|
+
pending = false;
|
|
1336
|
+
});
|
|
1337
|
+
};
|
|
1049
1338
|
};
|
|
1050
1339
|
|
|
1051
|
-
|
|
1052
|
-
|
|
1053
|
-
|
|
1054
|
-
|
|
1055
|
-
|
|
1056
|
-
|
|
1057
|
-
|
|
1058
|
-
|
|
1059
|
-
|
|
1060
|
-
|
|
1340
|
+
const deepClone = (value, onObservableFound) => {
|
|
1341
|
+
try {
|
|
1342
|
+
if(window.structuredClone !== undefined) {
|
|
1343
|
+
return window.structuredClone(value);
|
|
1344
|
+
}
|
|
1345
|
+
} catch (e){}
|
|
1346
|
+
|
|
1347
|
+
if (value === null || typeof value !== 'object') {
|
|
1348
|
+
return value;
|
|
1349
|
+
}
|
|
1350
|
+
|
|
1351
|
+
// Dates
|
|
1352
|
+
if (value instanceof Date) {
|
|
1353
|
+
return new Date(value.getTime());
|
|
1354
|
+
}
|
|
1355
|
+
|
|
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;
|
|
1061
1375
|
};
|
|
1062
1376
|
|
|
1063
|
-
|
|
1064
|
-
|
|
1065
|
-
|
|
1066
|
-
|
|
1067
|
-
|
|
1068
|
-
|
|
1069
|
-
|
|
1070
|
-
|
|
1071
|
-
|
|
1072
|
-
|
|
1073
|
-
|
|
1074
|
-
|
|
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
|
+
}
|
|
1385
|
+
},
|
|
1386
|
+
getNumber(key) {
|
|
1387
|
+
return Number(this.get(key));
|
|
1388
|
+
},
|
|
1389
|
+
getBool(key) {
|
|
1390
|
+
const value = this.get(key);
|
|
1391
|
+
return value === 'true' || value === '1';
|
|
1392
|
+
},
|
|
1393
|
+
setJson(key, value) {
|
|
1394
|
+
localStorage.setItem(key, JSON.stringify(value));
|
|
1395
|
+
},
|
|
1396
|
+
setBool(key, value) {
|
|
1397
|
+
localStorage.setItem(key, value ? 'true' : 'false');
|
|
1398
|
+
},
|
|
1399
|
+
get(key, defaultValue = null) {
|
|
1400
|
+
return localStorage.getItem(key) || defaultValue;
|
|
1401
|
+
},
|
|
1402
|
+
set(key, value) {
|
|
1403
|
+
return localStorage.setItem(key, value);
|
|
1404
|
+
},
|
|
1405
|
+
remove(key) {
|
|
1406
|
+
localStorage.removeItem(key);
|
|
1407
|
+
},
|
|
1408
|
+
has(key) {
|
|
1409
|
+
return localStorage.getItem(key) != null;
|
|
1075
1410
|
}
|
|
1076
|
-
const resetValue = (Validator.isObject(this.$initialValue))
|
|
1077
|
-
? deepClone(this.$initialValue, (observable) => {
|
|
1078
|
-
observable.reset();
|
|
1079
|
-
})
|
|
1080
|
-
: this.$initialValue;
|
|
1081
|
-
this.set(resetValue);
|
|
1082
1411
|
};
|
|
1083
1412
|
|
|
1084
|
-
|
|
1085
|
-
|
|
1086
|
-
|
|
1087
|
-
|
|
1088
|
-
|
|
1089
|
-
|
|
1090
|
-
|
|
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
|
+
}
|
|
1091
1423
|
};
|
|
1092
1424
|
|
|
1093
|
-
|
|
1094
|
-
|
|
1095
|
-
|
|
1096
|
-
|
|
1097
|
-
|
|
1098
|
-
|
|
1099
|
-
ObservableItem.prototype.valueOf = function() {
|
|
1100
|
-
return this.$currentValue;
|
|
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
|
+
}
|
|
1101
1431
|
};
|
|
1102
1432
|
|
|
1103
|
-
const
|
|
1104
|
-
mounted: new WeakMap(),
|
|
1105
|
-
beforeUnmount: new WeakMap(),
|
|
1106
|
-
mountedSupposedSize: 0,
|
|
1107
|
-
unmounted: new WeakMap(),
|
|
1108
|
-
unmountedSupposedSize: 0,
|
|
1109
|
-
observer: null,
|
|
1433
|
+
const StoreFactory = function() {
|
|
1110
1434
|
|
|
1111
|
-
|
|
1112
|
-
|
|
1113
|
-
if(!data) {
|
|
1114
|
-
return;
|
|
1115
|
-
}
|
|
1116
|
-
data.inDom = true;
|
|
1117
|
-
if(!data.mounted) {
|
|
1118
|
-
return;
|
|
1119
|
-
}
|
|
1120
|
-
if(Array.isArray(data.mounted)) {
|
|
1121
|
-
for(const cb of data.mounted) {
|
|
1122
|
-
cb(node);
|
|
1123
|
-
}
|
|
1124
|
-
return;
|
|
1125
|
-
}
|
|
1126
|
-
data.mounted(node);
|
|
1127
|
-
},
|
|
1435
|
+
const $stores = new Map();
|
|
1436
|
+
const $followersCache = new Map();
|
|
1128
1437
|
|
|
1129
|
-
|
|
1130
|
-
|
|
1131
|
-
|
|
1132
|
-
|
|
1133
|
-
|
|
1134
|
-
|
|
1135
|
-
|
|
1136
|
-
|
|
1438
|
+
/**
|
|
1439
|
+
* Internal helper — retrieves a store entry or throws if not found.
|
|
1440
|
+
*/
|
|
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
|
+
);
|
|
1137
1448
|
}
|
|
1449
|
+
return item;
|
|
1450
|
+
};
|
|
1138
1451
|
|
|
1139
|
-
|
|
1140
|
-
|
|
1141
|
-
|
|
1142
|
-
|
|
1143
|
-
|
|
1144
|
-
|
|
1145
|
-
|
|
1146
|
-
|
|
1147
|
-
|
|
1148
|
-
}
|
|
1452
|
+
/**
|
|
1453
|
+
* Internal helper — blocks write operations on a read-only observer.
|
|
1454
|
+
*/
|
|
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
|
+
};
|
|
1149
1466
|
|
|
1150
|
-
|
|
1151
|
-
|
|
1152
|
-
|
|
1467
|
+
const $createObservable = (value, options = {}) => {
|
|
1468
|
+
if(Array.isArray(value)) {
|
|
1469
|
+
return Observable$1.array(value, options);
|
|
1153
1470
|
}
|
|
1154
|
-
|
|
1471
|
+
if(typeof value === 'object') {
|
|
1472
|
+
return Observable$1.object(value, options);
|
|
1473
|
+
}
|
|
1474
|
+
return Observable$1(value, options);
|
|
1475
|
+
};
|
|
1155
1476
|
|
|
1156
|
-
|
|
1157
|
-
|
|
1158
|
-
|
|
1159
|
-
|
|
1160
|
-
|
|
1161
|
-
|
|
1162
|
-
|
|
1163
|
-
|
|
1164
|
-
|
|
1165
|
-
|
|
1166
|
-
|
|
1167
|
-
|
|
1168
|
-
|
|
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
|
+
);
|
|
1169
1492
|
}
|
|
1493
|
+
const observer = $createObservable(value);
|
|
1494
|
+
$stores.set(name, { observer, subscribers: new Set(), resettable: false, composed: false });
|
|
1495
|
+
return observer;
|
|
1496
|
+
},
|
|
1170
1497
|
|
|
1171
|
-
|
|
1172
|
-
|
|
1173
|
-
|
|
1174
|
-
|
|
1175
|
-
|
|
1176
|
-
|
|
1177
|
-
|
|
1178
|
-
|
|
1179
|
-
|
|
1180
|
-
|
|
1181
|
-
|
|
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
|
+
);
|
|
1182
1513
|
}
|
|
1183
|
-
|
|
1184
|
-
|
|
1514
|
+
const observer = $createObservable(value, { reset: true });
|
|
1515
|
+
$stores.set(name, { observer, subscribers: new Set(), resettable: true, composed: false });
|
|
1516
|
+
return observer;
|
|
1517
|
+
},
|
|
1185
1518
|
|
|
1186
|
-
|
|
1187
|
-
|
|
1188
|
-
|
|
1189
|
-
|
|
1190
|
-
|
|
1191
|
-
|
|
1192
|
-
|
|
1193
|
-
|
|
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
|
+
}
|
|
1194
1560
|
|
|
1195
|
-
|
|
1196
|
-
|
|
1197
|
-
|
|
1198
|
-
|
|
1199
|
-
disconnect: () => {
|
|
1200
|
-
if (mountedRegistered) {
|
|
1201
|
-
DocumentObserver.mounted.delete(element);
|
|
1202
|
-
DocumentObserver.mountedSupposedSize--;
|
|
1561
|
+
// Resolve dependency observers
|
|
1562
|
+
const depObservers = dependencies.map(depName => {
|
|
1563
|
+
if(typeof depName !== 'string') {
|
|
1564
|
+
return depName;
|
|
1203
1565
|
}
|
|
1204
|
-
|
|
1205
|
-
|
|
1206
|
-
|
|
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
|
+
);
|
|
1207
1572
|
}
|
|
1208
|
-
|
|
1209
|
-
}
|
|
1210
|
-
};
|
|
1573
|
+
return depItem.observer;
|
|
1574
|
+
});
|
|
1211
1575
|
|
|
1212
|
-
|
|
1213
|
-
|
|
1214
|
-
|
|
1215
|
-
|
|
1576
|
+
// Create computed observable from dependency observers
|
|
1577
|
+
const observer = Observable$1.computed(computation, depObservers);
|
|
1578
|
+
|
|
1579
|
+
$stores.set(name, { observer, subscribers: new Set(), resettable: false, composed: true });
|
|
1580
|
+
return observer;
|
|
1581
|
+
},
|
|
1582
|
+
|
|
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
|
+
);
|
|
1216
1606
|
}
|
|
1217
|
-
if (!
|
|
1218
|
-
|
|
1219
|
-
|
|
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
|
+
);
|
|
1220
1612
|
}
|
|
1221
|
-
|
|
1222
|
-
}
|
|
1613
|
+
item.observer.reset();
|
|
1614
|
+
},
|
|
1223
1615
|
|
|
1224
|
-
|
|
1225
|
-
|
|
1226
|
-
|
|
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
|
+
);
|
|
1227
1633
|
}
|
|
1228
|
-
|
|
1229
|
-
|
|
1230
|
-
|
|
1231
|
-
|
|
1232
|
-
|
|
1233
|
-
|
|
1234
|
-
|
|
1235
|
-
|
|
1236
|
-
|
|
1237
|
-
|
|
1238
|
-
|
|
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.`);
|
|
1239
1719
|
return;
|
|
1240
1720
|
}
|
|
1241
|
-
|
|
1242
|
-
|
|
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);
|
|
1243
1783
|
|
|
1244
|
-
|
|
1245
|
-
|
|
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
|
+
};
|
|
1246
1798
|
|
|
1247
|
-
|
|
1248
|
-
|
|
1249
|
-
|
|
1250
|
-
if (!mountedRegistered) {
|
|
1251
|
-
DocumentObserver.mountedSupposedSize++;
|
|
1252
|
-
mountedRegistered = true;
|
|
1253
|
-
}
|
|
1254
|
-
},
|
|
1799
|
+
return observer;
|
|
1800
|
+
}
|
|
1801
|
+
};
|
|
1255
1802
|
|
|
1256
|
-
unmounted: (callback) => {
|
|
1257
|
-
addListener('unmounted', callback);
|
|
1258
|
-
DocumentObserver.unmounted.set(element, data);
|
|
1259
|
-
if (!unmountedRegistered) {
|
|
1260
|
-
DocumentObserver.unmountedSupposedSize++;
|
|
1261
|
-
unmountedRegistered = true;
|
|
1262
|
-
}
|
|
1263
|
-
},
|
|
1264
1803
|
|
|
1265
|
-
|
|
1266
|
-
|
|
1804
|
+
return new Proxy($api, {
|
|
1805
|
+
get(target, prop) {
|
|
1806
|
+
if (typeof prop === 'symbol' || prop.startsWith('$') || prop in target) {
|
|
1807
|
+
return target[prop];
|
|
1267
1808
|
}
|
|
1268
|
-
|
|
1269
|
-
|
|
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.`);
|
|
1825
|
+
}
|
|
1826
|
+
});
|
|
1270
1827
|
};
|
|
1271
1828
|
|
|
1272
|
-
|
|
1273
|
-
DocumentObserver.observer.observe(document.body, {
|
|
1274
|
-
childList: true,
|
|
1275
|
-
subtree: true,
|
|
1276
|
-
});
|
|
1277
|
-
|
|
1278
|
-
function NDElement(element) {
|
|
1279
|
-
this.$element = element;
|
|
1280
|
-
this.$observer = null;
|
|
1281
|
-
}
|
|
1282
|
-
|
|
1283
|
-
NDElement.prototype.__$isNDElement = true;
|
|
1829
|
+
const Store = StoreFactory();
|
|
1284
1830
|
|
|
1285
|
-
|
|
1286
|
-
return this.$element;
|
|
1287
|
-
};
|
|
1831
|
+
Store.create('locale', 'fr');
|
|
1288
1832
|
|
|
1289
|
-
|
|
1290
|
-
|
|
1291
|
-
return
|
|
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
|
+
}, {})
|
|
1848
|
+
};
|
|
1292
1849
|
};
|
|
1293
1850
|
|
|
1294
|
-
|
|
1295
|
-
|
|
1296
|
-
return
|
|
1297
|
-
|
|
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);
|
|
1894
|
+
}
|
|
1895
|
+
return new Intl.DateTimeFormat(locale, { dateStyle }).format(new Date(value));
|
|
1896
|
+
},
|
|
1298
1897
|
|
|
1299
|
-
|
|
1300
|
-
|
|
1301
|
-
|
|
1302
|
-
|
|
1303
|
-
if(!elementChildren.$ndProx) {
|
|
1304
|
-
elementChildren.nd?.remove();
|
|
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);
|
|
1305
1902
|
}
|
|
1306
|
-
|
|
1307
|
-
}
|
|
1308
|
-
element = null;
|
|
1309
|
-
return this;
|
|
1310
|
-
};
|
|
1903
|
+
return new Intl.DateTimeFormat(locale, { hour, minute, second }).format(new Date(value));
|
|
1904
|
+
},
|
|
1311
1905
|
|
|
1312
|
-
|
|
1313
|
-
|
|
1314
|
-
|
|
1315
|
-
|
|
1316
|
-
|
|
1317
|
-
|
|
1318
|
-
|
|
1319
|
-
element = null;
|
|
1320
|
-
return this;
|
|
1321
|
-
};
|
|
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);
|
|
1910
|
+
}
|
|
1911
|
+
return new Intl.DateTimeFormat(locale, { dateStyle, hour, minute, second }).format(new Date(value));
|
|
1912
|
+
},
|
|
1322
1913
|
|
|
1323
|
-
|
|
1324
|
-
|
|
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
|
+
},
|
|
1325
1918
|
|
|
1326
|
-
|
|
1327
|
-
|
|
1328
|
-
|
|
1329
|
-
}
|
|
1330
|
-
if(states.unmounted) {
|
|
1331
|
-
this.$element.setAttribute('data--nd-unmounted', '1');
|
|
1332
|
-
this.$observer.unmounted(states.unmounted);
|
|
1333
|
-
}
|
|
1334
|
-
return this;
|
|
1919
|
+
plural: (value, locale, { singular, plural } = {}) => {
|
|
1920
|
+
const rule = new Intl.PluralRules(locale).select(value);
|
|
1921
|
+
return `${value} ${rule === 'one' ? singular : plural}`;
|
|
1922
|
+
},
|
|
1335
1923
|
};
|
|
1336
1924
|
|
|
1337
|
-
|
|
1338
|
-
|
|
1339
|
-
|
|
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;
|
|
1340
1933
|
|
|
1341
|
-
|
|
1342
|
-
|
|
1343
|
-
};
|
|
1934
|
+
this.$previousValue = null;
|
|
1935
|
+
this.$currentValue = value;
|
|
1344
1936
|
|
|
1345
|
-
|
|
1346
|
-
|
|
1937
|
+
this.$firstListener = null;
|
|
1938
|
+
this.$listeners = null;
|
|
1939
|
+
this.$watchers = null;
|
|
1347
1940
|
|
|
1348
|
-
|
|
1349
|
-
DocumentObserver.beforeUnmount.set(el, new Map());
|
|
1350
|
-
const originalRemove = el.remove.bind(el);
|
|
1941
|
+
this.$memoryId = null;
|
|
1351
1942
|
|
|
1352
|
-
|
|
1943
|
+
if(configs) {
|
|
1944
|
+
this.configs = configs;
|
|
1945
|
+
if(configs.reset) {
|
|
1946
|
+
this.$initialValue = Validator.isObject(value) ? deepClone(value) : value;
|
|
1947
|
+
}
|
|
1948
|
+
}
|
|
1949
|
+
}
|
|
1353
1950
|
|
|
1354
|
-
|
|
1355
|
-
|
|
1356
|
-
|
|
1357
|
-
|
|
1358
|
-
|
|
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
|
+
});
|
|
1359
1960
|
|
|
1360
|
-
|
|
1361
|
-
|
|
1362
|
-
for (const cb of callbacks.values()) {
|
|
1363
|
-
await cb.call(this, el);
|
|
1364
|
-
}
|
|
1365
|
-
} finally {
|
|
1366
|
-
originalRemove();
|
|
1367
|
-
$isUnmounting = false;
|
|
1368
|
-
}
|
|
1369
|
-
};
|
|
1370
|
-
}
|
|
1961
|
+
ObservableItem.prototype.__$isObservable = true;
|
|
1962
|
+
const noneTrigger = function() {};
|
|
1371
1963
|
|
|
1372
|
-
|
|
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;
|
|
1373
1977
|
return this;
|
|
1374
1978
|
};
|
|
1375
1979
|
|
|
1376
|
-
|
|
1377
|
-
|
|
1980
|
+
ObservableItem.prototype.triggerFirstListener = function(operations) {
|
|
1981
|
+
this.$firstListener(this.$currentValue, this.$previousValue, operations);
|
|
1378
1982
|
};
|
|
1379
1983
|
|
|
1380
|
-
|
|
1984
|
+
ObservableItem.prototype.triggerListeners = function(operations) {
|
|
1985
|
+
const $listeners = this.$listeners;
|
|
1986
|
+
const $previousValue = this.$previousValue;
|
|
1987
|
+
const $currentValue = this.$currentValue;
|
|
1381
1988
|
|
|
1382
|
-
|
|
1383
|
-
|
|
1384
|
-
const children = Array.from($element.childNodes);
|
|
1385
|
-
const shadowRoot = $element.attachShadow({ mode });
|
|
1386
|
-
if(style) {
|
|
1387
|
-
const styleNode = document.createElement("style");
|
|
1388
|
-
styleNode.textContent = style;
|
|
1389
|
-
shadowRoot.appendChild(styleNode);
|
|
1989
|
+
for(let i = 0, length = $listeners.length; i < length; i++) {
|
|
1990
|
+
$listeners[i]($currentValue, $previousValue, operations);
|
|
1390
1991
|
}
|
|
1391
|
-
|
|
1392
|
-
$element.appendChild = shadowRoot.appendChild.bind(shadowRoot);
|
|
1393
|
-
shadowRoot.append(...children);
|
|
1992
|
+
};
|
|
1394
1993
|
|
|
1395
|
-
|
|
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
|
+
}
|
|
1396
2007
|
};
|
|
1397
2008
|
|
|
1398
|
-
|
|
1399
|
-
|
|
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;
|
|
1400
2040
|
};
|
|
2041
|
+
ObservableItem.prototype.trigger = noneTrigger;
|
|
1401
2042
|
|
|
1402
|
-
|
|
1403
|
-
|
|
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;
|
|
1404
2052
|
};
|
|
1405
2053
|
|
|
1406
2054
|
/**
|
|
1407
|
-
*
|
|
1408
|
-
*
|
|
1409
|
-
* @param {string} methodName - Name of the hydration method to call
|
|
1410
|
-
* @param {BindingHydrator} bindingHydrator - Template binding with $hydrate method
|
|
1411
|
-
* @returns {HTMLElement} The underlying HTML element
|
|
1412
|
-
* @example
|
|
1413
|
-
* const onClick = $binder.attach((event, data) => console.log(data));
|
|
1414
|
-
* element.nd.attach('onClick', onClick);
|
|
2055
|
+
* @param {*} data
|
|
1415
2056
|
*/
|
|
1416
|
-
|
|
1417
|
-
|
|
1418
|
-
|
|
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);
|
|
1419
2066
|
};
|
|
1420
2067
|
|
|
1421
2068
|
/**
|
|
1422
|
-
*
|
|
1423
|
-
* Methods are bound to the instance and available for chaining.
|
|
1424
|
-
*
|
|
1425
|
-
* @param {Object} methods - Object containing method definitions
|
|
1426
|
-
* @returns {this} The NDElement instance with added methods for chaining
|
|
1427
|
-
* @example
|
|
1428
|
-
* element.nd.with({
|
|
1429
|
-
* highlight() {
|
|
1430
|
-
* this.$element.style.background = 'yellow';
|
|
1431
|
-
* return this;
|
|
1432
|
-
* }
|
|
1433
|
-
* }).highlight().onClick(() => console.log('Clicked'));
|
|
2069
|
+
* @param {*} data
|
|
1434
2070
|
*/
|
|
1435
|
-
|
|
1436
|
-
|
|
1437
|
-
|
|
1438
|
-
|
|
2071
|
+
ObservableItem.prototype.$basicSet = function(data) {
|
|
2072
|
+
let newValue = (typeof data === 'function') ? data(this.$currentValue) : data;
|
|
2073
|
+
this.$updateWithNewValue(newValue);
|
|
2074
|
+
};
|
|
1439
2075
|
|
|
1440
|
-
|
|
1441
|
-
const method = methods[name];
|
|
2076
|
+
ObservableItem.prototype.set = ObservableItem.prototype.$basicSet;
|
|
1442
2077
|
|
|
1443
|
-
|
|
1444
|
-
|
|
1445
|
-
|
|
1446
|
-
}
|
|
2078
|
+
ObservableItem.prototype.val = function() {
|
|
2079
|
+
return this.$currentValue;
|
|
2080
|
+
};
|
|
1447
2081
|
|
|
1448
|
-
|
|
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
|
+
}
|
|
1449
2092
|
}
|
|
1450
|
-
|
|
1451
|
-
|
|
2093
|
+
this.$watchers?.clear();
|
|
2094
|
+
this.$listeners = null;
|
|
2095
|
+
this.$watchers = null;
|
|
2096
|
+
this.trigger = noneTrigger;
|
|
1452
2097
|
};
|
|
1453
2098
|
|
|
1454
2099
|
/**
|
|
1455
|
-
*
|
|
1456
|
-
*
|
|
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.
|
|
1457
2102
|
*
|
|
1458
|
-
* @param {
|
|
1459
|
-
* @returns {typeof NDElement} The NDElement constructor
|
|
1460
|
-
* @throws {NativeDocumentError} If methods is not an object or contains non-function values
|
|
2103
|
+
* @param {Function} callback - Cleanup function to execute on observable disposal
|
|
1461
2104
|
* @example
|
|
1462
|
-
*
|
|
1463
|
-
*
|
|
1464
|
-
*
|
|
1465
|
-
* return this;
|
|
1466
|
-
* }
|
|
1467
|
-
* });
|
|
1468
|
-
* // Now all NDElements have .fadeIn() method
|
|
1469
|
-
* Div().nd.fadeIn();
|
|
2105
|
+
* const obs = Observable(0);
|
|
2106
|
+
* obs.onCleanup(() => console.log('Cleaned up!'));
|
|
2107
|
+
* obs.cleanup(); // Logs: "Cleaned up!"
|
|
1470
2108
|
*/
|
|
1471
|
-
|
|
1472
|
-
|
|
1473
|
-
|
|
1474
|
-
|
|
1475
|
-
|
|
1476
|
-
if (Array.isArray(methods)) {
|
|
1477
|
-
throw new NativeDocumentError('NDElement.extend() requires an object, not an array');
|
|
1478
|
-
}
|
|
1479
|
-
|
|
1480
|
-
const protectedMethods = new Set([
|
|
1481
|
-
'constructor', 'valueOf', '$element', '$observer',
|
|
1482
|
-
'ref', 'remove', 'cleanup', 'with', 'extend', 'attach',
|
|
1483
|
-
'lifecycle', 'mounted', 'unmounted', 'unmountChildren'
|
|
1484
|
-
]);
|
|
1485
|
-
|
|
1486
|
-
for (const name in methods) {
|
|
1487
|
-
if (!Object.hasOwn(methods, name)) {
|
|
1488
|
-
continue;
|
|
1489
|
-
}
|
|
1490
|
-
|
|
1491
|
-
const method = methods[name];
|
|
1492
|
-
|
|
1493
|
-
if (typeof method !== 'function') {
|
|
1494
|
-
DebugManager$1.warn('NDElement.extend', `"${name}" is not a function, skipping`);
|
|
1495
|
-
continue;
|
|
1496
|
-
}
|
|
1497
|
-
|
|
1498
|
-
if (protectedMethods.has(name)) {
|
|
1499
|
-
DebugManager$1.error('NDElement.extend', `Cannot override protected method "${name}"`);
|
|
1500
|
-
throw new NativeDocumentError(`Cannot override protected method "${name}"`);
|
|
1501
|
-
}
|
|
2109
|
+
ObservableItem.prototype.onCleanup = function(callback) {
|
|
2110
|
+
this.$cleanupListeners = this.$cleanupListeners ?? [];
|
|
2111
|
+
this.$cleanupListeners.push(callback);
|
|
2112
|
+
};
|
|
1502
2113
|
|
|
1503
|
-
|
|
1504
|
-
|
|
2114
|
+
ObservableItem.prototype.cleanup = function() {
|
|
2115
|
+
if (this.$cleanupListeners) {
|
|
2116
|
+
for (let i = 0; i < this.$cleanupListeners.length; i++) {
|
|
2117
|
+
this.$cleanupListeners[i]();
|
|
1505
2118
|
}
|
|
1506
|
-
|
|
1507
|
-
NDElement.prototype[name] = method;
|
|
2119
|
+
this.$cleanupListeners = null;
|
|
1508
2120
|
}
|
|
1509
|
-
|
|
1510
|
-
|
|
2121
|
+
MemoryManager.unregister(this.$memoryId);
|
|
2122
|
+
this.disconnectAll();
|
|
2123
|
+
delete this.$value;
|
|
1511
2124
|
};
|
|
1512
2125
|
|
|
1513
|
-
|
|
1514
|
-
|
|
1515
|
-
|
|
1516
|
-
|
|
1517
|
-
|
|
1518
|
-
|
|
2126
|
+
/**
|
|
2127
|
+
*
|
|
2128
|
+
* @param {Function} callback
|
|
2129
|
+
* @returns {(function(): void)}
|
|
2130
|
+
*/
|
|
2131
|
+
ObservableItem.prototype.subscribe = function(callback) {
|
|
2132
|
+
this.$listeners = this.$listeners ?? [];
|
|
1519
2133
|
|
|
1520
|
-
|
|
1521
|
-
|
|
1522
|
-
|
|
1523
|
-
},
|
|
1524
|
-
isTemplateBinding(value) {
|
|
1525
|
-
return value?.__$isTemplateBinding;
|
|
1526
|
-
},
|
|
1527
|
-
isObservableWhenResult(value) {
|
|
1528
|
-
return value && (value.__$isObservableWhen || (typeof value === 'object' && '$target' in value && '$observer' in value));
|
|
1529
|
-
},
|
|
1530
|
-
isArrayObservable(value) {
|
|
1531
|
-
return value?.__$isObservableArray;
|
|
1532
|
-
},
|
|
1533
|
-
isProxy(value) {
|
|
1534
|
-
return value?.__isProxy__
|
|
1535
|
-
},
|
|
1536
|
-
isObservableOrProxy(value) {
|
|
1537
|
-
return Validator.isObservable(value) || Validator.isProxy(value);
|
|
1538
|
-
},
|
|
1539
|
-
isAnchor(value) {
|
|
1540
|
-
return value?.__Anchor__
|
|
1541
|
-
},
|
|
1542
|
-
isObservableChecker(value) {
|
|
1543
|
-
return value?.__$isObservableChecker || value instanceof ObservableChecker;
|
|
1544
|
-
},
|
|
1545
|
-
isArray(value) {
|
|
1546
|
-
return Array.isArray(value);
|
|
1547
|
-
},
|
|
1548
|
-
isString(value) {
|
|
1549
|
-
return typeof value === 'string';
|
|
1550
|
-
},
|
|
1551
|
-
isNumber(value) {
|
|
1552
|
-
return typeof value === 'number';
|
|
1553
|
-
},
|
|
1554
|
-
isBoolean(value) {
|
|
1555
|
-
return typeof value === 'boolean';
|
|
1556
|
-
},
|
|
1557
|
-
isFunction(value) {
|
|
1558
|
-
return typeof value === 'function';
|
|
1559
|
-
},
|
|
1560
|
-
isAsyncFunction(value) {
|
|
1561
|
-
return typeof value === 'function' && value.constructor.name === 'AsyncFunction';
|
|
1562
|
-
},
|
|
1563
|
-
isObject(value) {
|
|
1564
|
-
return typeof value === 'object' && value !== null;
|
|
1565
|
-
},
|
|
1566
|
-
isJson(value) {
|
|
1567
|
-
return !(typeof value !== 'object' || value === null || Array.isArray(value) || value.constructor.name !== 'Object')
|
|
1568
|
-
},
|
|
1569
|
-
isElement(value) {
|
|
1570
|
-
return value && (
|
|
1571
|
-
value.nodeType === COMMON_NODE_TYPES.ELEMENT ||
|
|
1572
|
-
value.nodeType === COMMON_NODE_TYPES.TEXT ||
|
|
1573
|
-
value.nodeType === COMMON_NODE_TYPES.DOCUMENT_FRAGMENT ||
|
|
1574
|
-
value.nodeType === COMMON_NODE_TYPES.COMMENT
|
|
1575
|
-
);
|
|
1576
|
-
},
|
|
1577
|
-
isFragment(value) {
|
|
1578
|
-
return value?.nodeType === COMMON_NODE_TYPES.DOCUMENT_FRAGMENT;
|
|
1579
|
-
},
|
|
1580
|
-
isStringOrObservable(value) {
|
|
1581
|
-
return this.isString(value) || this.isObservable(value);
|
|
1582
|
-
},
|
|
1583
|
-
isValidChild(child) {
|
|
1584
|
-
return child === null ||
|
|
1585
|
-
this.isElement(child) ||
|
|
1586
|
-
this.isObservable(child) ||
|
|
1587
|
-
this.isNDElement(child) ||
|
|
1588
|
-
['string', 'number', 'boolean'].includes(typeof child);
|
|
1589
|
-
},
|
|
1590
|
-
isNDElement(child) {
|
|
1591
|
-
return child?.__$isNDElement || child instanceof NDElement;
|
|
1592
|
-
},
|
|
1593
|
-
isValidChildren(children) {
|
|
1594
|
-
if (!Array.isArray(children)) {
|
|
1595
|
-
children = [children];
|
|
1596
|
-
}
|
|
2134
|
+
this.$listeners.push(callback);
|
|
2135
|
+
this.assocTrigger();
|
|
2136
|
+
};
|
|
1597
2137
|
|
|
1598
|
-
|
|
1599
|
-
|
|
1600
|
-
|
|
1601
|
-
|
|
1602
|
-
|
|
1603
|
-
|
|
1604
|
-
|
|
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();
|
|
1605
2151
|
|
|
1606
|
-
|
|
1607
|
-
if (invalid.length > 0) {
|
|
1608
|
-
throw new NativeDocumentError(`Invalid children detected: ${invalid.map(i => typeof i).join(', ')}`);
|
|
1609
|
-
}
|
|
2152
|
+
let watchValueList = this.$watchers.get(value);
|
|
1610
2153
|
|
|
1611
|
-
|
|
1612
|
-
|
|
1613
|
-
|
|
1614
|
-
* Check if the data contains observables.
|
|
1615
|
-
* @param {Array|Object} data
|
|
1616
|
-
* @returns {boolean}
|
|
1617
|
-
*/
|
|
1618
|
-
containsObservables(data) {
|
|
1619
|
-
if(!data) {
|
|
1620
|
-
return false;
|
|
1621
|
-
}
|
|
1622
|
-
return Validator.isObject(data)
|
|
1623
|
-
&& Object.values(data).some(value => Validator.isObservable(value));
|
|
1624
|
-
},
|
|
1625
|
-
/**
|
|
1626
|
-
* Check if the data contains an observable reference.
|
|
1627
|
-
* @param {string} data
|
|
1628
|
-
* @returns {boolean}
|
|
1629
|
-
*/
|
|
1630
|
-
containsObservableReference(data) {
|
|
1631
|
-
if(!data || typeof data !== 'string') {
|
|
1632
|
-
return false;
|
|
1633
|
-
}
|
|
1634
|
-
return /\{\{#ObItem::\([0-9]+\)\}\}/.test(data);
|
|
1635
|
-
},
|
|
1636
|
-
validateAttributes(attributes) {},
|
|
2154
|
+
if(callback.__$isObservable) {
|
|
2155
|
+
callback = callback.set.bind(callback);
|
|
2156
|
+
}
|
|
1637
2157
|
|
|
1638
|
-
|
|
1639
|
-
|
|
1640
|
-
|
|
1641
|
-
|
|
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);
|
|
1642
2172
|
}
|
|
1643
|
-
};
|
|
1644
2173
|
|
|
1645
|
-
|
|
1646
|
-
|
|
1647
|
-
anchorFragment.__Anchor__ = true;
|
|
2174
|
+
this.assocTrigger();
|
|
2175
|
+
};
|
|
1648
2176
|
|
|
1649
|
-
|
|
1650
|
-
|
|
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;
|
|
1651
2191
|
|
|
1652
|
-
|
|
1653
|
-
|
|
2192
|
+
const watchValueList = this.$watchers.get(value);
|
|
2193
|
+
if(!watchValueList) return;
|
|
1654
2194
|
|
|
1655
|
-
|
|
1656
|
-
|
|
1657
|
-
|
|
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
|
+
};
|
|
1658
2210
|
|
|
1659
|
-
|
|
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;
|
|
1660
2223
|
|
|
1661
|
-
const
|
|
1662
|
-
|
|
1663
|
-
|
|
1664
|
-
|
|
1665
|
-
return;
|
|
1666
|
-
}
|
|
1667
|
-
if(isParentUniqueChild(parent) && target === anchorEnd) {
|
|
1668
|
-
parent.append(childElement, target);
|
|
1669
|
-
return;
|
|
2224
|
+
const handler = (val) => {
|
|
2225
|
+
if (fn(val)) {
|
|
2226
|
+
this.unsubscribe(handler);
|
|
2227
|
+
callback(val);
|
|
1670
2228
|
}
|
|
1671
|
-
parent.insertBefore(childElement, target);
|
|
1672
2229
|
};
|
|
2230
|
+
this.subscribe(handler);
|
|
2231
|
+
};
|
|
1673
2232
|
|
|
1674
|
-
|
|
1675
|
-
|
|
1676
|
-
|
|
1677
|
-
|
|
1678
|
-
|
|
1679
|
-
|
|
1680
|
-
|
|
1681
|
-
|
|
1682
|
-
|
|
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
|
+
};
|
|
1683
2245
|
|
|
1684
|
-
|
|
1685
|
-
|
|
1686
|
-
|
|
1687
|
-
|
|
1688
|
-
|
|
1689
|
-
|
|
1690
|
-
|
|
1691
|
-
|
|
1692
|
-
};
|
|
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
|
+
};
|
|
1693
2254
|
|
|
1694
|
-
|
|
1695
|
-
|
|
1696
|
-
|
|
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;
|
|
1697
2259
|
|
|
1698
|
-
|
|
1699
|
-
|
|
1700
|
-
|
|
1701
|
-
|
|
1702
|
-
|
|
1703
|
-
|
|
1704
|
-
|
|
1705
|
-
|
|
1706
|
-
|
|
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
|
+
};
|
|
1707
2275
|
|
|
1708
|
-
|
|
1709
|
-
|
|
1710
|
-
|
|
1711
|
-
|
|
1712
|
-
|
|
1713
|
-
|
|
1714
|
-
|
|
1715
|
-
|
|
1716
|
-
|
|
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
|
+
};
|
|
1717
2291
|
|
|
1718
|
-
|
|
1719
|
-
|
|
1720
|
-
|
|
1721
|
-
|
|
1722
|
-
|
|
1723
|
-
|
|
1724
|
-
|
|
1725
|
-
|
|
1726
|
-
|
|
1727
|
-
|
|
1728
|
-
|
|
1729
|
-
|
|
1730
|
-
|
|
1731
|
-
|
|
1732
|
-
|
|
1733
|
-
|
|
1734
|
-
|
|
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
|
+
};
|
|
1735
2324
|
|
|
1736
|
-
|
|
1737
|
-
|
|
1738
|
-
|
|
1739
|
-
|
|
1740
|
-
|
|
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
|
+
};
|
|
1741
2336
|
|
|
1742
|
-
|
|
1743
|
-
|
|
1744
|
-
|
|
1745
|
-
|
|
1746
|
-
|
|
1747
|
-
|
|
1748
|
-
|
|
1749
|
-
|
|
1750
|
-
|
|
1751
|
-
|
|
1752
|
-
|
|
1753
|
-
|
|
1754
|
-
}
|
|
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
|
+
};
|
|
1755
2357
|
|
|
1756
|
-
|
|
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
|
+
};
|
|
1757
2366
|
|
|
1758
|
-
|
|
1759
|
-
|
|
1760
|
-
|
|
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
|
+
};
|
|
1761
2376
|
|
|
1762
2377
|
|
|
1763
|
-
|
|
1764
|
-
|
|
1765
|
-
|
|
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;
|
|
1766
2431
|
|
|
1767
|
-
|
|
1768
|
-
return
|
|
1769
|
-
}
|
|
1770
|
-
anchorFragment.restore = function() {
|
|
1771
|
-
anchorFragment.appendChild(anchorFragment);
|
|
1772
|
-
};
|
|
1773
|
-
anchorFragment.clear = anchorFragment.remove;
|
|
1774
|
-
anchorFragment.detach = anchorFragment.remove;
|
|
2432
|
+
if (typeof type === 'function') {
|
|
2433
|
+
return new ObservableChecker(self, type);
|
|
2434
|
+
}
|
|
1775
2435
|
|
|
1776
|
-
|
|
1777
|
-
|
|
1778
|
-
for(let i = 0; i <= index; i++) {
|
|
1779
|
-
if(!currentNode.nextSibling) {
|
|
1780
|
-
return null;
|
|
1781
|
-
}
|
|
1782
|
-
currentNode = currentNode.nextSibling;
|
|
1783
|
-
}
|
|
1784
|
-
return currentNode !== anchorStart ? currentNode : null;
|
|
1785
|
-
};
|
|
2436
|
+
const formatter = Formatters[type];
|
|
2437
|
+
const localeObservable = Store.follow('locale');
|
|
1786
2438
|
|
|
1787
|
-
return
|
|
1788
|
-
|
|
1789
|
-
|
|
2439
|
+
return Observable$1.computed(() => formatter(self.val(), localeObservable.val(), options),
|
|
2440
|
+
[self, localeObservable]
|
|
2441
|
+
);
|
|
2442
|
+
};
|
|
1790
2443
|
|
|
1791
|
-
|
|
1792
|
-
|
|
1793
|
-
|
|
1794
|
-
|
|
1795
|
-
|
|
1796
|
-
|
|
1797
|
-
|
|
1798
|
-
|
|
1799
|
-
|
|
1800
|
-
|
|
1801
|
-
|
|
1802
|
-
|
|
1803
|
-
'translate',
|
|
1804
|
-
'draggable',
|
|
1805
|
-
'async',
|
|
1806
|
-
'defer',
|
|
1807
|
-
'autoplay',
|
|
1808
|
-
'controls',
|
|
1809
|
-
'loop',
|
|
1810
|
-
'muted',
|
|
1811
|
-
'download',
|
|
1812
|
-
'reversed',
|
|
1813
|
-
'open',
|
|
1814
|
-
'default',
|
|
1815
|
-
'formnovalidate',
|
|
1816
|
-
'novalidate',
|
|
1817
|
-
'scoped',
|
|
1818
|
-
'itemscope',
|
|
1819
|
-
'allowfullscreen',
|
|
1820
|
-
'allowpaymentrequest',
|
|
1821
|
-
'playsinline'
|
|
1822
|
-
]);
|
|
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
|
+
};
|
|
1823
2456
|
|
|
1824
2457
|
/**
|
|
1825
2458
|
*
|
|
@@ -1828,17 +2461,17 @@ var NativeComponents = (function (exports) {
|
|
|
1828
2461
|
* @returns {ObservableItem}
|
|
1829
2462
|
* @constructor
|
|
1830
2463
|
*/
|
|
1831
|
-
function Observable(value, configs = null) {
|
|
2464
|
+
function Observable$1(value, configs = null) {
|
|
1832
2465
|
return new ObservableItem(value, configs);
|
|
1833
2466
|
}
|
|
1834
2467
|
|
|
1835
|
-
const $$1 = Observable;
|
|
2468
|
+
const $$1 = Observable$1;
|
|
1836
2469
|
|
|
1837
2470
|
/**
|
|
1838
2471
|
*
|
|
1839
2472
|
* @param {string} propertyName
|
|
1840
2473
|
*/
|
|
1841
|
-
Observable.useValueProperty = function(propertyName = 'value') {
|
|
2474
|
+
Observable$1.useValueProperty = function(propertyName = 'value') {
|
|
1842
2475
|
Object.defineProperty(ObservableItem.prototype, propertyName, {
|
|
1843
2476
|
get() {
|
|
1844
2477
|
return this.$currentValue;
|
|
@@ -1856,7 +2489,7 @@ var NativeComponents = (function (exports) {
|
|
|
1856
2489
|
* @param id
|
|
1857
2490
|
* @returns {ObservableItem|null}
|
|
1858
2491
|
*/
|
|
1859
|
-
Observable.getById = function(id) {
|
|
2492
|
+
Observable$1.getById = function(id) {
|
|
1860
2493
|
const item = MemoryManager.getObservableById(parseInt(id));
|
|
1861
2494
|
if(!item) {
|
|
1862
2495
|
throw new NativeDocumentError('Observable.getById : No observable found with id ' + id);
|
|
@@ -1868,7 +2501,7 @@ var NativeComponents = (function (exports) {
|
|
|
1868
2501
|
*
|
|
1869
2502
|
* @param {ObservableItem} observable
|
|
1870
2503
|
*/
|
|
1871
|
-
Observable.cleanup = function(observable) {
|
|
2504
|
+
Observable$1.cleanup = function(observable) {
|
|
1872
2505
|
observable.cleanup();
|
|
1873
2506
|
};
|
|
1874
2507
|
|
|
@@ -1877,7 +2510,7 @@ var NativeComponents = (function (exports) {
|
|
|
1877
2510
|
* @param {Boolean} enable
|
|
1878
2511
|
* @param {{interval:Boolean, threshold:number}} options
|
|
1879
2512
|
*/
|
|
1880
|
-
Observable.autoCleanup = function(enable = false, options = {}) {
|
|
2513
|
+
Observable$1.autoCleanup = function(enable = false, options = {}) {
|
|
1881
2514
|
if(!enable) {
|
|
1882
2515
|
return;
|
|
1883
2516
|
}
|
|
@@ -2635,7 +3268,7 @@ var NativeComponents = (function (exports) {
|
|
|
2635
3268
|
String.prototype.use = function(args) {
|
|
2636
3269
|
const value = this;
|
|
2637
3270
|
|
|
2638
|
-
return Observable.computed(() => {
|
|
3271
|
+
return Observable$1.computed(() => {
|
|
2639
3272
|
return value.replace(/\$\{(.*?)}/g, (match, key) => {
|
|
2640
3273
|
const data = args[key];
|
|
2641
3274
|
if(Validator.isObservable(data)) {
|
|
@@ -2655,7 +3288,7 @@ var NativeComponents = (function (exports) {
|
|
|
2655
3288
|
return value;
|
|
2656
3289
|
}
|
|
2657
3290
|
const [_, id] = value.match(/\{\{#ObItem::\(([0-9]+)\)\}\}/);
|
|
2658
|
-
return Observable.getById(id);
|
|
3291
|
+
return Observable$1.getById(id);
|
|
2659
3292
|
});
|
|
2660
3293
|
};
|
|
2661
3294
|
|
|
@@ -2928,7 +3561,7 @@ var NativeComponents = (function (exports) {
|
|
|
2928
3561
|
}
|
|
2929
3562
|
}
|
|
2930
3563
|
|
|
2931
|
-
const viewArray = Observable.array();
|
|
3564
|
+
const viewArray = Observable$1.array();
|
|
2932
3565
|
|
|
2933
3566
|
const filters = Object.entries(filterCallbacks);
|
|
2934
3567
|
const updateView = () => {
|
|
@@ -3014,7 +3647,7 @@ var NativeComponents = (function (exports) {
|
|
|
3014
3647
|
* items.push(4); // Triggers update
|
|
3015
3648
|
* items.subscribe((arr) => console.log(arr));
|
|
3016
3649
|
*/
|
|
3017
|
-
Observable.array = function(target = [], configs = null) {
|
|
3650
|
+
Observable$1.array = function(target = [], configs = null) {
|
|
3018
3651
|
return new ObservableArray(target, configs);
|
|
3019
3652
|
};
|
|
3020
3653
|
|
|
@@ -3023,8 +3656,8 @@ var NativeComponents = (function (exports) {
|
|
|
3023
3656
|
* @param {Function} callback
|
|
3024
3657
|
* @returns {Function}
|
|
3025
3658
|
*/
|
|
3026
|
-
Observable.batch = function(callback) {
|
|
3027
|
-
const $observer = Observable(0);
|
|
3659
|
+
Observable$1.batch = function(callback) {
|
|
3660
|
+
const $observer = Observable$1(0);
|
|
3028
3661
|
const batch = function() {
|
|
3029
3662
|
if(Validator.isAsyncFunction(callback)) {
|
|
3030
3663
|
return (callback(...arguments)).then(() => {
|
|
@@ -3038,10 +3671,71 @@ var NativeComponents = (function (exports) {
|
|
|
3038
3671
|
return batch;
|
|
3039
3672
|
};
|
|
3040
3673
|
|
|
3041
|
-
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() {
|
|
3042
3736
|
const result = {};
|
|
3043
|
-
for(const key in
|
|
3044
|
-
const dataItem =
|
|
3737
|
+
for(const key in this.$observables) {
|
|
3738
|
+
const dataItem = this.$observables[key];
|
|
3045
3739
|
if(Validator.isObservable(dataItem)) {
|
|
3046
3740
|
let value = dataItem.val();
|
|
3047
3741
|
if(Array.isArray(value)) {
|
|
@@ -3064,9 +3758,10 @@ var NativeComponents = (function (exports) {
|
|
|
3064
3758
|
}
|
|
3065
3759
|
return result;
|
|
3066
3760
|
};
|
|
3761
|
+
ObservableObject.prototype.$val = ObservableObject.prototype.val;
|
|
3067
3762
|
|
|
3068
|
-
|
|
3069
|
-
const item =
|
|
3763
|
+
ObservableObject.prototype.get = function(property) {
|
|
3764
|
+
const item = this.$observables[property];
|
|
3070
3765
|
if(Validator.isObservable(item)) {
|
|
3071
3766
|
return item.val();
|
|
3072
3767
|
}
|
|
@@ -3075,100 +3770,87 @@ var NativeComponents = (function (exports) {
|
|
|
3075
3770
|
}
|
|
3076
3771
|
return item;
|
|
3077
3772
|
};
|
|
3773
|
+
ObservableObject.prototype.$get = ObservableObject.prototype.get;
|
|
3078
3774
|
|
|
3079
|
-
|
|
3080
|
-
|
|
3081
|
-
|
|
3082
|
-
|
|
3083
|
-
|
|
3084
|
-
|
|
3085
|
-
|
|
3086
|
-
|
|
3087
|
-
|
|
3088
|
-
|
|
3089
|
-
|
|
3090
|
-
|
|
3091
|
-
|
|
3092
|
-
|
|
3093
|
-
|
|
3094
|
-
|
|
3095
|
-
|
|
3096
|
-
|
|
3097
|
-
|
|
3098
|
-
* user.name = 'Jane X'
|
|
3099
|
-
* user.age.subscribe(val => console.log('Age:', val));
|
|
3100
|
-
*/
|
|
3101
|
-
Observable.init = function(initialValue, configs = null) {
|
|
3102
|
-
const data = {};
|
|
3103
|
-
for(const key in initialValue) {
|
|
3104
|
-
const itemValue = initialValue[key];
|
|
3105
|
-
if(Array.isArray(itemValue)) {
|
|
3106
|
-
if(configs?.deep !== false) {
|
|
3107
|
-
const mappedItemValue = itemValue.map(item => {
|
|
3108
|
-
if(Validator.isJson(item)) {
|
|
3109
|
-
return Observable.json(item, configs);
|
|
3110
|
-
}
|
|
3111
|
-
if(Validator.isArray(item)) {
|
|
3112
|
-
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);
|
|
3113
3794
|
}
|
|
3114
|
-
return Observable(item, configs);
|
|
3795
|
+
return Observable$1(item, configs);
|
|
3115
3796
|
});
|
|
3116
|
-
|
|
3797
|
+
targetItem.set(newValues);
|
|
3117
3798
|
continue;
|
|
3118
3799
|
}
|
|
3119
|
-
|
|
3800
|
+
targetItem.set([...newValue]);
|
|
3120
3801
|
continue;
|
|
3121
3802
|
}
|
|
3122
|
-
if(Validator.
|
|
3123
|
-
|
|
3803
|
+
if(Validator.isProxy(targetItem)) {
|
|
3804
|
+
targetItem.update(newValue);
|
|
3124
3805
|
continue;
|
|
3125
3806
|
}
|
|
3126
|
-
|
|
3807
|
+
this[key] = newValue;
|
|
3127
3808
|
}
|
|
3809
|
+
};
|
|
3810
|
+
ObservableObject.prototype.$set = ObservableObject.prototype.set;
|
|
3811
|
+
ObservableObject.prototype.$updateWith = ObservableObject.prototype.set;
|
|
3128
3812
|
|
|
3129
|
-
|
|
3130
|
-
|
|
3131
|
-
|
|
3132
|
-
|
|
3133
|
-
}
|
|
3134
|
-
};
|
|
3813
|
+
ObservableObject.prototype.observables = function() {
|
|
3814
|
+
return Object.values(this.$observables);
|
|
3815
|
+
};
|
|
3816
|
+
ObservableObject.prototype.$observables = ObservableObject.prototype.observables;
|
|
3135
3817
|
|
|
3136
|
-
|
|
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
|
+
});
|
|
3137
3838
|
|
|
3138
|
-
|
|
3839
|
+
this.originalSubscribe(callback);
|
|
3139
3840
|
|
|
3140
|
-
|
|
3141
|
-
|
|
3142
|
-
|
|
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
|
+
};
|
|
3143
3849
|
|
|
3144
|
-
|
|
3145
|
-
|
|
3146
|
-
const proxy = new Proxy(data, {
|
|
3147
|
-
get(target, property) {
|
|
3148
|
-
if(property === '__isProxy__') { return true; }
|
|
3149
|
-
if(property === '$value') { return $val() }
|
|
3150
|
-
if(property === 'get' || property === '$get') { return $get; }
|
|
3151
|
-
if(property === 'val' || property === '$val') { return $val; }
|
|
3152
|
-
if(property === 'set' || property === '$set' || property === '$updateWith') { return $updateWith; }
|
|
3153
|
-
if(property === 'observables' || property === '$observables') { return Object.values(target); }
|
|
3154
|
-
if(property === 'keys'|| property === '$keys') { return Object.keys(initialValue); }
|
|
3155
|
-
if(property === 'clone' || property === '$clone') { return $clone; }
|
|
3156
|
-
if(property === 'reset') { return $reset; }
|
|
3157
|
-
if(property === 'configs') { return configs; }
|
|
3158
|
-
return target[property];
|
|
3159
|
-
},
|
|
3160
|
-
set(target, prop, newValue) {
|
|
3161
|
-
if(target[prop] !== undefined) {
|
|
3162
|
-
Validator.isObservable(newValue)
|
|
3163
|
-
? target[prop].set(newValue.val())
|
|
3164
|
-
: target[prop].set(newValue);
|
|
3165
|
-
return true;
|
|
3166
|
-
}
|
|
3167
|
-
return true;
|
|
3168
|
-
}
|
|
3169
|
-
});
|
|
3850
|
+
ObservableObject.prototype.update = ObservableObject.prototype.set;
|
|
3170
3851
|
|
|
3171
|
-
|
|
3852
|
+
Observable$1.init = function(initialValue, configs = null) {
|
|
3853
|
+
return new ObservableObject(initialValue, configs)
|
|
3172
3854
|
};
|
|
3173
3855
|
|
|
3174
3856
|
/**
|
|
@@ -3176,8 +3858,8 @@ var NativeComponents = (function (exports) {
|
|
|
3176
3858
|
* @param {any[]} data
|
|
3177
3859
|
* @return Proxy[]
|
|
3178
3860
|
*/
|
|
3179
|
-
Observable.arrayOfObject = function(data) {
|
|
3180
|
-
return data.map(item => Observable.object(item));
|
|
3861
|
+
Observable$1.arrayOfObject = function(data) {
|
|
3862
|
+
return data.map(item => Observable$1.object(item));
|
|
3181
3863
|
};
|
|
3182
3864
|
|
|
3183
3865
|
/**
|
|
@@ -3185,7 +3867,7 @@ var NativeComponents = (function (exports) {
|
|
|
3185
3867
|
* @param {ObservableItem|Object<ObservableItem>} data
|
|
3186
3868
|
* @returns {{}|*|null}
|
|
3187
3869
|
*/
|
|
3188
|
-
Observable.value = function(data) {
|
|
3870
|
+
Observable$1.value = function(data) {
|
|
3189
3871
|
if(Validator.isObservable(data)) {
|
|
3190
3872
|
return data.val();
|
|
3191
3873
|
}
|
|
@@ -3196,52 +3878,15 @@ var NativeComponents = (function (exports) {
|
|
|
3196
3878
|
const result = [];
|
|
3197
3879
|
for(let i = 0, length = data.length; i < length; i++) {
|
|
3198
3880
|
const item = data[i];
|
|
3199
|
-
result.push(Observable.value(item));
|
|
3881
|
+
result.push(Observable$1.value(item));
|
|
3200
3882
|
}
|
|
3201
3883
|
return result;
|
|
3202
3884
|
}
|
|
3203
3885
|
return data;
|
|
3204
3886
|
};
|
|
3205
3887
|
|
|
3206
|
-
|
|
3207
|
-
Observable.
|
|
3208
|
-
const data = Validator.isProxy(newData) ? newData.$value : newData;
|
|
3209
|
-
const configs = $target.configs;
|
|
3210
|
-
|
|
3211
|
-
for(const key in data) {
|
|
3212
|
-
const targetItem = $target[key];
|
|
3213
|
-
const newValueOrigin = newData[key];
|
|
3214
|
-
const newValue = data[key];
|
|
3215
|
-
|
|
3216
|
-
if(Validator.isObservable(targetItem)) {
|
|
3217
|
-
if(Validator.isArray(newValue)) {
|
|
3218
|
-
const firstElementFromOriginalValue = newValueOrigin.at(0);
|
|
3219
|
-
if(Validator.isObservable(firstElementFromOriginalValue) || Validator.isProxy(firstElementFromOriginalValue)) {
|
|
3220
|
-
const newValues = newValue.map(item => {
|
|
3221
|
-
if(Validator.isProxy(firstElementFromOriginalValue)) {
|
|
3222
|
-
return Observable.init(item, configs);
|
|
3223
|
-
}
|
|
3224
|
-
return Observable(item, configs);
|
|
3225
|
-
});
|
|
3226
|
-
targetItem.set(newValues);
|
|
3227
|
-
continue;
|
|
3228
|
-
}
|
|
3229
|
-
targetItem.set([...newValue]);
|
|
3230
|
-
continue;
|
|
3231
|
-
}
|
|
3232
|
-
targetItem.set(newValue);
|
|
3233
|
-
continue;
|
|
3234
|
-
}
|
|
3235
|
-
if(Validator.isProxy(targetItem)) {
|
|
3236
|
-
Observable.update(targetItem, newValue);
|
|
3237
|
-
continue;
|
|
3238
|
-
}
|
|
3239
|
-
$target[key] = newValue;
|
|
3240
|
-
}
|
|
3241
|
-
};
|
|
3242
|
-
|
|
3243
|
-
Observable.object = Observable.init;
|
|
3244
|
-
Observable.json = Observable.init;
|
|
3888
|
+
Observable$1.object = Observable$1.init;
|
|
3889
|
+
Observable$1.json = Observable$1.init;
|
|
3245
3890
|
|
|
3246
3891
|
/**
|
|
3247
3892
|
* Creates a computed observable that automatically updates when its dependencies change.
|
|
@@ -3262,7 +3907,7 @@ var NativeComponents = (function (exports) {
|
|
|
3262
3907
|
* const batch = Observable.batch(() => { ... });
|
|
3263
3908
|
* const computed = Observable.computed(() => { ... }, batch);
|
|
3264
3909
|
*/
|
|
3265
|
-
Observable.computed = function(callback, dependencies = []) {
|
|
3910
|
+
Observable$1.computed = function(callback, dependencies = []) {
|
|
3266
3911
|
const initialValue = callback();
|
|
3267
3912
|
const observable = new ObservableItem(initialValue);
|
|
3268
3913
|
const updatedValue = nextTick(() => observable.set(callback()));
|
|
@@ -6931,10 +7576,10 @@ var NativeComponents = (function (exports) {
|
|
|
6931
7576
|
this.$element = null;
|
|
6932
7577
|
this.$configs = configs;
|
|
6933
7578
|
this.$fields = new Map();
|
|
6934
|
-
this.$submitting = Observable(false);
|
|
6935
|
-
this.$errors = Observable(null);
|
|
6936
|
-
this.$isDirty = Observable(false);
|
|
6937
|
-
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);
|
|
6938
7583
|
}
|
|
6939
7584
|
|
|
6940
7585
|
FormControl.defaultLayoutTemplate = null;
|
|
@@ -8255,7 +8900,7 @@ var NativeComponents = (function (exports) {
|
|
|
8255
8900
|
defaultItem: null,
|
|
8256
8901
|
value: (config?.data && Validator.isObservable(config.data))
|
|
8257
8902
|
? config.data
|
|
8258
|
-
: Observable.array(config?.data || []),
|
|
8903
|
+
: Observable$1.array(config?.data || []),
|
|
8259
8904
|
rules: null,
|
|
8260
8905
|
layout: null,
|
|
8261
8906
|
template: null,
|
|
@@ -8352,8 +8997,8 @@ var NativeComponents = (function (exports) {
|
|
|
8352
8997
|
defaultItemData = Validator.isObservable(defaultItemData)
|
|
8353
8998
|
? defaultItemData
|
|
8354
8999
|
: Validator.isObject(defaultItemData)
|
|
8355
|
-
? Observable.init(defaultItemData)
|
|
8356
|
-
: Observable(defaultItemData);
|
|
9000
|
+
? Observable$1.init(defaultItemData)
|
|
9001
|
+
: Observable$1(defaultItemData);
|
|
8357
9002
|
|
|
8358
9003
|
this.$items = this.$items || new WeakMap();
|
|
8359
9004
|
|
|
@@ -10835,9 +11480,9 @@ var NativeComponents = (function (exports) {
|
|
|
10835
11480
|
...configs
|
|
10836
11481
|
});
|
|
10837
11482
|
|
|
10838
|
-
this.$currentPage = Observable(1);
|
|
10839
|
-
this.$selectedRows = Observable.array();
|
|
10840
|
-
this.$expandedRows = Observable.array();
|
|
11483
|
+
this.$currentPage = Observable$1(1);
|
|
11484
|
+
this.$selectedRows = Observable$1.array();
|
|
11485
|
+
this.$expandedRows = Observable$1.array();
|
|
10841
11486
|
}
|
|
10842
11487
|
|
|
10843
11488
|
DataTable.defaultToolbarTemplate = null;
|