tracked-instance 1.0.11 → 1.0.12
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 +91 -30
- package/dist/types/collection.d.ts +17 -0
- package/dist/types/index.d.ts +4 -0
- package/dist/types/tracked-instance.d.ts +13 -0
- package/package.json +2 -2
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
|
-
<
|
|
94
|
-
|
|
95
|
-
<
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
<input
|
|
99
|
-
|
|
100
|
-
|
|
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,68 @@ 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/#eNp9VcFu1DAQ/ZUhl81K2+QAp9W2orQ90ENBLZwIBzfx7rrrOJHtbLda5RsQSAhxob+BhMTH8AP0ExjbSdZJu+xlbc945s2b58k2OC7LaF3RYBrMVCpZqUFRXZVHiWB5WUgNW0nnNcxlkcMIHUc7Q6XoScE5TTUrROuiJUlXNDtgQmkiUuOfiLTAHWyZOmVS302AZNkEmKa5moCkebGm5h8TT4AXJDslmtRwCL0E4dhEas3hh60gOZ3C6BzzjeoJdPtiKfp7Oqo/2ssOhqC37xWVF2jGHFhdOBp5ZkXW9GRJxIIqNIdjODyCbSKgQxZa4NGa8IpGOSlDe2D9zCJqK48ydHZuY4xfJ2IWO4qRXNygb8mJprgDmF1XWhd4DPibZkyRa06zwyR41pCWBM72MuUsXaHBw+lsNg7Aw/2X33CFRkgbqwkfu/h+rl0oS30SHP35/uvvz09wabb9G0sJsUWN64ytjxpQU9huoVlDXc9iY/PcHKLjLDOkm3bKqTuaMVFW2q0B1gd5kVGOSLzetAUD6LuSok3TDYJ0hwaMjTMsBZUVNp33YtVjU92PrxaKgdGvzodd8TZ02x+ENy8kxrZ9Rt2KjG7G+OcUjJFboDPO0JnNbdusFNSlVXfmZOC5Dino07BPR5GpbEfMHnJ8gp4kyT250BWCoB7uv33GrpvDHjG9632kvSIv6G1boO/V5euXY9WGr9nz9bIB+Cr0UDwGZhM7CQ5woBa7nsSctR2N+08utr02y6ZLrqFWop4ObFOdAoy9a3w0Z1xTic/fvf1htw2zLYoO456e+oCH3XpKSTiZ5oQro6iWLhyU1zgKBzx15TfV9jgIJoGb5Qc4xqIbVQj8DNhZh1mtAdWNBLtQKLLBbDfGJFhqXappHFeiXC2itMjjx34mAlZYY0atcM7O2WKQD++VjFP5pjTDvp+XcF7cntszLSs6ac/TJU1XT5zfqI2D9tZoTa4RQGfTRC7MsDPms6sL+2w6I76/ijdl7TGiLAteGYzO7VUlMoTt+Vm0ry19TCzeqbONpkK1RRmglg3rnwT4ST35T+k7uM+jFx2L9T/LBo48)
|
|
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
|
|
202
|
+
const saveChanges = () => {
|
|
203
|
+
loadData(items.value.map((item) => item.instance.data.value))
|
|
204
|
+
}
|
|
161
205
|
</script>
|
|
162
206
|
|
|
163
207
|
<template>
|
|
208
|
+
<button
|
|
209
|
+
:disabled="!isDirty"
|
|
210
|
+
@click="saveChanges"
|
|
211
|
+
>
|
|
212
|
+
💾 Save changes
|
|
213
|
+
</button>
|
|
214
|
+
<button @click="reset">♻️ Reset</button>
|
|
215
|
+
<hr />
|
|
216
|
+
|
|
217
|
+
<div>isDirty: {{ isDirty }}</div>
|
|
218
|
+
|
|
164
219
|
<div>
|
|
165
|
-
|
|
220
|
+
Add new user:
|
|
221
|
+
<input
|
|
222
|
+
v-model="newUserName"
|
|
223
|
+
type="text"
|
|
224
|
+
/>
|
|
225
|
+
<button @click="add({name: newUserName})">➕ Add user</button>
|
|
166
226
|
</div>
|
|
167
227
|
|
|
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
228
|
<ul>
|
|
177
229
|
<template v-for="(item, index) in items">
|
|
178
|
-
<li v-if="!item.isRemoved">
|
|
179
|
-
<input
|
|
180
|
-
|
|
230
|
+
<li v-if="!item.isRemoved.value">
|
|
231
|
+
<input
|
|
232
|
+
v-model="item.instance.data.value.name"
|
|
233
|
+
type="text"
|
|
234
|
+
/>
|
|
181
235
|
<button @click="remove(index)">🗑 Remove</button>
|
|
236
|
+
<button
|
|
237
|
+
v-if="!item.isNew.value"
|
|
238
|
+
@click="item.instance.reset()"
|
|
239
|
+
>
|
|
240
|
+
♻️ Reset
|
|
241
|
+
</button>
|
|
242
|
+
isNew: {{ item.isNew.value }}
|
|
182
243
|
</li>
|
|
183
244
|
</template>
|
|
184
245
|
</ul>
|
|
185
246
|
|
|
186
247
|
Removed items:
|
|
187
248
|
<ul>
|
|
188
|
-
<li v-for="item in items.filter()">
|
|
189
|
-
{{item.instance.data.name}}
|
|
190
|
-
<button @click="item.isRemoved = false">♻️ Rollback</button>
|
|
249
|
+
<li v-for="item in items.filter((i) => i.isRemoved.value)">
|
|
250
|
+
{{ item.instance.data.value.name }}
|
|
251
|
+
<button @click="item.isRemoved.value = false">♻️ Rollback</button>
|
|
191
252
|
</li>
|
|
192
253
|
</ul>
|
|
193
254
|
</template>
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import { ShallowRef, ComputedRef, Ref } from 'vue';
|
|
2
|
+
import { TrackedInstance } from './tracked-instance';
|
|
3
|
+
export interface CollectionItem<Item extends Record<string, any>, Meta = Record<string, any>> {
|
|
4
|
+
instance: TrackedInstance<Item>;
|
|
5
|
+
meta: Meta;
|
|
6
|
+
isRemoved: Ref<boolean>;
|
|
7
|
+
isNew: Ref<boolean>;
|
|
8
|
+
}
|
|
9
|
+
export interface Collection<Item extends Record<string, any>, Meta = Record<string, any>> {
|
|
10
|
+
items: ShallowRef<CollectionItem<Item, Meta>[]>;
|
|
11
|
+
isDirty: ComputedRef<boolean>;
|
|
12
|
+
add: (item: Partial<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 extends Record<string, any>, Meta = Record<string, any>>(createItemMeta?: (instance: TrackedInstance<Item>) => Meta) => Collection<Item, Meta>;
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import { Ref } from 'vue';
|
|
2
|
+
type DeepPartial<T> = T extends object ? {
|
|
3
|
+
[P in keyof T]?: DeepPartial<T[P]>;
|
|
4
|
+
} : T;
|
|
5
|
+
export interface TrackedInstance<Data extends Record<string, any>> {
|
|
6
|
+
data: Ref<Data>;
|
|
7
|
+
isDirty: Ref<boolean>;
|
|
8
|
+
changedData: Ref<DeepPartial<Data>>;
|
|
9
|
+
loadData: (newData: DeepPartial<Data>) => void;
|
|
10
|
+
reset: () => void;
|
|
11
|
+
}
|
|
12
|
+
export declare const useTrackedInstance: <Data extends Record<string, any>>(initialData: Partial<Data>) => TrackedInstance<Data>;
|
|
13
|
+
export {};
|
package/package.json
CHANGED
|
@@ -1,10 +1,10 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "tracked-instance",
|
|
3
|
-
"version": "1.0.
|
|
3
|
+
"version": "1.0.12",
|
|
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",
|