@arrai-innovations/reactive-helpers 10.4.2 → 11.0.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.
@@ -0,0 +1,99 @@
1
+ export const listInstanceStateKeys = [
2
+ "crud",
3
+ "retrieveArgs",
4
+ "listArgs",
5
+ "objects",
6
+ "loading",
7
+ "errored",
8
+ "error",
9
+ "objectsInOrder",
10
+ "objectsInOrderRefs",
11
+ "order",
12
+ // when paged
13
+ "totalRecords",
14
+ "totalPages",
15
+ "perPage",
16
+ ];
17
+ export const listInstanceFunctions = [
18
+ "list",
19
+ "addListObject",
20
+ "updateListObject",
21
+ "deleteListObject",
22
+ "clearList",
23
+ "clearError",
24
+ "getFakeId",
25
+ "defaultPageCallback",
26
+ "pageCallback",
27
+ ];
28
+
29
+ export const listSubscriptionStateKeys = [
30
+ "subscriptionLoading",
31
+ "subscriptionErrored",
32
+ "subscriptionError",
33
+ "intendToList",
34
+ "intendToSubscribe",
35
+ "subscribed",
36
+ ];
37
+ export const listSubscriptionFunctions = ["subscribe", "unsubscribe", "clearError"];
38
+
39
+ export const listRelatedStateKeys = [
40
+ "relatedObjects",
41
+ "relatedObjectsRules",
42
+ "relatedObjectsWatchRunning",
43
+ "relatedObjectsParentStateObjectsWatchRunning",
44
+ "running",
45
+ ];
46
+ export const listRelatedFunctions = [];
47
+
48
+ export const listCalculatedStateKeys = [
49
+ "calculatedObjects",
50
+ "calculatedObjectsParentStateObjectsWatchRunning",
51
+ "calculatedObjectsRules",
52
+ "calculatedObjectsWatchRunning",
53
+ "running",
54
+ ];
55
+ export const listCalculatedFunctions = [];
56
+
57
+ export const listFilterStateKeys = [
58
+ "allowedFilter",
59
+ "excludedFilter",
60
+ "inResults",
61
+ "objects",
62
+ "objectsInOrder",
63
+ "objectsInOrderRefs",
64
+ "objectsWatchRunning",
65
+ "order",
66
+ "resultsWatchRunning",
67
+ "running",
68
+ ];
69
+ export const listFilterFunctions = [];
70
+
71
+ export const listSearchStateKeys = [
72
+ "objectIndexes",
73
+ "objects",
74
+ "order",
75
+ "objectsInOrder",
76
+ "objectsInOrderRefs",
77
+ "textSearchRules",
78
+ "textSearchValue",
79
+ "searched",
80
+ "updateSearchIndexesRunning",
81
+ "customDocumentOptions",
82
+ "customSearchOptions",
83
+ "running",
84
+ "newSearchComputeds",
85
+ ];
86
+ export const listSearchFunctions = [];
87
+ export const listSortStateKeys = [
88
+ "orderByRules",
89
+ "order",
90
+ "objectsInOrder",
91
+ "objectsInOrderRefs",
92
+ "sortCriteria",
93
+ "orderByDesc",
94
+ "sortCriteriaWatchRunning",
95
+ "sortWatchRunning",
96
+ "outstandingEffects",
97
+ "running",
98
+ ];
99
+ export const listSortFunctions = [];
@@ -1,7 +1,15 @@
1
+ import { difference } from "../utils/index.js";
1
2
  import { keyDiff } from "../utils/keyDiff.js";
2
3
  import { loadingCombine } from "../utils/loadingCombine.js";
3
- import { listInstanceStateKeys } from "./listInstance.js";
4
- import { listSubscriptionStateKeys } from "./listSubscription.js";
4
+ import {
5
+ listSubscriptionStateKeys,
6
+ listInstanceStateKeys,
7
+ listRelatedStateKeys,
8
+ listCalculatedStateKeys,
9
+ listSortStateKeys,
10
+ listFilterStateKeys,
11
+ listSearchStateKeys,
12
+ } from "./listKeys.js";
5
13
  import { useWatchesRunning } from "./watchesRunning.js";
6
14
  import get from "lodash-es/get.js";
7
15
  import identity from "lodash-es/identity.js";
