@arrai-innovations/reactive-helpers 10.0.1 → 10.1.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/.eslintrc.cjs CHANGED
@@ -19,5 +19,7 @@ module.exports = {
19
19
  },
20
20
  ],
21
21
  "jsdoc/require-jsdoc": "off", // let's ease into this
22
+ "jsdoc/check-types": "off", // this rule is overly broad and gives advice that doesn't work with jsdoc2md.
23
+ "jsdoc/no-undefined-types": "off", // jsdoc2md considers all types to be global and doesn't understand imports.
22
24
  },
23
25
  };
package/docs.md CHANGED
@@ -11,23 +11,27 @@
11
11
 
12
12
  ## Constants
13
13
 
14
- | Name | Description |
15
- | ------------------------------------------ | ------------------------------------------------------------------------------------------------------- |
14
+ | Name | Description |
15
+ | ------------------------------------------ | ---------------------------------------------------------------------------------------------------------- |
16
+ | [useObjects] | Initializes multiple useObject instances, returning an object of them based on the keys of the objectArgs. |
17
+ | [useObject] | Initializes a chain of useObject\* functions, returning an object of them. |
16
18
  | [objectifyClasses] |
17
19
  | [combineClasses] |
18
20
  | [stringifyClass] |
19
21
  | [stringifyClasses] |
20
- | [removeEmptyObjects] | Remove empty objects from a mixed object array tree. Mutates the object. |
21
- | [compactSparseArrays] | Remove undefined values from arrays in a mixed object array tree. Mutates the object. |
22
- | [removeEmptyObjectsAndCompactSparseArrays] | Remove empty objects and undefined values from arrays in a mixed object array tree. Mutates the object. |
22
+ | [removeEmptyObjects] | Remove empty objects from a mixed object array tree. Mutates the object. |
23
+ | [compactSparseArrays] | Remove undefined values from arrays in a mixed object array tree. Mutates the object. |
24
+ | [removeEmptyObjectsAndCompactSparseArrays] | Remove empty objects and undefined values from arrays in a mixed object array tree. Mutates the object. |
23
25
 
24
26
  ## Functions
25
27
 
26
- | Name | Description |
27
- | ------------------------------ | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
28
- | [useCombineClasses(classes)] |
29
- | [useListInstance(options)] | `useListInstance` is a Vue composition function that manages a list of objects. It has the ability to retrieve the list from an implementation, or subscribe to updates from an implementation. It tracks the objects in the list, and their added order. |
30
- | [useListSubscription(options)] | `useListSubscription` creates a reactive object that manages a list of objects, as returned by `useListInstance`, causing the list to be re-fetched as needed and listening for updates to the list. |
28
+ | Name | Description |
29
+ | ---------------------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
30
+ | [useCombineClasses(classes)] |
31
+ | [useListInstance(options)] | `useListInstance` is a Vue composition function that manages a list of objects. It has the ability to retrieve the list from an implementation, or subscribe to updates from an implementation. It tracks the objects in the list, and their added order. |
32
+ | [useListSubscription(options)] | `useListSubscription` creates a reactive object that manages a list of objects, as returned by `useListInstance`, causing the list to be re-fetched as needed and listening for updates to the list. |
33
+ | [useObjectInstances(instanceArgs)] | Initializes multiple useObjectInstance instances, returning an object of them based on the keys of the instanceArgs. |
34
+ | [useObjectInstance(options)] | Initializes an object instance to manage create, retrieve, update, delete, and patch operations. |
31
35
 
32
36
  ## Typedefs
33
37
 
@@ -43,6 +47,13 @@
43
47
  | [ListSubscriptionOptions] | The configuration options used to create a list subscription. |
44
48
  | [ListSubscriptionState] | A reactive object that manages a list of objects, as returned by `useListInstance`. |
45
49
  | [ListSubscription] |
50
+ | [ObjectCrudFunctions] |
51
+ | [ObjectInstanceOptions] |
52
+ | [ObjectInstance] |
53
+ | [ObjectInstanceProps] |
54
+ | [ObjectInstanceOptions] |
55
+ | [ObjectInstanceState] |
56
+ | [ObjectInstanceInstance] |
46
57
  | [CSSValue] | A string representing a CSS class or a space-separated list of CSS classes. |
47
58
  | [CSSObject] | A CSS object where keys are CSS classes and values are booleans indicating whether to apply the class. |
