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