-
-
Notifications
You must be signed in to change notification settings - Fork 2.4k
/
index.ts
125 lines (114 loc) · 2.91 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
import { isDef, isFunction } from '@vueuse/shared'
import type { UnwrapRef } from 'vue-demi'
import { computed, getCurrentInstance, isVue2, ref, watch } from 'vue-demi'
import type { CloneFn } from '../useCloned'
import { cloneFnJSON } from '../useCloned'
export interface UseVModelOptions<T> {
/**
* When passive is set to `true`, it will use `watch` to sync with props and ref.
* Instead of relying on the `v-model` or `.sync` to work.
*
* @default false
*/
passive?: boolean
/**
* When eventName is set, it's value will be used to overwrite the emit event name.
*
* @default undefined
*/
eventName?: string
/**
* Attempting to check for changes of properties in a deeply nested object or array.
* Apply only when `passive` option is set to `true`
*
* @default false
*/
deep?: boolean
/**
* Defining default value for return ref when no value is passed.
*
* @default undefined
*/
defaultValue?: T
/**
* Clone the props.
* Accepts a custom clone function.
* When setting to `true`, it will use `JSON.parse(JSON.stringify(value))` to clone.
*
* @default false
*/
clone?: boolean | CloneFn<T>
}
/**
* Shorthand for v-model binding, props + emit -> ref
*
* @see https://vueuse.org/useVModel
* @param props
* @param key (default 'value' in Vue 2 and 'modelValue' in Vue 3)
* @param emit
*/
export function useVModel<P extends object, K extends keyof P, Name extends string>(
props: P,
key?: K,
emit?: (name: Name, ...args: any[]) => void,
options: UseVModelOptions<P[K]> = {},
) {
const {
clone = false,
passive = false,
eventName,
deep = false,
defaultValue,
} = options
const vm = getCurrentInstance()
// @ts-expect-error mis-alignment with @vue/composition-api
const _emit = emit || vm?.emit || vm?.$emit?.bind(vm) || vm?.proxy?.$emit?.bind(vm?.proxy)
let event: string | undefined = eventName
if (!key) {
if (isVue2) {
const modelOptions = vm?.proxy?.$options?.model
key = modelOptions?.value || 'value' as K
if (!eventName)
event = modelOptions?.event || 'input'
}
else {
key = 'modelValue' as K
}
}
event = eventName || event || `update:${key!.toString()}`
const cloneFn = (val: P[K]) => !clone
? val
: isFunction(clone)
? clone(val)
: cloneFnJSON(val)
const getValue = () => isDef(props[key!])
? cloneFn(props[key!])
: defaultValue
if (passive) {
const initialValue = getValue()
const proxy = ref<P[K]>(initialValue!)
watch(
() => props[key!],
v => proxy.value = cloneFn(v) as UnwrapRef<P[K]>,
)
watch(
proxy,
(v) => {
if (v !== props[key!] || deep)
_emit(event, v)
},
{ deep },
)
return proxy
}
else {
return computed<P[K]>({
get() {
return getValue()!
},
set(value) {
_emit(event, value)
},
})
}
}