tracked-instance 1.0.11 → 1.0.13

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/README.md CHANGED
@@ -71,6 +71,7 @@ useTrackedInstance([1,2,3])
71
71
  ```
72
72
 
73
73
  ### Real-world example
74
+ [Try on playground](https://play.vuejs.org/#eNqNVc1u00AQfpXBl7RSapfCAVlJVGh7KEiloj36srEn8TbrtbU/aaMoz4DEjROvgYTEw/AC8AjM7tqpaarSm+d/vplv1uvobdPES4tRGo10rnhjQKOxzSSTvGpqZWBtNV4rli+wOJfaMJnjBmaqrmBggvqAt/pBJjOZ1yTBumCGDSEvmZxjceoFrk+5MqshKKQaQxA185YNjGG3yN46kwCGG4EpDAZDJ62QqRSkFcKLXF/aqeC6xCKFGRMavbpAw7jQKfgMAEkCuqytKGCKYBvqDOlzta3vvDaZ3Oy79sk5uACX3HAmwCGBW24ohYGKLRBmtaqgcFgy2SUJ7XJq5KVvomv8ukQ4ZWoBHySfl6aP4+jw8M0uDqPs4zCoIOampsBWASBZ5WqclIprUzclKrioBZMDh9l3qRHbJWgCBBK1A79kwqIOWQh5D37YnmZLPGmjxrC3D+NJKLpF64YS+zQURtGjJLCHeEOCwaoRNEOSAEYFX07a3VPv644HsNmMEmfzTqWCJHxNrTG1hONc8HwxziLPliya/Pr64/f3z/DJiaMkePlyFOOxHms7rbiJG4VLlIZCezgoQcA74rKxppvg8qCqCxTk6xH5rWVRZzWrBslk8I4aaMn0VJpY2mqKqsvm1ryTLLg8K12Xp8ePnXR5ifliWt/1Ez4jZUuuuCNV7Kj0X+Bt4jD7B75h+Pcp0oJrNhVYkO1Fu/LO2oIG+PPty0+4oi11JG0r3K/XCW673abLo0nYaOEPMx0lpPEWWvuE2NV7cjzDnNrxs8fJaBiFx+2gYk18o2tJz5/nd9YaiC7bM6MxPHjnnDGLSmManSaJlc1iHud1lez6dbdFFY2m25rx+YN6FNdwgepjYzjd3j91mRD17Xuv274KPsbt/BH9jSYauNYu3c2oJTWwtRmm5u6MnPns6sIvdmskaljH+yeMdHa1sK7H4PbOyoLa7vn5bs/9+LicX+uzO4NSd6Bco34a3j+L6J9z8gT0+3Zfxa+3U9z8BYQrOQM=)
74
75
  ```vue
75
76
  <script setup>
76
77
  import {useTrackedInstance} from 'tracked-instance'
@@ -78,28 +79,66 @@ useTrackedInstance([1,2,3])
78
79
  const {data, changedData, isDirty, reset, loadData} = useTrackedInstance({
79
80
  title: '',
80
81
  year: null,
81
- isPublished: false
82
+ isPublished: false,
83
+ details: {
84
+ // should be updated by loadData
85
+ }
82
86
  })
83
87
 
88
+ // update initial data without make form dirty
84
89
  loadData({
85
90
  id: 1,
86
91
  title: 'The Dark Knight',
87
92
  year: 2008,
88
- isPublished: true
93
+ isPublished: true,
94
+ details: {
95
+ director: {
96
+ name: 'Christopher Nolan' // form see changes in nested values
97
+ }
98
+ }
89
99
  })
100
+
101
+ const saveChanges = () => {
102
+ loadData(data.value)
103
+ }
90
104
  </script>
91
105
 
92
106
  <template>
