Skip to content
This repository was archived by the owner on Oct 23, 2023. It is now read-only.

Commit 23c540c

Browse files
authored
fix(vue): handle input without vmodel
fix(vue): handle input without vmodel
2 parents de7eaf6 + 4decb22 commit 23c540c

File tree

4 files changed

+72
-54
lines changed

4 files changed

+72
-54
lines changed

.changeset/fast-ravens-enjoy.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
---
2+
'@polymorphic-factory/vue': patch
3+
---
4+
5+
fix polymorphic input

packages/vue/src/polymorphic-factory.tsx

Lines changed: 4 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@ import {
1010
computed,
1111
} from 'vue'
1212
import type { IntrinsicElementAttributes } from './dom.types'
13-
import { useVModel } from './use-v-model'
13+
import { getAttributes } from './use-v-model'
1414

1515
export type DOMElements = keyof IntrinsicElementAttributes
1616

@@ -59,15 +59,11 @@ function defaultStyled(originalComponent: ElementType) {
5959
emits: ['update:modelValue', 'input', 'change'],
6060
setup(props, { slots, attrs, emit }) {
6161
const Component = props.as || originalComponent
62-
const vmodelAttrs = computed(() =>
63-
useVModel(Component as string, props.modelValue, emit, attrs),
62+
const componentAttrs = computed(() =>
63+
getAttributes(Component as string, props.modelValue, emit, attrs),
6464
)
6565

66-
return () => (
67-
<Component {...vmodelAttrs.value} {...attrs}>
68-
{slots?.default?.()}
69-
</Component>
70-
)
66+
return () => <Component {...componentAttrs.value}>{() => slots?.default?.()}</Component>
7167
},
7268
}) as ComponentWithAs<never>
7369
}

packages/vue/src/use-v-model.ts

Lines changed: 60 additions & 43 deletions
Original file line numberDiff line numberDiff line change
@@ -1,59 +1,76 @@
1-
import { computed } from 'vue'
1+
import { computed, ref } from 'vue'
22

33
const formElements = ['input', 'select', 'textarea', 'fieldset', 'datalist', 'option', 'optgroup']
44

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]
4017
}
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 })
4153

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)) {
4456
if (elementTag === 'input' && (attrs.type === 'checkbox' || attrs.type === 'radio')) {
4557
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
4660
typeof modelValue === 'boolean' ? modelValue : modelValue.includes(attrs.value),
4761
)
48-
val = {
62+
63+
val.value = {
4964
checked: isChecked.value,
5065
}
5166
}
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,
5572
}
5673
}
5774

58-
return vmodelAttrs
75+
return attributes.value
5976
}

packages/vue/test/poly-test.vue

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -20,9 +20,9 @@ const singleCheckbox = ref(true)
2020
<poly.p>selected : {{ selectValue }}</poly.p>
2121

2222
<poly.div>
23-
<poly.input type="radio" v-model="radioValue" value="vue"></poly.input>
24-
<poly.input type="radio" value="react" v-model="radioValue" data-testid="react-radio"></poly.input>
25-
<poly.input type="radio" value="angular" v-model="radioValue"></poly.input>
23+
<poly.input name="framework" type="radio" v-model="radioValue" value="vue"></poly.input>
24+
<poly.input name="framework" type="radio" value="react" v-model="radioValue" data-testid="react-radio"></poly.input>
25+
<poly.input name="framework" type="radio" value="angular" v-model="radioValue"></poly.input>
2626
</poly.div>
2727
<poly.p>Radio: {{ radioValue }}</poly.p>
2828

0 commit comments

Comments
 (0)