|
1 |
| -import { computed } from 'vue' |
| 1 | +import { computed, ref } from 'vue' |
2 | 2 |
|
3 | 3 | const formElements = ['input', 'select', 'textarea', 'fieldset', 'datalist', 'option', 'optgroup']
|
4 | 4 |
|
5 |
| -export const useVModel = ( |
6 |
| - elementTag: string, |
7 |
| - // eslint-disable-next-line @typescript-eslint/no-explicit-any |
8 |
| - modelValue: any, |
9 |
| - emit: CallableFunction, |
10 |
| - // eslint-disable-next-line @typescript-eslint/no-explicit-any |
11 |
| - attrs: any, |
12 |
| -) => { |
13 |
| - let vmodelAttrs = {} |
14 |
| - const handleMultipleCheckbox = (value: unknown) => { |
15 |
| - const currentModelValue = [...modelValue] |
16 |
| - if (currentModelValue.includes(value)) { |
17 |
| - currentModelValue.splice(currentModelValue.indexOf(value), 1) |
18 |
| - return currentModelValue |
19 |
| - } else { |
20 |
| - return [...currentModelValue, value] |
21 |
| - } |
22 |
| - } |
23 |
| - const handleInput = (e: Event) => { |
24 |
| - emit( |
25 |
| - 'update:modelValue', |
26 |
| - attrs.type === 'checkbox' && Array.isArray(modelValue) |
27 |
| - ? handleMultipleCheckbox((e?.currentTarget as HTMLInputElement)?.value) |
28 |
| - : typeof modelValue === 'boolean' |
29 |
| - ? (e?.currentTarget as HTMLInputElement)?.checked |
30 |
| - : (e?.currentTarget as HTMLInputElement)?.value, |
31 |
| - ) |
32 |
| - emit('input', e, (e?.currentTarget as HTMLInputElement)?.value) |
33 |
| - emit( |
34 |
| - 'change', |
35 |
| - e, |
36 |
| - typeof modelValue === 'boolean' |
37 |
| - ? (e?.currentTarget as HTMLInputElement)?.checked |
38 |
| - : (e?.currentTarget as HTMLInputElement)?.value, |
39 |
| - ) |
| 5 | +function handleMultipleCheckbox<TModelValue extends Array<any>>( |
| 6 | + value: unknown, |
| 7 | + modelValue: TModelValue, |
| 8 | +) { |
| 9 | + const currentModelValue = [...modelValue] |
| 10 | + // If the value is already checked, uncheck it |
| 11 | + // else, add it to the checked array. |
| 12 | + if (currentModelValue.includes(value)) { |
| 13 | + currentModelValue.splice(currentModelValue.indexOf(value), 1) |
| 14 | + return currentModelValue |
| 15 | + } else { |
| 16 | + return [...currentModelValue, value] |
40 | 17 | }
|
| 18 | +} |
| 19 | + |
| 20 | +/** |
| 21 | + * Function that emits the right events when using v-model. |
| 22 | + */ |
| 23 | +function handleInput< |
| 24 | + TEmit extends CallableFunction, |
| 25 | + TModelValue extends Array<any>, |
| 26 | + TAttrs extends Record<string, unknown>, |
| 27 | +>(e: Event, emit: TEmit, modelValue: TModelValue, attrs: TAttrs) { |
| 28 | + emit( |
| 29 | + 'update:modelValue', |
| 30 | + attrs.type === 'checkbox' && Array.isArray(modelValue) |
| 31 | + ? handleMultipleCheckbox((e?.currentTarget as HTMLInputElement)?.value, modelValue) |
| 32 | + : typeof modelValue === 'boolean' |
| 33 | + ? (e?.currentTarget as HTMLInputElement)?.checked |
| 34 | + : (e?.currentTarget as HTMLInputElement)?.value, |
| 35 | + ) |
| 36 | + emit('input', e, (e?.currentTarget as HTMLInputElement)?.value) |
| 37 | + emit( |
| 38 | + 'change', |
| 39 | + e, |
| 40 | + typeof modelValue === 'boolean' |
| 41 | + ? (e?.currentTarget as HTMLInputElement)?.checked |
| 42 | + : (e?.currentTarget as HTMLInputElement)?.value, |
| 43 | + ) |
| 44 | +} |
| 45 | + |
| 46 | +export function getAttributes< |
| 47 | + TModelValue extends Array<any>, |
| 48 | + TEmit extends CallableFunction, |
| 49 | + TAttrs extends Record<string, unknown>, |
| 50 | +>(elementTag: string, modelValue: TModelValue, emit: TEmit, attrs: TAttrs) { |
| 51 | + const val = ref<Record<string, unknown>>({ value: modelValue }) |
| 52 | + const attributes = ref({ ...attrs }) |
41 | 53 |
|
42 |
| - if (formElements.includes(elementTag)) { |
43 |
| - let val: Record<string, unknown> = { value: modelValue } |
| 54 | + // Only do this if v-model directive is provided, otherwise return user props |
| 55 | + if (formElements.includes(elementTag) && (modelValue !== null || modelValue !== undefined)) { |
44 | 56 | if (elementTag === 'input' && (attrs.type === 'checkbox' || attrs.type === 'radio')) {
|
45 | 57 | const isChecked = computed(() =>
|
| 58 | + // If it's a boolean, it's probably a single checkbox or a radio button |
| 59 | + // If it's not, it's multiple checkboxes |
46 | 60 | typeof modelValue === 'boolean' ? modelValue : modelValue.includes(attrs.value),
|
47 | 61 | )
|
48 |
| - val = { |
| 62 | + |
| 63 | + val.value = { |
49 | 64 | checked: isChecked.value,
|
50 | 65 | }
|
51 | 66 | }
|
52 |
| - vmodelAttrs = { |
53 |
| - ...val, |
54 |
| - onInput: handleInput, |
| 67 | + |
| 68 | + attributes.value = { |
| 69 | + ...val.value, |
| 70 | + onInput: (e: Event) => handleInput(e, emit, modelValue, attrs), |
| 71 | + ...attrs, |
55 | 72 | }
|
56 | 73 | }
|
57 | 74 |
|
58 |
| - return vmodelAttrs |
| 75 | + return attributes.value |
59 | 76 | }
|
0 commit comments