@arrai-innovations/reactive-helpers 10.3.0 → 10.4.1

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": "@arrai-innovations/reactive-helpers",
3
- "version": "10.3.0",
3
+ "version": "10.4.1",
4
4
  "description": "VueJS 3 utility composition functions to help manipulate objects and lists.",
5
5
  "main": "index.js",
6
6
  "type": "module",
@@ -1,4 +1,5 @@
1
1
  import { doAwaitTimeout } from "../../../utils/index.js";
2
+ import { reactive } from "vue";
2
3
  import { deepUnref } from "vue-deepunref";
3
4
 
4
5
  describe("use/useListSort", () => {
@@ -7,6 +8,8 @@ describe("use/useListSort", () => {
7
8
  sortThrottleWait,
8
9
  useListInstance,
9
10
  useListInstances,
11
+ useListRelated,
12
+ useListCalculated,
10
13
  useListSort,
11
14
  useListSorts,
12
15
  setListSortDefaultOptions,
@@ -41,6 +44,8 @@ describe("use/useListSort", () => {
41
44
  const imported = await import("../../../use");
42
45
  useListInstance = imported.useListInstance;
43
46
  useListInstances = imported.useListInstances;
47
+ useListRelated = imported.useListRelated;
48
+ useListCalculated = imported.useListCalculated;
44
49
  orderByRules = [
45
50
  { key: "organization", desc: true, localeCompare: false },
46
51
  { key: "lexical_name", desc: false, localeCompare: true },
@@ -207,6 +212,72 @@ describe("use/useListSort", () => {
207
212
  expect(deepUnref(listSorts.A.state)).toEqual(deepUnref(listSortA.state));
208
213
  expect(deepUnref(listSorts.B.state)).toEqual(deepUnref(listSortB.state));
209
214
  });
215
+ it("orderByRules can refer to relatedItem or calculatedItem", async () => {
216
+ const listInstance = useListInstance({
217
+ props: reactive({
218
+ crudArgs: { stream: "test_stream" },
219
+ listArgs: { user: 1 },
220
+ retrieveArgs: {
221
+ fields: ["id", "__str__", "name", "relatedItem", "calculatedItem"],
222
+ },
223
+ }),
224
+ });
225
+ const relatedListInstance = useListInstance({
226
+ props: reactive({
227
+ crudArgs: { stream: "test_related_stream" },
228
+ listArgs: { user: 1 },
229
+ retrieveArgs: {
230
+ fields: ["id", "__str__", "name"],
231
+ },
232
+ }),
233
+ });
234
+ const listRelated = useListRelated({
235
+ parentState: listInstance.state,
236
+ relatedObjectsRules: reactive({
237
+ relatedItemName: {
238
+ objects: relatedListInstance.state.objects,
239
+ pkKey: "relatedItem",
240
+ },
241
+ }),
242
+ });
243
+ const listCalculated = useListCalculated({
244
+ parentState: listRelated.state,
245
+ calculatedObjectsRules: reactive({
246
+ calculatedItemName: (obj, relatedObj) => {
247
+ return obj.sameValue + ":" + relatedObj.relatedItemName?.oppositeOrder;
248
+ },
249
+ }),
250
+ });
251
+ const orderByRules = reactive([
252
+ { key: "calculatedItem.calculatedItemName", desc: false, localeCompare: false },
253
+ ]);
254
+ const listSort = useListSort({
255
+ parentState: listCalculated.state,
256
+ orderByRules,
257
+ });
258
+ for (let i = 1; i <= 4; i++) {
259
+ listInstance.addListObject({
260
+ id: i,
261
+ name: `item${i}`,
262
+ relatedItem: i,
263
+ sameValue: "sameValue",
264
+ });
265
+ relatedListInstance.addListObject({
266
+ id: i,
267
+ name: `relatedItem${i}`,
268
+ oppositeOrder: 5 - i,
269
+ });
270
+ }
271
+ await waitForListSort(listSort);
272
+ expect(listSort.state.order).toEqual(["4", "3", "2", "1"]);
273
+ orderByRules[0].key = "relatedItemName.name";
274
+ await waitForListSort(listSort);
275
+ expect(listSort.state.order).toEqual(["1", "2", "3", "4"]);
276
+ orderByRules[0].key = "relatedItemName.sameValue";
277
+ orderByRules[1] = { key: "calculatedItem.calculatedItemName", desc: false, localeCompare: false };
278
+ await waitForListSort(listSort);
279
+ expect(listSort.state.order).toEqual(["4", "3", "2", "1"]);
280
+ });
210
281
  describe("useListSort/sortThrottleWait", () => {
211
282
  it("respects throttle time prior to triggering", async () => {
212
283
  setListSortDefaultOptions({
@@ -3,7 +3,7 @@ import {
3
3
  assignReactiveObject,
4
4
  AssignReactiveObjectError,
5
5
  } from "../../../utils/assignReactiveObject.js";
6
- import { computed, EffectScope, effectScope, reactive, toRef } from "vue";
6
+ import { computed, EffectScope, effectScope, reactive, toRef, unref } from "vue";
7
7
 
8
8
  describe("utils/assignReactiveObject", function () {
9
9
  describe.skip("addOrUpdateReactiveObject", function () {});
@@ -57,6 +57,68 @@ describe("utils/assignReactiveObject", function () {
57
57
  tes.stop();
58
58
  }
59
59
  });
60
+ it("when target is reactive and source are reactive, for non-primitive values", function () {
61
+ const target = reactive({
62
+ a: { e: 8 },
63
+ b: { f: 16 },
64
+ c: null,
65
+ });
66
+ const source = reactive({
67
+ a: { e: 1 },
68
+ b: { f: 2 },
69
+ c: { g: 4 },
70
+ });
71
+ target.c = toRef(source, "c");
72
+ const tes = effectScope();
73
+ let computedSum;
74
+ tes.run(() => {
75
+ computedSum = computed(() => target.a.e + target.b.f + target.c.g);
76
+ });
77
+ try {
78
+ expect(unref(target.a)).not.toBe(unref(source.a));
79
+ expect(unref(target.b)).not.toBe(unref(source.b));
80
+ expect(unref(target.c)).toBe(unref(source.c));
81
+ expect(computedSum.value).toBe(28);
82
+ assignReactiveObject(target, source);
83
+ expect(target).toEqual({
84
+ a: { e: 1 },
85
+ b: { f: 2 },
86
+ c: { g: 4 },
87
+ });
88
+ expect(unref(target.a)).toBe(unref(source.a));
89
+ expect(unref(target.b)).toBe(unref(source.b));
90
+ expect(unref(target.c)).toBe(unref(source.c));
91
+ expect(computedSum.value).toBe(7);
92
+ } finally {
93
+ computedSum = null;
94
+ tes.stop();
95
+ }
96
+ });
97
+ it("does nothing when all targets are objectlike and already point to the source", function () {
98
+ const target = reactive({});
99
+ const source = reactive({
100
+ a: { e: 1 },
101
+ b: { f: 2 },
102
+ c: { g: 4 },
103
+ });
104
+ target.a = toRef(source, "a");
105
+ target.b = toRef(source, "b");
106
+ target.c = toRef(source, "c");
107
+ expect(unref(toRef(target, "a"))).toBe(unref(source.a));
108
+ expect(unref(toRef(target, "b"))).toBe(unref(source.b));
109
+ expect(unref(toRef(target, "c"))).toBe(unref(source.c));
110
+ expect(target.a).toBe(source.a);
111
+ expect(target.b).toBe(source.b);
112
+ expect(target.c).toBe(source.c);
113
+ const didAnything = assignReactiveObject(target, source);
114
+ expect(didAnything).toBeFalsy();
115
+ expect(unref(toRef(target, "a"))).toBe(unref(source.a));
116
+ expect(unref(toRef(target, "b"))).toBe(unref(source.b));
117
+ expect(unref(toRef(target, "c"))).toBe(unref(source.c));
118
+ expect(target.a).toBe(source.a);
119
+ expect(target.b).toBe(source.b);
120
+ expect(target.c).toBe(source.c);
121
+ });
60
122
  });
61
123
  describe("should throw an error", function () {
62
124
  it("when target is not an array or object", function () {
package/use/listFilter.js CHANGED
@@ -42,6 +42,8 @@ export function useListFilter({
42
42
  excludedValues = {},
43
43
  allowedFilter,
44
44
  excludedFilter,
45
+ customIndexOptions = {},
46
+ customSearchOptions = {},
45
47
  }) {
46
48
  const state = reactive({
47
49
  objectIndexes: {},
@@ -77,7 +79,7 @@ export function useListFilter({
77
79
  state[key] = toRef(parentState, key);
78
80
  }
79
81
  if (useTextSearch) {
80
- textSearchIndex = useSearch();
82
+ textSearchIndex = useSearch(customIndexOptions, customSearchOptions);
81
83
  textSearchIndex.state.search = toRef(state, "textSearchValue");
82
84
  state.searched = toRef(textSearchIndex.state, "searched");
83
85
  state.searching = toRef(textSearchIndex.state, "searching");
package/use/listSort.js CHANGED
@@ -77,7 +77,7 @@ export function useListSort({ parentState, orderByRules, sortThrottleWait = defa
77
77
  delete state.sortCriteria[removedKey];
78
78
  }
79
79
 
80
- function addSortCriteria(object, key) {
80
+ function addSortCriteria(object, relatedObject, calculatedObject, key) {
81
81
  const oldScope = state.sortCriteriaEffectScopes[key];
82
82
  if (oldScope) {
83
83
  oldScope.stop();
@@ -88,16 +88,24 @@ export function useListSort({ parentState, orderByRules, sortThrottleWait = defa
88
88
  state.sortCriteria[key] = [];
89
89
  }
90
90
  watch(
91
- [object, toRef(state, "orderByRules")],
91
+ [object, relatedObject, calculatedObject, toRef(state, "orderByRules")],
92
92
  () => {
93
93
  const obj = unref(object);
94
+ const relatedObj = unref(relatedObject);
95
+ const calculatedObj = unref(calculatedObject);
94
96
  const newSearchCriteria = [];
95
97
  for (const orderByObj of state.orderByRules.filter(identity)) {
96
98
  let newItem;
97
99
  if (orderByObj.keyFn) {
98
100
  newItem = orderByObj.keyFn(obj, state);
99
101
  } else {
100
- newItem = get(obj, orderByObj.key);
102
+ if (orderByObj.key.startsWith("relatedItem.")) {
103
+ newItem = get(relatedObj, orderByObj.key.slice(12));
104
+ } else if (orderByObj.key.startsWith("calculatedItem.")) {
105
+ newItem = get(calculatedObj, orderByObj.key.slice(15));
106
+ } else {
107
+ newItem = get(obj, orderByObj.key);
108
+ }
101
109
  }
102
110
  newSearchCriteria.push(newItem);
103
111
  }
@@ -141,7 +149,9 @@ export function useListSort({ parentState, orderByRules, sortThrottleWait = defa
141
149
  es.run(() => {
142
150
  for (const addedKey of addedKeys) {
143
151
  const object = toRef(() => parentState.objects[addedKey]);
144
- addSortCriteria(object, addedKey);
152
+ const relatedObj = toRef(() => parentState.relatedObjects?.[addedKey]);
153
+ const calculatedObj = toRef(() => parentState.calculatedObjects?.[addedKey]);
154
+ addSortCriteria(object, relatedObj, calculatedObj, addedKey);
145
155
  }
146
156
  });
147
157
  assignReactiveObject(
@@ -2,6 +2,7 @@ import { keyDiff } from "./keyDiff.js";
2
2
  import inspect from "browser-util-inspect";
3
3
  import isArray from "lodash-es/isArray.js";
4
4
  import isObject from "lodash-es/isObject.js";
5
+ import isObjectLike from "lodash-es/isObjectLike.js";
5
6
  import { isReactive, isRef, toRef, unref } from "vue";
6
7
 
7
8
  /**
@@ -89,6 +90,16 @@ function reactiveReplaceKeys(target, source, keys, exclude = []) {
89
90
  for (const key of keys) {
90
91
  if (!exclude.includes(key)) {
91
92
  if (targetIsReactive && sourceIsReactive) {
93
+ const targetPropRaw = unref(toRef(target, key));
94
+ // if they are object like we can see if the values are the same
95
+ if (isObjectLike(targetPropRaw)) {
96
+ const sourcePropRaw = unref(toRef(source, key));
97
+ if (isObjectLike(sourcePropRaw)) {
98
+ if (targetPropRaw === sourcePropRaw) {
99
+ continue;
100
+ }
101
+ }
102
+ }
92
103
  target[key] = toRef(source, key);
93
104
  didAnything = true;
94
105
  } else if (target[key] !== source[key]) {
@@ -226,7 +237,7 @@ export function assignReactiveObject(target, source, exclude = []) {
226
237
  ({ target, source } = validateTargetAndSource(target, source));
227
238
  const { addedKeys, sameKeys, removedKeys } = keyDiff(Object.keys(source) || [], Object.keys(target) || []);
228
239
  let didAnything = false;
229
- didAnything |= trimReactiveObject(target, removedKeys, exclude);
240
+ didAnything |= trimReactiveObject(target, source, exclude, removedKeys);
230
241
  didAnything |= addOrUpdateReactiveObject(target, source, exclude, addedKeys, sameKeys);
231
242
  return didAnything;
232
243
  }
@@ -290,7 +301,7 @@ function recursiveInner(target, source, exclude, addedKeys, sameKeys, path, fn)
290
301
  function assignReactiveObjectRecursive(target, source, exclude = [], path = "") {
291
302
  let { addedKeys, sameKeys, removedKeys } = keyDiff(Object.keys(source) || [], Object.keys(target) || []);
292
303
  let didAnything = false;
293
- didAnything |= trimReactiveObject(target, removedKeys, exclude);
304
+ didAnything |= trimReactiveObject(target, source, exclude, removedKeys);
294
305
  didAnything |= recursiveInner(target, source, exclude, addedKeys, sameKeys, path, assignReactiveObjectRecursive);
295
306
  return didAnything;
296
307
  }