native-document 1.0.153 → 1.0.155
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/cdn.js +0 -11
- package/devtools/transformers/nd-vite-devtools.js +0 -1
- package/dist/native-document.components.min.js +1512 -255
- package/dist/native-document.dev.js +3238 -2974
- package/dist/native-document.dev.js.map +1 -1
- package/dist/native-document.min.js +1 -1
- package/elements.js +1 -6
- package/index.js +5 -8
- package/package.json +14 -2
- package/src/core/data/Observable.js +141 -0
- package/src/core/data/ObservableArray.js +1 -2
- package/src/core/data/ObservableItem.js +2 -0
- package/src/core/data/ObservableObject.js +10 -10
- package/src/core/data/observable-helpers/observable.is-to.js +1 -2
- package/src/core/data/observable-helpers/observable.prototypes.js +1 -2
- package/src/core/elements/index.js +2 -0
- package/src/core/wrappers/HtmlElementWrapper.js +8 -2
- package/src/core/data/observable-helpers/array.js +0 -22
- package/src/core/data/observable-helpers/batch.js +0 -22
- package/src/core/data/observable-helpers/computed.js +0 -55
- package/src/core/data/observable-helpers/object.js +0 -48
|
@@ -312,6 +312,19 @@ var NativeComponents = (function (exports) {
|
|
|
312
312
|
}
|
|
313
313
|
};
|
|
314
314
|
|
|
315
|
+
const nextTick = function(fn) {
|
|
316
|
+
let pending = false;
|
|
317
|
+
return function(...args) {
|
|
318
|
+
if (pending) return;
|
|
319
|
+
pending = true;
|
|
320
|
+
|
|
321
|
+
Promise.resolve().then(() => {
|
|
322
|
+
fn.apply(this, args);
|
|
323
|
+
pending = false;
|
|
324
|
+
});
|
|
325
|
+
};
|
|
326
|
+
};
|
|
327
|
+
|
|
315
328
|
const deepClone = (value, onObservableFound) => {
|
|
316
329
|
try {
|
|
317
330
|
if(window.structuredClone !== undefined) {
|
|
@@ -443,6 +456,7 @@ var NativeComponents = (function (exports) {
|
|
|
443
456
|
|
|
444
457
|
ObservableItem.prototype.__$Observable = true;
|
|
445
458
|
ObservableItem.prototype.__$isObservable = true;
|
|
459
|
+
ObservableItem.computed = () => {};
|
|
446
460
|
const noneTrigger = function() {};
|
|
447
461
|
|
|
448
462
|
/**
|
|
@@ -794,72 +808,1198 @@ var NativeComponents = (function (exports) {
|
|
|
794
808
|
};
|
|
795
809
|
|
|
796
810
|
/**
|
|
797
|
-
* Resets the observable to its initial value.
|
|
798
|
-
* Only works if the observable was created with { reset: true } config.
|
|
811
|
+
* Resets the observable to its initial value.
|
|
812
|
+
* Only works if the observable was created with { reset: true } config.
|
|
813
|
+
*
|
|
814
|
+
* @example
|
|
815
|
+
* const count = Observable(0, { reset: true });
|
|
816
|
+
* count.set(10);
|
|
817
|
+
* count.reset(); // Back to 0
|
|
818
|
+
*/
|
|
819
|
+
ObservableItem.prototype.reset = function() {
|
|
820
|
+
if(!this.configs?.reset) {
|
|
821
|
+
return;
|
|
822
|
+
}
|
|
823
|
+
const resetValue = (Validator.isObject(this.$initialValue))
|
|
824
|
+
? deepClone(this.$initialValue, (observable) => {
|
|
825
|
+
observable.reset();
|
|
826
|
+
})
|
|
827
|
+
: this.$initialValue;
|
|
828
|
+
this.set(resetValue);
|
|
829
|
+
};
|
|
830
|
+
|
|
831
|
+
/**
|
|
832
|
+
* Returns a string representation of the observable's current value.
|
|
833
|
+
*
|
|
834
|
+
* @returns {string} String representation of the current value
|
|
835
|
+
*/
|
|
836
|
+
ObservableItem.prototype.toString = function() {
|
|
837
|
+
return String(this.$currentValue);
|
|
838
|
+
};
|
|
839
|
+
|
|
840
|
+
/**
|
|
841
|
+
* Returns the primitive value of the observable (its current value).
|
|
842
|
+
* Called automatically in type coercion contexts.
|
|
843
|
+
*
|
|
844
|
+
* @returns {*} The current value of the observable
|
|
845
|
+
*/
|
|
846
|
+
ObservableItem.prototype.valueOf = function() {
|
|
847
|
+
return this.$currentValue;
|
|
848
|
+
};
|
|
849
|
+
|
|
850
|
+
|
|
851
|
+
|
|
852
|
+
ObservableItem.prototype.persist = function(key, options = {}) {
|
|
853
|
+
let value = $getFromStorage(key, this.$currentValue);
|
|
854
|
+
if(options.get) {
|
|
855
|
+
value = options.get(value);
|
|
856
|
+
}
|
|
857
|
+
this.set(value);
|
|
858
|
+
const saver = $saveToStorage(this.$currentValue);
|
|
859
|
+
this.subscribe((newValue) => {
|
|
860
|
+
saver(key, options.set ? options.set(newValue) : newValue);
|
|
861
|
+
});
|
|
862
|
+
return this;
|
|
863
|
+
};
|
|
864
|
+
|
|
865
|
+
ObservableItem.prototype.clone = function() {
|
|
866
|
+
let clonedValue = this.$currentValue;
|
|
867
|
+
|
|
868
|
+
if(clonedValue && typeof clonedValue === 'object') {
|
|
869
|
+
if(typeof clonedValue.clone === 'function') {
|
|
870
|
+
clonedValue = clonedValue.clone();
|
|
871
|
+
} else {
|
|
872
|
+
clonedValue = structuredClone(clonedValue);
|
|
873
|
+
}
|
|
874
|
+
}
|
|
875
|
+
|
|
876
|
+
return new ObservableItem(clonedValue);
|
|
877
|
+
};
|
|
878
|
+
|
|
879
|
+
function createMultiSourceFilter(sources, callbackFn){
|
|
880
|
+
const observables = sources.filter(Validator.isObservable);
|
|
881
|
+
|
|
882
|
+
const getValues = () => sources.map(src =>
|
|
883
|
+
Validator.isObservable(src) ? src.val() : src
|
|
884
|
+
);
|
|
885
|
+
|
|
886
|
+
return {
|
|
887
|
+
dependencies: observables.length > 0 ? observables : null,
|
|
888
|
+
callback: (value) => callbackFn(value, getValues())
|
|
889
|
+
};
|
|
890
|
+
}
|
|
891
|
+
|
|
892
|
+
function match(patternObservableOrValue, asRegexObservableOrValue = true, flagsObservableOrValue = ''){
|
|
893
|
+
return createMultiSourceFilter(
|
|
894
|
+
[patternObservableOrValue, asRegexObservableOrValue, flagsObservableOrValue],
|
|
895
|
+
(value, [pattern, asRegex, flags]) => {
|
|
896
|
+
if (!pattern) return true;
|
|
897
|
+
|
|
898
|
+
if (asRegex){
|
|
899
|
+
try {
|
|
900
|
+
const regex = new RegExp(pattern, flags);
|
|
901
|
+
return regex.test(String(value));
|
|
902
|
+
} catch (error){
|
|
903
|
+
DebugManager$1.warn('Invalid regex pattern:', pattern, error);
|
|
904
|
+
return false;
|
|
905
|
+
}
|
|
906
|
+
}
|
|
907
|
+
|
|
908
|
+
if (!flags || flags === ''){
|
|
909
|
+
return String(value).toLowerCase().includes(String(pattern).toLowerCase());
|
|
910
|
+
}
|
|
911
|
+
return String(value).includes(String(pattern));
|
|
912
|
+
}
|
|
913
|
+
);
|
|
914
|
+
}
|
|
915
|
+
|
|
916
|
+
const mutationMethods = ['push', 'pop', 'shift', 'unshift', 'reverse', 'sort', 'splice'];
|
|
917
|
+
const noMutationMethods = ['map', 'forEach', 'filter', 'reduce', 'some', 'every', 'find', 'findIndex', 'concat', 'includes', 'indexOf'];
|
|
918
|
+
|
|
919
|
+
/**
|
|
920
|
+
*
|
|
921
|
+
* @param target
|
|
922
|
+
* @param {{propagation: boolean, deep: boolean, reset: boolean}|null} configs
|
|
923
|
+
* @constructor
|
|
924
|
+
*/
|
|
925
|
+
const ObservableArray = function (target, configs = null) {
|
|
926
|
+
if(!Array.isArray(target)) {
|
|
927
|
+
throw new NativeDocumentError('Observable.array : target must be an array');
|
|
928
|
+
}
|
|
929
|
+
|
|
930
|
+
ObservableItem.call(this, target, configs);
|
|
931
|
+
};
|
|
932
|
+
|
|
933
|
+
ObservableArray.prototype = Object.create(ObservableItem.prototype);
|
|
934
|
+
ObservableArray.prototype.constructor = ObservableArray;
|
|
935
|
+
ObservableArray.prototype.__$isObservableArray = true;
|
|
936
|
+
|
|
937
|
+
|
|
938
|
+
Object.defineProperty(ObservableArray.prototype, 'length', {
|
|
939
|
+
get() {
|
|
940
|
+
return this.$currentValue.length;
|
|
941
|
+
}
|
|
942
|
+
});
|
|
943
|
+
|
|
944
|
+
|
|
945
|
+
ObservableArray.prototype.$mutate = function(action, args, mutateFn) {
|
|
946
|
+
if(this.$mutationInterceptor) {
|
|
947
|
+
const value = this.$mutationInterceptor(args, { action });
|
|
948
|
+
if(args !== undefined) {
|
|
949
|
+
args = value;
|
|
950
|
+
}
|
|
951
|
+
}
|
|
952
|
+
mutateFn(args);
|
|
953
|
+
};
|
|
954
|
+
|
|
955
|
+
mutationMethods.forEach((method) => {
|
|
956
|
+
ObservableArray.prototype[method] = function(...values) {
|
|
957
|
+
return this.$mutate(method, values, (argsToUse) => {
|
|
958
|
+
const result = this.$currentValue[method].apply(this.$currentValue, argsToUse);
|
|
959
|
+
this.trigger({ action: method, args: argsToUse, result });
|
|
960
|
+
return result;
|
|
961
|
+
})
|
|
962
|
+
};
|
|
963
|
+
});
|
|
964
|
+
|
|
965
|
+
noMutationMethods.forEach((method) => {
|
|
966
|
+
ObservableArray.prototype[method] = function(...values) {
|
|
967
|
+
return this.$currentValue[method].apply(this.$currentValue, values);
|
|
968
|
+
};
|
|
969
|
+
});
|
|
970
|
+
|
|
971
|
+
|
|
972
|
+
const $clearEvent = { action: 'clear' };
|
|
973
|
+
|
|
974
|
+
/**
|
|
975
|
+
* Removes all items from the array and triggers an update.
|
|
976
|
+
*
|
|
977
|
+
* @returns {boolean} True if array was cleared, false if it was already empty
|
|
978
|
+
* @example
|
|
979
|
+
* const items = Observable.array([1, 2, 3]);
|
|
980
|
+
* items.clear(); // []
|
|
981
|
+
*/
|
|
982
|
+
ObservableArray.prototype.clear = function() {
|
|
983
|
+
if(this.$currentValue.length === 0) {
|
|
984
|
+
return;
|
|
985
|
+
}
|
|
986
|
+
this.$mutate('clear', [], () => {
|
|
987
|
+
this.$currentValue.length = 0;
|
|
988
|
+
this.trigger($clearEvent);
|
|
989
|
+
});
|
|
990
|
+
return true;
|
|
991
|
+
};
|
|
992
|
+
|
|
993
|
+
/**
|
|
994
|
+
* Returns the element at the specified index in the array.
|
|
995
|
+
*
|
|
996
|
+
* @param {number} index - Zero-based index of the element to retrieve
|
|
997
|
+
* @returns {*} The element at the specified index
|
|
998
|
+
* @example
|
|
999
|
+
* const items = Observable.array(['a', 'b', 'c']);
|
|
1000
|
+
* items.at(1); // 'b'
|
|
1001
|
+
*/
|
|
1002
|
+
ObservableArray.prototype.at = function(index) {
|
|
1003
|
+
return this.$currentValue[index];
|
|
1004
|
+
};
|
|
1005
|
+
|
|
1006
|
+
|
|
1007
|
+
/**
|
|
1008
|
+
* Merges multiple values into the array and triggers an update.
|
|
1009
|
+
* Similar to push but with a different operation name.
|
|
1010
|
+
*
|
|
1011
|
+
* @param {Array} values - Array of values to merge
|
|
1012
|
+
* @example
|
|
1013
|
+
* const items = Observable.array([1, 2]);
|
|
1014
|
+
* items.merge([3, 4]); // [1, 2, 3, 4]
|
|
1015
|
+
*/
|
|
1016
|
+
ObservableArray.prototype.merge = function(values) {
|
|
1017
|
+
this.$mutate('merge', values, (valuesToMerge) => {
|
|
1018
|
+
this.$currentValue.push.apply(this.$currentValue, valuesToMerge);
|
|
1019
|
+
this.trigger({ action: 'merge', args: valuesToMerge });
|
|
1020
|
+
});
|
|
1021
|
+
};
|
|
1022
|
+
|
|
1023
|
+
/**
|
|
1024
|
+
* Counts the number of elements that satisfy the provided condition.
|
|
1025
|
+
*
|
|
1026
|
+
* @param {(value: *, index: number) => Boolean} condition - Function that tests each element (item, index) => boolean
|
|
1027
|
+
* @returns {number} The count of elements that satisfy the condition
|
|
1028
|
+
* @example
|
|
1029
|
+
* const numbers = Observable.array([1, 2, 3, 4, 5]);
|
|
1030
|
+
* numbers.count(n => n > 3); // 2
|
|
1031
|
+
*/
|
|
1032
|
+
ObservableArray.prototype.count = function(condition) {
|
|
1033
|
+
let count = 0;
|
|
1034
|
+
this.$currentValue.forEach((item, index) => {
|
|
1035
|
+
if(condition(item, index)) {
|
|
1036
|
+
count++;
|
|
1037
|
+
}
|
|
1038
|
+
});
|
|
1039
|
+
return count;
|
|
1040
|
+
};
|
|
1041
|
+
|
|
1042
|
+
/**
|
|
1043
|
+
* Swaps two elements at the specified indices and triggers an update.
|
|
1044
|
+
*
|
|
1045
|
+
* @param {number} indexA - Index of the first element
|
|
1046
|
+
* @param {number} indexB - Index of the second element
|
|
1047
|
+
* @returns {boolean} True if swap was successful, false if indices are out of bounds
|
|
1048
|
+
* @example
|
|
1049
|
+
* const items = Observable.array(['a', 'b', 'c']);
|
|
1050
|
+
* items.swap(0, 2); // ['c', 'b', 'a']
|
|
1051
|
+
*/
|
|
1052
|
+
ObservableArray.prototype.swap = function(indexA, indexB) {
|
|
1053
|
+
this.$mutate('swap', [indexA, indexB], ([indexA, indexB]) => {
|
|
1054
|
+
const value = this.$currentValue;
|
|
1055
|
+
const length = value.length;
|
|
1056
|
+
if(indexB < indexA) {
|
|
1057
|
+
const temp = indexA;
|
|
1058
|
+
indexA = indexB;
|
|
1059
|
+
indexB = temp;
|
|
1060
|
+
}
|
|
1061
|
+
if(length < indexA || length < indexB) {
|
|
1062
|
+
return false;
|
|
1063
|
+
}
|
|
1064
|
+
const elementA = value[indexA];
|
|
1065
|
+
const elementB = value[indexB];
|
|
1066
|
+
|
|
1067
|
+
value[indexA] = elementB;
|
|
1068
|
+
value[indexB] = elementA;
|
|
1069
|
+
this.trigger({ action: 'swap', args: [indexA, indexB], result: [elementA, elementB] });
|
|
1070
|
+
});
|
|
1071
|
+
return true;
|
|
1072
|
+
};
|
|
1073
|
+
|
|
1074
|
+
ObservableArray.prototype.swapItems = function(itemA, itemB) {
|
|
1075
|
+
const indexA = this.$currentValue.indexOf(itemA);
|
|
1076
|
+
const indexB = this.$currentValue.indexOf(itemB);
|
|
1077
|
+
|
|
1078
|
+
return this.swap(indexA, indexB);
|
|
1079
|
+
};
|
|
1080
|
+
|
|
1081
|
+
ObservableArray.prototype.insertAfter = function(data, target) {
|
|
1082
|
+
const targetIndex = this.$currentValue.indexOf(target);
|
|
1083
|
+
return this.splice(targetIndex + 1, 0, data);
|
|
1084
|
+
};
|
|
1085
|
+
|
|
1086
|
+
/**
|
|
1087
|
+
* Removes the element at the specified index and triggers an update.
|
|
1088
|
+
*
|
|
1089
|
+
* @param {number} index - Index of the element to remove
|
|
1090
|
+
* @returns {Array} Array containing the removed element, or empty array if index is invalid
|
|
1091
|
+
* @example
|
|
1092
|
+
* const items = Observable.array(['a', 'b', 'c']);
|
|
1093
|
+
* items.remove(1); // ['b'] - Array is now ['a', 'c']
|
|
1094
|
+
*/
|
|
1095
|
+
ObservableArray.prototype.remove = function(index) {
|
|
1096
|
+
let deleted = [];
|
|
1097
|
+
this.$mutate('remove', [index], ([idx]) => {
|
|
1098
|
+
deleted = this.$currentValue.splice(idx, 1);
|
|
1099
|
+
if(deleted.length === 0) {
|
|
1100
|
+
return;
|
|
1101
|
+
}
|
|
1102
|
+
this.trigger({action: 'remove', args: [idx], result: deleted[0]});
|
|
1103
|
+
});
|
|
1104
|
+
return deleted;
|
|
1105
|
+
};
|
|
1106
|
+
|
|
1107
|
+
/**
|
|
1108
|
+
* Removes the first occurrence of the specified item from the array.
|
|
1109
|
+
*
|
|
1110
|
+
* @param {*} item - The item to remove
|
|
1111
|
+
* @returns {Array} Array containing the removed element, or empty array if item not found
|
|
1112
|
+
* @example
|
|
1113
|
+
* const items = Observable.array(['a', 'b', 'c']);
|
|
1114
|
+
* items.removeItem('b'); // ['b'] - Array is now ['a', 'c']
|
|
1115
|
+
*/
|
|
1116
|
+
ObservableArray.prototype.removeItem = function(item) {
|
|
1117
|
+
const indexOfItem = this.$currentValue.indexOf(item);
|
|
1118
|
+
if(indexOfItem === -1) {
|
|
1119
|
+
return [];
|
|
1120
|
+
}
|
|
1121
|
+
return this.remove(indexOfItem);
|
|
1122
|
+
};
|
|
1123
|
+
|
|
1124
|
+
/**
|
|
1125
|
+
* Checks if the array is empty.
|
|
1126
|
+
*
|
|
1127
|
+
* @returns {boolean} True if array has no elements
|
|
1128
|
+
* @example
|
|
1129
|
+
* const items = Observable.array([]);
|
|
1130
|
+
* items.isEmpty(); // true
|
|
1131
|
+
*/
|
|
1132
|
+
ObservableArray.prototype.empty = function() {
|
|
1133
|
+
return this.$currentValue.length === 0;
|
|
1134
|
+
};
|
|
1135
|
+
|
|
1136
|
+
/**
|
|
1137
|
+
* Triggers a populate operation with the current array, iteration count, and callback.
|
|
1138
|
+
* Used internally for rendering optimizations.
|
|
1139
|
+
*
|
|
1140
|
+
* @param {number} iteration - Iteration count for rendering
|
|
1141
|
+
* @param {Function} callback - Callback function for rendering items
|
|
1142
|
+
*/
|
|
1143
|
+
ObservableArray.prototype.populateAndRender = function(iteration, callback) {
|
|
1144
|
+
this.trigger({ action: 'populate', args: [this.$currentValue, iteration, callback] });
|
|
1145
|
+
};
|
|
1146
|
+
|
|
1147
|
+
|
|
1148
|
+
/**
|
|
1149
|
+
* Creates a filtered view of the array based on predicates.
|
|
1150
|
+
* The filtered array updates automatically when source data or predicates change.
|
|
1151
|
+
*
|
|
1152
|
+
* @param {Object} predicates - Object mapping property names to filter conditions or functions
|
|
1153
|
+
* @returns {ObservableArray} A new observable array containing filtered items
|
|
1154
|
+
* @example
|
|
1155
|
+
* const users = Observable.array([
|
|
1156
|
+
* { name: 'John', age: 25 },
|
|
1157
|
+
* { name: 'Jane', age: 30 }
|
|
1158
|
+
* ]);
|
|
1159
|
+
*
|
|
1160
|
+
* const adults = users.where({ age: (val) => val >= 18 });
|
|
1161
|
+
*/
|
|
1162
|
+
ObservableArray.prototype.where = function(predicates) {
|
|
1163
|
+
if(typeof predicates === 'function') {
|
|
1164
|
+
predicates = { _: predicates };
|
|
1165
|
+
}
|
|
1166
|
+
const sourceArray = this;
|
|
1167
|
+
const observableDependencies = [sourceArray];
|
|
1168
|
+
const filterCallbacks = {};
|
|
1169
|
+
|
|
1170
|
+
for (const [key, rawPredicate] of Object.entries(predicates)) {
|
|
1171
|
+
const predicate = Validator.isObservable(rawPredicate) ? match(rawPredicate, false) : rawPredicate;
|
|
1172
|
+
if (predicate && typeof predicate === 'object' && 'callback' in predicate) {
|
|
1173
|
+
filterCallbacks[key] = predicate.callback;
|
|
1174
|
+
|
|
1175
|
+
if (predicate.dependencies) {
|
|
1176
|
+
const deps = Array.isArray(predicate.dependencies)
|
|
1177
|
+
? predicate.dependencies
|
|
1178
|
+
: [predicate.dependencies];
|
|
1179
|
+
observableDependencies.push.apply(observableDependencies, deps);
|
|
1180
|
+
}
|
|
1181
|
+
} else if(typeof predicate === 'function') {
|
|
1182
|
+
filterCallbacks[key] = predicate;
|
|
1183
|
+
} else {
|
|
1184
|
+
filterCallbacks[key] = (value) => value === predicate;
|
|
1185
|
+
}
|
|
1186
|
+
}
|
|
1187
|
+
|
|
1188
|
+
const viewArray = new ObservableArray([]);
|
|
1189
|
+
|
|
1190
|
+
const filters = Object.entries(filterCallbacks);
|
|
1191
|
+
const updateView = () => {
|
|
1192
|
+
const filtered = sourceArray.val().filter(item => {
|
|
1193
|
+
for (const [key, callback] of filters) {
|
|
1194
|
+
if(key === '_') {
|
|
1195
|
+
if (!callback(item)) return false;
|
|
1196
|
+
} else {
|
|
1197
|
+
if (!callback(item[key])) return false;
|
|
1198
|
+
}
|
|
1199
|
+
}
|
|
1200
|
+
return true;
|
|
1201
|
+
});
|
|
1202
|
+
|
|
1203
|
+
viewArray.set(filtered);
|
|
1204
|
+
};
|
|
1205
|
+
|
|
1206
|
+
observableDependencies.forEach(dep => dep.subscribe(updateView));
|
|
1207
|
+
|
|
1208
|
+
updateView();
|
|
1209
|
+
|
|
1210
|
+
return viewArray;
|
|
1211
|
+
};
|
|
1212
|
+
|
|
1213
|
+
/**
|
|
1214
|
+
* Creates a filtered view where at least one of the specified fields matches the filter.
|
|
1215
|
+
*
|
|
1216
|
+
* @param {Array<string>} fields - Array of field names to check
|
|
1217
|
+
* @param {FilterResult} filter - Filter condition with callback and dependencies
|
|
1218
|
+
* @returns {ObservableArray} A new observable array containing filtered items
|
|
1219
|
+
* @example
|
|
1220
|
+
* const products = Observable.array([
|
|
1221
|
+
* { name: 'Apple', category: 'Fruit' },
|
|
1222
|
+
* { name: 'Carrot', category: 'Vegetable' }
|
|
1223
|
+
* ]);
|
|
1224
|
+
* const searchTerm = Observable('App');
|
|
1225
|
+
* const filtered = products.whereSome(['name', 'category'], match(searchTerm));
|
|
1226
|
+
*/
|
|
1227
|
+
ObservableArray.prototype.whereSome = function(fields, filter) {
|
|
1228
|
+
return this.where({
|
|
1229
|
+
_: {
|
|
1230
|
+
dependencies: filter.dependencies,
|
|
1231
|
+
callback: (item) => fields.some(field => filter.callback(item[field]))
|
|
1232
|
+
}
|
|
1233
|
+
});
|
|
1234
|
+
};
|
|
1235
|
+
|
|
1236
|
+
/**
|
|
1237
|
+
* Creates a filtered view where all specified fields match the filter.
|
|
1238
|
+
*
|
|
1239
|
+
* @param {Array<string>} fields - Array of field names to check
|
|
1240
|
+
* @param {FilterResult} filter - Filter condition with callback and dependencies
|
|
1241
|
+
* @returns {ObservableArray} A new observable array containing filtered items
|
|
1242
|
+
* @example
|
|
1243
|
+
* const items = Observable.array([
|
|
1244
|
+
* { status: 'active', verified: true },
|
|
1245
|
+
* { status: 'active', verified: false }
|
|
1246
|
+
* ]);
|
|
1247
|
+
* const activeFilter = equals('active');
|
|
1248
|
+
* const filtered = items.whereEvery(['status', 'verified'], activeFilter);
|
|
1249
|
+
*/
|
|
1250
|
+
ObservableArray.prototype.whereEvery = function(fields, filter) {
|
|
1251
|
+
return this.where({
|
|
1252
|
+
_: {
|
|
1253
|
+
dependencies: filter.dependencies,
|
|
1254
|
+
callback: (item) => fields.every(field => filter.callback(item[field]))
|
|
1255
|
+
}
|
|
1256
|
+
});
|
|
1257
|
+
};
|
|
1258
|
+
|
|
1259
|
+
ObservableArray.prototype.deepSubscribe = function(callback) {
|
|
1260
|
+
const updatedValue = nextTick(() => callback(this.val()));
|
|
1261
|
+
const $listeners = new WeakMap();
|
|
1262
|
+
|
|
1263
|
+
const bindItem = (item) => {
|
|
1264
|
+
if ($listeners.has(item)) {
|
|
1265
|
+
return;
|
|
1266
|
+
}
|
|
1267
|
+
if (item?.__$isObservableArray) {
|
|
1268
|
+
$listeners.set(item, item.deepSubscribe(updatedValue));
|
|
1269
|
+
return;
|
|
1270
|
+
}
|
|
1271
|
+
if (item?.__$isObservable) {
|
|
1272
|
+
item.subscribe(updatedValue);
|
|
1273
|
+
$listeners.set(item, () => item.unsubscribe(updatedValue));
|
|
1274
|
+
}
|
|
1275
|
+
};
|
|
1276
|
+
|
|
1277
|
+
const unbindItem = (item) => {
|
|
1278
|
+
const unsub = $listeners.get(item);
|
|
1279
|
+
if (unsub) {
|
|
1280
|
+
unsub();
|
|
1281
|
+
$listeners.delete(item);
|
|
1282
|
+
}
|
|
1283
|
+
};
|
|
1284
|
+
|
|
1285
|
+
this.$currentValue.forEach(bindItem);
|
|
1286
|
+
this.subscribe(updatedValue);
|
|
1287
|
+
|
|
1288
|
+
this.subscribe((items, _, operations) => {
|
|
1289
|
+
switch (operations?.action) {
|
|
1290
|
+
case 'push':
|
|
1291
|
+
case 'unshift':
|
|
1292
|
+
operations.args.forEach(bindItem);
|
|
1293
|
+
break;
|
|
1294
|
+
|
|
1295
|
+
case 'splice': {
|
|
1296
|
+
const [start, deleteCount, ...newItems] = operations.args;
|
|
1297
|
+
operations.result?.forEach(unbindItem);
|
|
1298
|
+
newItems.forEach(bindItem);
|
|
1299
|
+
break;
|
|
1300
|
+
}
|
|
1301
|
+
|
|
1302
|
+
case 'remove':
|
|
1303
|
+
unbindItem(operations.result);
|
|
1304
|
+
break;
|
|
1305
|
+
|
|
1306
|
+
case 'merge':
|
|
1307
|
+
operations.args.forEach(bindItem);
|
|
1308
|
+
break;
|
|
1309
|
+
|
|
1310
|
+
case 'clear':
|
|
1311
|
+
this.$currentValue.forEach(unbindItem);
|
|
1312
|
+
break;
|
|
1313
|
+
}
|
|
1314
|
+
});
|
|
1315
|
+
|
|
1316
|
+
return () => {
|
|
1317
|
+
this.$currentValue.forEach(unbindItem);
|
|
1318
|
+
};
|
|
1319
|
+
};
|
|
1320
|
+
|
|
1321
|
+
|
|
1322
|
+
ObservableArray.prototype.sync = function(targetObservable) {
|
|
1323
|
+
if (!targetObservable || !targetObservable.__$isObservableArray) {
|
|
1324
|
+
throw new NativeDocumentError('ObservableArray.sync : target must be an ObservableArray');
|
|
1325
|
+
}
|
|
1326
|
+
|
|
1327
|
+
targetObservable.set([...this.$currentValue]);
|
|
1328
|
+
|
|
1329
|
+
const sync = (currentValue, _, operations) => {
|
|
1330
|
+
if (!operations) {
|
|
1331
|
+
targetObservable.set([...currentValue]);
|
|
1332
|
+
return;
|
|
1333
|
+
}
|
|
1334
|
+
|
|
1335
|
+
const { action, args } = operations;
|
|
1336
|
+
targetObservable[action].apply(targetObservable, args);
|
|
1337
|
+
};
|
|
1338
|
+
this.subscribe(sync);
|
|
1339
|
+
|
|
1340
|
+
return () => this.unsubscribe(sync);
|
|
1341
|
+
};
|
|
1342
|
+
|
|
1343
|
+
ObservableArray.prototype.clone = function() {
|
|
1344
|
+
return new ObservableArray(this.resolve());
|
|
1345
|
+
};
|
|
1346
|
+
|
|
1347
|
+
const ObservableObject = function(target, configs) {
|
|
1348
|
+
ObservableItem.call(this, target);
|
|
1349
|
+
this.$observables = {};
|
|
1350
|
+
this.configs = configs;
|
|
1351
|
+
|
|
1352
|
+
this.$load(target);
|
|
1353
|
+
|
|
1354
|
+
for(const name in target) {
|
|
1355
|
+
if(!Object.hasOwn(this, name)) {
|
|
1356
|
+
Object.defineProperty(this, name, {
|
|
1357
|
+
get: () => this.$observables[name],
|
|
1358
|
+
set: (value) => this.$observables[name].set(value)
|
|
1359
|
+
});
|
|
1360
|
+
}
|
|
1361
|
+
}
|
|
1362
|
+
|
|
1363
|
+
};
|
|
1364
|
+
|
|
1365
|
+
ObservableObject.prototype = Object.create(ObservableItem.prototype);
|
|
1366
|
+
|
|
1367
|
+
Object.defineProperty(ObservableObject, '$value', {
|
|
1368
|
+
get() {
|
|
1369
|
+
return this.val();
|
|
1370
|
+
},
|
|
1371
|
+
set(value) {
|
|
1372
|
+
this.set(value);
|
|
1373
|
+
}
|
|
1374
|
+
});
|
|
1375
|
+
|
|
1376
|
+
ObservableObject.prototype.__$isObservableObject = true;
|
|
1377
|
+
ObservableObject.prototype.__isProxy__ = true;
|
|
1378
|
+
|
|
1379
|
+
ObservableObject.prototype.$load = function(initialValue) {
|
|
1380
|
+
const configs = this.configs;
|
|
1381
|
+
for(const key in initialValue) {
|
|
1382
|
+
const itemValue = initialValue[key];
|
|
1383
|
+
if(Array.isArray(itemValue)) {
|
|
1384
|
+
if(configs?.deep !== false) {
|
|
1385
|
+
const mappedItemValue = itemValue.map(item => {
|
|
1386
|
+
if(Validator.isJson(item)) {
|
|
1387
|
+
return new ObservableObject(item, configs);
|
|
1388
|
+
}
|
|
1389
|
+
if(Validator.isArray(item)) {
|
|
1390
|
+
return new ObservableArray(item, configs);
|
|
1391
|
+
}
|
|
1392
|
+
return new ObservableItem(item, configs);
|
|
1393
|
+
});
|
|
1394
|
+
this.$observables[key] = new ObservableArray(mappedItemValue, configs);
|
|
1395
|
+
continue;
|
|
1396
|
+
}
|
|
1397
|
+
this.$observables[key] = new ObservableArray(itemValue, configs);
|
|
1398
|
+
continue;
|
|
1399
|
+
}
|
|
1400
|
+
if(Validator.isObservable(itemValue) || Validator.isProxy(itemValue)) {
|
|
1401
|
+
this.$observables[key] = itemValue;
|
|
1402
|
+
continue;
|
|
1403
|
+
}
|
|
1404
|
+
this.$observables[key] = (typeof itemValue === 'object') ? new ObservableObject(itemValue, configs) : new ObservableItem(itemValue, configs);
|
|
1405
|
+
}
|
|
1406
|
+
};
|
|
1407
|
+
|
|
1408
|
+
ObservableObject.prototype.val = function() {
|
|
1409
|
+
const result = {};
|
|
1410
|
+
for(const key in this.$observables) {
|
|
1411
|
+
const dataItem = this.$observables[key];
|
|
1412
|
+
if(Validator.isObservable(dataItem)) {
|
|
1413
|
+
let value = dataItem.val();
|
|
1414
|
+
if(Array.isArray(value)) {
|
|
1415
|
+
value = value.map(item => {
|
|
1416
|
+
if(Validator.isObservable(item)) {
|
|
1417
|
+
return item.val();
|
|
1418
|
+
}
|
|
1419
|
+
if(Validator.isProxy(item)) {
|
|
1420
|
+
return item.$value;
|
|
1421
|
+
}
|
|
1422
|
+
return item;
|
|
1423
|
+
});
|
|
1424
|
+
}
|
|
1425
|
+
result[key] = value;
|
|
1426
|
+
} else if(Validator.isProxy(dataItem)) {
|
|
1427
|
+
result[key] = dataItem.$value;
|
|
1428
|
+
} else {
|
|
1429
|
+
result[key] = dataItem;
|
|
1430
|
+
}
|
|
1431
|
+
}
|
|
1432
|
+
return result;
|
|
1433
|
+
};
|
|
1434
|
+
ObservableObject.prototype.$val = ObservableObject.prototype.val;
|
|
1435
|
+
|
|
1436
|
+
ObservableObject.prototype.get = function(property) {
|
|
1437
|
+
const item = this.$observables[property];
|
|
1438
|
+
if(Validator.isObservable(item)) {
|
|
1439
|
+
return item.val();
|
|
1440
|
+
}
|
|
1441
|
+
if(Validator.isProxy(item)) {
|
|
1442
|
+
return item.$value;
|
|
1443
|
+
}
|
|
1444
|
+
return item;
|
|
1445
|
+
};
|
|
1446
|
+
ObservableObject.prototype.$get = ObservableObject.prototype.get;
|
|
1447
|
+
|
|
1448
|
+
ObservableObject.prototype.set = function(newData) {
|
|
1449
|
+
const data = Validator.isProxy(newData) ? newData.$value : newData;
|
|
1450
|
+
const configs = this.configs;
|
|
1451
|
+
|
|
1452
|
+
for(const key in data) {
|
|
1453
|
+
const targetItem = this.$observables[key];
|
|
1454
|
+
const newValueOrigin = newData[key];
|
|
1455
|
+
const newValue = data[key];
|
|
1456
|
+
|
|
1457
|
+
if(Validator.isObservable(targetItem)) {
|
|
1458
|
+
if(!Validator.isArray(newValue)) {
|
|
1459
|
+
targetItem.set(newValue);
|
|
1460
|
+
continue;
|
|
1461
|
+
}
|
|
1462
|
+
const firstElementFromOriginalValue = newValueOrigin.at(0);
|
|
1463
|
+
if(Validator.isObservable(firstElementFromOriginalValue) || Validator.isProxy(firstElementFromOriginalValue)) {
|
|
1464
|
+
const newValues = newValue.map(item => {
|
|
1465
|
+
if(Validator.isProxy(firstElementFromOriginalValue)) {
|
|
1466
|
+
return new ObservableObject(item, configs);
|
|
1467
|
+
}
|
|
1468
|
+
return ObservableItem(item, configs);
|
|
1469
|
+
});
|
|
1470
|
+
targetItem.set(newValues);
|
|
1471
|
+
continue;
|
|
1472
|
+
}
|
|
1473
|
+
targetItem.set([...newValue]);
|
|
1474
|
+
continue;
|
|
1475
|
+
}
|
|
1476
|
+
if(Validator.isProxy(targetItem)) {
|
|
1477
|
+
targetItem.update(newValue);
|
|
1478
|
+
continue;
|
|
1479
|
+
}
|
|
1480
|
+
this[key] = newValue;
|
|
1481
|
+
}
|
|
1482
|
+
};
|
|
1483
|
+
ObservableObject.prototype.$set = ObservableObject.prototype.set;
|
|
1484
|
+
ObservableObject.prototype.$updateWith = ObservableObject.prototype.set;
|
|
1485
|
+
|
|
1486
|
+
ObservableObject.prototype.observables = function() {
|
|
1487
|
+
return Object.values(this.$observables);
|
|
1488
|
+
};
|
|
1489
|
+
ObservableObject.prototype.$observables = ObservableObject.prototype.observables;
|
|
1490
|
+
|
|
1491
|
+
ObservableObject.prototype.keys = function() {
|
|
1492
|
+
return Object.keys(this.$observables);
|
|
1493
|
+
};
|
|
1494
|
+
ObservableObject.prototype.$keys = ObservableObject.prototype.keys;
|
|
1495
|
+
ObservableObject.prototype.clone = function() {
|
|
1496
|
+
return new ObservableObject(this.val(), this.configs);
|
|
1497
|
+
};
|
|
1498
|
+
ObservableObject.prototype.$clone = ObservableObject.prototype.clone;
|
|
1499
|
+
ObservableObject.prototype.reset = function() {
|
|
1500
|
+
for(const key in this.$observables) {
|
|
1501
|
+
this.$observables[key].reset();
|
|
1502
|
+
}
|
|
1503
|
+
};
|
|
1504
|
+
ObservableObject.prototype.originalSubscribe = ObservableObject.prototype.subscribe;
|
|
1505
|
+
ObservableObject.prototype.subscribe = function(callback) {
|
|
1506
|
+
const observables = this.observables();
|
|
1507
|
+
const updatedValue = nextTick(() => this.trigger());
|
|
1508
|
+
|
|
1509
|
+
this.originalSubscribe(callback);
|
|
1510
|
+
|
|
1511
|
+
for (let i = 0, length = observables.length; i < length; i++) {
|
|
1512
|
+
const observable = observables[i];
|
|
1513
|
+
if (observable.__$isObservableArray) {
|
|
1514
|
+
observable.deepSubscribe(updatedValue);
|
|
1515
|
+
continue
|
|
1516
|
+
}
|
|
1517
|
+
observable.subscribe(updatedValue);
|
|
1518
|
+
}
|
|
1519
|
+
};
|
|
1520
|
+
ObservableObject.prototype.configs = function() {
|
|
1521
|
+
return this.configs;
|
|
1522
|
+
};
|
|
1523
|
+
|
|
1524
|
+
ObservableObject.prototype.update = ObservableObject.prototype.set;
|
|
1525
|
+
|
|
1526
|
+
const $computed = (fn, dependencies) => ObservableItem.computed(fn, dependencies);
|
|
1527
|
+
const $checker = (obs, fn) => obs.transform(fn);
|
|
1528
|
+
|
|
1529
|
+
//
|
|
1530
|
+
// is... -> ObservableChecker<boolean>
|
|
1531
|
+
//
|
|
1532
|
+
|
|
1533
|
+
ObservableItem.prototype.isEqualTo = function (value) {
|
|
1534
|
+
if (value?.__$Observable) {
|
|
1535
|
+
return $computed((a, b) => a === b, [this, value]);
|
|
1536
|
+
}
|
|
1537
|
+
return $checker(this, x => x === value);
|
|
1538
|
+
};
|
|
1539
|
+
|
|
1540
|
+
ObservableItem.prototype.isNotEqualTo = function (value) {
|
|
1541
|
+
if (value?.__$Observable) {
|
|
1542
|
+
return $computed((a, b) => a !== b, [this, value]);
|
|
1543
|
+
}
|
|
1544
|
+
return $checker(this, x => x !== value);
|
|
1545
|
+
};
|
|
1546
|
+
|
|
1547
|
+
ObservableItem.prototype.isGreaterThan = function (value) {
|
|
1548
|
+
if (value?.__$Observable) {
|
|
1549
|
+
return $computed((a, b) => a > b, [this, value]);
|
|
1550
|
+
}
|
|
1551
|
+
return $checker(this, x => x > value);
|
|
1552
|
+
};
|
|
1553
|
+
|
|
1554
|
+
ObservableItem.prototype.isGreaterThanOrEqualTo = function (value) {
|
|
1555
|
+
if (value?.__$Observable) {
|
|
1556
|
+
return $computed((a, b) => a >= b, [this, value]);
|
|
1557
|
+
}
|
|
1558
|
+
return $checker(this, x => x >= value);
|
|
1559
|
+
};
|
|
1560
|
+
|
|
1561
|
+
ObservableItem.prototype.isLessThan = function (value) {
|
|
1562
|
+
if (value?.__$Observable) {
|
|
1563
|
+
return $computed((a, b) => a < b, [this, value]);
|
|
1564
|
+
}
|
|
1565
|
+
return $checker(this, x => x < value);
|
|
1566
|
+
};
|
|
1567
|
+
|
|
1568
|
+
ObservableItem.prototype.isLessThanOrEqualTo = function (value) {
|
|
1569
|
+
if (value?.__$Observable) {
|
|
1570
|
+
return $computed((a, b) => a <= b, [this, value]);
|
|
1571
|
+
}
|
|
1572
|
+
return $checker(this, x => x <= value);
|
|
1573
|
+
};
|
|
1574
|
+
|
|
1575
|
+
ObservableItem.prototype.isBetween = function (min, max) {
|
|
1576
|
+
if (min.__$Observable && max.__$Observable) {
|
|
1577
|
+
return $computed((x, a, b) => x >= a && x <= b, [this, min, max]);
|
|
1578
|
+
}
|
|
1579
|
+
if (min.__$Observable) {
|
|
1580
|
+
return $computed((x, a) => x >= a && x <= max, [this, min]);
|
|
1581
|
+
}
|
|
1582
|
+
if (max.__$Observable) {
|
|
1583
|
+
return $computed((x, b) => x >= min && x <= b, [this, max]);
|
|
1584
|
+
}
|
|
1585
|
+
return $checker(this, x => x >= min && x <= max);
|
|
1586
|
+
};
|
|
1587
|
+
|
|
1588
|
+
ObservableItem.prototype.isNull = function () {
|
|
1589
|
+
return $checker(this, x => x == null);
|
|
1590
|
+
};
|
|
1591
|
+
|
|
1592
|
+
ObservableItem.prototype.isTruthy = function () {
|
|
1593
|
+
return $checker(this, x => !!x);
|
|
1594
|
+
};
|
|
1595
|
+
|
|
1596
|
+
ObservableItem.prototype.isFalsy = function () {
|
|
1597
|
+
return $checker(this, x => !x);
|
|
1598
|
+
};
|
|
1599
|
+
|
|
1600
|
+
ObservableItem.prototype.isStartingWith = function (str) {
|
|
1601
|
+
if (str?.__$Observable) {
|
|
1602
|
+
return $computed((a, b) => String(a).startsWith(b), [this, str]);
|
|
1603
|
+
}
|
|
1604
|
+
return $checker(this, x => String(x).startsWith(str));
|
|
1605
|
+
};
|
|
1606
|
+
|
|
1607
|
+
ObservableItem.prototype.isEndingWith = function (str) {
|
|
1608
|
+
if (str?.__$Observable) {
|
|
1609
|
+
return $computed((a, b) => String(a).endsWith(b), [this, str]);
|
|
1610
|
+
}
|
|
1611
|
+
return $checker(this, x => String(x).endsWith(str));
|
|
1612
|
+
};
|
|
1613
|
+
|
|
1614
|
+
ObservableItem.prototype.isMatchingPattern = function (regex) {
|
|
1615
|
+
if (regex?.__$Observable) {
|
|
1616
|
+
return $computed((a, b) => new RegExp(b).test(String(a)), [this, regex]);
|
|
1617
|
+
}
|
|
1618
|
+
return $checker(this, x => regex.test(String(x)));
|
|
1619
|
+
};
|
|
1620
|
+
|
|
1621
|
+
ObservableItem.prototype.isEmpty = function () {
|
|
1622
|
+
return $checker(this, x => x == null || x === '' || (Array.isArray(x) && x.length === 0));
|
|
1623
|
+
};
|
|
1624
|
+
|
|
1625
|
+
ObservableItem.prototype.isNotEmpty = function () {
|
|
1626
|
+
return $checker(this, x => x == null || x === '' || (Array.isArray(x) && x.length !== 0));
|
|
1627
|
+
};
|
|
1628
|
+
|
|
1629
|
+
ObservableItem.prototype.isIncludes = function (value) {
|
|
1630
|
+
if (value?.__$Observable) {
|
|
1631
|
+
return $computed((a, b) => {
|
|
1632
|
+
if (Array.isArray(a)) return a.includes(b);
|
|
1633
|
+
return String(a).includes(String(b));
|
|
1634
|
+
}, [this, value]);
|
|
1635
|
+
}
|
|
1636
|
+
return $checker(this, x => {
|
|
1637
|
+
if (Array.isArray(x)) return x.includes(value);
|
|
1638
|
+
return String(x).includes(String(value));
|
|
1639
|
+
});
|
|
1640
|
+
};
|
|
1641
|
+
|
|
1642
|
+
ObservableItem.prototype.isIncludedIn = function (array) {
|
|
1643
|
+
if (array?.__$Observable) {
|
|
1644
|
+
return $computed((a, b) => b.includes(a), [this, array]);
|
|
1645
|
+
}
|
|
1646
|
+
return $checker(this, x => array.includes(x));
|
|
1647
|
+
};
|
|
1648
|
+
|
|
1649
|
+
ObservableItem.prototype.isOneOf = ObservableItem.prototype.isIncludedIn;
|
|
1650
|
+
|
|
1651
|
+
ObservableItem.prototype.isHaving = function (key) {
|
|
1652
|
+
if (key?.__$Observable) {
|
|
1653
|
+
return $computed((a, b) => b in Object(a), [this, key]);
|
|
1654
|
+
}
|
|
1655
|
+
return $checker(this, x => key in Object(x));
|
|
1656
|
+
};
|
|
1657
|
+
|
|
1658
|
+
//
|
|
1659
|
+
// to... -> ObservableChecker<any>
|
|
1660
|
+
//
|
|
1661
|
+
|
|
1662
|
+
ObservableItem.prototype.toUpperCase = function () {
|
|
1663
|
+
return $checker(this, x => String(x).toUpperCase());
|
|
1664
|
+
};
|
|
1665
|
+
|
|
1666
|
+
ObservableItem.prototype.toLowerCase = function () {
|
|
1667
|
+
return $checker(this, x => String(x).toLowerCase());
|
|
1668
|
+
};
|
|
1669
|
+
|
|
1670
|
+
ObservableItem.prototype.toTrimmed = function () {
|
|
1671
|
+
return $checker(this, x => String(x).trim());
|
|
1672
|
+
};
|
|
1673
|
+
|
|
1674
|
+
ObservableItem.prototype.toBoolean = function () {
|
|
1675
|
+
return $checker(this, x => !!x);
|
|
1676
|
+
};
|
|
1677
|
+
|
|
1678
|
+
ObservableItem.prototype.toLiteral = function (template, placeholder = '${v}') {
|
|
1679
|
+
return $checker(this, x => template.replace(placeholder, x));
|
|
1680
|
+
};
|
|
1681
|
+
|
|
1682
|
+
ObservableItem.prototype.toFormatted = ObservableItem.prototype.toLiteral;
|
|
1683
|
+
|
|
1684
|
+
ObservableItem.prototype.toProperty = function (key) {
|
|
1685
|
+
const keys = key.split('.');
|
|
1686
|
+
return $checker(this, x => {
|
|
1687
|
+
let value = x;
|
|
1688
|
+
for (const k of keys) {
|
|
1689
|
+
if (value == null) return undefined;
|
|
1690
|
+
value = value[k];
|
|
1691
|
+
}
|
|
1692
|
+
return value;
|
|
1693
|
+
});
|
|
1694
|
+
};
|
|
1695
|
+
|
|
1696
|
+
ObservableItem.prototype.toLength = function () {
|
|
1697
|
+
return $checker(this, x => (x == null ? 0 : x.length));
|
|
1698
|
+
};
|
|
1699
|
+
|
|
1700
|
+
ObservableItem.prototype.toClamped = function (min, max) {
|
|
1701
|
+
if (min.__$Observable && max.__$Observable) {
|
|
1702
|
+
return $computed((x, a, b) => Math.min(Math.max(x, a), b), [this, min, max]);
|
|
1703
|
+
}
|
|
1704
|
+
if (min.__$Observable) {
|
|
1705
|
+
return $computed((x, a) => Math.min(Math.max(x, a), max), [this, min]);
|
|
1706
|
+
}
|
|
1707
|
+
if (max.__$Observable) {
|
|
1708
|
+
return $computed((x, b) => Math.min(Math.max(x, min), b), [this, max]);
|
|
1709
|
+
}
|
|
1710
|
+
return $checker(this, x => Math.min(Math.max(x, min), max));
|
|
1711
|
+
};
|
|
1712
|
+
|
|
1713
|
+
ObservableItem.prototype.toPercent = function (total) {
|
|
1714
|
+
if (total?.__$Observable) {
|
|
1715
|
+
return $computed((a, b) => (b === 0 ? 0 : (a / b) * 100), [this, total]);
|
|
1716
|
+
}
|
|
1717
|
+
return $checker(this, x => (total === 0 ? 0 : (x / total) * 100));
|
|
1718
|
+
};
|
|
1719
|
+
|
|
1720
|
+
/**
|
|
1721
|
+
* Creates an ObservableWhen that tracks whether an observable equals a specific value.
|
|
1722
|
+
*
|
|
1723
|
+
* @param {ObservableItem} observer - The observable to watch
|
|
1724
|
+
* @param {*} value - The value to compare against
|
|
1725
|
+
* @class ObservableWhen
|
|
1726
|
+
*/
|
|
1727
|
+
const ObservableWhen = function(observer, value) {
|
|
1728
|
+
this.$target = value;
|
|
1729
|
+
this.$observer = observer;
|
|
1730
|
+
};
|
|
1731
|
+
|
|
1732
|
+
ObservableWhen.prototype.__$Observable = true;
|
|
1733
|
+
ObservableWhen.prototype.__$isObservableWhen = true;
|
|
1734
|
+
|
|
1735
|
+
/**
|
|
1736
|
+
* Subscribes to changes in the match status (true when observable equals target value).
|
|
1737
|
+
*
|
|
1738
|
+
* @param {Function} callback - Function called with boolean indicating if values match
|
|
1739
|
+
* @returns {Function} Unsubscribe function
|
|
1740
|
+
* @example
|
|
1741
|
+
* const status = Observable('idle');
|
|
1742
|
+
* const isLoading = status.when('loading');
|
|
1743
|
+
* isLoading.subscribe(active => console.log('Loading:', active));
|
|
1744
|
+
*/
|
|
1745
|
+
ObservableWhen.prototype.subscribe = function(callback) {
|
|
1746
|
+
return this.$observer.on(this.$target, callback);
|
|
1747
|
+
};
|
|
1748
|
+
|
|
1749
|
+
/**
|
|
1750
|
+
* Returns true if the observable's current value equals the target value.
|
|
1751
|
+
*
|
|
1752
|
+
* @returns {boolean} True if observable value matches target value
|
|
1753
|
+
*/
|
|
1754
|
+
ObservableWhen.prototype.val = function() {
|
|
1755
|
+
return this.$observer.$currentValue === this.$target;
|
|
1756
|
+
};
|
|
1757
|
+
|
|
1758
|
+
/**
|
|
1759
|
+
* Returns true if the observable's current value equals the target value.
|
|
1760
|
+
* Alias for val().
|
|
1761
|
+
*
|
|
1762
|
+
* @returns {boolean} True if observable value matches target value
|
|
1763
|
+
*/
|
|
1764
|
+
ObservableWhen.prototype.isMatch = ObservableWhen.prototype.val;
|
|
1765
|
+
|
|
1766
|
+
/**
|
|
1767
|
+
* Returns true if the observable's current value equals the target value.
|
|
1768
|
+
* Alias for val().
|
|
1769
|
+
*
|
|
1770
|
+
* @returns {boolean} True if observable value matches target value
|
|
1771
|
+
*/
|
|
1772
|
+
ObservableWhen.prototype.isActive = ObservableWhen.prototype.val;
|
|
1773
|
+
|
|
1774
|
+
/**
|
|
1775
|
+
*
|
|
1776
|
+
* @param {ObservableItem} $observable
|
|
1777
|
+
* @param {Function} $checker
|
|
1778
|
+
* @class ObservableChecker
|
|
1779
|
+
*/
|
|
1780
|
+
function ObservableChecker($observable, $checker) {
|
|
1781
|
+
this.observable = $observable;
|
|
1782
|
+
|
|
1783
|
+
ObservableItem.call(this);
|
|
1784
|
+
|
|
1785
|
+
this.$mutation = $checker;
|
|
1786
|
+
|
|
1787
|
+
$observable.subscribe((newValue) => {
|
|
1788
|
+
this.$updateWithMutation(newValue);
|
|
1789
|
+
});
|
|
1790
|
+
|
|
1791
|
+
this.$updateWithMutation($observable.val());
|
|
1792
|
+
}
|
|
1793
|
+
|
|
1794
|
+
ObservableChecker.prototype = Object.create(ObservableItem.prototype);
|
|
1795
|
+
ObservableChecker.prototype.constructor = ObservableChecker;
|
|
1796
|
+
ObservableChecker.prototype.__$Observable = true;
|
|
1797
|
+
ObservableChecker.prototype.__$isObservableChecker = true;
|
|
1798
|
+
|
|
1799
|
+
|
|
1800
|
+
const ObservablePipe = ObservableChecker;
|
|
1801
|
+
ObservablePipe.prototype.constructor = ObservablePipe;
|
|
1802
|
+
|
|
1803
|
+
ObservableChecker.prototype.$updateWithMutation = function(newValue) {
|
|
1804
|
+
newValue = this.$mutation(newValue);
|
|
1805
|
+
return this.set(newValue);
|
|
1806
|
+
};
|
|
1807
|
+
|
|
1808
|
+
const $parseDateParts = (value, locale) => {
|
|
1809
|
+
const d = new Date(value);
|
|
1810
|
+
return {
|
|
1811
|
+
d,
|
|
1812
|
+
parts: new Intl.DateTimeFormat(locale, {
|
|
1813
|
+
year: 'numeric',
|
|
1814
|
+
month: 'long',
|
|
1815
|
+
day: '2-digit',
|
|
1816
|
+
hour: '2-digit',
|
|
1817
|
+
minute: '2-digit',
|
|
1818
|
+
second: '2-digit',
|
|
1819
|
+
}).formatToParts(d).reduce((acc, { type, value }) => {
|
|
1820
|
+
acc[type] = value;
|
|
1821
|
+
return acc;
|
|
1822
|
+
}, {})
|
|
1823
|
+
};
|
|
1824
|
+
};
|
|
1825
|
+
|
|
1826
|
+
const $applyDatePattern = (pattern, d, parts) => {
|
|
1827
|
+
const pad = n => String(n).padStart(2, '0');
|
|
1828
|
+
return pattern
|
|
1829
|
+
.replace('YYYY', parts.year)
|
|
1830
|
+
.replace('YY', parts.year.slice(-2))
|
|
1831
|
+
.replace('MMMM', parts.month)
|
|
1832
|
+
.replace('MMM', parts.month.slice(0, 3))
|
|
1833
|
+
.replace('MM', pad(d.getMonth() + 1))
|
|
1834
|
+
.replace('DD', pad(d.getDate()))
|
|
1835
|
+
.replace('D', d.getDate())
|
|
1836
|
+
.replace('HH', parts.hour)
|
|
1837
|
+
.replace('mm', parts.minute)
|
|
1838
|
+
.replace('ss', parts.second);
|
|
1839
|
+
};
|
|
1840
|
+
|
|
1841
|
+
const Formatters = {
|
|
1842
|
+
currency: (value, locale, { currency = 'XOF', notation, minimumFractionDigits, maximumFractionDigits } = {}) =>
|
|
1843
|
+
new Intl.NumberFormat(locale, {
|
|
1844
|
+
style: 'currency',
|
|
1845
|
+
currency,
|
|
1846
|
+
notation,
|
|
1847
|
+
minimumFractionDigits,
|
|
1848
|
+
maximumFractionDigits
|
|
1849
|
+
}).format(value),
|
|
1850
|
+
|
|
1851
|
+
number: (value, locale, { notation, minimumFractionDigits, maximumFractionDigits } = {}) =>
|
|
1852
|
+
new Intl.NumberFormat(locale, {
|
|
1853
|
+
notation,
|
|
1854
|
+
minimumFractionDigits,
|
|
1855
|
+
maximumFractionDigits
|
|
1856
|
+
}).format(value),
|
|
1857
|
+
|
|
1858
|
+
percent: (value, locale, { decimals = 1 } = {}) =>
|
|
1859
|
+
new Intl.NumberFormat(locale, {
|
|
1860
|
+
style: 'percent',
|
|
1861
|
+
maximumFractionDigits: decimals
|
|
1862
|
+
}).format(value),
|
|
1863
|
+
|
|
1864
|
+
date: (value, locale, { format, dateStyle = 'long' } = {}) => {
|
|
1865
|
+
if (format) {
|
|
1866
|
+
const { d, parts } = $parseDateParts(value, locale);
|
|
1867
|
+
return $applyDatePattern(format, d, parts);
|
|
1868
|
+
}
|
|
1869
|
+
return new Intl.DateTimeFormat(locale, { dateStyle }).format(new Date(value));
|
|
1870
|
+
},
|
|
1871
|
+
|
|
1872
|
+
time: (value, locale, { format, hour = '2-digit', minute = '2-digit', second } = {}) => {
|
|
1873
|
+
if (format) {
|
|
1874
|
+
const { d, parts } = $parseDateParts(value, locale);
|
|
1875
|
+
return $applyDatePattern(format, d, parts);
|
|
1876
|
+
}
|
|
1877
|
+
return new Intl.DateTimeFormat(locale, { hour, minute, second }).format(new Date(value));
|
|
1878
|
+
},
|
|
1879
|
+
|
|
1880
|
+
datetime: (value, locale, { format, dateStyle = 'long', hour = '2-digit', minute = '2-digit', second } = {}) => {
|
|
1881
|
+
if (format) {
|
|
1882
|
+
const { d, parts } = $parseDateParts(value, locale);
|
|
1883
|
+
return $applyDatePattern(format, d, parts);
|
|
1884
|
+
}
|
|
1885
|
+
return new Intl.DateTimeFormat(locale, { dateStyle, hour, minute, second }).format(new Date(value));
|
|
1886
|
+
},
|
|
1887
|
+
|
|
1888
|
+
relative: (value, locale, { unit = 'day', numeric = 'auto' } = {}) => {
|
|
1889
|
+
const diff = Math.round((value - Date.now()) / (1000 * 60 * 60 * 24));
|
|
1890
|
+
return new Intl.RelativeTimeFormat(locale, { numeric }).format(diff, unit);
|
|
1891
|
+
},
|
|
1892
|
+
|
|
1893
|
+
plural: (value, locale, { singular, plural } = {}) => {
|
|
1894
|
+
const rule = new Intl.PluralRules(locale).select(value);
|
|
1895
|
+
return `${value} ${rule === 'one' ? singular : plural}`;
|
|
1896
|
+
},
|
|
1897
|
+
};
|
|
1898
|
+
|
|
1899
|
+
/**
|
|
1900
|
+
* Creates an ObservableWhen that represents whether the observable equals a specific value.
|
|
1901
|
+
* Returns an object that can be subscribed to and will emit true/false.
|
|
799
1902
|
*
|
|
1903
|
+
* @param {*} value - The value to compare against
|
|
1904
|
+
* @returns {ObservableWhen} An ObservableWhen instance that tracks when the observable equals the value
|
|
800
1905
|
* @example
|
|
801
|
-
* const
|
|
802
|
-
*
|
|
803
|
-
*
|
|
1906
|
+
* const status = Observable('idle');
|
|
1907
|
+
* const isLoading = status.when('loading');
|
|
1908
|
+
* isLoading.subscribe(active => console.log('Loading:', active));
|
|
1909
|
+
* status.set('loading'); // Logs: "Loading: true"
|
|
804
1910
|
*/
|
|
805
|
-
ObservableItem.prototype.reset = function() {
|
|
806
|
-
if(!this.configs?.reset) {
|
|
807
|
-
return;
|
|
808
|
-
}
|
|
809
|
-
const resetValue = (Validator.isObject(this.$initialValue))
|
|
810
|
-
? deepClone(this.$initialValue, (observable) => {
|
|
811
|
-
observable.reset();
|
|
812
|
-
})
|
|
813
|
-
: this.$initialValue;
|
|
814
|
-
this.set(resetValue);
|
|
815
|
-
};
|
|
816
1911
|
|
|
817
|
-
|
|
818
|
-
|
|
819
|
-
*
|
|
820
|
-
* @returns {string} String representation of the current value
|
|
821
|
-
*/
|
|
822
|
-
ObservableItem.prototype.toString = function() {
|
|
823
|
-
return String(this.$currentValue);
|
|
1912
|
+
ObservableItem.prototype.when = function(value) {
|
|
1913
|
+
return new ObservableWhen(this, value);
|
|
824
1914
|
};
|
|
825
1915
|
|
|
1916
|
+
|
|
1917
|
+
|
|
826
1918
|
/**
|
|
827
|
-
*
|
|
828
|
-
*
|
|
829
|
-
*
|
|
830
|
-
* @returns {*} The current value of the observable
|
|
1919
|
+
* Create an Observable checker instance
|
|
1920
|
+
* @param callback
|
|
1921
|
+
* @returns {ObservableChecker}
|
|
831
1922
|
*/
|
|
832
|
-
ObservableItem.prototype.
|
|
833
|
-
return this
|
|
1923
|
+
ObservableItem.prototype.check = function(callback) {
|
|
1924
|
+
return new ObservableChecker(this, callback)
|
|
834
1925
|
};
|
|
835
1926
|
|
|
836
|
-
|
|
837
|
-
|
|
838
|
-
|
|
839
|
-
|
|
840
|
-
|
|
841
|
-
|
|
1927
|
+
ObservableItem.prototype.transform = ObservableItem.prototype.check;
|
|
1928
|
+
ObservableItem.prototype.pluck = function(property) {
|
|
1929
|
+
return new ObservableChecker(this, (value) => value[property]);
|
|
1930
|
+
};
|
|
1931
|
+
ObservableItem.prototype.is = function(callbackOrValue) {
|
|
1932
|
+
if(typeof callbackOrValue === 'function') {
|
|
1933
|
+
return new ObservableChecker(this, callbackOrValue);
|
|
842
1934
|
}
|
|
843
|
-
this
|
|
844
|
-
const saver = $saveToStorage(this.$currentValue);
|
|
845
|
-
this.subscribe((newValue) => {
|
|
846
|
-
saver(key, options.set ? options.set(newValue) : newValue);
|
|
847
|
-
});
|
|
848
|
-
return this;
|
|
1935
|
+
return new ObservableChecker(this, (value) => value === callbackOrValue);
|
|
849
1936
|
};
|
|
1937
|
+
ObservableItem.prototype.select = ObservableItem.prototype.check;
|
|
850
1938
|
|
|
851
|
-
|
|
852
|
-
|
|
1939
|
+
/**
|
|
1940
|
+
* Creates a derived observable that formats the current value using Intl.
|
|
1941
|
+
* Automatically reacts to both value changes and locale changes (Store.__nd.locale).
|
|
1942
|
+
*
|
|
1943
|
+
* @param {string | Function} type - Format type or custom formatter function
|
|
1944
|
+
* @param {Object} [options={}] - Options passed to the formatter
|
|
1945
|
+
* @returns {ObservableItem<string>}
|
|
1946
|
+
*
|
|
1947
|
+
* @example
|
|
1948
|
+
* // Currency
|
|
1949
|
+
* price.format('currency') // "15 000 FCFA"
|
|
1950
|
+
* price.format('currency', { currency: 'EUR' }) // "15 000,00 €"
|
|
1951
|
+
* price.format('currency', { notation: 'compact' }) // "15 K FCFA"
|
|
1952
|
+
*
|
|
1953
|
+
* // Number
|
|
1954
|
+
* count.format('number') // "15 000"
|
|
1955
|
+
*
|
|
1956
|
+
* // Percent
|
|
1957
|
+
* rate.format('percent') // "15,0 %"
|
|
1958
|
+
* rate.format('percent', { decimals: 2 }) // "15,00 %"
|
|
1959
|
+
*
|
|
1960
|
+
* // Date
|
|
1961
|
+
* date.format('date') // "3 mars 2026"
|
|
1962
|
+
* date.format('date', { dateStyle: 'full' }) // "mardi 3 mars 2026"
|
|
1963
|
+
* date.format('date', { format: 'DD/MM/YYYY' }) // "03/03/2026"
|
|
1964
|
+
* date.format('date', { format: 'DD MMM YYYY' }) // "03 mar 2026"
|
|
1965
|
+
* date.format('date', { format: 'DD MMMM YYYY' }) // "03 mars 2026"
|
|
1966
|
+
*
|
|
1967
|
+
* // Time
|
|
1968
|
+
* date.format('time') // "20:30"
|
|
1969
|
+
* date.format('time', { second: '2-digit' }) // "20:30:00"
|
|
1970
|
+
* date.format('time', { format: 'HH:mm:ss' }) // "20:30:00"
|
|
1971
|
+
*
|
|
1972
|
+
* // Datetime
|
|
1973
|
+
* date.format('datetime') // "3 mars 2026, 20:30"
|
|
1974
|
+
* date.format('datetime', { dateStyle: 'full' }) // "mardi 3 mars 2026, 20:30"
|
|
1975
|
+
* date.format('datetime', { format: 'DD/MM/YYYY HH:mm' }) // "03/03/2026 20:30"
|
|
1976
|
+
*
|
|
1977
|
+
* // Relative
|
|
1978
|
+
* date.format('relative') // "dans 11 jours"
|
|
1979
|
+
* date.format('relative', { unit: 'month' }) // "dans 1 mois"
|
|
1980
|
+
*
|
|
1981
|
+
* // Plural
|
|
1982
|
+
* count.format('plural', { singular: 'billet', plural: 'billets' }) // "3 billets"
|
|
1983
|
+
*
|
|
1984
|
+
* // Custom formatter
|
|
1985
|
+
* price.format(value => `${value.toLocaleString()} FCFA`)
|
|
1986
|
+
*
|
|
1987
|
+
* // Reacts to locale changes automatically
|
|
1988
|
+
* Store.setLocale('en-US');
|
|
1989
|
+
*/
|
|
1990
|
+
ObservableItem.prototype.format = function(type, options = {}) {
|
|
1991
|
+
const self = this;
|
|
853
1992
|
|
|
854
|
-
if(
|
|
855
|
-
|
|
856
|
-
clonedValue = clonedValue.clone();
|
|
857
|
-
} else {
|
|
858
|
-
clonedValue = structuredClone(clonedValue);
|
|
859
|
-
}
|
|
1993
|
+
if (typeof type === 'function') {
|
|
1994
|
+
return new ObservableChecker(self, type);
|
|
860
1995
|
}
|
|
861
1996
|
|
|
862
|
-
|
|
1997
|
+
const formatter = Formatters[type];
|
|
1998
|
+
const localeObservable = Formatters.locale;
|
|
1999
|
+
|
|
2000
|
+
return ObservableItem.computed(() => formatter(self.val(), localeObservable.val(), options),
|
|
2001
|
+
[self, localeObservable]
|
|
2002
|
+
);
|
|
863
2003
|
};
|
|
864
2004
|
|
|
865
2005
|
/**
|
|
@@ -931,6 +2071,138 @@ var NativeComponents = (function (exports) {
|
|
|
931
2071
|
setInterval(() => MemoryManager.cleanObservables(threshold), interval);
|
|
932
2072
|
};
|
|
933
2073
|
|
|
2074
|
+
|
|
2075
|
+
/**
|
|
2076
|
+
* Creates an observable array with reactive array methods.
|
|
2077
|
+
* All mutations trigger updates automatically.
|
|
2078
|
+
*
|
|
2079
|
+
* @param {Array} [target=[]] - Initial array value
|
|
2080
|
+
* @param {Object|null} [configs=null] - Configuration options
|
|
2081
|
+
* // @param {boolean} [configs.propagation=true] - Whether to propagate changes to parent observables
|
|
2082
|
+
* // @param {boolean} [configs.deep=false] - Whether to make nested objects observable
|
|
2083
|
+
* @param {boolean} [configs.reset=false] - Whether to store initial value for reset()
|
|
2084
|
+
* @returns {ObservableArray} An observable array with reactive methods
|
|
2085
|
+
* @example
|
|
2086
|
+
* const items = Observable.array([1, 2, 3]);
|
|
2087
|
+
* items.push(4); // Triggers update
|
|
2088
|
+
* items.subscribe((arr) => console.log(arr));
|
|
2089
|
+
*/
|
|
2090
|
+
Observable.array = function(target = [], configs = null) {
|
|
2091
|
+
return new ObservableArray(target, configs);
|
|
2092
|
+
};
|
|
2093
|
+
|
|
2094
|
+
/**
|
|
2095
|
+
*
|
|
2096
|
+
* @param {Function} callback
|
|
2097
|
+
* @returns {Function}
|
|
2098
|
+
*/
|
|
2099
|
+
Observable.batch = function(callback) {
|
|
2100
|
+
const $observer = Observable(0);
|
|
2101
|
+
const batch = function() {
|
|
2102
|
+
if(Validator.isAsyncFunction(callback)) {
|
|
2103
|
+
return (callback(...arguments)).then(() => {
|
|
2104
|
+
$observer.trigger();
|
|
2105
|
+
}).catch(error => { throw error; });
|
|
2106
|
+
}
|
|
2107
|
+
callback(...arguments);
|
|
2108
|
+
$observer.trigger();
|
|
2109
|
+
};
|
|
2110
|
+
batch.$observer = $observer;
|
|
2111
|
+
return batch;
|
|
2112
|
+
};
|
|
2113
|
+
|
|
2114
|
+
|
|
2115
|
+
/**
|
|
2116
|
+
* Creates a computed observable that automatically updates when its dependencies change.
|
|
2117
|
+
* The callback is re-executed whenever any dependency observable changes.
|
|
2118
|
+
*
|
|
2119
|
+
* @param {Function} callback - Function that returns the computed value
|
|
2120
|
+
* @param {Array<ObservableItem|ObservableChecker|ObservableProxy>|Function} [dependencies=[]] - Array of observables to watch, or batch function
|
|
2121
|
+
* @returns {ObservableItem} A new observable that updates automatically
|
|
2122
|
+
* @example
|
|
2123
|
+
* const firstName = Observable('John');
|
|
2124
|
+
* const lastName = Observable('Doe');
|
|
2125
|
+
* const fullName = Observable.computed(
|
|
2126
|
+
* () => `${firstName.val()} ${lastName.val()}`,
|
|
2127
|
+
* [firstName, lastName]
|
|
2128
|
+
* );
|
|
2129
|
+
*
|
|
2130
|
+
* // With batch function
|
|
2131
|
+
* const batch = Observable.batch(() => { ... });
|
|
2132
|
+
* const computed = Observable.computed(() => { ... }, batch);
|
|
2133
|
+
*/
|
|
2134
|
+
Observable.computed = function(callback, dependencies = []) {
|
|
2135
|
+
const initialValue = callback();
|
|
2136
|
+
const observable = new ObservableItem(initialValue);
|
|
2137
|
+
const getValues = () => dependencies.map((item) => item.val());
|
|
2138
|
+
const updatedValue = nextTick(() => observable.set(callback(...getValues())));
|
|
2139
|
+
|
|
2140
|
+
if(Validator.isFunction(dependencies)) {
|
|
2141
|
+
if(!Validator.isObservable(dependencies.$observer)) {
|
|
2142
|
+
throw new NativeDocumentError('Observable.computed : dependencies must be valid batch function');
|
|
2143
|
+
}
|
|
2144
|
+
dependencies.$observer.subscribe(updatedValue);
|
|
2145
|
+
return observable;
|
|
2146
|
+
}
|
|
2147
|
+
|
|
2148
|
+
dependencies.forEach(dependency => {
|
|
2149
|
+
if(Validator.isProxy(dependency)) {
|
|
2150
|
+
dependency.$observables.forEach((observable) => {
|
|
2151
|
+
observable.subscribe(updatedValue);
|
|
2152
|
+
});
|
|
2153
|
+
return;
|
|
2154
|
+
}
|
|
2155
|
+
dependency.subscribe(updatedValue);
|
|
2156
|
+
});
|
|
2157
|
+
|
|
2158
|
+
return observable;
|
|
2159
|
+
};
|
|
2160
|
+
ObservableItem.computed = Observable.computed;
|
|
2161
|
+
|
|
2162
|
+
|
|
2163
|
+
Observable.init = function(initialValue, configs = null) {
|
|
2164
|
+
return new ObservableObject(initialValue, configs)
|
|
2165
|
+
};
|
|
2166
|
+
|
|
2167
|
+
/**
|
|
2168
|
+
*
|
|
2169
|
+
* @param {any[]} data
|
|
2170
|
+
* @return Proxy[]
|
|
2171
|
+
*/
|
|
2172
|
+
Observable.arrayOfObject = function(data) {
|
|
2173
|
+
return data.map(item => Observable.object(item));
|
|
2174
|
+
};
|
|
2175
|
+
|
|
2176
|
+
/**
|
|
2177
|
+
* Get the value of an observable or an object of observables.
|
|
2178
|
+
* @param {ObservableItem|Object<ObservableItem>} data
|
|
2179
|
+
* @returns {{}|*|null}
|
|
2180
|
+
*/
|
|
2181
|
+
Observable.value = function(data) {
|
|
2182
|
+
if(data?.__$isObservableArray) {
|
|
2183
|
+
const result = [];
|
|
2184
|
+
for(let i = 0, length = data.length; i < length; i++) {
|
|
2185
|
+
const item = data.at(i);
|
|
2186
|
+
result.push(Observable.value(item));
|
|
2187
|
+
}
|
|
2188
|
+
return result;
|
|
2189
|
+
}
|
|
2190
|
+
if(data?.__$Observable) {
|
|
2191
|
+
return data.val();
|
|
2192
|
+
}
|
|
2193
|
+
if(Validator.isProxy(data)) {
|
|
2194
|
+
return data.$value;
|
|
2195
|
+
}
|
|
2196
|
+
return data;
|
|
2197
|
+
};
|
|
2198
|
+
|
|
2199
|
+
ObservableItem.prototype.resolve = function () {
|
|
2200
|
+
return Observable.value(this);
|
|
2201
|
+
};
|
|
2202
|
+
|
|
2203
|
+
Observable.object = Observable.init;
|
|
2204
|
+
Observable.json = Observable.init;
|
|
2205
|
+
|
|
934
2206
|
const BOOLEAN_ATTRIBUTES = new Set([
|
|
935
2207
|
'checked',
|
|
936
2208
|
'selected',
|
|
@@ -10442,6 +11714,18 @@ var NativeComponents = (function (exports) {
|
|
|
10442
11714
|
const Row = HStack;
|
|
10443
11715
|
const Col = VStack;
|
|
10444
11716
|
|
|
11717
|
+
let withValidation = (fn) => fn;
|
|
11718
|
+
|
|
11719
|
+
const normalizeComponentArgs = function(props, children = null) {
|
|
11720
|
+
if(props && children) {
|
|
11721
|
+
return { props, children };
|
|
11722
|
+
}
|
|
11723
|
+
if(typeof props !== 'object' || Array.isArray(props) || props === null || props.constructor.name !== 'Object' || props.$hydrate) { // IF it's not a JSON
|
|
11724
|
+
return { props: children, children: props }
|
|
11725
|
+
}
|
|
11726
|
+
return { props, children };
|
|
11727
|
+
};
|
|
11728
|
+
|
|
10445
11729
|
const EVENTS = [
|
|
10446
11730
|
"Click",
|
|
10447
11731
|
"DblClick",
|
|
@@ -10723,26 +12007,188 @@ var NativeComponents = (function (exports) {
|
|
|
10723
12007
|
}
|
|
10724
12008
|
};
|
|
10725
12009
|
|
|
10726
|
-
Object.defineProperty(HTMLElement.prototype, 'classes', {
|
|
10727
|
-
configurable: true,
|
|
10728
|
-
get() {
|
|
10729
|
-
return {
|
|
10730
|
-
$element: this,
|
|
10731
|
-
...classListMethods
|
|
10732
|
-
};
|
|
10733
|
-
}
|
|
10734
|
-
});
|
|
12010
|
+
Object.defineProperty(HTMLElement.prototype, 'classes', {
|
|
12011
|
+
configurable: true,
|
|
12012
|
+
get() {
|
|
12013
|
+
return {
|
|
12014
|
+
$element: this,
|
|
12015
|
+
...classListMethods
|
|
12016
|
+
};
|
|
12017
|
+
}
|
|
12018
|
+
});
|
|
12019
|
+
|
|
12020
|
+
DocumentFragment.prototype.__IS_FRAGMENT = true;
|
|
12021
|
+
|
|
12022
|
+
Function.prototype.args = function(...args) {
|
|
12023
|
+
return withValidation(this);
|
|
12024
|
+
};
|
|
12025
|
+
|
|
12026
|
+
Function.prototype.errorBoundary = function(callback) {
|
|
12027
|
+
const handler = (...args) => {
|
|
12028
|
+
try {
|
|
12029
|
+
return this.apply(this, args);
|
|
12030
|
+
} catch(e) {
|
|
12031
|
+
return callback(e, {caller: handler, args: args });
|
|
12032
|
+
}
|
|
12033
|
+
};
|
|
12034
|
+
return handler;
|
|
12035
|
+
};
|
|
12036
|
+
|
|
12037
|
+
NDElement.$getChild = ElementCreator.getChild;
|
|
12038
|
+
|
|
12039
|
+
String.prototype.toNdElement = function () {
|
|
12040
|
+
return ElementCreator.createStaticTextNode(null, this);
|
|
12041
|
+
};
|
|
12042
|
+
|
|
12043
|
+
Number.prototype.toNdElement = function () {
|
|
12044
|
+
return ElementCreator.createStaticTextNode(null, this.toString());
|
|
12045
|
+
};
|
|
12046
|
+
|
|
12047
|
+
Element.prototype.toNdElement = function () {
|
|
12048
|
+
return this;
|
|
12049
|
+
};
|
|
12050
|
+
Text.prototype.toNdElement = function () {
|
|
12051
|
+
return this;
|
|
12052
|
+
};
|
|
12053
|
+
Comment.prototype.toNdElement = function () {
|
|
12054
|
+
return this;
|
|
12055
|
+
};
|
|
12056
|
+
Document.prototype.toNdElement = function () {
|
|
12057
|
+
return this;
|
|
12058
|
+
};
|
|
12059
|
+
DocumentFragment.prototype.toNdElement = function () {
|
|
12060
|
+
return this;
|
|
12061
|
+
};
|
|
12062
|
+
|
|
12063
|
+
ObservableItem.prototype.toNdElement = function () {
|
|
12064
|
+
return ElementCreator.createObservableNode(null, this);
|
|
12065
|
+
};
|
|
12066
|
+
|
|
12067
|
+
ObservableChecker.prototype.toNdElement = ObservableItem.prototype.toNdElement;
|
|
12068
|
+
|
|
12069
|
+
NDElement.prototype.toNdElement = function () {
|
|
12070
|
+
const element = this.$element ?? this.$build?.() ?? this.build?.() ?? null;
|
|
12071
|
+
if(this.$attachements) {
|
|
12072
|
+
if(!this.$attachements.contains(this.$element)) {
|
|
12073
|
+
this.$attachements.append(this.$element);
|
|
12074
|
+
}
|
|
12075
|
+
return this.$attachements;
|
|
12076
|
+
}
|
|
12077
|
+
return element;
|
|
12078
|
+
};
|
|
12079
|
+
|
|
12080
|
+
Array.prototype.toNdElement = function () {
|
|
12081
|
+
const fragment = document.createDocumentFragment();
|
|
12082
|
+
for(let i = 0, length = this.length; i < length; i++) {
|
|
12083
|
+
const child = ElementCreator.getChild(this[i]);
|
|
12084
|
+
if(child === null) continue;
|
|
12085
|
+
fragment.appendChild(child);
|
|
12086
|
+
}
|
|
12087
|
+
return fragment;
|
|
12088
|
+
};
|
|
12089
|
+
|
|
12090
|
+
Function.prototype.toNdElement = function () {
|
|
12091
|
+
const child = this;
|
|
12092
|
+
return ElementCreator.getChild(child());
|
|
12093
|
+
};
|
|
12094
|
+
|
|
12095
|
+
/**
|
|
12096
|
+
* @param {HTMLElement} el
|
|
12097
|
+
* @param {number} timeout
|
|
12098
|
+
*/
|
|
12099
|
+
const waitForVisualEnd = (el, timeout = 1000) => {
|
|
12100
|
+
return new Promise((resolve) => {
|
|
12101
|
+
let isResolved = false;
|
|
12102
|
+
|
|
12103
|
+
const cleanupAndResolve = (e) => {
|
|
12104
|
+
if (e && e.target !== el) return;
|
|
12105
|
+
if (isResolved) return;
|
|
12106
|
+
|
|
12107
|
+
isResolved = true;
|
|
12108
|
+
el.removeEventListener('transitionend', cleanupAndResolve);
|
|
12109
|
+
el.removeEventListener('animationend', cleanupAndResolve);
|
|
12110
|
+
clearTimeout(timer);
|
|
12111
|
+
resolve();
|
|
12112
|
+
};
|
|
12113
|
+
|
|
12114
|
+
el.addEventListener('transitionend', cleanupAndResolve);
|
|
12115
|
+
el.addEventListener('animationend', cleanupAndResolve);
|
|
12116
|
+
|
|
12117
|
+
const timer = setTimeout(cleanupAndResolve, timeout);
|
|
12118
|
+
|
|
12119
|
+
const style = window.getComputedStyle(el);
|
|
12120
|
+
const hasTransition = style.transitionDuration !== '0s';
|
|
12121
|
+
const hasAnimation = style.animationDuration !== '0s';
|
|
12122
|
+
|
|
12123
|
+
if (!hasTransition && !hasAnimation) {
|
|
12124
|
+
cleanupAndResolve();
|
|
12125
|
+
}
|
|
12126
|
+
});
|
|
12127
|
+
};
|
|
12128
|
+
|
|
12129
|
+
NDElement.prototype.transitionOut = function(transitionName) {
|
|
12130
|
+
const exitClass = transitionName + '-exit';
|
|
12131
|
+
const el = this.$element;
|
|
12132
|
+
this.beforeUnmount('transition-exit', async function() {
|
|
12133
|
+
el.classes.add(exitClass);
|
|
12134
|
+
await waitForVisualEnd(el);
|
|
12135
|
+
el.classes.remove(exitClass);
|
|
12136
|
+
});
|
|
12137
|
+
return this;
|
|
12138
|
+
};
|
|
12139
|
+
|
|
12140
|
+
NDElement.prototype.transitionIn = function(transitionName) {
|
|
12141
|
+
const startClass = transitionName + '-enter-from';
|
|
12142
|
+
const endClass = transitionName + '-enter-to';
|
|
10735
12143
|
|
|
10736
|
-
|
|
10737
|
-
|
|
10738
|
-
|
|
10739
|
-
|
|
10740
|
-
|
|
10741
|
-
|
|
12144
|
+
const el = this.$element;
|
|
12145
|
+
|
|
12146
|
+
el.classes.add(startClass);
|
|
12147
|
+
|
|
12148
|
+
this.mounted(() => {
|
|
12149
|
+
requestAnimationFrame(() => {
|
|
12150
|
+
requestAnimationFrame(() => {
|
|
12151
|
+
el.classes.remove(startClass);
|
|
12152
|
+
el.classes.add(endClass);
|
|
12153
|
+
|
|
12154
|
+
waitForVisualEnd(el).then(() => {
|
|
12155
|
+
el.classes.remove(endClass);
|
|
12156
|
+
});
|
|
12157
|
+
});
|
|
12158
|
+
});
|
|
12159
|
+
});
|
|
12160
|
+
return this;
|
|
12161
|
+
};
|
|
12162
|
+
|
|
12163
|
+
|
|
12164
|
+
NDElement.prototype.transition = function (transitionName) {
|
|
12165
|
+
this.transitionIn(transitionName);
|
|
12166
|
+
this.transitionOut(transitionName);
|
|
12167
|
+
return this;
|
|
12168
|
+
};
|
|
12169
|
+
|
|
12170
|
+
NDElement.prototype.animate = function(animationName) {
|
|
12171
|
+
const el = this.$element;
|
|
12172
|
+
el.classes.add(animationName);
|
|
12173
|
+
|
|
12174
|
+
waitForVisualEnd(el).then(() => {
|
|
12175
|
+
el.classes.remove(animationName);
|
|
12176
|
+
});
|
|
12177
|
+
|
|
12178
|
+
return this;
|
|
12179
|
+
};
|
|
12180
|
+
|
|
12181
|
+
ObservableItem.prototype.handleNdAttribute = function(element, attributeName) {
|
|
12182
|
+
if(BOOLEAN_ATTRIBUTES.has(attributeName)) {
|
|
12183
|
+
bindBooleanAttribute(element, attributeName, this);
|
|
12184
|
+
return;
|
|
10742
12185
|
}
|
|
10743
|
-
|
|
12186
|
+
|
|
12187
|
+
bindAttributeWithObservable(element, attributeName, this);
|
|
10744
12188
|
};
|
|
10745
12189
|
|
|
12190
|
+
ObservableChecker.prototype.handleNdAttribute = ObservableItem.prototype.handleNdAttribute;
|
|
12191
|
+
|
|
10746
12192
|
const createHtmlElement = (element, _attributes, _children = null) => {
|
|
10747
12193
|
let { props: attributes, children = null } = normalizeComponentArgs(_attributes, _children);
|
|
10748
12194
|
|
|
@@ -12274,195 +13720,6 @@ var NativeComponents = (function (exports) {
|
|
|
12274
13720
|
return this;
|
|
12275
13721
|
};
|
|
12276
13722
|
|
|
12277
|
-
/**
|
|
12278
|
-
*
|
|
12279
|
-
* @param {ObservableItem} $observable
|
|
12280
|
-
* @param {Function} $checker
|
|
12281
|
-
* @class ObservableChecker
|
|
12282
|
-
*/
|
|
12283
|
-
function ObservableChecker($observable, $checker) {
|
|
12284
|
-
this.observable = $observable;
|
|
12285
|
-
|
|
12286
|
-
ObservableItem.call(this);
|
|
12287
|
-
|
|
12288
|
-
this.$mutation = $checker;
|
|
12289
|
-
|
|
12290
|
-
$observable.subscribe((newValue) => {
|
|
12291
|
-
this.$updateWithMutation(newValue);
|
|
12292
|
-
});
|
|
12293
|
-
|
|
12294
|
-
this.$updateWithMutation($observable.val());
|
|
12295
|
-
}
|
|
12296
|
-
|
|
12297
|
-
ObservableChecker.prototype = Object.create(ObservableItem.prototype);
|
|
12298
|
-
ObservableChecker.prototype.constructor = ObservableChecker;
|
|
12299
|
-
ObservableChecker.prototype.__$Observable = true;
|
|
12300
|
-
ObservableChecker.prototype.__$isObservableChecker = true;
|
|
12301
|
-
|
|
12302
|
-
|
|
12303
|
-
const ObservablePipe = ObservableChecker;
|
|
12304
|
-
ObservablePipe.prototype.constructor = ObservablePipe;
|
|
12305
|
-
|
|
12306
|
-
ObservableChecker.prototype.$updateWithMutation = function(newValue) {
|
|
12307
|
-
newValue = this.$mutation(newValue);
|
|
12308
|
-
return this.set(newValue);
|
|
12309
|
-
};
|
|
12310
|
-
|
|
12311
|
-
NDElement.$getChild = ElementCreator.getChild;
|
|
12312
|
-
|
|
12313
|
-
String.prototype.toNdElement = function () {
|
|
12314
|
-
return ElementCreator.createStaticTextNode(null, this);
|
|
12315
|
-
};
|
|
12316
|
-
|
|
12317
|
-
Number.prototype.toNdElement = function () {
|
|
12318
|
-
return ElementCreator.createStaticTextNode(null, this.toString());
|
|
12319
|
-
};
|
|
12320
|
-
|
|
12321
|
-
Element.prototype.toNdElement = function () {
|
|
12322
|
-
return this;
|
|
12323
|
-
};
|
|
12324
|
-
Text.prototype.toNdElement = function () {
|
|
12325
|
-
return this;
|
|
12326
|
-
};
|
|
12327
|
-
Comment.prototype.toNdElement = function () {
|
|
12328
|
-
return this;
|
|
12329
|
-
};
|
|
12330
|
-
Document.prototype.toNdElement = function () {
|
|
12331
|
-
return this;
|
|
12332
|
-
};
|
|
12333
|
-
DocumentFragment.prototype.toNdElement = function () {
|
|
12334
|
-
return this;
|
|
12335
|
-
};
|
|
12336
|
-
|
|
12337
|
-
ObservableItem.prototype.toNdElement = function () {
|
|
12338
|
-
return ElementCreator.createObservableNode(null, this);
|
|
12339
|
-
};
|
|
12340
|
-
|
|
12341
|
-
ObservableChecker.prototype.toNdElement = ObservableItem.prototype.toNdElement;
|
|
12342
|
-
|
|
12343
|
-
NDElement.prototype.toNdElement = function () {
|
|
12344
|
-
const element = this.$element ?? this.$build?.() ?? this.build?.() ?? null;
|
|
12345
|
-
if(this.$attachements) {
|
|
12346
|
-
if(!this.$attachements.contains(this.$element)) {
|
|
12347
|
-
this.$attachements.append(this.$element);
|
|
12348
|
-
}
|
|
12349
|
-
return this.$attachements;
|
|
12350
|
-
}
|
|
12351
|
-
return element;
|
|
12352
|
-
};
|
|
12353
|
-
|
|
12354
|
-
Array.prototype.toNdElement = function () {
|
|
12355
|
-
const fragment = document.createDocumentFragment();
|
|
12356
|
-
for(let i = 0, length = this.length; i < length; i++) {
|
|
12357
|
-
const child = ElementCreator.getChild(this[i]);
|
|
12358
|
-
if(child === null) continue;
|
|
12359
|
-
fragment.appendChild(child);
|
|
12360
|
-
}
|
|
12361
|
-
return fragment;
|
|
12362
|
-
};
|
|
12363
|
-
|
|
12364
|
-
Function.prototype.toNdElement = function () {
|
|
12365
|
-
const child = this;
|
|
12366
|
-
return ElementCreator.getChild(child());
|
|
12367
|
-
};
|
|
12368
|
-
|
|
12369
|
-
/**
|
|
12370
|
-
* @param {HTMLElement} el
|
|
12371
|
-
* @param {number} timeout
|
|
12372
|
-
*/
|
|
12373
|
-
const waitForVisualEnd = (el, timeout = 1000) => {
|
|
12374
|
-
return new Promise((resolve) => {
|
|
12375
|
-
let isResolved = false;
|
|
12376
|
-
|
|
12377
|
-
const cleanupAndResolve = (e) => {
|
|
12378
|
-
if (e && e.target !== el) return;
|
|
12379
|
-
if (isResolved) return;
|
|
12380
|
-
|
|
12381
|
-
isResolved = true;
|
|
12382
|
-
el.removeEventListener('transitionend', cleanupAndResolve);
|
|
12383
|
-
el.removeEventListener('animationend', cleanupAndResolve);
|
|
12384
|
-
clearTimeout(timer);
|
|
12385
|
-
resolve();
|
|
12386
|
-
};
|
|
12387
|
-
|
|
12388
|
-
el.addEventListener('transitionend', cleanupAndResolve);
|
|
12389
|
-
el.addEventListener('animationend', cleanupAndResolve);
|
|
12390
|
-
|
|
12391
|
-
const timer = setTimeout(cleanupAndResolve, timeout);
|
|
12392
|
-
|
|
12393
|
-
const style = window.getComputedStyle(el);
|
|
12394
|
-
const hasTransition = style.transitionDuration !== '0s';
|
|
12395
|
-
const hasAnimation = style.animationDuration !== '0s';
|
|
12396
|
-
|
|
12397
|
-
if (!hasTransition && !hasAnimation) {
|
|
12398
|
-
cleanupAndResolve();
|
|
12399
|
-
}
|
|
12400
|
-
});
|
|
12401
|
-
};
|
|
12402
|
-
|
|
12403
|
-
NDElement.prototype.transitionOut = function(transitionName) {
|
|
12404
|
-
const exitClass = transitionName + '-exit';
|
|
12405
|
-
const el = this.$element;
|
|
12406
|
-
this.beforeUnmount('transition-exit', async function() {
|
|
12407
|
-
el.classes.add(exitClass);
|
|
12408
|
-
await waitForVisualEnd(el);
|
|
12409
|
-
el.classes.remove(exitClass);
|
|
12410
|
-
});
|
|
12411
|
-
return this;
|
|
12412
|
-
};
|
|
12413
|
-
|
|
12414
|
-
NDElement.prototype.transitionIn = function(transitionName) {
|
|
12415
|
-
const startClass = transitionName + '-enter-from';
|
|
12416
|
-
const endClass = transitionName + '-enter-to';
|
|
12417
|
-
|
|
12418
|
-
const el = this.$element;
|
|
12419
|
-
|
|
12420
|
-
el.classes.add(startClass);
|
|
12421
|
-
|
|
12422
|
-
this.mounted(() => {
|
|
12423
|
-
requestAnimationFrame(() => {
|
|
12424
|
-
requestAnimationFrame(() => {
|
|
12425
|
-
el.classes.remove(startClass);
|
|
12426
|
-
el.classes.add(endClass);
|
|
12427
|
-
|
|
12428
|
-
waitForVisualEnd(el).then(() => {
|
|
12429
|
-
el.classes.remove(endClass);
|
|
12430
|
-
});
|
|
12431
|
-
});
|
|
12432
|
-
});
|
|
12433
|
-
});
|
|
12434
|
-
return this;
|
|
12435
|
-
};
|
|
12436
|
-
|
|
12437
|
-
|
|
12438
|
-
NDElement.prototype.transition = function (transitionName) {
|
|
12439
|
-
this.transitionIn(transitionName);
|
|
12440
|
-
this.transitionOut(transitionName);
|
|
12441
|
-
return this;
|
|
12442
|
-
};
|
|
12443
|
-
|
|
12444
|
-
NDElement.prototype.animate = function(animationName) {
|
|
12445
|
-
const el = this.$element;
|
|
12446
|
-
el.classes.add(animationName);
|
|
12447
|
-
|
|
12448
|
-
waitForVisualEnd(el).then(() => {
|
|
12449
|
-
el.classes.remove(animationName);
|
|
12450
|
-
});
|
|
12451
|
-
|
|
12452
|
-
return this;
|
|
12453
|
-
};
|
|
12454
|
-
|
|
12455
|
-
ObservableItem.prototype.handleNdAttribute = function(element, attributeName) {
|
|
12456
|
-
if(BOOLEAN_ATTRIBUTES.has(attributeName)) {
|
|
12457
|
-
bindBooleanAttribute(element, attributeName, this);
|
|
12458
|
-
return;
|
|
12459
|
-
}
|
|
12460
|
-
|
|
12461
|
-
bindAttributeWithObservable(element, attributeName, this);
|
|
12462
|
-
};
|
|
12463
|
-
|
|
12464
|
-
ObservableChecker.prototype.handleNdAttribute = ObservableItem.prototype.handleNdAttribute;
|
|
12465
|
-
|
|
12466
13723
|
function ColumnGroup(title, props = {}) {
|
|
12467
13724
|
this.$description = {
|
|
12468
13725
|
header: title,
|