48
59
  | [CSSClasses] | A mixed array containing multiple ways of specifying CSS classes. |
@@ -367,10 +378,32 @@ const transformed = transformWalk(obj, (key, value, path) => {
367
378
  // }
368
379
  ```
369
380
 
381
+ ## useObjects
382
+
383
+ Initializes multiple useObject instances, returning an object of them based on the keys of the objectArgs.
384
+
385
+ **Kind**: global constant
386
+ **Returns**: `Object.<string, ObjectInstance>` - - An object of useObject instances.
387
+
388
+ | Param | Type | Description |
389
+ | ---------- | ---------------------------------------- | ----------------------------------------------- |
390
+ | objectArgs | `Object.<string, ObjectInstanceOptions>` | An object of objects to be passed to useObject. |
391
+
392
+ ## useObject
393
+
394
+ Initializes a chain of useObject\* functions, returning an object of them.
395
+
396
+ **Kind**: global constant
397
+ **Returns**: [`ObjectInstance`][1] - - An object managing a chain of useObject\* instances.
398
+
399
+ | Param | Type | Description |
400
+ | ------- | ------------------------- | ---------------------------------------------------------------------------------------------------------------- |
401
+ | options | [`ObjectInstanceOptions`] | The options to be passed to useObjectInstance, useObjectSubscription, useObjectRelated, and useObjectCalculated. |
402
+
370
403
  ## objectifyClasses
371
404
 
372
405
  **Kind**: global constant
373
- **Returns**: [`CSSStringOrObject`][1] - A CSS object or a space-separated list of CSS classes.
406
+ **Returns**: [`CSSStringOrObject`] - A CSS object or a space-separated list of CSS classes.
374
407
 
375
408
  | Param | Type | Description |
376
409
  | ---------- | -------------- | ----------------------------------------------------------------- |
@@ -467,6 +500,28 @@ causing the list to be re-fetched as needed and listening for updates to the lis
467
500
  | ------- | --------------------------- |
468
501
  | options | [`ListSubscriptionOptions`] |
469
502
 
503
+ ## useObjectInstances(instanceArgs)
504
+
505
+ Initializes multiple useObjectInstance instances, returning an object of them based on the keys of the instanceArgs.
506
+
507
+ **Kind**: global function
508
+ **Returns**: `Object.<string, ObjectInstanceInstance>` - - An object of useObjectInstance instances.
509
+
510
+ | Param | Type | Description |
511
+ | ------------ | ---------------------------------------- | ------------------------------------------------------- |
512
+ | instanceArgs | `Object.<string, ObjectInstanceOptions>` | An object of objects to be passed to useObjectInstance. |
513
+
514
+ ## useObjectInstance(options)
515
+
516
+ Initializes an object instance to manage create, retrieve, update, delete, and patch operations.
517
+
518
+ **Kind**: global function
519
+ **Returns**: [`ObjectInstanceInstance`] - - An object used to manage create, retrieve, update, delete, and patch operations.
520
+
521
+ | Param | Type | Description |
522
+ | ------- | ------------------------- | ---------------------------------------------- |
523
+ | options | [`ObjectInstanceOptions`] | The options to be passed to useObjectInstance. |
524
+
470
525
  ## CSSValue
471
526
 
472
527
  A string representing a CSS class or a space-separated list of CSS classes.
@@ -600,6 +655,102 @@ A reactive object that manages a list of objects, as returned by `useListInstanc
600
655
  | clearError | `function` | clear the subscription error |
601
656
  | effectScope | `object` | a Vue effect scope |
602
657
 
658
+ ## ObjectCrudFunctions
659
+
660
+ **Kind**: global typedef
661
+ **Properties**
662
+
663
+ | Name | Type | Description |
664
+ | --------- | ---------- | ------------------------------------- |
665
+ | create | `function` | A function to create an object. |
666
+ | retrieve | `function` | A function to retrieve an object. |
667
+ | update | `function` | A function to update an object. |
668
+ | delete | `function` | A function to delete an object. |
669
+ | patch | `function` | A function to patch an object. |
670
+ | subscribe | `function` | A function to subscribe to an object. |
671
+
672
+ ## ObjectInstanceOptions
673
+
674
+ **Kind**: global typedef
675
+ **Properties**
676
+
677
+ | Name | Type | Description |
678
+ | --------- | ------------------------------------------------------------------------------------------------------- | -------------------------------------------------------------------------------------------------------------- |
679
+ | props | [`ObjectInstanceProps`] \| `ObjectSubscriptionProps` \| `ObjectRelatedProps` \| `ObjectCalculatedProps` | The props to be passed to useObjectInstance, useObjectSubscription, useObjectRelated, and useObjectCalculated. |
680
+ | functions | [`ObjectCrudFunctions`] | An object of custom crud functions to use instead of the defaults. |
681
+
682
+ ## ObjectInstance
683
+
684
+ **Kind**: global typedef
685
+ **Properties**
686
+
687
+ | Name | Type | Description |
688
+ | ----------- | ------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------- |
689
+ | state | [`ObjectInstanceState`] \| `ObjectSubscriptionState` \| `ObjectRelatedState` \| `ObjectCalculatedState` | The state of the instance. |
690
+ | clearError | `function` | A function to clear the error on both the instance and subscription. |
691
+ | clear | `function` | A function to clear the instance, which also clears errors on both the instance and subscription. |
692
+ | effectScope | `effectScope` | The effectScope of the instance. |
693
+
694
+ ## ObjectInstanceProps
695
+
696
+ **Kind**: global typedef
697
+ **Properties**
698
+
699
+ | Name | Type | Description |
700
+ | ------------ | -------- | ---------------------------------------------------- |
701
+ | id | `string` | The id of the object. |
702
+ | retrieveArgs | `Object` | The arguments to be passed to the retrieve function. |
703
+ | crudArgs | `Object` | The arguments to be passed to the crud functions. |
704
+
705
+ ## ObjectInstanceOptions
706
+
707
+ **Kind**: global typedef
708
+ **Properties**
709
+
710
+ | Name | Type | Description |
711
+ | ------------------- | ----------------------- | --------------------------------------------------------------------- |
712
+ | props | [`ObjectInstanceProps`] | The reactive configuration object. |
713
+ | functions | `object` | An object of custom crud functions to use instead of the defaults. |
714
+ | functions.create | `function` | A function to be used instead of the default crud create function. |
715
+ | functions.retrieve | `function` | A function to be used instead of the default crud retrieve function. |
716
+ | functions.update | `function` | A function to be used instead of the default crud update function. |
717
+ | functions.delete | `function` | A function to be used instead of the default crud delete function. |
718
+ | functions.patch | `function` | A function to be used instead of the default crud patch function. |
719
+ | functions.subscribe | `function` | A function to be used instead of the default crud subscribe function. |
720
+
721
+ ## ObjectInstanceState
722
+
723
+ **Kind**: global typedef
724
+ **Properties**
725
+
726
+ | Name | Type | Description |
727
+ | ------------ | ----------------------- | ---------------------------------------------------- |
728
+ | crud | [`ObjectCrudFunctions`] | The crud functions. |
729
+ | crud.args | `object` | The arguments to be passed to the crud functions. |
730
+ | id | `string` | The id of the object. |
731
+ | retrieveArgs | `object` | The arguments to be passed to the retrieve function. |
732
+ | object | `object` | The object. |
733
+ | loading | `boolean` | Whether the object is loading. |
734
+ | errored | `boolean` | Whether the object errored. |
735
+ | error | `Error` | The error. |
736
+ | deleted | `boolean` | Whether the object is deleted. |
737
+
738
+ ## ObjectInstanceInstance
739
+
740
+ **Kind**: global typedef
741
+ **Properties**
742
+
743
+ | Name | Type | Description |
744
+ | ---------- | ----------------------- | ---------------------------------------------------------------- |
745
+ | state | [`ObjectInstanceState`] | The reactive state object. |
746
+ | create | `function` | call to turn the current object into a new object on the server. |
747
+ | retrieve | `function` | call to retrieve the current object by id from the server. |
748
+ | update | `function` | call to update the current object on the server. |
749
+ | delete | `function` | call to delete the current object on the server. |
750
+ | patch | `function` | call to patch the current object on the server. |
751
+ | clearError | `function` | call to clear the error state. |
752
+ | clear | `function` | call to clear the object state. |
753
+
603
754
  ## CSSValue
604
755
 
605
756
  A string representing a CSS class or a space-separated list of CSS classes.
@@ -638,6 +789,8 @@ A CSS object or a space-separated list of CSS classes.
638
789
  [utils/keydiff]: #utilskeydiff
639
790
  [utils/lifecycledebug]: #utilslifecycledebug
640
791
  [utils/transformwalk]: #utilstransformwalk
792
+ [useobjects]: #useobjects
793
+ [useobject]: #useobject
641
794
  [objectifyclasses]: #objectifyclasses
642
795
  [combineclasses]: #combineclasses
643
796
  [stringifyclass]: #stringifyclass
@@ -655,6 +808,12 @@ A CSS object or a space-separated list of CSS classes.
655
808
  [listsubscriptionoptions]: #listsubscriptionoptions
656
809
  [listsubscriptionstate]: #listsubscriptionstate
657
810
  [listsubscription]: #listsubscription
811
+ [objectcrudfunctions]: #objectcrudfunctions
812
+ [objectinstanceoptions]: #objectinstanceoptions
813
+ [objectinstance]: #objectinstance
814
+ [objectinstanceprops]: #objectinstanceprops
815
+ [objectinstancestate]: #objectinstancestate
816
+ [objectinstanceinstance]: #objectinstanceinstance
658
817
  [cssclasseswithrefs]: #cssclasseswithrefs
659
818
  [~validtargetorsource]: #utilsassignreactiveobjectvalidtargetorsource
660
819
  [`utils/assignreactiveobject`]: #utilsassignreactiveobject
@@ -666,18 +825,25 @@ A CSS object or a space-separated list of CSS classes.
666
825
  [`utils/keydiff`]: #utilskeydiff
667
826
  [`utils/lifecycledebug`]: #utilslifecycledebug
668
827
  [`utils/transformwalk`]: #utilstransformwalk
669
- [1]: #cssstringorobject
670
- [`cssclasses`]: #cssclasses
828
+ [1]: #objectinstance
829
+ [`objectinstanceoptions`]: #objectinstanceoptions
671
830
  [`cssstringorobject`]: #cssstringorobject
831
+ [`cssclasses`]: #cssclasses
672
832
  [`cssclasseswithrefs`]: #cssclasseswithrefs
673
833
  [`listinstance`]: #listinstance
674
834
  [`listinstanceoptions`]: #listinstanceoptions
675
835
  [`listsubscriptionoptions`]: #listsubscriptionoptions
836
+ [`objectinstanceinstance`]: #objectinstanceinstance
676
837
  [`listinstancestate`]: #listinstancestate
677
838
  [`listsubscriptionstate`]: #listsubscriptionstate
839
+ [`objectinstanceprops`]: #objectinstanceprops
840
+ [`objectcrudfunctions`]: #objectcrudfunctions
841
+ [`objectinstancestate`]: #objectinstancestate
678
842
  [usecombineclasses(classes)]: #usecombineclassesclasses
679
843
  [uselistinstance(options)]: #uselistinstanceoptions
680
844
  [uselistsubscription(options)]: #uselistsubscriptionoptions
845
+ [useobjectinstances(instanceargs)]: #useobjectinstancesinstanceargs
846
+ [useobjectinstance(options)]: #useobjectinstanceoptions
681
847
  [~addreactiveobject(target, source, \[exclude\], \[addedkeys\])]: #utilsassignreactiveobjectaddreactiveobjecttarget-source-exclude-addedkeys
682
848
  [~updatereactiveobject(target, source, \[exclude\], \[samekeys\])]: #utilsassignreactiveobjectupdatereactiveobjecttarget-source-exclude-samekeys
683
849
  [~addorupdatereactiveobject(target, source, \[exclude\], \[addedkeys\], \[samekeys\])]: #utilsassignreactiveobjectaddorupdatereactiveobjecttarget-source-exclude-addedkeys-samekeys
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@arrai-innovations/reactive-helpers",
3
- "version": "10.0.1",
3
+ "version": "10.1.0",
4
4
  "description": "VueJS 3 utility composition functions to help manipulate objects and lists.",
5
5
  "main": "index.js",
6
6
  "type": "module",
@@ -49,7 +49,7 @@
49
49
  },
50
50
  "dependencies": {
51
51
  "browser-util-inspect": "^0.2.0",
52
- "flexsearch": "^0.7.21",
52
+ "flexsearch": "0.7.21",
53
53
  "husky": "^7.0.4",
54
54
  "pinst": "^3.0.0",
55
55
  "vue-deepunref": "^1.0.1"
@@ -63,7 +63,7 @@ export function useCancellableIntent({
63
63
  let awaitablePromise = awaitableWithCancel();
64
64
 
65
65
  if (awaitablePromise.cancel) {
66
- cancelFunction = awaitablePromise.cancel;
66
+ cancelFunction = awaitablePromise.cancel.bind(awaitablePromise);
67
67
  }
68
68
  // we don't want to await this, because we want to be able to cancel it
69
69
  awaitablePromise
@@ -4,7 +4,7 @@ import { listRelatedStateKeys } from "./listRelated.js";
4
4
  import { listSubscriptionStateKeys } from "./listSubscription.js";
5
5
  import { useWatchesRunning } from "./watchesRunning.js";
6
6
  import isEmpty from "lodash-es/isEmpty.js";
7
- import { computed, effectScope, onScopeDispose, reactive, toRef, watch } from "vue";
7
+ import { computed, effectScope, onScopeDispose, reactive, ref, toRef, unref, watch } from "vue";
8
8
 
9
9
  export const listCalculatedStateKeys = [
10
10
  "calculatedObjects",
@@ -60,7 +60,10 @@ export function useListCalculated({ parentState, calculatedObjectsRules }) {
60
60
  if (!calculatedObjectsEffectScopes[objectKey]) {
61
61
  calculatedObjectsEffectScopes[objectKey] = effectScope();
62
62
  }
63
- const originalObject = parentState.objects[objectKey];
63
+ const originalObject = toRef(parentState.objects, objectKey);
64
+ const relatedObject = parentState.relatedObjects
65
+ ? toRef(parentState.relatedObjects, objectKey)
66
+ : ref(undefined);
64
67
  if (!state.calculatedObjects[objectKey]) {
65
68
  state.calculatedObjects[objectKey] = {};
66
69
  }
@@ -84,7 +87,7 @@ export function useListCalculated({ parentState, calculatedObjectsRules }) {
84
87
  calculatedObjectsEffectScopes[objectKey].run(() => {
85
88
  for (const addedRuleKey of addedRuleKeys) {
86
89
  calculatedObjectsObject[addedRuleKey] = computed(() =>
87
- state.calculatedObjectsRules?.[addedRuleKey]?.(originalObject)
90
+ state.calculatedObjectsRules?.[addedRuleKey]?.(unref(originalObject), unref(relatedObject))
88
91
  );
89
92
  }
90
93
  });
package/use/listSort.js CHANGED
@@ -110,6 +110,8 @@ export function useListSort({ parentState, orderByRules, sortThrottleWait = defa
110
110
  removeSortCriteria(removedKey);
111
111
  }
112
112
  }
113
+ assignReactiveObject(state.order, cloneDeep(parentState.order));
114
+ assignReactiveObject(state.objectsInOrder, cloneDeep(parentState.objectsInOrder));
113
115
  return;
114
116
  }
115
117
  const { removedKeys, addedKeys } = keyDiff(Object.keys(parentState.objects), Object.keys(state.sortCriteria));
@@ -131,7 +133,6 @@ export function useListSort({ parentState, orderByRules, sortThrottleWait = defa
131
133
 
132
134
  function sortWatch() {
133
135
  if (!state.orderByRules || !state.orderByRules.length) {
134
- console.log("sortWatch no orderByRules");
135
136
  assignReactiveObject(state.order, cloneDeep(parentState.order));
136
137
  assignReactiveObject(state.objectsInOrder, cloneDeep(parentState.objectsInOrder));
137
138
  return;
@@ -198,16 +199,16 @@ export function useListSort({ parentState, orderByRules, sortThrottleWait = defa
198
199
  for (const key of listFilterStateKeys) {
199
200
  state[key] = toRef(parentState, key);
200
201
  }
202
+ // this watch must come first or be immediate.
203
+ watch([toRef(state, "orderByDesc"), toRef(state, "sortCriteria")], throttledSortWatch, {
204
+ deep: true,
205
+ });
201
206
  // we do not need two immediate watches to the same function.
202
207
  watch(() => Object.keys(parentState.objects), sortCriteriaWatch);
203
208
  watch(toRef(state, "orderByRules"), sortCriteriaWatch, {
204
209
  deep: true,
205
210
  immediate: true,
206
211
  });
207
-
208
- watch([toRef(state, "orderByDesc"), toRef(state, "sortCriteria")], throttledSortWatch, {
209
- deep: true,
210
- });
211
212
  });
212
213
 
213
214
  return {
@@ -163,7 +163,8 @@ export function useListSubscription({ listInstance, props, functions, keepOldPag
163
163
  listInstance.addListObject(data);
164
164
  } catch (err) {
165
165
  if (err.name === "ListError" && err.code === "duplicate-id") {
166
- throw new ListSubscriptionError(`addFromSubscription: add for existing id in objects (${data.id}).`);
166
+ console.warn(`addFromSubscription: add for id already in objects (${data.id}).`);
167
+ return;
167
168
  }
168
169
  throw err;
169
170
  }
@@ -177,9 +178,8 @@ export function useListSubscription({ listInstance, props, functions, keepOldPag
177
178
  listInstance.updateListObject(data);
178
179
  } catch (err) {
179
180
  if (err.name === "ListError" && err.code === "missing-object") {
180
- throw new ListSubscriptionError(
181
- `updateFromSubscription: update for id not in objects (${inspect(data.id)}).`
182
- );
181
+ console.warn(`updateFromSubscription: update for id not in objects (${data.id}).`);
182
+ return;
183
183
  }
184
184
  throw err;
185
185
  }
@@ -190,9 +190,8 @@ export function useListSubscription({ listInstance, props, functions, keepOldPag
190
190
  listInstance.deleteListObject(id);
191
191
  } catch (err) {
192
192
  if (err.name === "ListError" && err.code === "missing-object") {
193
- throw new ListSubscriptionError(
194
- `deleteFromSubscription: delete for id not in objects (${inspect(id)}).`
195
- );
193
+ console.warn(`deleteFromSubscription: delete for id not in objects (${id}).`);
194
+ return;
196
195
  }
197
196
  throw err;
198
197
  }
package/use/object.js CHANGED
@@ -4,7 +4,48 @@ import { useObjectRelated, objectRelatedFunctions } from "./objectRelated.js";
4
4
  import { useObjectSubscription, objectSubscriptionFunctions } from "./objectSubscription.js";
5
5
  import { effectScope, reactive, shallowReadonly, toRef } from "vue";
6
6
 
7
- // Manages a chain of useObject* functions
7
+ /**
8
+ * @typedef {Object} ObjectCrudFunctions
9
+ * @property {function} create - A function to create an object.
10
+ * @property {function} retrieve - A function to retrieve an object.
11
+ * @property {function} update - A function to update an object.
12
+ * @property {function} delete - A function to delete an object.
13
+ * @property {function} patch - A function to patch an object.
14
+ * @property {function} subscribe - A function to subscribe to an object.
15
+ */
16
+
17
+ /**
18
+ * @typedef {Object} ObjectInstanceOptions
19
+ * @property {ObjectInstanceProps | ObjectSubscriptionProps | ObjectRelatedProps | ObjectCalculatedProps} props - The props to be passed to useObjectInstance, useObjectSubscription, useObjectRelated, and useObjectCalculated.
20
+ * @property {ObjectCrudFunctions} functions - An object of custom crud functions to use instead of the defaults.
21
+ */
22
+
23
+ /**
24
+ * @typedef {ObjectInstanceInstance | ObjectSubscriptionInstance | ObjectRelatedInstance | ObjectCalculatedInstance} ObjectInstance
25
+ * @property {ObjectInstanceState | ObjectSubscriptionState | ObjectRelatedState | ObjectCalculatedState} state - The state of the instance.
26
+ * @property {function} clearError - A function to clear the error on both the instance and subscription.
27
+ * @property {function} clear - A function to clear the instance, which also clears errors on both the instance and subscription.
28
+ * @property {effectScope} effectScope - The effectScope of the instance.
29
+ */
30
+
31
+ /**
32
+ * Initializes multiple useObject instances, returning an object of them based on the keys of the objectArgs.
33
+ * @param {Object.<string, ObjectInstanceOptions>} objectArgs - An object of objects to be passed to useObject.
34
+ * @returns {Object.<string, ObjectInstance>} - An object of useObject instances.
35
+ */
36
+ export const useObjects = (objectArgs) => {
37
+ const objects = {};
38
+ for (const [key, value] of Object.entries(objectArgs)) {
39
+ objects[key] = useObject(value);
40
+ }
41
+ return objects;
42
+ };
43
+
44
+ /**
45
+ * Initializes a chain of useObject* functions, returning an object of them.
46
+ * @param {ObjectInstanceOptions} options - The options to be passed to useObjectInstance, useObjectSubscription, useObjectRelated, and useObjectCalculated.
47
+ * @returns {ObjectInstance} - An object managing a chain of useObject* instances.
48
+ */
8
49
  export const useObject = ({ props, functions }) => {
9
50
  const managed = reactive({
10
51
  objectInstance: null,
@@ -82,7 +82,7 @@ export function useObjectCalculated({ parentState, calculatedObjectRules }) {
82
82
  calculatedObjectEffectScopes[addedKey] = effectScope();
83
83
  calculatedObjectEffectScopes[addedKey].run(() => {
84
84
  state.calculatedObject[addedKey] = computed(() =>
85
- calculatedObjectOriginalFunctions[addedKey](state.object)
85
+ calculatedObjectOriginalFunctions[addedKey](state.object, state.relatedObject)
86
86
  );
87
87
  });
88
88
  }
@@ -2,6 +2,25 @@ import { getObjectCrud } from "../config/objectCrud.js";
2
2
  import { assignReactiveObject } from "../utils/assignReactiveObject.js";
3
3
  import { reactive, toRef } from "vue";
4
4
 
5
+ /**
6
+ * @typedef {reactive} ObjectInstanceProps
7
+ * @property {string} id - The id of the object.
8
+ * @property {Object} retrieveArgs - The arguments to be passed to the retrieve function.
9
+ * @property {Object} crudArgs - The arguments to be passed to the crud functions.
10
+ */
11
+
12
+ /**
13
+ * @typedef {Object} ObjectInstanceOptions
14
+ * @property {ObjectInstanceProps} props - The reactive configuration object.
15
+ * @property {object} functions - An object of custom crud functions to use instead of the defaults.
16
+ * @property {function} functions.create - A function to be used instead of the default crud create function.
17
+ * @property {function} functions.retrieve - A function to be used instead of the default crud retrieve function.
18
+ * @property {function} functions.update - A function to be used instead of the default crud update function.
19
+ * @property {function} functions.delete - A function to be used instead of the default crud delete function.
20
+ * @property {function} functions.patch - A function to be used instead of the default crud patch function.
21
+ * @property {function} functions.subscribe - A function to be used instead of the default crud subscribe function.
22
+ */
23
+
5
24
  export class ObjectError extends Error {
6
25
  constructor(message) {
7
26
  super(message);
@@ -22,6 +41,11 @@ export const objectInstanceStateKeys = [
22
41
 
23
42
  export const objectInstanceFunctions = ["create", "retrieve", "update", "delete", "patch", "clearError", "clear"];
24
43
 
44
+ /**
45
+ * Initializes multiple useObjectInstance instances, returning an object of them based on the keys of the instanceArgs.
46
+ * @param {Object.<string, ObjectInstanceOptions>} instanceArgs - An object of objects to be passed to useObjectInstance.
47
+ * @returns {Object.<string, ObjectInstanceInstance>} - An object of useObjectInstance instances.
48
+ */
25
49
  export function useObjectInstances(instanceArgs) {
26
50
  const instances = {};
27
51
  for (const [key, value] of Object.entries(instanceArgs)) {
@@ -30,6 +54,36 @@ export function useObjectInstances(instanceArgs) {
30
54
  return instances;
31
55
  }
32
56
 
57
+ /**
58
+ * @typedef {reactive} ObjectInstanceState
59
+ * @property {ObjectCrudFunctions} crud - The crud functions.
60
+ * @property {object} crud.args - The arguments to be passed to the crud functions.
61
+ * @property {string} id - The id of the object.
62
+ * @property {object} retrieveArgs - The arguments to be passed to the retrieve function.
63
+ * @property {object} object - The object.
64
+ * @property {boolean} loading - Whether the object is loading.
65
+ * @property {boolean} errored - Whether the object errored.
66
+ * @property {Error} error - The error.
67
+ * @property {boolean} deleted - Whether the object is deleted.
68
+ */
69
+
70
+ /**
71
+ * @typedef {reactive} ObjectInstanceInstance
72
+ * @property {ObjectInstanceState} state - The reactive state object.
73
+ * @property {function} create - call to turn the current object into a new object on the server.
74
+ * @property {function} retrieve - call to retrieve the current object by id from the server.
75
+ * @property {function} update - call to update the current object on the server.
76
+ * @property {function} delete - call to delete the current object on the server.
77
+ * @property {function} patch - call to patch the current object on the server.
78
+ * @property {function} clearError - call to clear the error state.
79
+ * @property {function} clear - call to clear the object state.
80
+ */
81
+
82
+ /**
83
+ * Initializes an object instance to manage create, retrieve, update, delete, and patch operations.
84
+ * @param {ObjectInstanceOptions} options - The options to be passed to useObjectInstance.
85
+ * @returns {ObjectInstanceInstance} - An object used to manage create, retrieve, update, delete, and patch operations.
86
+ */
33
87
  export function useObjectInstance({ props, functions = {} }) {
34
88
  const state = reactive({
35
89
  crud: {
@@ -39,6 +93,7 @@ export function useObjectInstance({ props, functions = {} }) {
39
93
  update: undefined,
40
94
  delete: undefined,
41
95
  patch: undefined,
96
+ subscribe: undefined,
42
97
  },
43
98
  object: {},
44
99
  id: toRef(props, "id"),
@@ -4,8 +4,10 @@ import { objectInstanceStateKeys } from "./objectInstance.js";
4
4
  import { objectSubscriptionStateKeys } from "./objectSubscription.js";
5
5
  import { useWatchesRunning } from "./watchesRunning.js";
6
6
  import get from "lodash-es/get.js";
7
+ import identity from "lodash-es/identity.js";
7
8
  import isArray from "lodash-es/isArray.js";
8
9
  import isEmpty from "lodash-es/isEmpty.js";
10
+ import isEqual from "lodash-es/isEqual.js";
9
11
  import isUndefined from "lodash-es/isUndefined.js";
10
12
  import { computed, effectScope, onScopeDispose, reactive, toRef, unref, watch } from "vue";
11
13
 
@@ -71,22 +73,47 @@ export function useObjectRelated({ parentState, relatedObjectRules }) {
71
73
  for (const addedRuleKey of addedRuleKeys) {
72
74
  relatedObjectEffectScopes[addedRuleKey] = effectScope();
73
75
  relatedObjectEffectScopes[addedRuleKey].run(() => {
74
- state.relatedObject[addedRuleKey] = computed(() => {
76
+ const relatedObjectObjectWatchFn = () => {
75
77
  // deal with computed objects being passed.
76
78
  const ruleObjects = unref(state.relatedObjectRules?.[addedRuleKey]?.objects);
77
79
  const rulePkKey = state.relatedObjectRules?.[addedRuleKey]?.pkKey || addedRuleKey;
80
+ const ruleOrder = unref(state.relatedObjectRules?.[addedRuleKey]?.order);
78
81
  if (!ruleObjects || !rulePkKey) {
79
- return undefined;
82
+ state.relatedObject[addedRuleKey] = undefined;
83
+ return;
80
84
  }
81
- const value = get(parentState.object, rulePkKey);
85
+ let value = get(parentState.object, rulePkKey);
82
86
  if (isUndefined(value)) {
83
- return undefined;
87
+ state.relatedObject[addedRuleKey] = undefined;
88
+ return;
84
89
  }
85
90
  if (isArray(value)) {
86
- return value.map((e) => ruleObjects[e]);
91
+ // the related list could be sorted differently than the original list.
92
+ if (ruleOrder?.length) {
93
+ value = value.filter(identity);
94
+ const indexById = Object.fromEntries(ruleOrder.map((e, i) => [e, i]));
95
+ value.sort((a, b) => {
96
+ const aIndex = indexById[a];
97
+ const bIndex = indexById[b];
98
+ return aIndex - bIndex;
99
+ });
100
+ }
101
+ value = value.map((e) => ruleObjects[e]).filter(identity);
102
+ } else {
103
+ value = ruleObjects[value];
87
104
  }
88
- return ruleObjects[value];
89
- });
105
+ if (!isEqual(value, state.relatedObject[addedRuleKey])) {
106
+ state.relatedObject[addedRuleKey] = value;
107
+ }
108
+ };
109
+ watch(
110
+ [toRef(state.relatedObjectsRules, addedRuleKey), toRef(parentState, "object")],
111
+ relatedObjectObjectWatchFn,
112
+ {
113
+ deep: true,
114
+ immediate: true,
115
+ }
116
+ );
90
117
  });
91
118
  }
92
119
  });