@arrai-innovations/reactive-helpers 10.4.2 → 11.0.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/.circleci/config.yml +3 -3
- package/README.md +120 -143
- package/config/listCrud.js +0 -1
- package/docs.md +178 -34
- package/package.json +3 -1
- package/tests/unit/use/listFilter.spec.js +195 -167
- package/tests/unit/use/listSearch.spec.js +505 -0
- package/tests/unit/use/listSort.spec.js +0 -1
- package/tests/unit/use/listSubscription.spec.js +91 -108
- package/tests/unit/use/search.spec.js +217 -36
- package/tests/unit/utils/assignReactiveObject.spec.js +0 -1
- package/tests/unit/{use → utils}/watches.spec.js +27 -3
- package/use/cancellableIntent.js +8 -3
- package/use/index.js +2 -0
- package/use/list.js +41 -14
- package/use/listCalculated.js +22 -19
- package/use/listFilter.js +128 -154
- package/use/listInstance.js +0 -28
- package/use/listKeys.js +94 -0
- package/use/listRelated.js +22 -14
- package/use/listSearch.js +358 -0
- package/use/listSort.js +30 -42
- package/use/listSubscription.js +3 -13
- package/use/search.js +154 -64
- package/utils/assignReactiveObject.js +58 -14
- package/utils/index.js +1 -0
- package/utils/keyDiff.js +13 -7
- package/utils/relatedCalculatedHelpers.js +17 -0
- package/utils/watches.js +14 -11
package/.circleci/config.yml
CHANGED
package/README.md
CHANGED
|
@@ -21,11 +21,11 @@ Vue.js 3 utility composition functions to help manipulate objects and lists.
|
|
|
21
21
|
- [Related](#related)
|
|
22
22
|
- [Calculated](#calculated)
|
|
23
23
|
- [Filter](#filter)
|
|
24
|
+
- [Search](#search)
|
|
24
25
|
- [Sort](#sort)
|
|
25
26
|
- [All](#all)
|
|
26
|
-
- [List](#list-1)
|
|
27
27
|
- [Object](#object)
|
|
28
|
-
- [Search](#search)
|
|
28
|
+
- [Search](#search-1)
|
|
29
29
|
- [Utils](#utils)
|
|
30
30
|
- [addOrUpdateReactiveObject & assignReactiveObject](#addorupdatereactiveobject--assignreactiveobject)
|
|
31
31
|
- [keyDiff](#keydiff)
|
|
@@ -148,6 +148,7 @@ setListCrud({
|
|
|
148
148
|
```js
|
|
149
149
|
// then use in your component
|
|
150
150
|
import { useListInstance, useListSubscription } from "@arrai-innovations/reactive-helpers";
|
|
151
|
+
import { reactive } from "vue";
|
|
151
152
|
|
|
152
153
|
const listProps = reactive({
|
|
153
154
|
// crudArgs are implementation specific to your crud functions.
|
|
@@ -186,6 +187,7 @@ delete contacts.listArgs.has_organization;
|
|
|
186
187
|
```js
|
|
187
188
|
// or you can have listSubscription create the listInstance for you.
|
|
188
189
|
import { useListSubscription } from "@arrai-innovations/reactive-helpers";
|
|
190
|
+
import { reactive } from "vue";
|
|
189
191
|
|
|
190
192
|
const listProps = reactive({
|
|
191
193
|
// crudArgs are implementation specific to your crud functions.
|
|
@@ -214,33 +216,35 @@ Lookup foreign keys between list instances via watch, for using dot notation in
|
|
|
214
216
|
```js
|
|
215
217
|
// no main.js setup required.
|
|
216
218
|
// used in example below.
|
|
217
|
-
|
|
218
|
-
import {
|
|
219
|
-
import { nextTick } from "vue";
|
|
219
|
+
import { useListInstance, useListRelated, doAwaitNot } from "@arrai-innovations/reactive-helpers";
|
|
220
|
+
import { reactive, toRef } from "vue";
|
|
220
221
|
|
|
222
|
+
const organizationsProps = reactive({
|
|
223
|
+
retrieveArgs: {
|
|
224
|
+
fields: ["id", "name"],
|
|
225
|
+
},
|
|
226
|
+
});
|
|
221
227
|
const organizations = useListInstance({
|
|
222
|
-
props:
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
228
|
+
props: organizationsProps,
|
|
229
|
+
});
|
|
230
|
+
const contactsProps = reactive({
|
|
231
|
+
retrieveArgs: {
|
|
232
|
+
fields: ["id", "lexical_name", "organization"],
|
|
226
233
|
},
|
|
227
234
|
});
|
|
228
235
|
const contacts = useListInstance({
|
|
229
|
-
props:
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
236
|
+
props: contactsProps,
|
|
237
|
+
});
|
|
238
|
+
const contactsRelatedRules = reactive({
|
|
239
|
+
organization: {
|
|
240
|
+
// desired key on relatedObjects
|
|
241
|
+
objects: toRef(organizations.state, "objects"), // organizations by id
|
|
242
|
+
pkKey: "organization", // reference key on contact for org id.
|
|
233
243
|
},
|
|
234
244
|
});
|
|
235
245
|
const contactsRelated = useListRelated({
|
|
236
246
|
parentState: contacts.state,
|
|
237
|
-
relatedObjectsRules:
|
|
238
|
-
organization: {
|
|
239
|
-
// desired key on relatedObjects
|
|
240
|
-
objects: toRef(organizations.state, "objects"), // organizations by id
|
|
241
|
-
pkKey: "organization", // reference key on contact for org id.
|
|
242
|
-
},
|
|
243
|
-
},
|
|
247
|
+
relatedObjectsRules: contactsRelatedRules,
|
|
244
248
|
});
|
|
245
249
|
await organizations.list();
|
|
246
250
|
await contacts.list();
|
|
@@ -270,7 +274,10 @@ console.log(contacts.relatedObjects);
|
|
|
270
274
|
}
|
|
271
275
|
*/
|
|
272
276
|
contacts.objects["15"].organization = 24;
|
|
273
|
-
await
|
|
277
|
+
await doAwaitNot({
|
|
278
|
+
obj: contactsRelated.state,
|
|
279
|
+
prop: "running",
|
|
280
|
+
});
|
|
274
281
|
console.log(contacts.relatedObjects["15"]);
|
|
275
282
|
/*
|
|
276
283
|
{
|
|
@@ -278,12 +285,13 @@ console.log(contacts.relatedObjects["15"]);
|
|
|
278
285
|
}
|
|
279
286
|
*/
|
|
280
287
|
delete contactsRelated.relatedObjectRules.organization;
|
|
288
|
+
await doAwaitNot({
|
|
289
|
+
obj: contactsRelated.state,
|
|
290
|
+
prop: "running",
|
|
291
|
+
});
|
|
281
292
|
await nextTick();
|
|
282
293
|
console.log(contacts.relatedObjects["15"]);
|
|
283
294
|
/* {} */
|
|
284
|
-
// manual stopage. inside a setup or another effect scope, there isnt a need to manually call this.
|
|
285
|
-
contactsRelated.effectScope.stop();
|
|
286
|
-
await nextTick();
|
|
287
295
|
```
|
|
288
296
|
|
|
289
297
|
#### Calculated
|
|
@@ -291,22 +299,25 @@ await nextTick();
|
|
|
291
299
|
```js
|
|
292
300
|
// no main.js setup required.
|
|
293
301
|
// used in example below.
|
|
294
|
-
// use in your component
|
|
295
302
|
import { useListInstance, useListCalculated } from "@arrai-innovations/reactive-helpers";
|
|
296
|
-
import { nextTick } from "vue";
|
|
303
|
+
import { nextTick, reactive } from "vue";
|
|
297
304
|
|
|
298
|
-
const
|
|
305
|
+
const contactsProps = reactive({
|
|
299
306
|
retrieveArgs: {
|
|
300
307
|
fields: ["id", "has_name", "has_billing", "lexical_name", "organization"],
|
|
301
308
|
},
|
|
302
309
|
});
|
|
310
|
+
const contacts = useListInstance({
|
|
311
|
+
props: contactsProps,
|
|
312
|
+
});
|
|
313
|
+
const contactsCalculatedRules = reactive({
|
|
314
|
+
first_letter_of_name: (object) => {
|
|
315
|
+
return object.lexical_name[0];
|
|
316
|
+
},
|
|
317
|
+
});
|
|
303
318
|
const contactsCalculated = useListCalculated({
|
|
304
319
|
parentState: contacts.state,
|
|
305
|
-
calculatedObjectsRules:
|
|
306
|
-
first_letter_of_name: (object) => {
|
|
307
|
-
return object.lexical_name[0];
|
|
308
|
-
},
|
|
309
|
-
},
|
|
320
|
+
calculatedObjectsRules: contactsCalculatedRules,
|
|
310
321
|
});
|
|
311
322
|
await contacts.list();
|
|
312
323
|
console.log(contacts.objects);
|
|
@@ -342,57 +353,105 @@ console.log(contacts.calculatedObjects);
|
|
|
342
353
|
```js
|
|
343
354
|
// no main.js setup required.
|
|
344
355
|
// used in example below.
|
|
345
|
-
|
|
346
|
-
import {
|
|
347
|
-
import { nextTick } from "vue";
|
|
356
|
+
import { useListInstance, useListFilter, doAwaitNot } from "@arrai-innovations/reactive-helpers";
|
|
357
|
+
import { reactive, ref } from "vue";
|
|
348
358
|
|
|
349
|
-
const
|
|
359
|
+
const contactsProps = reactive({
|
|
350
360
|
retrieveArgs: {
|
|
351
361
|
fields: ["id", "has_name", "has_billing", "lexical_name", "organization"],
|
|
352
362
|
},
|
|
353
363
|
});
|
|
364
|
+
const contacts = useListInstance({
|
|
365
|
+
props: contactsProps,
|
|
366
|
+
});
|
|
367
|
+
|
|
368
|
+
const allowedFilter = ref((object) => {
|
|
369
|
+
return object.has_name === true;
|
|
370
|
+
});
|
|
371
|
+
const excludedFilter = ref((object) => {
|
|
372
|
+
return object.has_billing === true;
|
|
373
|
+
});
|
|
354
374
|
|
|
355
|
-
const myAllowedValues = ref(["15", "16"]);
|
|
356
|
-
const myExcludedValues = ref(["17"]);
|
|
357
375
|
// conditions are all optional but anded together if present.
|
|
358
376
|
const contactsFilter = useListFilter({
|
|
359
377
|
parentState: contacts.state,
|
|
360
|
-
|
|
361
|
-
|
|
362
|
-
return object.has_name === true;
|
|
363
|
-
},
|
|
364
|
-
excludedValues: myExcludedValues,
|
|
365
|
-
excludedFilter: function (object) {
|
|
366
|
-
return object.has_billing === true;
|
|
367
|
-
},
|
|
378
|
+
allowedFilter,
|
|
379
|
+
excludedFilter,
|
|
368
380
|
});
|
|
369
381
|
await contacts.list();
|
|
370
382
|
console.log(contactsFilter.state.objects);
|
|
371
383
|
console.log(contactsFilter.state.objectsInOrder);
|
|
372
384
|
console.log(contactsFilter.state.order);
|
|
373
|
-
|
|
374
|
-
await
|
|
385
|
+
contacts.objects["15"].has_name = false;
|
|
386
|
+
await doAwaitNot({
|
|
387
|
+
obj: contactsFilter.state,
|
|
388
|
+
prop: "running",
|
|
389
|
+
});
|
|
375
390
|
console.log(contactsFilter.state.objects);
|
|
376
391
|
console.log(contactsFilter.state.objectsInOrder);
|
|
377
392
|
console.log(contactsFilter.state.order);
|
|
378
393
|
```
|
|
379
394
|
|
|
380
|
-
####
|
|
395
|
+
#### Search
|
|
381
396
|
|
|
382
397
|
```js
|
|
383
398
|
// no main.js setup required.
|
|
384
399
|
// used in example below.
|
|
385
|
-
|
|
386
|
-
import {
|
|
387
|
-
import { nextTick } from "vue";
|
|
400
|
+
import { useListInstance, useListSearch, doAwaitNot } from "@arrai-innovations/reactive-helpers";
|
|
401
|
+
import { reactive } from "vue";
|
|
388
402
|
|
|
403
|
+
const contactsProps = reactive({
|
|
404
|
+
retrieveArgs: {
|
|
405
|
+
fields: ["id", "has_name", "has_billing", "lexical_name", "organization"],
|
|
406
|
+
},
|
|
407
|
+
});
|
|
389
408
|
const contacts = useListInstance({
|
|
390
|
-
props:
|
|
391
|
-
|
|
392
|
-
|
|
393
|
-
|
|
409
|
+
props: contactsProps,
|
|
410
|
+
});
|
|
411
|
+
const searchProps = reactive({
|
|
412
|
+
textSearchRules: ["lexical_name"],
|
|
413
|
+
textSearchValue: "",
|
|
414
|
+
});
|
|
415
|
+
const search = useListSearch({
|
|
416
|
+
parentState: contacts.state,
|
|
417
|
+
props: searchProps,
|
|
418
|
+
});
|
|
419
|
+
await contacts.list();
|
|
420
|
+
await doAwaitNot({
|
|
421
|
+
obj: search.state,
|
|
422
|
+
prop: "running",
|
|
423
|
+
});
|
|
424
|
+
// all values show when search is empty.
|
|
425
|
+
console.log(search.state.objects);
|
|
426
|
+
console.log(search.state.objectsInOrder);
|
|
427
|
+
console.log(search.state.order);
|
|
428
|
+
searchProps.textSearchValue = "one";
|
|
429
|
+
await doAwaitNot({
|
|
430
|
+
obj: search.state,
|
|
431
|
+
prop: "running",
|
|
432
|
+
});
|
|
433
|
+
// only values that match the search show.
|
|
434
|
+
console.log(search.state.objects);
|
|
435
|
+
console.log(search.state.objectsInOrder);
|
|
436
|
+
console.log(search.state.order);
|
|
437
|
+
```
|
|
438
|
+
|
|
439
|
+
#### Sort
|
|
440
|
+
|
|
441
|
+
```js
|
|
442
|
+
// no main.js setup required.
|
|
443
|
+
// used in example below.
|
|
444
|
+
import { useListInstance, useListSort, doAwaitNot } from "@arrai-innovations/reactive-helpers";
|
|
445
|
+
import { reactive } from "vue";
|
|
446
|
+
|
|
447
|
+
const contactsProps = reactive({
|
|
448
|
+
retrieveArgs: {
|
|
449
|
+
fields: ["id", "has_name", "lexical_name", "organization"],
|
|
394
450
|
},
|
|
395
451
|
});
|
|
452
|
+
const contacts = useListInstance({
|
|
453
|
+
props: contactsProps,
|
|
454
|
+
});
|
|
396
455
|
const contactsSort = useListSort({
|
|
397
456
|
parentState: contacts.state,
|
|
398
457
|
orderByRules: [
|
|
@@ -407,101 +466,20 @@ console.log(contactsSort.state.order);
|
|
|
407
466
|
console.log(contactsSort.state.objectsInOrder);
|
|
408
467
|
// computed array of the previous that also looks up the object ids in .objects
|
|
409
468
|
contactsSort.state.orderByRules[0].desc = false;
|
|
410
|
-
await
|
|
469
|
+
await doAwaitNot({
|
|
470
|
+
obj: contactsSort.state,
|
|
471
|
+
prop: "running",
|
|
472
|
+
});
|
|
411
473
|
console.log(contactsSort.state.order);
|
|
412
474
|
// array of ids in order, based on updated rules.
|
|
413
475
|
```
|
|
414
476
|
|
|
415
477
|
#### All
|
|
416
478
|
|
|
417
|
-
Example using all of the above.
|
|
418
|
-
|
|
419
|
-
```js
|
|
420
|
-
import {
|
|
421
|
-
useListInstance,
|
|
422
|
-
useListSubscription,
|
|
423
|
-
useListRelated,
|
|
424
|
-
useListCalculated,
|
|
425
|
-
useListFilter,
|
|
426
|
-
useListSort,
|
|
427
|
-
} from "@arrai-innovations/reactive-helpers";
|
|
428
|
-
|
|
429
|
-
const organizationNameSearch = ref("");
|
|
430
|
-
const organizations = useListInstance({
|
|
431
|
-
// crudArgs are implementation specific to your crud functions.
|
|
432
|
-
crudArgs: {
|
|
433
|
-
stream: "organizations",
|
|
434
|
-
},
|
|
435
|
-
});
|
|
436
|
-
const contacts = useListInstance({
|
|
437
|
-
// crudArgs are implementation specific to your crud functions.
|
|
438
|
-
crudArgs: {
|
|
439
|
-
stream: "contacts",
|
|
440
|
-
},
|
|
441
|
-
retrieveArgs: {
|
|
442
|
-
fields: ["id", "has_name", "lexical_name", "organization", "phone"],
|
|
443
|
-
},
|
|
444
|
-
listArgs: {
|
|
445
|
-
has_organization: true,
|
|
446
|
-
},
|
|
447
|
-
});
|
|
448
|
-
const contactsSubscription = useListSubscription({
|
|
449
|
-
// crudArgs are implementation specific to your crud functions.
|
|
450
|
-
crudArgs: {
|
|
451
|
-
stream: "contacts",
|
|
452
|
-
includeCreateEvents: true,
|
|
453
|
-
},
|
|
454
|
-
listInstance: contacts,
|
|
455
|
-
});
|
|
456
|
-
const contactsRelated = useListRelated({
|
|
457
|
-
parentState: contactsSubscription.state,
|
|
458
|
-
relatedObjectsRules: {
|
|
459
|
-
organization: {
|
|
460
|
-
// desired key on relatedObjects
|
|
461
|
-
objects: toRef(organizations.state, "objects"), // organizations by id
|
|
462
|
-
pkKey: "organization", // reference key on contact for org id.
|
|
463
|
-
},
|
|
464
|
-
},
|
|
465
|
-
});
|
|
466
|
-
const contactsCalculated = useListCalculated({
|
|
467
|
-
parentState: contactsRelated.state,
|
|
468
|
-
calculatedObjectsRules: {
|
|
469
|
-
first_letter_of_name: (object) => {
|
|
470
|
-
return object.lexical_name[0];
|
|
471
|
-
},
|
|
472
|
-
},
|
|
473
|
-
});
|
|
474
|
-
const contactsFiltered = useListFilter({
|
|
475
|
-
parentState: contactsCalculated.state,
|
|
476
|
-
useTextSearch: true,
|
|
477
|
-
textSearchRules: ["relatedObjects.organization.name"],
|
|
478
|
-
textSearchValue: organizationNameSearch,
|
|
479
|
-
});
|
|
480
|
-
const contactsSorted = useListSort({
|
|
481
|
-
parentState: contactsFiltered.state,
|
|
482
|
-
orderByRules: [
|
|
483
|
-
{ key: "relatedObjects.organization.name", desc: false, localeCompare: true },
|
|
484
|
-
{ key: "lexical_name", desc: false, localeCompare: true },
|
|
485
|
-
],
|
|
486
|
-
});
|
|
487
|
-
console.log(contactsSorted.state.value.objects);
|
|
488
|
-
// object of contacts, updating as new ones are created, filtered by organziation name, sort organization name & lexical name.
|
|
489
|
-
console.log(contactsSorted.state.value.order);
|
|
490
|
-
// array of ids in order, based on the specified rules.
|
|
491
|
-
console.log(contactsSorted.state.value.objectsInOrder);
|
|
492
|
-
// array of contacts, updating as new ones are created, filtered by organziation name, sort organization name & lexical name.
|
|
493
|
-
console.log(contactsSorted.state.value.calculatedObjects);
|
|
494
|
-
// object of calculated objects, updating as new ones are created, for objects above.
|
|
495
|
-
console.log(contactsSorted.state.value.relatedObjects);
|
|
496
|
-
// object of related objects, updating as new ones are created, for objects above.
|
|
497
|
-
```
|
|
498
|
-
|
|
499
|
-
#### List
|
|
500
|
-
|
|
501
479
|
```js
|
|
502
480
|
// you can also let the library manage a full stack for you.
|
|
503
481
|
import { useList } from "@arrai-innovations/reactive-helpers";
|
|
504
|
-
import { reactive } from "vue";
|
|
482
|
+
import { reactive, toRef } from "vue";
|
|
505
483
|
|
|
506
484
|
const managedListProps = reactive({
|
|
507
485
|
// crudArgs are implementation specific to your crud functions.
|
|
@@ -533,7 +511,6 @@ const managedListProps = reactive({
|
|
|
533
511
|
{ key: "relatedObjects.organization.name", desc: false, localeCompare: true },
|
|
534
512
|
{ key: "lexical_name", desc: false, localeCompare: true },
|
|
535
513
|
],
|
|
536
|
-
useTextSearch: true,
|
|
537
514
|
textSearchRules: ["relatedObjects.organization.name"],
|
|
538
515
|
textSearchValue: organizationNameSearch,
|
|
539
516
|
sortThrottleWait: 100, // default, ms to wait before sorting after a change.
|
package/config/listCrud.js
CHANGED
|
@@ -12,7 +12,6 @@ const defaultCrud = {
|
|
|
12
12
|
export const setListCrud = ({ list, subscribe, args = {}, ...rest }) => {
|
|
13
13
|
defaultCrud.list = list;
|
|
14
14
|
defaultCrud.subscribe = subscribe;
|
|
15
|
-
// defensive cloning
|
|
16
15
|
Object.assign(defaultCrud.args, cloneDeep(args));
|
|
17
16
|
if (Object.keys(rest).length) {
|
|
18
17
|
throw new Error(`Unknown key(s) passed to setListCrud: ${Object.keys(rest).join(", ")}`);
|