93
- <button @click="reset">reset</button>
94
-
95
- <form @submit.prevent="console.log(changedData.value)">
96
- <input v-model="data.title" type="text">
97
- <input v-model.number="data.year" type="text">
98
- <input v-model="data.isPublished" type="checkbox">
99
-
100
- <button type="submit" :disabled="!isDirty">Show changed data</button>
107
+ <div>isDirty: {{ isDirty }}</div>
108
+ <hr />
109
+ <button @click="reset">♻️ Reset</button>
110
+
111
+ <form @submit.prevent="saveChanges">
112
+ <input
113
+ v-model="data.title"
114
+ type="text"
115
+ />
116
+ <input
117
+ v-model.number="data.year"
118
+ type="number"
119
+ />
120
+ <input
121
+ v-model="data.isPublished"
122
+ type="checkbox"
123
+ />
124
+
125
+ <input
126
+ v-model="data.details.director.name"
127
+ type="text"
128
+ />
129
+
130
+ <button
131
+ type="submit"
132
+ :disabled="!isDirty"
133
+ >
134
+ 💾 Save changes
135
+ </button>
101
136
  </form>
137
+
138
+ <h2>Changed data:</h2>
139
+ <pre>{{ changedData }}</pre>
102
140
  </template>
141
+
103
142
  ```
104
143
 
105
144
  ## Collection
@@ -148,46 +187,72 @@ console.log(items.value[0].meta.isValidName.value) // false
148
187
  ```
149
188
 
150
189
  ### Real-world example
