native-document 1.0.257 → 1.0.258
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/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "native-document",
|
|
3
|
-
"version": "1.0.
|
|
3
|
+
"version": "1.0.258",
|
|
4
4
|
"description": "A reactive JavaScript framework that preserves native DOM simplicity without sacrificing modern features",
|
|
5
5
|
"author": "AfroCodeur <https://github.com/afrocodeur>",
|
|
6
6
|
"license": "MIT",
|
|
@@ -12,6 +12,7 @@ import './observable-helpers/observable.prototypes';
|
|
|
12
12
|
import ObservableResource from './ObservableResource';
|
|
13
13
|
import {Formatters} from '../utils/formatters';
|
|
14
14
|
import Ref from './Ref';
|
|
15
|
+
import ObservableQuery from './ObservableQuery';
|
|
15
16
|
/**
|
|
16
17
|
*
|
|
17
18
|
* @param {*} value
|
|
@@ -188,9 +189,16 @@ Observable.auto = (value, options = null) => {
|
|
|
188
189
|
if(value == null) {
|
|
189
190
|
return new ObservableItem(value, options);
|
|
190
191
|
}
|
|
192
|
+
if(value.__$Observable) {
|
|
193
|
+
return value;
|
|
194
|
+
}
|
|
191
195
|
if(Validator.isArray(value)) {
|
|
192
196
|
return new ObservableArray(value, options);
|
|
193
|
-
}
|
|
197
|
+
}
|
|
198
|
+
else if(typeof value === 'object') {
|
|
199
|
+
if(!Validator.isJson(value)) {
|
|
200
|
+
return value;
|
|
201
|
+
}
|
|
194
202
|
return new ObservableObject(value, options);
|
|
195
203
|
}
|
|
196
204
|
return new ObservableItem(value, options);
|
|
@@ -252,6 +260,10 @@ Observable.collection = function() {
|
|
|
252
260
|
return new Ref(true);
|
|
253
261
|
};
|
|
254
262
|
|
|
263
|
+
Observable.from = function(target) {
|
|
264
|
+
return new ObservableQuery(target);
|
|
265
|
+
};
|
|
266
|
+
|
|
255
267
|
ObservableItem.prototype.resolve = function () {
|
|
256
268
|
return Observable.value(this);
|
|
257
269
|
};
|
|
@@ -30,7 +30,7 @@ const ObservableArray = function (target, configs = { propagation: false, deep:
|
|
|
30
30
|
}
|
|
31
31
|
|
|
32
32
|
|
|
33
|
-
if(configs?.deep) {
|
|
33
|
+
if(configs?.deep !== false) {
|
|
34
34
|
target = target.map(toObservable);
|
|
35
35
|
this.interceptMutations((data, operations) => {
|
|
36
36
|
if(!operations.action || ['push', 'unshift', 'set', 'splice'].includes(operations.action)) {
|
|
@@ -0,0 +1,153 @@
|
|
|
1
|
+
|
|
2
|
+
|
|
3
|
+
export default function ObservableQuery($target) {
|
|
4
|
+
if(!(this instanceof ObservableQuery)) {
|
|
5
|
+
return new ObservableQuery($target);
|
|
6
|
+
}
|
|
7
|
+
|
|
8
|
+
if(!$target?.__$isObservableArray) {
|
|
9
|
+
throw new Error('ObservableQuery target must be a ObservableArray');
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
this.$target = $target;
|
|
13
|
+
this.$parentMap = new Map([$target]);
|
|
14
|
+
this.$currentResultData = $target;
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
ObservableQuery.prototype.flush = function () {
|
|
18
|
+
this.$currentResultData = this.$target;
|
|
19
|
+
return this;
|
|
20
|
+
};
|
|
21
|
+
|
|
22
|
+
const predicateFns = {
|
|
23
|
+
gt: (source, target) => source.valueOf() > target.valueOf(),
|
|
24
|
+
gte: (source, target) => source.valueOf() >= target.valueOf(),
|
|
25
|
+
lt: (source, target) => source.valueOf() < target.valueOf(),
|
|
26
|
+
lte: (source, target) => source.valueOf() <= target.valueOf(),
|
|
27
|
+
eq: (source, target) => source.valueOf() === target.valueOf(),
|
|
28
|
+
neq: (source, target) => source.valueOf() !== target.valueOf(),
|
|
29
|
+
in: (source, target) => target.includes(source.valueOf()),
|
|
30
|
+
nin: (source, target) => !target.includes(source.valueOf()),
|
|
31
|
+
};
|
|
32
|
+
|
|
33
|
+
const getPredicateFn = (column, predicate) => {
|
|
34
|
+
if(typeof column === 'function') {
|
|
35
|
+
return column;
|
|
36
|
+
}
|
|
37
|
+
if(predicate == null) {
|
|
38
|
+
return (element) => {
|
|
39
|
+
return element[column] == null;
|
|
40
|
+
};
|
|
41
|
+
}
|
|
42
|
+
if(typeof predicate?.valueOf === 'function') {
|
|
43
|
+
return (element) => {
|
|
44
|
+
return element[column]?.valueOf() === predicate?.valueOf();
|
|
45
|
+
};
|
|
46
|
+
}
|
|
47
|
+
const operator = Object.keys(predicate).at(0);
|
|
48
|
+
if(!operator) {
|
|
49
|
+
return () => true;
|
|
50
|
+
}
|
|
51
|
+
const value = Object.values(predicate).at(0);
|
|
52
|
+
const operation = predicateFns[operator];
|
|
53
|
+
if(!operation) {
|
|
54
|
+
throw new Error('Unknown predicate operator '+operator);
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
return (element) => operation(element[column], value);
|
|
58
|
+
};
|
|
59
|
+
|
|
60
|
+
// --------------------------------------------------------------------------------------------------------------------
|
|
61
|
+
ObservableQuery.prototype.where = function(column, predicate) {
|
|
62
|
+
const predicateFn = getPredicateFn(column, predicate);
|
|
63
|
+
this.$currentResultData = this.$currentResultData.valueOf().flatMap((item) => {
|
|
64
|
+
if(item?.__$isObservableArray || Array.isArray(item)) {
|
|
65
|
+
const values = item.filter(predicateFn).filter(Boolean);
|
|
66
|
+
values.forEach((value) => {
|
|
67
|
+
this.$parentMap.set(value, item);
|
|
68
|
+
});
|
|
69
|
+
return values;
|
|
70
|
+
}
|
|
71
|
+
return predicateFn(item) ? [item] : [];
|
|
72
|
+
});
|
|
73
|
+
return this;
|
|
74
|
+
};
|
|
75
|
+
|
|
76
|
+
ObservableQuery.prototype.in = function(relation) {
|
|
77
|
+
|
|
78
|
+
this.$currentResultData = this.$currentResultData
|
|
79
|
+
.map((parentElement) => {
|
|
80
|
+
const childCollection = typeof relation === 'function' ? relation(parentElement) : parentElement[relation];
|
|
81
|
+
if(childCollection) {
|
|
82
|
+
this.$parentMap.set(childCollection, parentElement);
|
|
83
|
+
}
|
|
84
|
+
return childCollection;
|
|
85
|
+
})
|
|
86
|
+
.filter(Boolean);
|
|
87
|
+
|
|
88
|
+
return this;
|
|
89
|
+
};
|
|
90
|
+
|
|
91
|
+
ObservableQuery.prototype.first = function() {
|
|
92
|
+
|
|
93
|
+
return this.$currentResultData[0] ?? null;
|
|
94
|
+
};
|
|
95
|
+
|
|
96
|
+
ObservableQuery.prototype.all = function() {
|
|
97
|
+
|
|
98
|
+
return this.$currentResultData;
|
|
99
|
+
};
|
|
100
|
+
|
|
101
|
+
|
|
102
|
+
// --------------------------------------------------------------------------------------------------------------------
|
|
103
|
+
ObservableQuery.prototype.count = function() {
|
|
104
|
+
return this.$currentResultData.length;
|
|
105
|
+
};
|
|
106
|
+
|
|
107
|
+
ObservableQuery.prototype.exists = function() {
|
|
108
|
+
return this.$currentResultData.length > 0;
|
|
109
|
+
};
|
|
110
|
+
|
|
111
|
+
ObservableQuery.prototype.sum = function(key) {
|
|
112
|
+
return this.$currentResultData.reduce((acc, element) => {
|
|
113
|
+
return acc + element[key]?.valueOf();
|
|
114
|
+
}, 0);
|
|
115
|
+
};
|
|
116
|
+
|
|
117
|
+
ObservableQuery.prototype.map = function(fn) {
|
|
118
|
+
return this.$currentResultData.map(fn);
|
|
119
|
+
};
|
|
120
|
+
|
|
121
|
+
|
|
122
|
+
// --------------------------------------------------------------------------------------------------------------------
|
|
123
|
+
|
|
124
|
+
ObservableQuery.prototype.set = function(dataset) {
|
|
125
|
+
this.$currentResultData.forEach((element) => {
|
|
126
|
+
for(const key in dataset) {
|
|
127
|
+
const prop = element[key];
|
|
128
|
+
const value = dataset[key];
|
|
129
|
+
if(prop?.__$isObservable) {
|
|
130
|
+
prop.set(value);
|
|
131
|
+
return;
|
|
132
|
+
}
|
|
133
|
+
element[key] = value;
|
|
134
|
+
}
|
|
135
|
+
});
|
|
136
|
+
return this.flush();
|
|
137
|
+
};
|
|
138
|
+
|
|
139
|
+
ObservableQuery.prototype.delete = function() {
|
|
140
|
+
this.$currentResultData.forEach((element) => {
|
|
141
|
+
const parent = this.$parentMap.get(element) ?? this.$target;
|
|
142
|
+
const indexOfElement = parent.indexOf(element);
|
|
143
|
+
parent.splice(indexOfElement, 1);
|
|
144
|
+
});
|
|
145
|
+
return this.flush();
|
|
146
|
+
};
|
|
147
|
+
|
|
148
|
+
ObservableQuery.prototype.push = function(value) {
|
|
149
|
+
this.$currentResultData.forEach((element) => {
|
|
150
|
+
element.push && element.push(value);
|
|
151
|
+
});
|
|
152
|
+
return this.flush();
|
|
153
|
+
};
|
|
@@ -0,0 +1,67 @@
|
|
|
1
|
+
import type { ObservableArray, ObservableItem } from './observable';
|
|
2
|
+
|
|
3
|
+
type PredicateOperator = 'gt' | 'gte' | 'lt' | 'lte' | 'eq' | 'neq' | 'in' | 'nin';
|
|
4
|
+
|
|
5
|
+
type PredicateFn<T> = (element: T) => boolean;
|
|
6
|
+
|
|
7
|
+
type Predicate<T> =
|
|
8
|
+
| null
|
|
9
|
+
| { [K in PredicateOperator]?: any }
|
|
10
|
+
| PredicateFn<T>;
|
|
11
|
+
|
|
12
|
+
type Dataset<T> = Partial<{
|
|
13
|
+
[K in keyof T]: T[K] extends ObservableItem<infer V> ? V : T[K];
|
|
14
|
+
}>;
|
|
15
|
+
|
|
16
|
+
/**
|
|
17
|
+
* Fluent query builder for ObservableArray collections.
|
|
18
|
+
* Supports filtering, navigation into nested relations, aggregation and mutations.
|
|
19
|
+
*
|
|
20
|
+
* @template T - Type of items in the target ObservableArray
|
|
21
|
+
*
|
|
22
|
+
* @example
|
|
23
|
+
* // Select
|
|
24
|
+
* $.from($events).where('id', 1).first();
|
|
25
|
+
*
|
|
26
|
+
* // Navigate into relation
|
|
27
|
+
* $.from($events).where('id', 1).in('tickets').where('type', 'VIP').all();
|
|
28
|
+
*
|
|
29
|
+
* // Delete from relation
|
|
30
|
+
* $.from($events).in('tickets').where('type', 'VIP').delete();
|
|
31
|
+
*
|
|
32
|
+
* // Insert into relation
|
|
33
|
+
* $.from($events).where('id', 1).in('tickets').push(newTicket);
|
|
34
|
+
*
|
|
35
|
+
* // Update
|
|
36
|
+
* $.from($events).where('id', 1).set({ name: 'New name' });
|
|
37
|
+
*
|
|
38
|
+
* // Aggregation
|
|
39
|
+
* $.from($events).in('tickets').sum('price');
|
|
40
|
+
* $.from($events).where('active', true).count();
|
|
41
|
+
*/
|
|
42
|
+
export declare function ObservableQuery<T = any>(
|
|
43
|
+
target: ObservableArray<T>
|
|
44
|
+
): ObservableQueryInstance<T>;
|
|
45
|
+
|
|
46
|
+
export interface ObservableQueryInstance<T = any> {
|
|
47
|
+
|
|
48
|
+
// -- Navigation / filtering -----------------------------------------------
|
|
49
|
+
flush(): this;
|
|
50
|
+
where(column: keyof T | PredicateFn<T>, predicate?: Predicate<T>): this;
|
|
51
|
+
in(relation: keyof T | ((element: T) => any)): ObservableQueryInstance<any>;
|
|
52
|
+
|
|
53
|
+
// -- Read -----------------------------------------------------------------
|
|
54
|
+
first(): T | null;
|
|
55
|
+
all(): T[];
|
|
56
|
+
|
|
57
|
+
// -- Aggregation ----------------------------------------------------------
|
|
58
|
+
count(): number;
|
|
59
|
+
exists(): boolean;
|
|
60
|
+
sum(key: keyof T): number;
|
|
61
|
+
map<U>(fn: (element: T) => U): U[];
|
|
62
|
+
|
|
63
|
+
// -- Mutations ------------------------------------------------------------
|
|
64
|
+
set(dataset: Dataset<T>): this;
|
|
65
|
+
delete(): this;
|
|
66
|
+
push(value: any): this;
|
|
67
|
+
}
|
package/types/observable.d.ts
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { FilterResult, PredicateMap } from './filters';
|
|
2
2
|
import Ref, {RefItem} from './ref';
|
|
3
|
+
import {ObservableQueryInstance} from "./observable-query";
|
|
3
4
|
|
|
4
5
|
export type Unsubscribe = () => void;
|
|
5
6
|
|
|
@@ -340,6 +341,7 @@ export interface ObservableStatic {
|
|
|
340
341
|
json<T extends Record<string, any>>(value: T, configs?: ObservableConfig | null): ObservableObject<T>;
|
|
341
342
|
|
|
342
343
|
auto<T>(value: T): ObservableArray<T>|ObservableObject<T>|ObservableItem<T>;
|
|
344
|
+
from<T>(value: ObservableArray<T>): ObservableQueryInstance<T>;
|
|
343
345
|
|
|
344
346
|
resource<T = any>(
|
|
345
347
|
fn: (...args: any[]) => Promise<T>,
|