forked from vueuse/vueuse
-
Notifications
You must be signed in to change notification settings - Fork 0
/
index.ts
176 lines (152 loc) · 3.98 KB
/
index.ts
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
import type { ConfigurableEventFilter, Fn } from '@vueuse/shared'
import { pausableFilter, watchIgnorable } from '@vueuse/shared'
import type { Ref } from 'vue-demi'
import type { CloneFn } from '../useCloned'
import type { UseManualRefHistoryReturn } from '../useManualRefHistory'
import { useManualRefHistory } from '../useManualRefHistory'
export interface UseRefHistoryOptions<Raw, Serialized = Raw> extends ConfigurableEventFilter {
/**
* Watch for deep changes, default to false
*
* When set to true, it will also create clones for values store in the history
*
* @default false
*/
deep?: boolean
/**
* The flush option allows for greater control over the timing of a history point, default to 'pre'
*
* Possible values: 'pre', 'post', 'sync'
* It works in the same way as the flush option in watch and watch effect in vue reactivity
*
* @default 'pre'
*/
flush?: 'pre' | 'post' | 'sync'
/**
* Maximum number of history to be kept. Default to unlimited.
*/
capacity?: number
/**
* Clone when taking a snapshot, shortcut for dump: JSON.parse(JSON.stringify(value)).
* Default to false
*
* @default false
*/
clone?: boolean | CloneFn<Raw>
/**
* Serialize data into the history
*/
dump?: (v: Raw) => Serialized
/**
* Deserialize data from the history
*/
parse?: (v: Serialized) => Raw
}
export interface UseRefHistoryReturn<Raw, Serialized> extends UseManualRefHistoryReturn<Raw, Serialized> {
/**
* A ref representing if the tracking is enabled
*/
isTracking: Ref<boolean>
/**
* Pause change tracking
*/
pause(): void
/**
* Resume change tracking
*
* @param [commit] if true, a history record will be create after resuming
*/
resume(commit?: boolean): void
/**
* A sugar for auto pause and auto resuming within a function scope
*
* @param fn
*/
batch(fn: (cancel: Fn) => void): void
/**
* Clear the data and stop the watch
*/
dispose(): void
}
/**
* Track the change history of a ref, also provides undo and redo functionality.
*
* @see https://vueuse.org/useRefHistory
* @param source
* @param options
*/
export function useRefHistory<Raw, Serialized = Raw>(
source: Ref<Raw>,
options: UseRefHistoryOptions<Raw, Serialized> = {},
): UseRefHistoryReturn<Raw, Serialized> {
const {
deep = false,
flush = 'pre',
eventFilter,
} = options
const {
eventFilter: composedFilter,
pause,
resume: resumeTracking,
isActive: isTracking,
} = pausableFilter(eventFilter)
const {
ignoreUpdates,
ignorePrevAsyncUpdates,
stop,
} = watchIgnorable(
source,
commit,
{ deep, flush, eventFilter: composedFilter },
)
function setSource(source: Ref<Raw>, value: Raw) {
// Support changes that are done after the last history operation
// examples:
// undo, modify
// undo, undo, modify
// If there were already changes in the state, they will be ignored
// examples:
// modify, undo
// undo, modify, undo
ignorePrevAsyncUpdates()
ignoreUpdates(() => {
source.value = value
})
}
const manualHistory = useManualRefHistory(source, { ...options, clone: options.clone || deep, setSource })
const { clear, commit: manualCommit } = manualHistory
function commit() {
// This guard only applies for flush 'pre' and 'post'
// If the user triggers a commit manually, then reset the watcher
// so we do not trigger an extra commit in the async watcher
ignorePrevAsyncUpdates()
manualCommit()
}
function resume(commitNow?: boolean) {
resumeTracking()
if (commitNow)
commit()
}
function batch(fn: (cancel: Fn) => void) {
let canceled = false
const cancel = () => canceled = true
ignoreUpdates(() => {
fn(cancel)
})
if (!canceled)
commit()
}
function dispose() {
stop()
clear()
}
return {
...manualHistory,
isTracking,
pause,
resume,
commit,
batch,
dispose,
}
}