@@ -11,14 +19,17 @@ import isEqual from "lodash-es/isEqual.js";
11
19
  import isUndefined from "lodash-es/isUndefined.js";
12
20
  import { computed, effectScope, onScopeDispose, reactive, toRef, unref, watch } from "vue";
13
21
 
14
- export const listRelatedStateKeys = [
15
- "relatedObjects",
16
- "relatedObjectsRules",
17
- "relatedObjectsWatchRunning",
18
- "relatedObjectsParentStateObjectsWatchRunning",
19
- ];
20
-
21
- export const listRelatedFunctions = [];
22
+ const parentStateKeys = difference(
23
+ new Set([
24
+ ...listInstanceStateKeys,
25
+ ...listSubscriptionStateKeys,
26
+ ...listCalculatedStateKeys,
27
+ ...listFilterStateKeys,
28
+ ...listSortStateKeys,
29
+ ...listSearchStateKeys,
30
+ ]),
31
+ new Set(listRelatedStateKeys)
32
+ );
22
33
 
23
34
  export function useListRelateds(instances, args) {
24
35
  for (const [key, value] of Object.entries(args)) {
@@ -151,10 +162,7 @@ export function useListRelated({ parentState, relatedObjectsRules }) {
151
162
  const es = effectScope();
152
163
 
153
164
  es.run(() => {
154
- for (const key of listInstanceStateKeys) {
155
- state[key] = toRef(parentState, key);
156
- }
157
- for (const key of listSubscriptionStateKeys) {
165
+ for (const key of parentStateKeys) {
158
166
  state[key] = toRef(parentState, key);
159
167
  }
160
168
 
@@ -0,0 +1,357 @@
1
+ import { difference, loadingCombine, assignReactiveObject, keyDiff } from "../utils/index.js";
2
+ import { getObjectRelatedCalculatedByKey } from "../utils/relatedCalculatedHelpers.js";
3
+ import {
4
+ listInstanceStateKeys,
5
+ listSubscriptionStateKeys,
6
+ listRelatedStateKeys,
7
+ listCalculatedStateKeys,
8
+ listSortStateKeys,
9
+ listFilterStateKeys,
10
+ listSearchStateKeys,
11
+ } from "./listKeys.js";
12
+ import { useSearch } from "./search.js";
13
+ import get from "lodash-es/get.js";
14
+ import isEqual from "lodash-es/isEqual.js";
15
+ import { reactive, effectScope, toRef, computed, watch, unref, onScopeDispose } from "vue";
16
+ import { deepUnref } from "vue-deepunref";
17
+
18
+ const parentStateKeys = difference(
19
+ new Set([
20
+ ...listInstanceStateKeys,
21
+ ...listSubscriptionStateKeys,
22
+ ...listRelatedStateKeys,
23
+ ...listCalculatedStateKeys,
24
+ ...listSortStateKeys,
25
+ ...listFilterStateKeys,
26
+ ]),
27
+ new Set(listSearchStateKeys)
28
+ );
29
+
30
+ /* eslint-disable jsdoc/check-types */
31
+ // types valid for jsdoc-to-markdown, which uses the strict jsdoc.app. Object shorthand syntax doesn't work.
32
+ /**
33
+ * A Vue composition function that creates multiple list instances, and returns them as an object.
34
+ * @param {Object.<string, ListSearchInstanceOptions>} listInstanceArgs - each desired list instance options, keyed by an instance name.
35
+ * @returns {Object.<string, ListSearchInstance>} - each list instance, keyed by the instance name.
36
+ */
37
+
38
+ /* eslint-enable jsdoc/check-types */
39
+ export function useListSearches(args, instances) {
40
+ const searches = {};
41
+ for (const [key, value] of Object.entries(args)) {
42
+ searches[key] = useListSearch({ parentState: instances[key].state, ...value });
43
+ }
44
+ return searches;
45
+ }
46
+
47
+ /**
48
+ * @typedef {object} ListSearchProps
49
+ * @property {array} textSearchRules - rules for what to search for. Keys are the keys to search for, values are functions that take the object and return the value to search for.
50
+ * @property {string} textSearchValue - the value to search for.
51
+ * @property {object} customDocumentOptions - FlexSearch.Document options
52
+ * @property {object} customSearchOptions - FlexSearch.Search options
53
+ * @property {object} [customSearchOptions.limit=1000] - FlexSearch.Search options
54
+ */
55
+
56
+ /**
57
+ * @typedef {object} ListSearchInstance
58
+ * @property {object} state - the state
59
+ * @property {SearchInstance} textSearchIndex - the text search index
60
+ * @property {object} effectScope - a Vue effect scope
61
+ */
62
+
63
+ /**
64
+ * @typedef {object} ListSearchInstanceOptions
65
+ * @property {object} parentState - the list being filtered
66
+ * @property {ListSearchProps} props - reactive properties
67
+ * @property {number} [throttle=500] - throttle wait time
68
+ * @property {boolean} [showAllWhenEmpty=true] - whether to show all items when the search is empty
69
+ */
70
+
71
+ /**
72
+ * Text filter for list items. This will not be performant for large lists, as each item will be watched.
73
+ * However, the results will reactively update.
74
+ * @param {ListSearchInstanceOptions} options - the arguments
75
+ * @returns {ListSearchInstance} - the instance
76
+ */
77
+ export function useListSearch({ parentState, props, throttle = 500, showAllWhenEmpty = true }) {
78
+ if (!parentState) {
79
+ throw new Error("parentState is required");
80
+ }
81
+ const state = reactive({
82
+ objects: {},
83
+ objectsInOrder: [],
84
+ objectsInOrderRefs: [],
85
+ order: [],
86
+ textSearchRules: toRef(props, "textSearchRules"),
87
+ textSearchValue: toRef(props, "textSearchValue"),
88
+ objectIndexes: {},
89
+ updateSearchIndexesRunning: undefined,
90
+ customDocumentOptions: toRef(props, "customDocumentOptions"),
91
+ customSearchOptions: toRef(props, "customSearchOptions"),
92
+ searched: undefined,
93
+ running: undefined,
94
+ newSearchComputeds: undefined,
95
+ });
96
+ if (!state.customDocumentOptions) {
97
+ state.customDocumentOptions = {};
98
+ }
99
+ if (!state.customSearchOptions) {
100
+ state.customSearchOptions = {};
101
+ }
102
+ const textSearchIndexProps = reactive({
103
+ customDocumentOptions: computed(() => {
104
+ const options = {
105
+ tokenize: "forward",
106
+ minlength: 2,
107
+ ...state.customDocumentOptions, // todo: not sure if this is ok inside a computed
108
+ };
109
+ if (!options.document) {
110
+ options.document = {
111
+ id: "id",
112
+ };
113
+ }
114
+ options.document.index = state.textSearchRules;
115
+ return options;
116
+ }),
117
+ customSearchOptions: computed(() => ({
118
+ ...state.customSearchOptions, // todo: not sure if this is ok inside a computed
119
+ limit: state.customSearchOptions.limit ?? 1000,
120
+ })),
121
+ });
122
+
123
+ const es = effectScope();
124
+
125
+ let textSearchIndex;
126
+
127
+ const objectEffectScopes = {};
128
+ const objectComputeds = {};
129
+
130
+ const previousTextSearchRules = [];
131
+ const previousObjectIndexes = {};
132
+
133
+ const doPassthrough = (cleanComputed = false) => {
134
+ // pass through the objects if there are no rules.
135
+ assignReactiveObject(state.objects, showAllWhenEmpty ? parentState.objects : {});
136
+ if (!cleanComputed) {
137
+ return;
138
+ }
139
+ // if there were indexes or computeds, there is no point in keeping them.
140
+ for (const objectKey of Object.keys(objectEffectScopes)) {
141
+ objectEffectScopes[objectKey].stop();
142
+ }
143
+ assignReactiveObject(objectEffectScopes, {});
144
+ assignReactiveObject(objectComputeds, {});
145
+ assignReactiveObject(state.objectIndexes, {});
146
+ };
147
+
148
+ const makeComputeds = () => {
149
+ if (!state.textSearchRules?.length) {
150
+ doPassthrough(true);
151
+ return;
152
+ }
153
+ const {
154
+ addedKeys: addedObjectIds,
155
+ removedKeys: removedObjectIds,
156
+ sameKeys: sameObjectIds,
157
+ } = keyDiff(Object.keys(parentState.objects), Object.keys(state.objects));
158
+ const { addedKeys: addedTextSearchRules, removedKeys: removedTextSearchRules } = keyDiff(
159
+ state.textSearchRules,
160
+ previousTextSearchRules
161
+ );
162
+ for (const removedObjectId of removedObjectIds) {
163
+ delete state.objectIndexes[removedObjectId];
164
+ // the effect scope will be stopped when the object is removed.
165
+ delete objectComputeds[removedObjectId];
166
+ if (objectEffectScopes[removedObjectId]) {
167
+ objectEffectScopes[removedObjectId].stop();
168
+ delete objectEffectScopes[removedObjectId];
169
+ }
170
+ }
171
+ for (const addedObjectId of addedObjectIds) {
172
+ state.objectIndexes[addedObjectId] = { id: addedObjectId };
173
+ objectComputeds[addedObjectId] = {};
174
+ objectEffectScopes[addedObjectId] = effectScope();
175
+ const objectEffectScope = objectEffectScopes[addedObjectId];
176
+ const objectRef = toRef(parentState.objects, addedObjectId);
177
+ const relatedRef = parentState.relatedObjects
178
+ ? toRef(parentState.relatedObjects, addedObjectId)
179
+ : undefined;
180
+ const calculatedRef = parentState.calculatedObjects
181
+ ? toRef(parentState.calculatedObjects, addedObjectId)
182
+ : undefined;
183
+ objectEffectScope.run(() => {
184
+ for (const rule of state.textSearchRules || []) {
185
+ const [obj, key] = getObjectRelatedCalculatedByKey(objectRef, relatedRef, calculatedRef, rule);
186
+ state.newSearchComputeds = true;
187
+ state.objectIndexes[addedObjectId][rule] = objectComputeds[addedObjectId][rule] = computed(() => {
188
+ return get(unref(obj), key);
189
+ });
190
+ }
191
+ });
192
+ }
193
+ for (const sameObjectId of sameObjectIds) {
194
+ const objectEffectScope = objectEffectScopes[sameObjectId];
195
+ const objectRef = toRef(parentState.objects, sameObjectId);
196
+ const relatedRef = parentState.relatedObjects ? toRef(parentState.relatedObjects, sameObjectId) : undefined;
197
+ const calculatedRef = parentState.calculatedObjects
198
+ ? toRef(parentState.calculatedObjects, sameObjectId)
199
+ : undefined;
200
+ for (const key of removedTextSearchRules) {
201
+ delete state.objectIndexes[sameObjectId][key];
202
+ // stop a computed earlier than the effect scope
203
+ objectComputeds[sameObjectId][key].effect.stop();
204
+ delete objectComputeds[sameObjectId][key];
205
+ }
206
+ objectEffectScope.run(() => {
207
+ for (const rule of addedTextSearchRules) {
208
+ const [obj, key] = getObjectRelatedCalculatedByKey(objectRef, relatedRef, calculatedRef, rule);
209
+ state.newSearchComputeds = true;
210
+ state.objectIndexes[sameObjectId][rule] = objectComputeds[sameObjectId][rule] = computed(() => {
211
+ return get(unref(obj), key);
212
+ });
213
+ }
214
+ });
215
+ }
216
+ previousTextSearchRules.length = 0;
217
+ if (state.textSearchRules?.length) {
218
+ previousTextSearchRules.push(...state.textSearchRules);
219
+ }
220
+ };
221
+
222
+ const updateSearchIndexes = async () => {
223
+ if (state.updateSearchIndexesRunning === undefined) {
224
+ state.updateSearchIndexesRunning = 0;
225
+ }
226
+ state.updateSearchIndexesRunning++;
227
+ try {
228
+ const { addedKeys, removedKeys, sameKeys } = keyDiff(
229
+ Object.keys(state.objectIndexes),
230
+ Object.keys(previousObjectIndexes)
231
+ );
232
+ const promises = [];
233
+ for (const removedKey of removedKeys) {
234
+ promises.push(textSearchIndex.removeIndex(removedKey));
235
+ delete previousObjectIndexes[removedKey];
236
+ }
237
+ for (const addedKey of addedKeys) {
238
+ promises.push(textSearchIndex.addIndex(state.objectIndexes[addedKey]));
239
+ previousObjectIndexes[addedKey] = deepUnref(state.objectIndexes[addedKey]);
240
+ }
241
+ for (const sameKey of sameKeys) {
242
+ if (!isEqual(previousObjectIndexes[sameKey], state.objectIndexes[sameKey])) {
243
+ promises.push(textSearchIndex.updateIndex(state.objectIndexes[sameKey]));
244
+ previousObjectIndexes[sameKey] = deepUnref(state.objectIndexes[sameKey]);
245
+ }
246
+ }
247
+ if (promises.length) {
248
+ await Promise.all(promises);
249
+ }
250
+ } finally {
251
+ if (state.newSearchComputeds) {
252
+ state.newSearchComputeds = false;
253
+ }
254
+ state.updateSearchIndexesRunning--;
255
+ }
256
+ };
257
+
258
+ const updateObjectsForResults = () => {
259
+ if (!state.textSearchRules?.length || !state.textSearchValue?.length) {
260
+ doPassthrough();
261
+ return;
262
+ }
263
+ assignReactiveObject(
264
+ state.objects,
265
+ Object.fromEntries(
266
+ Object.entries(textSearchIndex.state.results)
267
+ .filter(([, value]) => !!value)
268
+ .map(([id]) => [id, toRef(parentState.objects, id)])
269
+ )
270
+ );
271
+ };
272
+
273
+ const updateOrder = () => {
274
+ state.order = parentState.order.filter((id) => !!state.objects[id]);
275
+ assignReactiveObject(
276
+ state.objectsInOrderRefs,
277
+ parentState.order.filter((id) => !!state.objects[id]).map((id) => toRef(state.objects, id))
278
+ );
279
+ };
280
+
281
+ let firstIndexWasCleared = false;
282
+
283
+ const indexWasCleared = async () => {
284
+ // skip the first time, preventing clearing the index after makeComputeds already ran.
285
+ if (firstIndexWasCleared) {
286
+ return;
287
+ }
288
+ firstIndexWasCleared = true;
289
+ assignReactiveObject(previousObjectIndexes, {});
290
+ await makeComputeds();
291
+ };
292
+
293
+ let watchesRunning;
294
+
295
+ es.run(() => {
296
+ for (const key of parentStateKeys) {
297
+ state[key] = toRef(parentState, key);
298
+ }
299
+
300
+ textSearchIndex = useSearch({
301
+ props: textSearchIndexProps,
302
+ throttle,
303
+ });
304
+ textSearchIndex.state.search = toRef(state, "textSearchValue");
305
+ textSearchIndex.events.addEventListener("newIndex", indexWasCleared);
306
+ state.searched = toRef(() => textSearchIndex.state.searched);
307
+ state.running = computed(() => {
308
+ return loadingCombine(parentState.running, state.newSearchComputeds, textSearchIndex.state.running);
309
+ });
310
+ state.objectsInOrder = computed(() => state.objectsInOrderRefs.map((ref) => unref(ref)));
311
+
312
+ watch([() => Object.keys(parentState.objects), toRef(state.textSearchRules)], makeComputeds, {
313
+ immediate: true,
314
+ });
315
+
316
+ watch(
317
+ toRef(state, "objectIndexes"),
318
+ () => {
319
+ updateSearchIndexes();
320
+ },
321
+ {
322
+ deep: true,
323
+ immediate: true,
324
+ }
325
+ );
326
+
327
+ watch(
328
+ [
329
+ toRef(state, "textSearchValue"),
330
+ () => Object.keys(textSearchIndex.state.results),
331
+ toRef(textSearchIndex.state, "running"),
332
+ ],
333
+ updateObjectsForResults,
334
+ {
335
+ immediate: true,
336
+ }
337
+ );
338
+
339
+ watch(toRef(parentState, "order"), updateOrder, {
340
+ immediate: true,
341
+ deep: true,
342
+ });
343
+
344
+ onScopeDispose(() => {
345
+ for (const objectKey of Object.keys(objectEffectScopes)) {
346
+ objectEffectScopes[objectKey].stop();
347
+ }
348
+ });
349
+ });
350
+
351
+ return {
352
+ state,
353
+ effectScope: es,
354
+ textSearchIndex,
355
+ watchesRunning,
356
+ };
357
+ }
package/use/listSort.js CHANGED
@@ -1,11 +1,14 @@
1
- import { assignReactiveObject, keyDiff, loadingCombine } from "../utils/index.js";
2
- import { listCalculatedStateKeys } from "./listCalculated.js";
3
- import { listFilterStateKeys } from "./listFilter.js";
4
- import { listInstanceStateKeys } from "./listInstance.js";
5
- import { listRelatedStateKeys } from "./listRelated.js";
6
- import { listSubscriptionStateKeys } from "./listSubscription.js";
1
+ import { assignReactiveObject, keyDiff, loadingCombine, difference } from "../utils/index.js";
2
+ import {
3
+ listSortStateKeys,
4
+ listFilterStateKeys,
5
+ listRelatedStateKeys,
6
+ listCalculatedStateKeys,
7
+ listSubscriptionStateKeys,
8
+ listInstanceStateKeys,
9
+ listSearchStateKeys,
10
+ } from "./listKeys.js";
7
11
  import { useWatchesRunning } from "./watchesRunning.js";
8
- import cloneDeep from "lodash-es/cloneDeep.js";
9
12
  import get from "lodash-es/get.js";
10
13
  import identity from "lodash-es/identity.js";
11
14
  import isEmpty from "lodash-es/isEmpty.js";
@@ -15,21 +18,7 @@ import isUndefined from "lodash-es/isUndefined.js";
15
18
  import throttle from "lodash-es/throttle.js";
16
19
  import zip from "lodash-es/zip.js";
17
20
  import { effectScope, reactive, toRef, unref, watch, computed } from "vue";
18
-
19
- export const listSortStateKeys = [
20
- "orderByRules",
21
- // "order",
22
- // "objectsInOrder",
23
- "sortCriteria",
24
- "sortCriteriaEffectScopes",
25
- "orderByDesc",
26
- "sortCriteriaWatchRunning",
27
- "sortWatchRunning",
28
- "outstandingEffects",
29
- // "running",
30
- ];
31
-
32
- export const listSortFunctions = [];
21
+ import { deepUnref } from "vue-deepunref";
33
22
 
34
23
  const collator = new Intl.Collator(undefined, { numeric: true });
35
24
 
@@ -39,6 +28,18 @@ const defaultOptions = {
39
28
  sortThrottleWait: 100,
40
29
  };
41
30
 
31
+ const parentStateKeys = difference(
32
+ new Set([
33
+ ...listInstanceStateKeys,
34
+ ...listSubscriptionStateKeys,
35
+ ...listRelatedStateKeys,
36
+ ...listCalculatedStateKeys,
37
+ ...listFilterStateKeys,
38
+ ...listSearchStateKeys,
39
+ ]),
40
+ new Set(listSortStateKeys)
41
+ );
42
+
42
43
  export function setListSortDefaultOptions({ sortThrottleWait }) {
43
44
  defaultOptions.sortThrottleWait = sortThrottleWait;
44
45
  }
@@ -55,12 +56,15 @@ export function useListSort({ parentState, orderByRules, sortThrottleWait = defa
55
56
  if (sortThrottleWait === defaultSortThrottleWait) {
56
57
  sortThrottleWait = defaultOptions.sortThrottleWait;
57
58
  }
59
+
60
+ const sortCriteriaEffectScopes = {};
61
+
58
62
  const state = reactive({
59
63
  orderByRules,
60
64
  order: [],
65
+ objectsInOrderRefs: [],
61
66
  objectsInOrder: [],
62
67
  sortCriteria: {},
63
- sortCriteriaEffectScopes: {},
64
68
  orderByDesc: [],
65
69
  sortCriteriaWatchRunning: false,
66
70
  sortWatchRunning: false,
@@ -69,16 +73,16 @@ export function useListSort({ parentState, orderByRules, sortThrottleWait = defa
69
73
  const es = effectScope();
70
74
 
71
75
  function removeSortCriteria(removedKey) {
72
- const oldScope = state.sortCriteriaEffectScopes[removedKey];
76
+ const oldScope = sortCriteriaEffectScopes[removedKey];
73
77
  if (oldScope) {
74
78
  oldScope.stop();
75
- delete state.sortCriteriaEffectScopes[removedKey];
79
+ delete sortCriteriaEffectScopes[removedKey];
76
80
  }
77
81
  delete state.sortCriteria[removedKey];
78
82
  }
79
83
 
80
84
  function addSortCriteria(object, relatedObject, calculatedObject, key) {
81
- const oldScope = state.sortCriteriaEffectScopes[key];
85
+ const oldScope = sortCriteriaEffectScopes[key];
82
86
  if (oldScope) {
83
87
  oldScope.stop();
84
88
  }
@@ -123,7 +127,7 @@ export function useListSort({ parentState, orderByRules, sortThrottleWait = defa
123
127
  }
124
128
  );
125
129
  });
126
- state.sortCriteriaEffectScopes[key] = newScope;
130
+ sortCriteriaEffectScopes[key] = newScope;
127
131
  }
128
132
 
129
133
  function sortCriteriaWatch() {
@@ -134,8 +138,8 @@ export function useListSort({ parentState, orderByRules, sortThrottleWait = defa
134
138
  removeSortCriteria(removedKey);
135
139
  }
136
140
  }
137
- assignReactiveObject(state.order, cloneDeep(parentState.order));
138
- assignReactiveObject(state.objectsInOrder, cloneDeep(parentState.objectsInOrder));
141
+ assignReactiveObject(state.order, deepUnref(parentState.order));
142
+ assignReactiveObject(state.objectsInOrder, deepUnref(parentState.objectsInOrder));
139
143
  return;
140
144
  }
141
145
  const { removedKeys, addedKeys } = keyDiff(
@@ -166,8 +170,12 @@ export function useListSort({ parentState, orderByRules, sortThrottleWait = defa
166
170
  function sortWatch() {
167
171
  try {
168
172
  if (!state.orderByRules || !state.orderByRules.length) {
169
- assignReactiveObject(state.order, cloneDeep(parentState.order));
170
- assignReactiveObject(state.objectsInOrder, cloneDeep(parentState.objectsInOrder));
173
+ state.order = [...parentState.order];
174
+ assignReactiveObject(
175
+ state.objectsInOrderRefs,
176
+ state.order.map((e) => toRef(parentState.objects, e))
177
+ );
178
+ 3;
171
179
  return;
172
180
  }
173
181
 
@@ -207,8 +215,11 @@ export function useListSort({ parentState, orderByRules, sortThrottleWait = defa
207
215
  }
208
216
  return 0;
209
217
  });
210
- assignReactiveObject(state.order, idList);
211
- assignReactiveObject(state.objectsInOrder, idList.map((e) => parentState.objects[e]).filter(identity));
218
+ state.order = idList;
219
+ assignReactiveObject(
220
+ state.objectsInOrderRefs,
221
+ idList.map((e) => toRef(parentState.objects, e))
222
+ );
212
223
  } finally {
213
224
  state.sortWatchRunning = false;
214
225
  state.outstandingEffects = false;
@@ -220,22 +231,7 @@ export function useListSort({ parentState, orderByRules, sortThrottleWait = defa
220
231
  let watchesRunning = null;
221
232
 
222
233
  es.run(() => {
223
- for (const key of listInstanceStateKeys) {
224
- if (["order", "objectsInOrder"].includes(key)) {
225
- continue;
226
- }
227
- state[key] = toRef(parentState, key);
228
- }
229
- for (const key of listSubscriptionStateKeys) {
230
- state[key] = toRef(parentState, key);
231
- }
232
- for (const key of listRelatedStateKeys) {
233
- state[key] = toRef(parentState, key);
234
- }
235
- for (const key of listCalculatedStateKeys) {
236
- state[key] = toRef(parentState, key);
237
- }
238
- for (const key of listFilterStateKeys) {
234
+ for (const key of parentStateKeys) {
239
235
  state[key] = toRef(parentState, key);
240
236
  }
241
237
  // this watch must come first or be immediate.
@@ -255,6 +251,7 @@ export function useListSort({ parentState, orderByRules, sortThrottleWait = defa
255
251
  watchSentinelRefs: [toRef(state, "sortCriteriaWatchRunning"), toRef(state, "sortWatchRunning")],
256
252
  });
257
253
 
254
+ state.objectsInOrder = computed(() => state.objectsInOrderRefs.map((e) => unref(e)));
258
255
  state.sortRunning = computed(() => loadingCombine(watchesRunning.state.running, state.outstandingEffects));
259
256
  state.running = computed(() =>
260
257
  loadingCombine(watchesRunning.state.running, state.outstandingEffects, parentState.running)