@arrai-innovations/reactive-helpers 20.1.3 → 21.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.
Files changed (197) hide show
  1. package/README.md +22 -893
  2. package/config/listCrud.js +30 -27
  3. package/config/objectCrud.js +20 -13
  4. package/index.js +4 -3
  5. package/package.json +5 -3
  6. package/types/config/commonCrud.d.ts +0 -1
  7. package/types/config/listCrud.d.ts +28 -41
  8. package/types/config/objectCrud.d.ts +14 -29
  9. package/types/index.d.ts +4 -4
  10. package/types/tests/unit/config/listCrud.spec.d.ts +0 -1
  11. package/types/tests/unit/config/objectCrud.spec.d.ts +0 -1
  12. package/types/tests/unit/crudPromise.d.ts +42 -15
  13. package/types/tests/unit/mockOnUnmounted.d.ts +0 -1
  14. package/types/tests/unit/poll.d.ts +0 -1
  15. package/types/tests/unit/scopedIt.d.ts +15 -0
  16. package/types/tests/unit/setup.d.ts +2 -0
  17. package/types/tests/unit/use/cancellableIntent.spec.d.ts +0 -1
  18. package/types/tests/unit/use/combineClasses.spec.d.ts +1 -0
  19. package/types/tests/unit/use/list.spec.d.ts +1 -0
  20. package/types/tests/unit/use/listCalculated.spec.d.ts +0 -1
  21. package/types/tests/unit/use/listFilter.spec.d.ts +0 -1
  22. package/types/tests/unit/use/listInstance.spec.d.ts +0 -1
  23. package/types/tests/unit/use/listRelated.spec.d.ts +0 -1
  24. package/types/tests/unit/use/listSearch.spec.d.ts +0 -1
  25. package/types/tests/unit/use/listSort.spec.d.ts +0 -1
  26. package/types/tests/unit/use/listSubscription.spec.d.ts +0 -1
  27. package/types/tests/unit/use/loadingError.spec.d.ts +0 -1
  28. package/types/tests/unit/use/object.spec.d.ts +1 -0
  29. package/types/tests/unit/use/objectCalculated.spec.d.ts +1 -0
  30. package/types/tests/unit/use/objectInstance.spec.d.ts +0 -1
  31. package/types/tests/unit/use/objectRelated.spec.d.ts +1 -0
  32. package/types/tests/unit/use/objectSubscription.spec.d.ts +0 -1
  33. package/types/tests/unit/use/proxyLoadingError.spec.d.ts +0 -1
  34. package/types/tests/unit/use/search.error.spec.d.ts +1 -0
  35. package/types/tests/unit/use/search.spec.d.ts +0 -1
  36. package/types/tests/unit/utils/assignReactiveObject.extra.spec.d.ts +1 -0
  37. package/types/tests/unit/utils/assignReactiveObject.refs.spec.d.ts +1 -0
  38. package/types/tests/unit/utils/assignReactiveObject.spec.d.ts +0 -1
  39. package/types/tests/unit/utils/cancellableFetch.spec.d.ts +1 -0
  40. package/types/tests/unit/utils/classes.spec.d.ts +0 -1
  41. package/types/tests/unit/utils/compact.spec.d.ts +1 -0
  42. package/types/tests/unit/utils/deepUnref.spec.d.ts +1 -0
  43. package/types/tests/unit/utils/deleteKey.spec.d.ts +0 -1
  44. package/types/tests/unit/utils/flattenPaths.spec.d.ts +0 -1
  45. package/types/tests/unit/utils/flattenPathsWithValues.spec.d.ts +1 -0
  46. package/types/tests/unit/utils/getFakePk.spec.d.ts +1 -0
  47. package/types/tests/unit/utils/keepAliveTry.spec.d.ts +1 -0
  48. package/types/tests/unit/utils/keyDiff.spec.d.ts +0 -1
  49. package/types/tests/unit/utils/set.spec.d.ts +0 -1
  50. package/types/tests/unit/utils/transformWalk.spec.d.ts +1 -0
  51. package/types/tests/unit/utils/unwrapNested.spec.d.ts +1 -0
  52. package/types/tests/unit/utils/watches.spec.d.ts +0 -1
  53. package/types/use/cancellableIntent.d.ts +121 -48
  54. package/types/use/combineClasses.d.ts +0 -1
  55. package/types/use/error.d.ts +48 -0
  56. package/types/use/list.d.ts +9 -32
  57. package/types/use/listCalculated.d.ts +107 -49
  58. package/types/use/listFilter.d.ts +13 -31
  59. package/types/use/listInstance.d.ts +137 -71
  60. package/types/use/listRelated.d.ts +106 -43
  61. package/types/use/listSearch.d.ts +24 -12
  62. package/types/use/listSort.d.ts +3 -34
  63. package/types/use/listSubscription.d.ts +40 -57
  64. package/types/use/loading.d.ts +37 -0
  65. package/types/use/loadingError.d.ts +5 -100
  66. package/types/use/object.d.ts +3 -12
  67. package/types/use/objectCalculated.d.ts +3 -9
  68. package/types/use/objectInstance.d.ts +59 -47
  69. package/types/use/objectRelated.d.ts +3 -9
  70. package/types/use/objectSubscription.d.ts +33 -51
  71. package/types/use/proxyError.d.ts +27 -0
  72. package/types/use/proxyLoading.d.ts +15 -0
  73. package/types/use/proxyLoadingError.d.ts +8 -16
  74. package/types/use/search.d.ts +2 -3
  75. package/types/utils/assignReactiveObject.d.ts +0 -1
  76. package/types/utils/cancellableFetch.d.ts +0 -1
  77. package/types/utils/cancellablePromise.d.ts +0 -1
  78. package/types/utils/classes.d.ts +2 -8
  79. package/types/utils/compact.d.ts +0 -1
  80. package/types/utils/deepUnref.d.ts +0 -1
  81. package/types/utils/deleteKey.d.ts +5 -6
  82. package/types/utils/flattenPaths.d.ts +0 -1
  83. package/types/utils/flattenPathsWithValues.d.ts +0 -1
  84. package/types/utils/getFakePk.d.ts +0 -1
  85. package/types/utils/keepAliveTry.d.ts +0 -1
  86. package/types/utils/keyDiff.d.ts +0 -1
  87. package/types/utils/loadingCombine.d.ts +0 -1
  88. package/types/utils/proxyRunning.d.ts +2 -3
  89. package/types/utils/refIfReactive.d.ts +0 -1
  90. package/types/utils/relatedCalculatedHelpers.d.ts +0 -1
  91. package/types/utils/set.d.ts +0 -1
  92. package/types/utils/transformWalk.d.ts +0 -1
  93. package/types/utils/unwrapNested.d.ts +0 -1
  94. package/types/utils/watches.d.ts +0 -1
  95. package/use/cancellableIntent.js +185 -100
  96. package/use/error.js +44 -0
  97. package/use/list.js +51 -111
  98. package/use/listCalculated.js +75 -98
  99. package/use/listFilter.js +91 -186
  100. package/use/listInstance.js +357 -282
  101. package/use/listRelated.js +52 -72
  102. package/use/listSearch.js +49 -86
  103. package/use/listSort.js +121 -237
  104. package/use/listSubscription.js +181 -274
  105. package/use/loading.js +35 -0
  106. package/use/loadingError.js +7 -81
  107. package/use/object.js +47 -67
  108. package/use/objectCalculated.js +103 -65
  109. package/use/objectInstance.js +253 -241
  110. package/use/objectRelated.js +48 -30
  111. package/use/objectSubscription.js +110 -175
  112. package/use/proxyError.js +48 -0
  113. package/use/proxyLoading.js +22 -0
  114. package/use/proxyLoadingError.js +9 -35
  115. package/use/search.js +4 -2
  116. package/utils/assignReactiveObject.js +8 -8
  117. package/utils/classes.js +7 -28
  118. package/utils/deleteKey.js +9 -3
  119. package/utils/proxyRunning.js +16 -10
  120. package/types/config/commonCrud.d.ts.map +0 -1
  121. package/types/config/listCrud.d.ts.map +0 -1
  122. package/types/config/objectCrud.d.ts.map +0 -1
  123. package/types/index.d.ts.map +0 -1
  124. package/types/tests/unit/config/listCrud.spec.d.ts.map +0 -1
  125. package/types/tests/unit/config/objectCrud.spec.d.ts.map +0 -1
  126. package/types/tests/unit/crudPromise.d.ts.map +0 -1
  127. package/types/tests/unit/expectHelpers.d.ts +0 -2
  128. package/types/tests/unit/expectHelpers.d.ts.map +0 -1
  129. package/types/tests/unit/mockOnUnmounted.d.ts.map +0 -1
  130. package/types/tests/unit/poll.d.ts.map +0 -1
  131. package/types/tests/unit/use/cancellableIntent.spec.d.ts.map +0 -1
  132. package/types/tests/unit/use/listCalculated.spec.d.ts.map +0 -1
  133. package/types/tests/unit/use/listFilter.spec.d.ts.map +0 -1
  134. package/types/tests/unit/use/listInstance.spec.d.ts.map +0 -1
  135. package/types/tests/unit/use/listRelated.spec.d.ts.map +0 -1
  136. package/types/tests/unit/use/listSearch.spec.d.ts.map +0 -1
  137. package/types/tests/unit/use/listSort.spec.d.ts.map +0 -1
  138. package/types/tests/unit/use/listSubscription.spec.d.ts.map +0 -1
  139. package/types/tests/unit/use/loadingError.spec.d.ts.map +0 -1
  140. package/types/tests/unit/use/objectInstance.spec.d.ts.map +0 -1
  141. package/types/tests/unit/use/objectSubscription.spec.d.ts.map +0 -1
  142. package/types/tests/unit/use/proxyLoadingError.spec.d.ts.map +0 -1
  143. package/types/tests/unit/use/search.spec.d.ts.map +0 -1
  144. package/types/tests/unit/utils/assignReactiveObject.spec.d.ts.map +0 -1
  145. package/types/tests/unit/utils/classes.spec.d.ts.map +0 -1
  146. package/types/tests/unit/utils/deleteKey.spec.d.ts.map +0 -1
  147. package/types/tests/unit/utils/flattenPaths.spec.d.ts.map +0 -1
  148. package/types/tests/unit/utils/keyDiff.spec.d.ts.map +0 -1
  149. package/types/tests/unit/utils/set.spec.d.ts.map +0 -1
  150. package/types/tests/unit/utils/watches.spec.d.ts.map +0 -1
  151. package/types/use/cancellableIntent.d.ts.map +0 -1
  152. package/types/use/combineClasses.d.ts.map +0 -1
  153. package/types/use/list.d.ts.map +0 -1
  154. package/types/use/listCalculated.d.ts.map +0 -1
  155. package/types/use/listFilter.d.ts.map +0 -1
  156. package/types/use/listInstance.d.ts.map +0 -1
  157. package/types/use/listKeys.d.ts +0 -15
  158. package/types/use/listKeys.d.ts.map +0 -1
  159. package/types/use/listRelated.d.ts.map +0 -1
  160. package/types/use/listSearch.d.ts.map +0 -1
  161. package/types/use/listSort.d.ts.map +0 -1
  162. package/types/use/listSubscription.d.ts.map +0 -1
  163. package/types/use/loadingError.d.ts.map +0 -1
  164. package/types/use/object.d.ts.map +0 -1
  165. package/types/use/objectCalculated.d.ts.map +0 -1
  166. package/types/use/objectInstance.d.ts.map +0 -1
  167. package/types/use/objectRelated.d.ts.map +0 -1
  168. package/types/use/objectSubscription.d.ts.map +0 -1
  169. package/types/use/paginatedListInstance.d.ts +0 -60
  170. package/types/use/paginatedListInstance.d.ts.map +0 -1
  171. package/types/use/proxyLoadingError.d.ts.map +0 -1
  172. package/types/use/search.d.ts.map +0 -1
  173. package/types/use/watchesRunning.d.ts +0 -63
  174. package/types/use/watchesRunning.d.ts.map +0 -1
  175. package/types/utils/assignReactiveObject.d.ts.map +0 -1
  176. package/types/utils/cancellableFetch.d.ts.map +0 -1
  177. package/types/utils/cancellablePromise.d.ts.map +0 -1
  178. package/types/utils/classes.d.ts.map +0 -1
  179. package/types/utils/compact.d.ts.map +0 -1
  180. package/types/utils/deepUnref.d.ts.map +0 -1
  181. package/types/utils/deleteKey.d.ts.map +0 -1
  182. package/types/utils/flattenPaths.d.ts.map +0 -1
  183. package/types/utils/flattenPathsWithValues.d.ts.map +0 -1
  184. package/types/utils/getFakePk.d.ts.map +0 -1
  185. package/types/utils/keepAliveTry.d.ts.map +0 -1
  186. package/types/utils/keyDiff.d.ts.map +0 -1
  187. package/types/utils/loadingCombine.d.ts.map +0 -1
  188. package/types/utils/proxyRunning.d.ts.map +0 -1
  189. package/types/utils/refIfReactive.d.ts.map +0 -1
  190. package/types/utils/relatedCalculatedHelpers.d.ts.map +0 -1
  191. package/types/utils/set.d.ts.map +0 -1
  192. package/types/utils/transformWalk.d.ts.map +0 -1
  193. package/types/utils/unwrapNested.d.ts.map +0 -1
  194. package/types/utils/watches.d.ts.map +0 -1
  195. package/use/listKeys.js +0 -105
  196. package/use/paginatedListInstance.js +0 -126
  197. package/use/watchesRunning.js +0 -69
