@arrai-innovations/reactive-helpers 2.6.1 → 2.8.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/package.json +1 -1
- package/use/index.js +2 -0
- package/use/listCalculated.js +3 -0
- package/use/object.js +99 -0
- package/use/objectCalculated.js +11 -15
- package/use/objectInstance.js +4 -2
- package/use/objectRelated.js +121 -0
- package/use/objectSubscription.js +14 -6
package/package.json
CHANGED
package/use/index.js
CHANGED
|
@@ -4,8 +4,10 @@ export * from "./listInstance.js";
|
|
|
4
4
|
export * from "./listRelated.js";
|
|
5
5
|
export * from "./listSort.js";
|
|
6
6
|
export * from "./listSubscription.js";
|
|
7
|
+
export * from "./object.js";
|
|
7
8
|
export * from "./objectCalculated.js";
|
|
8
9
|
export * from "./objectInstance.js";
|
|
10
|
+
export * from "./objectRelated.js";
|
|
9
11
|
export * from "./objectSubscription.js";
|
|
10
12
|
export * from "./paginatedListInstance.js";
|
|
11
13
|
export * from "./search.js";
|
package/use/listCalculated.js
CHANGED
|
@@ -93,6 +93,9 @@ export function useListCalculated({
|
|
|
93
93
|
});
|
|
94
94
|
}
|
|
95
95
|
});
|
|
96
|
+
if (calculatedObjectsEffectScopes[objectKey]) {
|
|
97
|
+
calculatedObjectsEffectScopes[objectKey].stop();
|
|
98
|
+
}
|
|
96
99
|
calculatedObjectsEffectScopes[objectKey] = calculatedObjectsEffectScope;
|
|
97
100
|
}
|
|
98
101
|
parentStateObjectsWatch();
|
package/use/object.js
ADDED
|
@@ -0,0 +1,99 @@
|
|
|
1
|
+
import { effectScope, shallowReactive, shallowReadonly, toRef, watch } from "vue";
|
|
2
|
+
import { useObjectCalculated } from "./objectCalculated";
|
|
3
|
+
import { useObjectInstance } from "./objectInstance";
|
|
4
|
+
import { useObjectRelated } from "./objectRelated";
|
|
5
|
+
import { useObjectSubscription } from "./objectSubscription";
|
|
6
|
+
|
|
7
|
+
// Manages a chain of useObject functions, based on existence of keys in props: intendToRetrieve, relatedObjectRules, calculatedObjectRules
|
|
8
|
+
export const useObject = ({ props }) => {
|
|
9
|
+
const managed = shallowReactive({
|
|
10
|
+
objectInstance: null,
|
|
11
|
+
objectSubscription: null,
|
|
12
|
+
objectRelated: null,
|
|
13
|
+
objectCalculated: null,
|
|
14
|
+
});
|
|
15
|
+
const es = effectScope();
|
|
16
|
+
|
|
17
|
+
managed.objectInstance = useObjectInstance({
|
|
18
|
+
crudArgs: toRef(props, "crudArgs"),
|
|
19
|
+
id: toRef(props, "id"),
|
|
20
|
+
retrieveArgs: toRef(props, "retrieveArgs"),
|
|
21
|
+
});
|
|
22
|
+
|
|
23
|
+
const intentPropsWatch = () => {
|
|
24
|
+
es.run(() => {
|
|
25
|
+
let nextState = managed.objectInstance?.state;
|
|
26
|
+
// true or false, having a key is intent to use
|
|
27
|
+
const hasIntendToRetrieve = "intendToRetrieve" in props;
|
|
28
|
+
if (hasIntendToRetrieve && !managed.objectSubscription) {
|
|
29
|
+
managed.objectSubscription = useObjectSubscription({
|
|
30
|
+
objectInstance: managed.objectInstance,
|
|
31
|
+
});
|
|
32
|
+
managed.objectSubscription.state.intendToRetrieve = toRef(props, "intendToRetrieve");
|
|
33
|
+
} else if (!hasIntendToRetrieve && managed.objectSubscription) {
|
|
34
|
+
managed.objectSubscription.effectScope.stop();
|
|
35
|
+
managed.objectSubscription = null;
|
|
36
|
+
}
|
|
37
|
+
const hasRelatedObjectRules = "relatedObjectRules" in props;
|
|
38
|
+
if (hasRelatedObjectRules && !managed.objectRelated) {
|
|
39
|
+
nextState = managed.objectSubscription?.state || nextState;
|
|
40
|
+
managed.objectRelated = useObjectRelated({
|
|
41
|
+
parentState: nextState,
|
|
42
|
+
relatedObjectRules: toRef(props, "relatedObjectRules"),
|
|
43
|
+
});
|
|
44
|
+
} else if (!hasRelatedObjectRules && managed.objectRelated) {
|
|
45
|
+
managed.objectRelated.effectScope.stop();
|
|
46
|
+
managed.objectRelated = null;
|
|
47
|
+
}
|
|
48
|
+
const hasCalculatedObjectRules = "calculatedObjectRules" in props;
|
|
49
|
+
if (hasCalculatedObjectRules && !managed.objectCalculated) {
|
|
50
|
+
nextState = managed.objectRelated?.state || nextState;
|
|
51
|
+
managed.objectCalculated = useObjectCalculated({
|
|
52
|
+
parentState: nextState,
|
|
53
|
+
calculatedObjectRules: toRef(props, "calculatedObjectRules"),
|
|
54
|
+
});
|
|
55
|
+
} else if (!hasCalculatedObjectRules && managed.objectCalculated) {
|
|
56
|
+
managed.objectCalculated.effectScope.stop();
|
|
57
|
+
managed.objectCalculated = null;
|
|
58
|
+
}
|
|
59
|
+
});
|
|
60
|
+
};
|
|
61
|
+
|
|
62
|
+
let exposedState;
|
|
63
|
+
|
|
64
|
+
es.run(() => {
|
|
65
|
+
watch(
|
|
66
|
+
[
|
|
67
|
+
toRef(props, "intendToRetrieve"),
|
|
68
|
+
toRef(props, "relatedObjectRules"),
|
|
69
|
+
toRef(props, "calculatedObjectRules"),
|
|
70
|
+
],
|
|
71
|
+
intentPropsWatch,
|
|
72
|
+
{ immediate: true }
|
|
73
|
+
);
|
|
74
|
+
const getState = () =>
|
|
75
|
+
managed.objectCalculated?.state ||
|
|
76
|
+
managed.objectRelated?.state ||
|
|
77
|
+
managed.objectSubscription?.state ||
|
|
78
|
+
managed.objectInstance.state;
|
|
79
|
+
// used as proxy to have the properties and not be settable, so we only have to override get
|
|
80
|
+
const proxyBase = shallowReadonly({
|
|
81
|
+
loading: null,
|
|
82
|
+
error: null,
|
|
83
|
+
errored: null,
|
|
84
|
+
object: null,
|
|
85
|
+
});
|
|
86
|
+
exposedState = new Proxy(proxyBase, {
|
|
87
|
+
// get values from the current state
|
|
88
|
+
get(target, key) {
|
|
89
|
+
return Reflect.get(getState(), key);
|
|
90
|
+
},
|
|
91
|
+
});
|
|
92
|
+
});
|
|
93
|
+
|
|
94
|
+
return {
|
|
95
|
+
managed: shallowReadonly(managed),
|
|
96
|
+
state: exposedState,
|
|
97
|
+
effectScope: es,
|
|
98
|
+
};
|
|
99
|
+
};
|
package/use/objectCalculated.js
CHANGED
|
@@ -18,7 +18,7 @@ export function useObjectCalculated({
|
|
|
18
18
|
}) {
|
|
19
19
|
const state = reactive({
|
|
20
20
|
calculatedObjectRules,
|
|
21
|
-
|
|
21
|
+
calculatedObjectObjects: {},
|
|
22
22
|
object: {},
|
|
23
23
|
});
|
|
24
24
|
const calculatedObjectEffectScopes = {};
|
|
@@ -33,7 +33,7 @@ export function useObjectCalculated({
|
|
|
33
33
|
state.object = new Proxy(parentState.object, {
|
|
34
34
|
get(target, key, receiver) {
|
|
35
35
|
if (key === copn) {
|
|
36
|
-
return state.
|
|
36
|
+
return state.calculatedObjectObjects;
|
|
37
37
|
}
|
|
38
38
|
return Reflect.get(target, key, receiver);
|
|
39
39
|
},
|
|
@@ -51,7 +51,7 @@ export function useObjectCalculated({
|
|
|
51
51
|
return {
|
|
52
52
|
configurable: true,
|
|
53
53
|
enumerable: true,
|
|
54
|
-
value: state.
|
|
54
|
+
value: state.calculatedObjectObjects,
|
|
55
55
|
writable: true,
|
|
56
56
|
};
|
|
57
57
|
}
|
|
@@ -73,9 +73,15 @@ export function useObjectCalculated({
|
|
|
73
73
|
Object.keys(state.calculatedObjectRules),
|
|
74
74
|
Object.keys(calculatedObjectOriginalFunctions)
|
|
75
75
|
);
|
|
76
|
+
for (const sameKey of sameKeys) {
|
|
77
|
+
if (calculatedObjectOriginalFunctions[sameKey] !== state.calculatedObjectRules[sameKey]) {
|
|
78
|
+
removedKeys.push(sameKey);
|
|
79
|
+
addedKeys.push(sameKey);
|
|
80
|
+
}
|
|
81
|
+
}
|
|
76
82
|
for (const removedKey of removedKeys) {
|
|
77
83
|
delete calculatedObjectOriginalFunctions[removedKey];
|
|
78
|
-
delete state.
|
|
84
|
+
delete state.calculatedObjectObjects[removedKey];
|
|
79
85
|
if (calculatedObjectEffectScopes[removedKey]) {
|
|
80
86
|
calculatedObjectEffectScopes[removedKey].stop();
|
|
81
87
|
delete calculatedObjectEffectScopes[removedKey];
|
|
@@ -85,21 +91,11 @@ export function useObjectCalculated({
|
|
|
85
91
|
calculatedObjectOriginalFunctions[addedKey] = state.calculatedObjectRules[addedKey];
|
|
86
92
|
calculatedObjectEffectScopes[addedKey] = effectScope();
|
|
87
93
|
calculatedObjectEffectScopes[addedKey].run(() => {
|
|
88
|
-
state.
|
|
94
|
+
state.calculatedObjectObjects[addedKey] = computed(() =>
|
|
89
95
|
calculatedObjectOriginalFunctions[addedKey](state.object)
|
|
90
96
|
);
|
|
91
97
|
});
|
|
92
98
|
}
|
|
93
|
-
for (const sameKey of sameKeys) {
|
|
94
|
-
calculatedObjectOriginalFunctions[sameKey] = state.calculatedObjectRules[sameKey];
|
|
95
|
-
calculatedObjectEffectScopes[sameKey].stop();
|
|
96
|
-
calculatedObjectEffectScopes[sameKey] = effectScope();
|
|
97
|
-
calculatedObjectEffectScopes[sameKey].run(() => {
|
|
98
|
-
state.calculatedObjectObject[sameKey] = computed(() =>
|
|
99
|
-
calculatedObjectOriginalFunctions[sameKey](state.object)
|
|
100
|
-
);
|
|
101
|
-
});
|
|
102
|
-
}
|
|
103
99
|
},
|
|
104
100
|
{
|
|
105
101
|
immediate: true,
|
package/use/objectInstance.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { cloneDeep } from "lodash";
|
|
2
|
-
import { effectScope, reactive } from "vue";
|
|
2
|
+
import { effectScope, reactive, unref } from "vue";
|
|
3
3
|
import { assignReactiveObject } from "../utils/assignReactiveObject";
|
|
4
4
|
|
|
5
5
|
export class ObjectError extends Error {
|
|
@@ -56,7 +56,9 @@ export function useObjectInstance({ crudArgs, id, retrieveArgs }) {
|
|
|
56
56
|
// prevent linking of all instances to the same default .args object
|
|
57
57
|
Object.assign(state.crud, cloneDeep(defaultCrud));
|
|
58
58
|
if (crudArgs) {
|
|
59
|
-
|
|
59
|
+
// generally you won't have a ref to an object, but indirectly, you could have a ref to a reactive.
|
|
60
|
+
// either way, we want a reactive or plain object. computed isn't going to work for this.
|
|
61
|
+
assignReactiveObject(state.crud.args, unref(crudArgs));
|
|
60
62
|
}
|
|
61
63
|
|
|
62
64
|
// due to retrieve being called by `useCancelleableIntent`, if called manually then by the watch,
|
|
@@ -0,0 +1,121 @@
|
|
|
1
|
+
import { get, isArray, isUndefined } from "lodash";
|
|
2
|
+
import { computed, effectScope, onScopeDispose, reactive, toRef, unref, watch } from "vue";
|
|
3
|
+
import { keyDiff } from "../utils";
|
|
4
|
+
|
|
5
|
+
export function useObjectRelateds(instances, args) {
|
|
6
|
+
for (const [key, value] of Object.entries(args)) {
|
|
7
|
+
useObjectRelated({
|
|
8
|
+
parentState: instances[key].state,
|
|
9
|
+
...value,
|
|
10
|
+
});
|
|
11
|
+
}
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
// the single object version of useListRelated
|
|
15
|
+
export function useObjectRelated({
|
|
16
|
+
parentState,
|
|
17
|
+
relatedObjectRules,
|
|
18
|
+
relatedObjectPropertyName = "relatedObject", // NOT REACTIVE
|
|
19
|
+
}) {
|
|
20
|
+
const state = reactive({
|
|
21
|
+
relatedObjectRules,
|
|
22
|
+
relatedObjectObjects: {},
|
|
23
|
+
object: {},
|
|
24
|
+
});
|
|
25
|
+
const relatedObjectEffectScopes = {};
|
|
26
|
+
|
|
27
|
+
// don't change relatedObjectPropertyName on us or it will break
|
|
28
|
+
const ropn = relatedObjectPropertyName + "";
|
|
29
|
+
|
|
30
|
+
const es = effectScope();
|
|
31
|
+
|
|
32
|
+
es.run(() => {
|
|
33
|
+
state.object = new Proxy(parentState.object, {
|
|
34
|
+
get(target, key, receiver) {
|
|
35
|
+
if (key === ropn) {
|
|
36
|
+
return state.relatedObjectObjects;
|
|
37
|
+
}
|
|
38
|
+
return Reflect.get(target, key, receiver);
|
|
39
|
+
},
|
|
40
|
+
ownKeys(target) {
|
|
41
|
+
return Reflect.ownKeys(target).concat(ropn);
|
|
42
|
+
},
|
|
43
|
+
has(target, key) {
|
|
44
|
+
if (key === ropn) {
|
|
45
|
+
return true;
|
|
46
|
+
}
|
|
47
|
+
return Reflect.has(target, key);
|
|
48
|
+
},
|
|
49
|
+
getOwnPropertyDescriptor(target, key) {
|
|
50
|
+
if (key === ropn) {
|
|
51
|
+
return {
|
|
52
|
+
configurable: true,
|
|
53
|
+
enumerable: true,
|
|
54
|
+
value: state.relatedObjectObjects,
|
|
55
|
+
writable: true,
|
|
56
|
+
};
|
|
57
|
+
}
|
|
58
|
+
return Reflect.getOwnPropertyDescriptor(target, key);
|
|
59
|
+
},
|
|
60
|
+
defineProperty() {
|
|
61
|
+
return false;
|
|
62
|
+
},
|
|
63
|
+
});
|
|
64
|
+
state.loading = toRef(parentState, "loading");
|
|
65
|
+
state.error = toRef(parentState, "error");
|
|
66
|
+
state.errored = toRef(parentState, "errored");
|
|
67
|
+
state.deleted = toRef(parentState, "deleted");
|
|
68
|
+
|
|
69
|
+
watch(
|
|
70
|
+
[() => Object.keys(state.relatedObjectRules)],
|
|
71
|
+
() => {
|
|
72
|
+
const { addedKeys: addedRuleKeys, removedKeys: removedRuleKeys } = keyDiff(
|
|
73
|
+
Object.keys(state.relatedObjectRules),
|
|
74
|
+
Object.keys(state.relatedObjectObjects)
|
|
75
|
+
);
|
|
76
|
+
for (const removedRuleKey of removedRuleKeys) {
|
|
77
|
+
delete state.relatedObjectObjects[removedRuleKey];
|
|
78
|
+
if (relatedObjectEffectScopes[removedRuleKey]) {
|
|
79
|
+
relatedObjectEffectScopes[removedRuleKey].stop();
|
|
80
|
+
delete relatedObjectEffectScopes[removedRuleKey];
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
for (const addedRuleKey of addedRuleKeys) {
|
|
84
|
+
relatedObjectEffectScopes[addedRuleKey] = effectScope();
|
|
85
|
+
relatedObjectEffectScopes[addedRuleKey].run(() => {
|
|
86
|
+
state.relatedObjectObjects[addedRuleKey] = computed(() => {
|
|
87
|
+
// deal with computed objects being passed.
|
|
88
|
+
const ruleObjects = unref(state.relatedObjectRules?.[addedRuleKey]?.objects);
|
|
89
|
+
const rulePkKey = state.relatedObjectRules?.[addedRuleKey]?.pkKey || addedRuleKey;
|
|
90
|
+
if (!ruleObjects || !rulePkKey) {
|
|
91
|
+
return undefined;
|
|
92
|
+
}
|
|
93
|
+
const value = get(parentState.object, rulePkKey);
|
|
94
|
+
if (isUndefined(value)) {
|
|
95
|
+
return undefined;
|
|
96
|
+
}
|
|
97
|
+
if (isArray(value)) {
|
|
98
|
+
return value.map((e) => ruleObjects[e]);
|
|
99
|
+
}
|
|
100
|
+
return ruleObjects[value];
|
|
101
|
+
});
|
|
102
|
+
});
|
|
103
|
+
}
|
|
104
|
+
},
|
|
105
|
+
{
|
|
106
|
+
immediate: true,
|
|
107
|
+
}
|
|
108
|
+
);
|
|
109
|
+
|
|
110
|
+
onScopeDispose(() => {
|
|
111
|
+
for (const key in relatedObjectEffectScopes) {
|
|
112
|
+
relatedObjectEffectScopes[key].stop();
|
|
113
|
+
}
|
|
114
|
+
});
|
|
115
|
+
});
|
|
116
|
+
return {
|
|
117
|
+
state,
|
|
118
|
+
parentState,
|
|
119
|
+
effectScope: es,
|
|
120
|
+
};
|
|
121
|
+
}
|
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
import { cloneDeep } from "lodash";
|
|
2
2
|
import { computed, effectScope, reactive, toRef } from "vue";
|
|
3
3
|
import { assignReactiveObject } from "../utils/assignReactiveObject";
|
|
4
|
-
import { useObjectInstance } from "./objectInstance";
|
|
5
4
|
import { useCancellableIntent } from "../utils/cancellableIntent";
|
|
6
5
|
import { loadingCombine } from "../utils/loadingCombine";
|
|
6
|
+
import { useObjectInstance } from "./objectInstance";
|
|
7
7
|
|
|
8
8
|
export class ObjectSubscriptionError extends Error {
|
|
9
9
|
constructor(message) {
|
|
@@ -30,7 +30,7 @@ export function useObjectSubscriptions(subscriptionArgs) {
|
|
|
30
30
|
return subscriptions;
|
|
31
31
|
}
|
|
32
32
|
|
|
33
|
-
export function useObjectSubscription({ objectInstance, crudArgs, id, retrieveArgs
|
|
33
|
+
export function useObjectSubscription({ objectInstance, crudArgs, id, retrieveArgs }) {
|
|
34
34
|
if (retrieveArgs && objectInstance) {
|
|
35
35
|
throw new ObjectSubscriptionError(
|
|
36
36
|
"Cannot use retrieveArgs and objectInstance together, set retrieveArgs on objectInstance instead"
|
|
@@ -44,7 +44,6 @@ export function useObjectSubscription({ objectInstance, crudArgs, id, retrieveAr
|
|
|
44
44
|
args: {},
|
|
45
45
|
subscribe: undefined,
|
|
46
46
|
},
|
|
47
|
-
id,
|
|
48
47
|
subscriptionLoading: undefined,
|
|
49
48
|
subscriptionErrored: false,
|
|
50
49
|
subscriptionError: null,
|
|
@@ -95,7 +94,7 @@ export function useObjectSubscription({ objectInstance, crudArgs, id, retrieveAr
|
|
|
95
94
|
let subscribePromise;
|
|
96
95
|
subscribePromise = state.crud.subscribe({
|
|
97
96
|
crudArgs: state.crud.args,
|
|
98
|
-
id,
|
|
97
|
+
id: objectInstance.state.id,
|
|
99
98
|
retrieveArgs: state.retrieveArgs,
|
|
100
99
|
callback: (data, action) => {
|
|
101
100
|
if (action === "delete") {
|
|
@@ -166,13 +165,22 @@ export function useObjectSubscription({ objectInstance, crudArgs, id, retrieveAr
|
|
|
166
165
|
|
|
167
166
|
subscribeIntent = useCancellableIntent({
|
|
168
167
|
awaitableWithCancel: subscribe,
|
|
169
|
-
watchArguments: [
|
|
168
|
+
watchArguments: [
|
|
169
|
+
toRef(state, "intendToSubscribe"),
|
|
170
|
+
toRef(objectInstance.state, "id"),
|
|
171
|
+
toRef(state, "retrieveArgs"),
|
|
172
|
+
],
|
|
170
173
|
clearActiveOnResolved: false,
|
|
171
174
|
});
|
|
172
175
|
|
|
173
176
|
retrieveIntent = useCancellableIntent({
|
|
174
177
|
awaitableWithCancel: objectInstance.retrieve,
|
|
175
|
-
watchArguments: [
|
|
178
|
+
watchArguments: [
|
|
179
|
+
toRef(state, "intendToRetrieve"),
|
|
180
|
+
toRef(objectInstance.state, "id"),
|
|
181
|
+
toRef(state, "retrieveArgs"),
|
|
182
|
+
],
|
|
183
|
+
nameForLog: "retrieveIntent",
|
|
176
184
|
});
|
|
177
185
|
});
|
|
178
186
|
|