190
+ [Try on playground](https://play.vuejs.org/#eNp9VcFy0zAQ/ZWtL3ZnUvsAp5BmKG0P9FCYFk6Yg2sriRpZ9khymk7G38DADMNwob/BDDN8DD9AP4GVZDuym3BJLO1q973dp9XGOynLcFURb+xNZCpoqUASVZXTmNO8LISCjSCzGmaiyMFHR39rqCQ5LRgjqaIFb12USNIlyY4olyrhqfaPeVrgCjZUnlGh7keQZNkIqCK5HIEgebEi+h8Tj4AVSXaWqKSGY+glCA51pNYcfNjwJCdj8C8wn1+PoFsXC95fE7/+aA5bGJzcvZdEXKIZcyC7wPcds0xW5HSR8DmRaA4O4XgKm5hDhyzQC7Dww1XCKmI3AMIZZYqIQJv0sQP9EVJ5ZShm1hlTNd55UgbG1+Swvk3VwgwTOf74U8d8EtkWYXNwgf4lSxTBFcDkplKqwG0deZxRmdwwkh3H3kFT9Niztpcpo+kSDQ5PazNxAB4fvvyGazRC2lh1+MjGd3NtQ5nWxd70z/dff39+giu97J9YCIgMavzO6GragBrDZgPNN9T1JNI2x80iOsky3TQtBzG2WxPKy0q1lVwd5UVGGCJxetsSBlD3JUGbImsEaTc1GBNnSAWVGTTKcWLVhy8GsvF9zffHVwNOA+vzdYlUrE3WdgwBzwqB2Uz38SbwjKwP8c+KCiO30CeMojOdmUbuEJPjOixKvzD71BVqrttS7SmXW7KdZbOXOLBEENTjw7fPqAO92StM73gfaY/kJblrCbpeXb4+HaM/nA+Or5MNwNWlg+IpMJPYinKAA9XZ9SRitO1o1L+Ekem1/my6ZBtqROvowDTVKsCMirbx7fwIqJ0IT0fHtt0dxj09dQEPu7VLSajpWcKkVlRbLhy9NzhcB3Xq6DdsezXwRp59HY5wuIW3suD4sJjpiVmNAdWNBbahUGSD10IbY2+hVCnHUVTxcjkP0yKPnvrpCMiwxoxK4uSe0fkgH54rKSPiTamfj37ehLHi7sLsKVGRUbufLki63LF/K9cW2lutNbFCAJ1NJWKux582n19fmmvTGfH+VayhtceIsixYpTFat1cVzxC242fQvjblo3z+Tp6vFeGyJaWBmmoY/9jDR/r0P9S3cJ+Fz7sq1v8AiAKmmg==)
151
191
  ```vue
152
192
  <script setup>
153
- import {ref} from 'vue'
154
- import {useCollection} from 'tracked-instance'
193
+ import {ref} from 'vue'
194
+ import {useCollection} from 'tracked-instance'
155
195
 
156
- const {isDirty, add, items, remove, reset, loadData} = useCollection()
196
+ const {isDirty, add, items, remove, reset, loadData} = useCollection()
157
197
 
158
- loadData([{name: 'Jack'}, {name: 'John'}, {name: 'Joe'}])
198
+ loadData([{name: 'Jack'}, {name: 'John'}, {name: 'Joe'}])
199
+
200
+ const newUserName = ref('')
159
201
 
160
- const newUserName = ref('')
202
+ const saveChanges = () => {
203
+ loadData(
204
+ items.value
205
+ .filter(item => !item.isRemoved.value)
206
+ .map((item) => item.instance.data.value)
207
+ )
208
+ }
161
209
  </script>
162
210
 
163
211
  <template>
212
+ <button
213
+ :disabled="!isDirty"
214
+ @click="saveChanges"
215
+ >
216
+ 💾 Save changes
217
+ </button>
218
+ <button @click="reset">♻️ Reset</button>
219
+ <hr />
220
+
221
+ <div>isDirty: {{ isDirty }}</div>
222
+
164
223
  <div>
165
- isDirty: {{isDirty}}
224
+ Add new user:
225
+ <input
226
+ v-model="newUserName"
227
+ type="text"
228
+ />
229
+ <button @click="add({name: newUserName}); newUserName = ''">➕ Add user</button>
166
230
  </div>
167
231
 
168
- <button @click="reset">Reset</button>
169
-
170
- <div>
171
- Add new user:
172
- <input v-model="newUserName" type="text">
173
- <button @click="add({name: newUserName})">➕ Add user</button>
174
- </div>
175
-
176
232
  <ul>
177
233
  <template v-for="(item, index) in items">
178
- <li v-if="!item.isRemoved">
179
- <input v-model="item.instance.data.value.name" type="text">
180
- <button @click="remove(index)">♻️ Rollback</button>
234
+ <li v-if="!item.isRemoved.value">
235
+ <input
236
+ v-model="item.instance.data.value.name"
237
+ type="text"
238
+ />
181
239
  <button @click="remove(index)">🗑 Remove</button>
240
+ <button
241
+ v-if="!item.isNew.value"
242
+ @click="item.instance.reset()"
243
+ >
244
+ ♻️ Reset
245
+ </button>
246
+ isNew: {{ item.isNew.value }}
182
247
  </li>
183
248
  </template>
184
249
  </ul>
185
250
 
186
251
  Removed items:
187
252
  <ul>
188
- <li v-for="item in items.filter()">
189
- {{item.instance.data.name}}
190
- <button @click="item.isRemoved = false">♻️ Rollback</button>
253
+ <li v-for="item in items.filter((i) => i.isRemoved.value)">
254
+ {{ item.instance.data.value.name }}
255
+ <button @click="item.isRemoved.value = false">♻️ Rollback</button>
191
256
  </li>
192
257
  </ul>
193
258
  </template>
package/dist/index.mjs CHANGED
@@ -708,7 +708,10 @@ function unset(object, path) {
708
708
  var unset_default = unset;
709
709
 
710
710
  // src/tracked-instance.ts
711
- import { computed, customRef } from "vue";
711
+ import { computed } from "vue";
712
+
713
+ // src/utils.ts
714
+ import { customRef } from "vue";
712
715
  var isObject2 = (value) => typeof value === "object" && value !== null && !Array.isArray(value) && !(value instanceof Date) && !(value instanceof File) && !(value instanceof Map) && !(value instanceof Set);
713
716
  var isEmpty = (value) => Object.keys(value).length === 0;
714
717
  var iterateObject = function* (source, params = {}) {
@@ -765,6 +768,8 @@ var createNestedRef = (source, handler) => customRef((track, trigger) => {
765
768
  }
766
769
  };
767
770
  });
771
+
772
+ // src/tracked-instance.ts
768
773
  var ArrayInOriginalData = class {
769
774
  length;
770
775
  constructor(length) {
@@ -845,7 +850,7 @@ var snapshotValueToOriginalData = (originalData, path, value) => {
845
850
  }
846
851
  }
847
852
  };
848
- var useTrackedInstance = (initialData) => {
853
+ function useTrackedInstance(initialData) {
849
854
  const _originalData = createNestedRef({}, (path) => ({
850
855
  deleteProperty(target, property) {
851
856
  const result = Reflect.deleteProperty(target, property);
@@ -941,11 +946,11 @@ var useTrackedInstance = (initialData) => {
941
946
  loadData,
942
947
  reset
943
948
  };
944
- };
949
+ }
945
950
 
946
951
  // src/collection.ts
947
- import { computed as computed2, shallowRef, triggerRef, ref } from "vue";
948
- var useCollection = (createItemMeta = () => ({})) => {
952
+ import { computed as computed2, ref, shallowRef, triggerRef } from "vue";
953
+ var useCollection = (createItemMeta = () => void 0) => {
949
954
  const items = shallowRef([]);
950
955
  const isDirty = computed2(
951
956
  () => items.value.some(({ instance, isRemoved, isNew }) => instance.isDirty.value || isNew.value || isRemoved.value)
@@ -0,0 +1,17 @@
1
+ import { ComputedRef, Ref, ShallowRef } from 'vue';
2
+ import { TrackedInstance } from './tracked-instance';
3
+ export interface CollectionItem<Item, Meta> {
4
+ instance: TrackedInstance<Item>;
5
+ meta: Meta;
6
+ isRemoved: Ref<boolean>;
7
+ isNew: Ref<boolean>;
8
+ }
9
+ export interface Collection<Item, Meta> {
10
+ items: ShallowRef<CollectionItem<Item, Meta>[]>;
11
+ isDirty: ComputedRef<boolean>;
12
+ add: (item: Item, afterIndex?: number) => CollectionItem<Item, Meta>;
13
+ remove: (index: number, isHardRemove?: boolean) => void;
14
+ loadData: (items: Item[]) => void;
15
+ reset: () => void;
16
+ }
17
+ export declare const useCollection: <Item = any, Meta = any>(createItemMeta?: (instance: TrackedInstance<Item>) => Meta) => Collection<Item, Meta>;
@@ -0,0 +1,4 @@
1
+ export type { TrackedInstance } from './tracked-instance';
2
+ export type { Collection, CollectionItem } from './collection';
3
+ export { useTrackedInstance } from './tracked-instance';
4
+ export { useCollection } from './collection';
@@ -0,0 +1,11 @@
1
+ import { Ref } from 'vue';
2
+ import { DeepPartial } from './utils';
3
+ export interface TrackedInstance<Data> {
4
+ data: Ref<Data>;
5
+ isDirty: Ref<boolean>;
6
+ changedData: Ref<DeepPartial<Data>>;
7
+ loadData: (newData: Data) => void;
8
+ reset: () => void;
9
+ }
10
+ export declare function useTrackedInstance<Data = any>(): TrackedInstance<Data | undefined>;
11
+ export declare function useTrackedInstance<Data>(value: Data): TrackedInstance<Data>;
@@ -0,0 +1,15 @@
1
+ export type DeepPartial<T> = T extends object ? {
2
+ [P in keyof T]?: DeepPartial<T[P]>;
3
+ } : T;
4
+ export interface NestedProxyPathItem {
5
+ target: Record<string, any>;
6
+ property: string;
7
+ receiver?: Record<string, any>;
8
+ }
9
+ export declare const isObject: (value: unknown) => boolean;
10
+ export declare const isEmpty: (value: object) => boolean;
11
+ export declare const iterateObject: (source: Record<string, any>, params?: {
12
+ goDeepCondition?: ((path: string[], value: any) => boolean) | undefined;
13
+ includeParent?: boolean | undefined;
14
+ }) => Generator<[string[], any], void, unknown>;
15
+ export declare const createNestedRef: <Source extends Record<string, any>>(source: Source, handler: <InnerSource extends Record<string, any>>(path: NestedProxyPathItem[]) => ProxyHandler<InnerSource>) => import("vue").Ref<Source>;
package/package.json CHANGED
@@ -1,10 +1,10 @@
1
1
  {
2
2
  "name": "tracked-instance",
3
- "version": "1.0.11",
3
+ "version": "1.0.13",
4
4
  "description": "Build large forms and track all changes",
5
5
  "type": "module",
6
6
  "scripts": {
7
- "build": "esbuild ./src/index.ts --bundle --external:vue --outfile=./dist/index.mjs --format=esm",
7
+ "build": "tsc && esbuild ./src/index.ts --bundle --external:vue --outfile=./dist/index.mjs --format=esm",
8
8
  "test": "vitest run"
9
9
  },
10
10
  "main": "./dist/index.mjs",