package/README.md CHANGED
@@ -5,7 +5,7 @@
5
5
  ![ESLint](https://docs.arrai-dev.com/reactive-helpers/artifacts/main/eslint.svg)
6
6
  ![Prettier](https://docs.arrai-dev.com/reactive-helpers/artifacts/main/prettier.svg)
7
7
 
8
- Vue.js 3 utility composition functions to help manipulate objects and lists.
8
+ Vue.js 3 composition utilities to manage reactive lists, objects and other helpers.
9
9
 
10
10
  <!-- prettier-ignore-start -->
11
11
  <!-- START doctoc generated TOC please keep comment here to allow auto update -->
@@ -13,24 +13,7 @@ Vue.js 3 utility composition functions to help manipulate objects and lists.
13
13
 
14
14
  - [Install](#install)
15
15
  - [Usage](#usage)
16
- - [Import](#import)
17
- - [Documentation](#documentation)
18
- - [List](#list)
19
- - [Instance](#instance)
20
- - [Subscription](#subscription)
21
- - [Related](#related)
22
- - [Calculated](#calculated)
23
- - [Filter](#filter)
24
- - [Search](#search)
25
- - [Sort](#sort)
26
- - [All](#all)
27
- - [Object](#object)
28
- - [Search](#search-1)
29
- - [Utils](#utils)
30
- - [addOrUpdateReactiveObject & assignReactiveObject](#addorupdatereactiveobject--assignreactiveobject)
31
- - [keyDiff](#keydiff)
32
- - [set](#set)
33
- - [watches](#watches)
16
+ - [Changelog](#changelog)
34
17
  - [Development](#development)
35
18
 
36
19
  <!-- END doctoc generated TOC please keep comment here to allow auto update -->
@@ -38,911 +21,56 @@ Vue.js 3 utility composition functions to help manipulate objects and lists.
38
21
 
39
22
  ## Install
40
23
 
41
- ```console
24
+ ```bash
42
25
  $ npm install @arrai-innovations/reactive-helpers
43
26
  ```
44
27
 
45
28
  ## Usage
46
29
 
47
- ### Import
30
+ The package exposes many composables and helper utilities. A minimal example of using a list instance is shown below.
48
31
 
49
- ```js
50
- // base import contains all exports
51
- import { useListInstance, useObjectInstance } from "@arrai-innovations/reactive-helpers";
52
- // you can also import individual modules
53
- import { useListInstance } from "@arrai-innovations/reactive-helpers/use/listInstance";
54
- // or the module categories
55
- import { useObjectInstance } from "@arrai-innovations/reactive-helpers/use";
56
- ```
57
-
58
- ### Documentation
59
-
60
- See the [generated documentation](./docs/README.md) for list of available modules and imports.
61
-
62
- ### List
63
-
64
- #### Instance
65
-
66
- The container for your list of objects, providing loading or error status.
67
-
68
- ```js
69
- // do this in your main.js
70
- import { setListCrud } from "@arrai-innovations/reactive-helpers";
71
-
72
- setListCrud({
73
- list: async function listCrudAdaptor({ target, params, pageCallback }) {
74
- // todo: your implemenation here.
75
- const listOfObjects = await talkToServer(target, params);
76
- pageCallback(listOfObjects);
77
- const nextListOfObjects = await talkToServerAgain(target, params);
78
- pageCallback(nextListOfObjects);
79
- },
80
- });
81
- ```
82
-
83
- ```js
84
- // then use in your component
32
+ ```javascript
85
33
  import { useListInstance } from "@arrai-innovations/reactive-helpers";
86
34
  import { reactive } from "vue";
87
35
 
88
- const listProps = reactive({
89
- // target are implementation specific to your crud handlers.
90
- target: {
91
- stream: "contacts",
92
- },
36
+ const props = reactive({
37
+ target: { stream: "contacts" },
93
38
  pkKey: "id",
94
- params,
95
- });
96
- const contacts = useListInstance({
97
- props: listProps,
98
- });
99
-
100
- await contacts.list();
101
- console.log(contacts.loading);
102
- // False
103
- console.log(contacts.errored);
104
- // False
105
- console.log(contacts.error);
106
- // null
107
- console.log(contacts.objects);
108
- // { contacts keyed by 'id' }
109
- // change list or retrieve args directly
110
- contacts.state.params.fields.push("message_count");
111
- await contacts.list();
112
- console.log(contacts.objects);
113
- // { contacts keyed by 'id' with message_count }
114
- // change list or retrieve args indirectly
115
- listProps.params.has_organization = false;
116
- await contacts.list();
117
- console.log(contacts.objects);
118
- // { contacts keyed by 'id' with organizationless contacts }
119
- ```
120
-
121
- #### Subscription
122
-
123
- Adds functionality to a list instance to receive updates from the server.
124
-
125
- ```js
126
- // do this in your main.js
127
- import { setListCrud } from "@arrai-innovations/reactive-helpers";
128
- setListCrud({
129
- ..., // in addition to the list crud adaptor above
130
- subscribe: function subscribeCrudAdaptor({ target, params, eventCallback }) {
131
- // todo: your implemenation here.
132
- const subscription = talkToServer(function (data, action) {
133
- eventCallback(data, action);
134
- });
135
- // return a promise with a cancel action
136
- subscription.cancel = async () => {
137
- await cancelSubOnServer();
138
- };
139
- return subscription;
140
- },
141
- });
142
- ```
143
-
144
- ```js
145
- // then use in your component
146
- import { useListInstance, useListSubscription } from "@arrai-innovations/reactive-helpers";
147
- import { reactive } from "vue";
148
-
149
- const listProps = reactive({
150
- // target are implementation specific to your crud handlers.
151
- target: {
152
- stream: "contacts",
153
- includeCreateEvents: true,
154
- subscribeAction: "subscribe_contacts",
155
- unsubscribeAction: "unsubscribe_contacts",
156
- },
157
- pkKey: "id",
158
- params: {
159
- has_organization: true,
160
- fields: ["id", "has_name", "lexical_name", "organization", "phone"],
161
- },
162
- });
163
- const contacts = useListInstance({
164
- props: listProps,
165
- });
166
- const contactsSubscription = useListSubscription({
167
- listInstance: contacts,
168
- });
169
-
170
- // only get new or updated contacts, not existing.
171
- contactsSubscription.subscribe();
172
- // or, subscribe and get the existing list.
173
- contactsSubscription.subscribe();
174
- // stop getting updates.
175
- contactsSubscription.unsubscribe();
176
- // re-retreive the list of existing contacts including another field.
177
- contacts.params.fields.push("message_count");
178
- // re-retreive the list of all existing contacts.
179
- delete contacts.params.has_organization;
180
- ```
181
-
182
- ```js
183
- // or you can have listSubscription create the listInstance for you.
184
- import { useListSubscription } from "@arrai-innovations/reactive-helpers";
185
- import { reactive } from "vue";
186
-
187
- const listProps = reactive({
188
- // target are implementation specific to your crud handlers.
189
- target: {
190
- stream: "contacts",
191
- includeCreateEvents: true,
192
- subscribeAction: "subscribe_contacts",
193
- unsubscribeAction: "unsubscribe_contacts",
194
- },
195
- params: {
196
- fields: ["id", "has_name", "lexical_name", "organization", "phone"],
197
- has_organization: true,
198
- },
199
- });
200
- const contactsSubscription = useListSubscription({
201
- props: listProps,
202
- });
203
- ```
204
-
205
- #### Related
206
-
207
- Lookup foreign keys between list instances via watch, for using dot notation in templates to cross object relations.
208
-
209
- ```js
210
- // no main.js setup required.
211
- // used in example below.
212
- import { useListInstance, useListRelated, doAwaitNot } from "@arrai-innovations/reactive-helpers";
213
- import { reactive, toRef } from "vue";
214
-
215
- const organizationsProps = reactive({
216
- pkKey: "id",
217
- params: {
218
- fields: ["id", "name"],
219
- },
220
- });
221
- const organizations = useListInstance({
222
- props: organizationsProps,
223
- });
224
- const contactsProps = reactive({
225
- pkKey: "id",
226
- params: {
227
- fields: ["id", "lexical_name", "organization"],
228
- },
229
- });
230
- const contacts = useListInstance({
231
- props: contactsProps,
232
- });
233
- const contactsRelatedRules = reactive({
234
- organization: {
235
- // desired key on relatedObjects
236
- objects: toRef(organizations.state, "objects"), // organizations by id
237
- pkKey: "organization", // reference key on contact for org id.
238
- },
239
- });
240
- const contactsRelated = useListRelated({
241
- parentState: contacts.state,
242
- relatedObjectsRules: contactsRelatedRules,
243
- });
244
- await organizations.list();
245
- await contacts.list();
246
- console.log(organizations.objects);
247
- /*
248
- {
249
- "24": { "id": 24, "name": "org 24" },
250
- "42": { "id": 42, "name": "org 42" },
251
- }
252
- */
253
- console.log(contacts.objects);
254
- /*
255
- {
256
- "15": {
257
- "id": 15,
258
- "lexical_name": "one, contact",
259
- "organization": 42,
260
- }
261
- }
262
- */
263
- console.log(contacts.relatedObjects);
264
- /*
265
- {
266
- "15": {
267
- "organization": { "id": 42, "name": "org 42" }
268
- }
269
- }
270
- */
271
- contacts.objects["15"].organization = 24;
272
- await doAwaitNot({
273
- obj: contactsRelated.state,
274
- prop: "running",
275
- });
276
- console.log(contacts.relatedObjects["15"]);
277
- /*
278
- {
279
- "organization": { "id": 24, "name": "org 24" }
280
- }
281
- */
282
- delete contactsRelated.relatedObjectRules.organization;
283
- await doAwaitNot({
284
- obj: contactsRelated.state,
285
- prop: "running",
286
- });
287
- await nextTick();
288
- console.log(contacts.relatedObjects["15"]);
289
- /* {} */
290
- ```
291
-
292
- #### Calculated
293
-
294
- ```js
295
- // no main.js setup required.
296
- // used in example below.
297
- import { useListInstance, useListCalculated } from "@arrai-innovations/reactive-helpers";
298
- import { nextTick, reactive } from "vue";
299
-
300
- const contactsProps = reactive({
301
- pkKey: "id",
302
- params: {
303
- fields: ["id", "has_name", "has_billing", "lexical_name", "organization"],
304
- },
305
- });
306
- const contacts = useListInstance({
307
- props: contactsProps,
308
- });
309
- const contactsCalculatedRules = reactive({
310
- first_letter_of_name: (object) => {
311
- return object.lexical_name[0];
312
- },
313
- });
314
- const contactsCalculated = useListCalculated({
315
- parentState: contacts.state,
316
- calculatedObjectsRules: contactsCalculatedRules,
317
- });
318
- await contacts.list();
319
- console.log(contacts.objects);
320
- /*
321
- {
322
- "15": {
323
- "id": 15,
324
- "lexical_name": "one, contact",
325
- "organization": 42,
326
- },
327
- "16": {
328
- "id": 16,
329
- "lexical_name": "two, contact",
330
- "organization": 42,
331
- },
332
- }
333
- */
334
- console.log(contacts.calculatedObjects);
335
- /*
336
- {
337
- "15": {
338
- "first_letter_of_name": "o"
339
- },
340
- "16": {
341
- "first_letter_of_name": "t"
342
- },
343
- }
344
- */
345
- ```
346
-
347
- #### Filter
348
-
349
- ```js
350
- // no main.js setup required.
351
- // used in example below.
352
- import { useListInstance, useListFilter, doAwaitNot } from "@arrai-innovations/reactive-helpers";
353
- import { reactive, ref } from "vue";
354
-
355
- const contactsProps = reactive({
356
- pkKey: "id",
357
- params: {
358
- fields: ["id", "has_name", "has_billing", "lexical_name", "organization"],
359
- },
360
- });
361
- const contacts = useListInstance({
362
- props: contactsProps,
363
- });
364
-
365
- const allowedFilter = ref((object) => {
366
- return object.has_name === true;
367
- });
368
- const excludedFilter = ref((object) => {
369
- return object.has_billing === true;
370
- });
371
-
372
- // conditions are all optional but anded together if present.
373
- const contactsFilter = useListFilter({
374
- parentState: contacts.state,
375
- allowedFilter,
376
- excludedFilter,
377
- });
378
- await contacts.list();
379
- console.log(contactsFilter.state.objects);
380
- console.log(contactsFilter.state.objectsInOrder);
381
- console.log(contactsFilter.state.order);
382
- contacts.objects["15"].has_name = false;
383
- await doAwaitNot({
384
- obj: contactsFilter.state,
385
- prop: "running",
386
- });
387
- console.log(contactsFilter.state.objects);
388
- console.log(contactsFilter.state.objectsInOrder);
389
- console.log(contactsFilter.state.order);
390
- ```
391
-
392
- #### Search
393
-
394
- ```js
395
- // no main.js setup required.
396
- // used in example below.
397
- import { useListInstance, useListSearch, doAwaitNot } from "@arrai-innovations/reactive-helpers";
398
- import { reactive } from "vue";
399
-
400
- const contactsProps = reactive({
401
- pkKey: "id",
402
- params: {
403
- fields: ["id", "has_name", "has_billing", "lexical_name", "organization"],
404
- },
405
- });
406
- const contacts = useListInstance({
407
- props: contactsProps,
408
- });
409
- const searchProps = reactive({
410
- textSearchRules: ["lexical_name"],
411
- textSearchValue: "",
412
- });
413
- const search = useListSearch({
414
- parentState: contacts.state,
415
- props: searchProps,
416
- });
417
- await contacts.list();
418
- await doAwaitNot({
419
- obj: search.state,
420
- prop: "running",
421
- });
422
- // all values show when search is empty.
423
- console.log(search.state.objects);
424
- console.log(search.state.objectsInOrder);
425
- console.log(search.state.order);
426
- searchProps.textSearchValue = "one";
427
- await doAwaitNot({
428
- obj: search.state,
429
- prop: "running",
39
+ params: { fields: ["id", "name"] },
430
40
  });
431
- // only values that match the search show.
432
- console.log(search.state.objects);
433
- console.log(search.state.objectsInOrder);
434
- console.log(search.state.order);
435
- ```
436
-
437
- #### Sort
438
-
439
- ```js
440
- // no main.js setup required.
441
- // used in example below.
442
- import { useListInstance, useListSort, doAwaitNot } from "@arrai-innovations/reactive-helpers";
443
- import { reactive } from "vue";
444
41
 
445
- const contactsProps = reactive({
446
- pkKey: "id",
447
- params: {
448
- fields: ["id", "has_name", "lexical_name", "organization"],
449
- },
450
- });
451
- const contacts = useListInstance({
452
- props: contactsProps,
453
- });
454
- const contactsSort = useListSort({
455
- parentState: contacts.state,
456
- orderByRules: [
457
- { key: "has_name", desc: true, localeCompare: false },
458
- { key: "lexical_name", desc: false, localeCompare: true },
459
- ],
460
- sortThrottleWait: 100, // default, ms to wait before sorting after a change.
461
- });
42
+ const contacts = useListInstance({ props });
462
43
  await contacts.list();
463
- console.log(contactsSort.state.order);
464
- // array of ids in order, based on the specified rules.
465
- console.log(contactsSort.state.objectsInOrder);
466
- // computed array of the previous that also looks up the object ids in .objects
467
- contactsSort.state.orderByRules[0].desc = false;
468
- await doAwaitNot({
469
- obj: contactsSort.state,
470
- prop: "running",
471
- });
472
- console.log(contactsSort.state.order);
473
- // array of ids in order, based on updated rules.
44
+ console.log(contacts.state.objects);
474
45
  ```
475
46
 
476
- #### All
477
-
478
- ```js
479
- // you can also let the library manage a full stack for you.
480
- import { useList } from "@arrai-innovations/reactive-helpers";
481
- import { reactive, toRef } from "vue";
482
-
483
- const managedListProps = reactive({
484
- // target are implementation specific to your crud handlers.
485
- target: {
486
- stream: "contacts",
487
- includeCreateEvents: true,
488
- subscribeAction: "subscribe_contacts",
489
- unsubscribeAction: "unsubscribe_contacts",
490
- },
491
- pkKey: "id",
492
- params: {
493
- has_organization: true,
494
- fields: ["id", "has_name", "lexical_name", "organization", "phone"],
495
- },
496
- relatedObjectsRules: {
497
- organization: {
498
- // desired key on relatedObjects
499
- objects: toRef(organizations.state, "objects"), // organizations by id
500
- pkKey: "organization", // reference key on contact for org id.
501
- },
502
- },
503
- calculatedObjectsRules: {
504
- first_letter_of_name: (object) => {
505
- return object.lexical_name[0];
506
- },
507
- },
508
- orderByRules: [
509
- { key: "relatedObjects.organization.name", desc: false, localeCompare: true },
510
- { key: "lexical_name", desc: false, localeCompare: true },
511
- ],
512
- textSearchRules: ["relatedObjects.organization.name"],
513
- textSearchValue: organizationNameSearch,
514
- sortThrottleWait: 100, // default, ms to wait before sorting after a change.
515
- });
516
- const managedList = useList({
517
- props: managedListProps,
518
- handlers: {
519
- list: customListFunction,
520
- subscribe: customSubscribeFunction,
521
- },
522
- });
523
- // the state expected of each are all available on the same state.
524
- console.log(managedList.state.objects);
525
- // managed instances can also be accessed directly.
526
- console.log(managedList.managed);
527
- /*
528
- {
529
- listInstance: {...},
530
- listSubscription: {...},
531
- listRelated: {...},
532
- listCalculated: {...},
533
- listFilter: {...},
534
- listSort: {...},
535
- }
536
- */
537
- // the stack is applied in the order of the keys as by managedList.managed.
538
- ```
539
-
540
- ### Object
541
-
542
- ```js
543
- // do this in your main.js
544
- import { setObjectCrud } from "@arrai-innovations/reactive-helpers";
545
-
546
- setObjectCrud({
547
- create: async function createCrudAdaptor({ target, params, object }) {
548
- // todo: your implemenation here.
549
- const newObject = await talkToServer(object);
550
- return newObject;
551
- },
552
- retrieve: async function retrieveCrudAdaptor({ target, params, id }) {
553
- // todo: your implemenation here.
554
- const retrievedObject = await talkToServer(id);
555
- return retrievedObject;
556
- },
557
- update: async function updateCrudAdaptor({ target, params, object }) {
558
- // todo: your implemenation here.
559
- const updatedObject = await talkToServer(object);
560
- return updatedObject;
561
- },
562
- delete: async function deleteCrudAdaptor({ target, id }) {
563
- // todo: your implemenation here.
564
- await talkToServer(id);
565
- },
566
- patch: async function patchCrudAdaptor({ target, params, id, partialObject }) {
567
- // todo: your implemenation here.
568
- const patchedObject = await talkToServer(object);
569
- return patchedObject; // still return the full object.
570
- },
571
- subscribe: function subscribeCrudAdaptor({ target, params, params, eventCallback }) {
572
- // todo: your implemenation here.
573
- const subscription = talkToServer(function (data, action) {
574
- eventCallback(data, action);
575
- });
576
- // return a promise with a cancel action
577
- subscription.cancel = async () => {
578
- await cancelSubOnServer();
579
- };
580
- return subscription;
581
- },
582
- });
583
- ```
584
-
585
- ```js
586
- // similar to list, but for a single object.
587
- import {
588
- useObjectInstance,
589
- useObjectSubscription,
590
- useObjectRelated,
591
- useObjectCalculated,
592
- } from "@arrai-innovations/reactive-helpers";
593
-
594
- const contactObject = useObjectInstance({
595
- props: {
596
- target: {
597
- stream: "contacts",
598
- },
599
- params: {
600
- fields: ["id", "has_name", "lexical_name", "organization", "phone"],
601
- },
602
- id: contactId,
603
- },
604
- });
605
- console.log(contactObject.state.object);
606
- const contactSubscription = useObjectSubscription({
607
- objectInstance: contactObject,
608
- });
609
- // or
610
- const contactSubscription = useObjectSubscription({
611
- props: {
612
- target: {
613
- stream: "contacts",
614
- },
615
- params: {
616
- fields: ["id", "has_name", "lexical_name", "organization", "phone"],
617
- },
618
- id: contactId,
619
- },
620
- });
621
- console.log(contactSubscription.state.object);
622
- const organizations = useList({
623
- props: {
624
- target: {
625
- stream: "organizations",
626
- },
627
- params: {
628
- fields: ["id", "name"],
629
- },
630
- },
631
- });
632
- await organizations.list();
633
- const contactRelated = useObjectRelated({
634
- parentState: contactSubscription.state,
635
- relatedObjectRules: {
636
- organization: {
637
- // desired key on relatedObjects
638
- objects: toRef(organizations.state, "objects"), // organizations by id
639
- pkKey: "organization", // reference key on contact for org id.
640
- },
641
- },
642
- });
643
- console.log(contactRelated.state.object.relatedObject.organization.name);
644
- const contactCalculated = useObjectCalculated({
645
- parentState: contactSubscription.state,
646
- calculatedObjectRules: {
647
- first_letter_of_name: (object) => {
648
- return object.lexical_name[0];
649
- },
650
- },
651
- });
652
- console.log(contactCalculated.state.object.calculatedObject.first_letter_of_name);
653
- ```
654
-
655
- ```js
656
- // you can also let the library managed a full stack for you.
657
- import { useObject } from "@arrai-innovations/reactive-helpers";
658
- import { reactive } from "vue";
659
-
660
- const managedObjectProps = reactive({
661
- target: {
662
- stream: "contacts",
663
- },
664
- params: {
665
- fields: ["id", "has_name", "lexical_name", "organization", "phone"],
666
- },
667
- pk: contactId,
668
- pkKey: "id",
669
- relatedObjectRules: {
670
- organization: {
671
- // desired key on relatedObjects
672
- objects: toRef(organizations.state, "objects"), // organizations by id
673
- pkKey: "organization", // reference key on contact for org id.
674
- },
675
- },
676
- calculatedObjectRules: {
677
- first_letter_of_name: (object) => {
678
- return object.lexical_name[0];
679
- },
680
- },
681
- });
682
- const managedObject = useObject({
683
- props: managedObjectProps,
684
- handlers: {
685
- retrieve: customRetrieveFunction,
686
- subscribe: customSubscribeFunction,
687
- },
688
- });
689
- // the state expected of each are all available on the same state.
690
- console.log(managedObject.state.objects);
691
- // managed instances can also be accessed directly.
692
- console.log(managedObject.managed);
693
- /*
694
- {
695
- objectInstance: {...},
696
- objectSubscription: {...},
697
- objectRelated: {...},
698
- objectCalculated: {...},\
699
- }
700
- */
701
- // the stack is applied in the order of the keys as by managedObject.managed.
702
- ```
703
-
704
- ### Search
705
-
706
- ```js
707
- // no main.js setup required.
708
- import { useSearch } from "@arrai-innovations/reactive-helpers";
709
-
710
- const search = useSearch({});
711
- ```
712
-
713
- ### Utils
714
-
715
- #### addOrUpdateReactiveObject & assignReactiveObject
716
-
717
- `addOrUpdateReactiveObject` - Assigns properties of a source object onto a target object, using refs if both source and
718
- target are reactive.
719
-
720
- `assignReactiveObject` - same as `addOrUpdateReactiveObject`, but deletes keys from target that are not in source.
721
-
722
- ```js
723
- import { assignReactiveObject, addOrUpdateReactiveObject } from "@arrai-innovations/reactive-helpers";
724
- import { reactive, toRef, computed } from "vue";
47
+ See [docs/README.md](./docs/README.md) for a full list of modules, composables, utilities, as well as, their arguments
48
+ and properties.
725
49
 
726
- const target = reactive({ a: 1 });
727
- const source = { a: 3, b: 4 };
728
- const source2 = reactive({ b: 5 });
50
+ ## Changelog
729
51
 
730
- const a = toRef(target, "a");
731
- const b = toRef(target, "b");
732
- const mySum = computed(() => (unref(a) || 0) + (unref(b) || 0));
733
-
734
- console.log(mySum.value); // 1
735
- assignReactiveObject(target, source);
736
- console.log(mySum.value); // 7
737
- addOrUpdateReactiveObject(target, source2);
738
- console.log({ ...target }); // { a: 3, b: 5 }
739
- console.log(mySum.value); // 8
740
- source2.b = 6;
741
- console.log(mySum.value); // 9
742
- assignReactiveObject(target, source2);
743
- console.log({ ...target }); // { b: 6 }
744
- console.log(mySum.value); // 6
745
- source2.b = 10;
746
- console.log(mySum.value); // 10
747
- ```
748
-
749
- #### keyDiff
750
-
751
- `keyDiff` - returns the various set results related to figuring out the changes in keys over time on an object or related objects. Returns the intersection as sameKeys, the difference of old and new as removedKeys, and the difference of new and old as addedKeys.
752
-
753
- ```js
754
- import { keyDiff } from "@arrai-innovations/reactive-helpers";
755
-
756
- const newKeys = Object.keys({ a: 1, b: 2 });
757
- const oldKeys = Object.keys({ a: 1, c: 3 });
758
- const { addedKeys, removedKeys, sameKeys } = keyDiff(newKeys, oldKeys);
759
- console.log({ addedKeys, removedKeys, sameKeys });
760
- // { addedKeys: Set(1) { 'b' }, removedKeys: Set(1) { 'c' }, sameKeys: Set(1) { 'a' } }
761
- ```
762
-
763
- #### set
764
-
765
- We make use of the basic set operations provided as example on mdn:
766
- https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Set#implementing_basic_set_operations
767
-
768
- ```js
769
- import {
770
- isSuperset,
771
- union,
772
- intersection,
773
- symmetricDifference,
774
- difference,
775
- equals,
776
- } from "@arrai-innovations/reactive-helpers";
777
-
778
- isSuperset(new Set([1, 2, 3, 4]), new Set([1, 2, 3])); // true
779
- union(new Set([1, 2, 3, 4]), new Set([1, 2, 3])); // Set { 1, 2, 3, 4 }
780
- intersection(new Set([1, 2, 3, 4]), new Set([1, 2, 3])); // Set { 1, 2, 3 }
781
- symmetricDifference(new Set([1, 2, 3, 4]), new Set([1, 2, 3, 5])); // Set { 4, 5 }
782
- difference(new Set([1, 2, 3, 4]), new Set([1, 2, 3, 5])); // Set { 4 }
783
- difference(new Set([1, 2, 3, 5]), new Set([1, 2, 3, 4])); // Set { 5 }
784
- equals(new Set([1, 2, 3, 4]), new Set([1, 2, 3, 5])); // false
785
- ```
786
-
787
- #### watches
788
-
789
- `ImmediateStopWatch` - a vue.js watch that can be stopped in the first iteration
790
-
791
- ```js
792
- import { ImmediateStopWatch } from "@arrai-innovations/reactive-helpers";
793
-
794
- const reactiveObject = reactive({ a: 1 });
795
- const watch = new ImmediateStopWatch();
796
- const watch2 = new ImmediateStopWatch();
797
- watch.start(
798
- () => reactiveObject.a,
799
- (newA) => {
800
- if (newA === 2) {
801
- console.log("a is now 2!");
802
- watch.stop();
803
- }
804
- },
805
- [reactiveObject.a]
806
- );
807
- watch2.start(
808
- () => reactiveObject.a,
809
- (newA) => {
810
- if (newA === 1) {
811
- console.log("a is now 1!");
812
- watch2.stop();
813
- }
814
- },
815
- [reactiveObject.a]
816
- );
817
- // a is now 1!
818
- await nextTick();
819
- reactiveObject.a = 2;
820
- await nextTick();
821
- // a is now 2!
822
- await nextTick();
823
- reactiveObject.a = 1;
824
- await nextTick();
825
- // no output;
826
- reactiveObject.a = 2;
827
- await nextTick();
828
- // no output;
829
- ```
830
-
831
- `AwaitTimeout` - a class that provides a promise that resolves after a timeout.
832
-
833
- ```js
834
- import { AwaitTimeout } from "@arrai-innovations/reactive-helpers";
835
-
836
- const awaitTimeout = new AwaitTimeout({ timeout: 1000 });
837
- awaitTimeout.start();
838
- await awaitTimeout.promise; // waits 1 second
839
- const awaitTimeout2 = new AwaitTimeout({ timeout: 1000 });
840
- awaitTimeout2.start();
841
- setTimeout(() => awaitTimeout2.stop(), 500);
842
- await awaitTimeout2.promise; // waits 500 ms then rejects with new AwaitTimeoutError("Cancelled", "timeout_cancelled")
843
- ```
844
-
845
- `doAwaitTimeout` - a function that returns a promise that resolves after a timeout.
846
-
847
- ```js
848
- import { doAwaitTimeout } from "@arrai-innovations/reactive-helpers";
849
-
850
- // non cancellable AwaitTimeout helper
851
- await doAwaitTimeout(1000); // waits 1 second
852
- ```
853
-
854
- `AwaitNot` - a class that provides a promise that resolves when a watched prop switches to true then to false.
855
-
856
- ```mermaid
857
- flowchart TD
858
- A["new AwaitNot"] --> B["AwaitNot.start()"];
859
- B -- timeout --> G["AwaitNot.promise is rejected"]
860
- B --> C{"prop is false"};
861
- C -- false --> D["AwaitNot.waitForTrue()"];
862
- C -- true --> H{"canAlreadyBeFalse"}
863
- H -- true --> D;
864
- H -- false --> E["AwaitNot.waitForFalse()"];
865
-
866
- D -- timeout --> G
867
- E -- timeout --> G
868
- E -- prop is false --> F["AwaitNot.promise is resolved"]
869
- D -- prop is true --> E
870
- ```
871
-
872
- ```js
873
- import { AwaitNot } from "@arrai-innovations/reactive-helpers";
874
- import { reactive } from "vue";
875
-
876
- const reactiveObject = reactive({});
877
-
878
- const awaitNot = new AwaitNot({
879
- obj: reactiveObject,
880
- prop: "prop",
881
- couldAlreadyBeFalse: true, // default
882
- timeout: 1000, // default
883
- });
884
- awaitNot.promise
885
- .then(() => {
886
- console.log("resolved");
887
- })
888
- .catch(() => {
889
- console.log("rejected");
890
- });
891
- awaitNot.start();
892
- await nextTick();
893
- reactiveObject.prop = true;
894
- await nextTick();
895
- reactiveObject.prop = false;
896
- await nextTick();
897
- // resolved
898
- const awaitNot2 = new AwaitNot({
899
- obj: reactiveObject,
900
- prop: "prop",
901
- couldAlreadyBeFalse: true, // default
902
- timeout: 500,
903
- });
904
- awaitNot2.promise
905
- .then(() => {
906
- console.log("resolved");
907
- })
908
- .catch(() => {
909
- console.log("rejected");
910
- });
911
- awaitNot2.start();
912
- await awaitNot2.promise;
913
- // rejected
914
- const awaitNot3 = new AwaitNot({
915
- obj: reactiveObject,
916
- prop: "prop",
917
- couldAlreadyBeFalse: false,
918
- timeout: 1000, // default
919
- });
920
- awaitNot3.promise
921
- .then(() => {
922
- console.log("resolved");
923
- })
924
- .catch(() => {
925
- console.log("rejected");
926
- });
927
- awaitNot3.start();
928
- await awaitNot3.promise;
929
- // resolved
930
- ```
52
+ Since v21.0.0, the changelog is available in the [CHANGELOG.md](./CHANGELOG.md) file.
931
53
 
932
54
  ## Development
933
55
 
934
56
  1. Checkout this repo:
57
+
935
58
  ```bash
936
59
  $ git clone git@github.com:arrai-innovations/reactive-helpers.git
937
60
  ```
61
+
938
62
  2. Install dependencies:
63
+
939
64
  ```bash
940
- $ npm ci
65
+ $ npm install --dev
941
66
  ```
67
+
942
68
  3. Run tests via vitest:
69
+
943
70
  ```bash
944
71
  $ npm test
945
72
  ```
73
+
946
74
  4. Run tests with coverage output:
947
75
 
948
76
  ```bash
@@ -950,6 +78,7 @@ await awaitNot3.promise;
950
78
  ```
951
79
 
952
80
  5. Generate types and typedocs:
81
+
953
82
  ```bash
954
83
  $ npm run docs
955
84
  ```