Skip to content

Commit fb9e7bb

Browse files
feat(Input/Textarea): add default-value prop (#4404)
Co-authored-by: Benjamin Canac <canacb1@gmail.com>
1 parent 69a7b95 commit fb9e7bb

File tree

2 files changed

+28
-23
lines changed

2 files changed

+28
-23
lines changed

‎src/runtime/components/Input.vue

Lines changed: 14 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@ import type { AcceptableValue, ComponentConfig } from '../types/utils'
88
99
type Input = ComponentConfig<typeof theme, AppConfig, 'input'>
1010
11-
export interface InputProps extends UseComponentIconsProps {
11+
export interface InputProps<T extends AcceptableValue = AcceptableValue> extends UseComponentIconsProps {
1212
/**
1313
* The element or component this component should render as.
1414
* @defaultValue 'div'
@@ -38,6 +38,8 @@ export interface InputProps extends UseComponentIconsProps {
3838
disabled?: boolean
3939
/** Highlight the ring color like a focus state. */
4040
highlight?: boolean
41+
modelValue?: T
42+
defaultValue?: T
4143
modelModifiers?: {
4244
string?: boolean
4345
number?: boolean
@@ -65,6 +67,7 @@ export interface InputSlots {
6567
<script setup lang="ts" generic="T extends AcceptableValue">
6668
import { ref, computed, onMounted } from 'vue'
6769
import { Primitive } from 'reka-ui'
70+
import { useVModel } from '@vueuse/core'
6871
import { useAppConfig } from '#imports'
6972
import { useButtonGroup } from '../composables/useButtonGroup'
7073
import { useComponentIcons } from '../composables/useComponentIcons'
@@ -76,21 +79,20 @@ import UAvatar from './Avatar.vue'
7679
7780
defineOptions({ inheritAttrs: false })
7881
79-
const props = withDefaults(defineProps<InputProps>(), {
82+
const props = withDefaults(defineProps<InputProps<T>>(), {
8083
type: 'text',
8184
autocomplete: 'off',
8285
autofocusDelay: 0
8386
})
8487
const emits = defineEmits<InputEmits<T>>()
8588
const slots = defineSlots<InputSlots>()
8689
87-
// eslint-disable-next-line vue/no-dupe-keys
88-
const [modelValue, modelModifiers] = defineModel<T>()
90+
const modelValue = useVModel<InputProps<T>, 'modelValue', 'update:modelValue'>(props, 'modelValue', emits, { defaultValue: props.defaultValue })
8991
9092
const appConfig = useAppConfig() as Input['AppConfig']
9193
92-
const { emitFormBlur, emitFormInput, emitFormChange, size: formGroupSize, color, id, name, highlight, disabled, emitFormFocus, ariaAttrs } = useFormField<InputProps>(props, { deferInputValidation: true })
93-
const { orientation, size: buttonGroupSize } = useButtonGroup<InputProps>(props)
94+
const { emitFormBlur, emitFormInput, emitFormChange, size: formGroupSize, color, id, name, highlight, disabled, emitFormFocus, ariaAttrs } = useFormField<InputProps<T>>(props, { deferInputValidation: true })
95+
const { orientation, size: buttonGroupSize } = useButtonGroup<InputProps<T>>(props)
9496
const { isLeading, isTrailing, leadingIconName, trailingIconName } = useComponentIcons(props)
9597
9698
const inputSize = computed(() => buttonGroupSize.value || formGroupSize.value)
@@ -111,15 +113,15 @@ const inputRef = ref<HTMLInputElement | null>(null)
111113
112114
// Custom function to handle the v-model properties
113115
function updateInput(value: string | null) {
114-
if (modelModifiers.trim) {
116+
if (props.modelModifiers?.trim) {
115117
value = value?.trim() ?? null
116118
}
117119
118-
if (modelModifiers.number || props.type === 'number') {
120+
if (props.modelModifiers?.number || props.type === 'number') {
119121
value = looseToNumber(value)
120122
}
121123
122-
if (modelModifiers.nullify) {
124+
if (props.modelModifiers?.nullify) {
123125
value ||= null
124126
}
125127
@@ -128,20 +130,20 @@ function updateInput(value: string | null) {
128130
}
129131
130132
function onInput(event: Event) {
131-
if (!modelModifiers.lazy) {
133+
if (!props.modelModifiers?.lazy) {
132134
updateInput((event.target as HTMLInputElement).value)
133135
}
134136
}
135137
136138
function onChange(event: Event) {
137139
const value = (event.target as HTMLInputElement).value
138140
139-
if (modelModifiers.lazy) {
141+
if (props.modelModifiers?.lazy) {
140142
updateInput(value)
141143
}
142144
143145
// Update trimmed input so that it has same behavior as native input https://github.com/vuejs/core/blob/5ea8a8a4fab4e19a71e123e4d27d051f5e927172/packages/runtime-dom/src/directives/vModel.ts#L63
144-
if (modelModifiers.trim) {
146+
if (props.modelModifiers?.trim) {
145147
(event.target as HTMLInputElement).value = value.trim()
146148
}
147149

‎src/runtime/components/Textarea.vue

Lines changed: 14 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@ type Textarea = ComponentConfig<typeof theme, AppConfig, 'textarea'>
99
1010
type TextareaValue = string | number | null
1111
12-
export interface TextareaProps extends UseComponentIconsProps {
12+
export interface TextareaProps<T extends TextareaValue = TextareaValue> extends UseComponentIconsProps {
1313
/**
1414
* The element or component this component should render as.
1515
* @defaultValue 'div'
@@ -41,8 +41,11 @@ export interface TextareaProps extends UseComponentIconsProps {
4141
maxrows?: number
4242
/** Highlight the ring color like a focus state. */
4343
highlight?: boolean
44+
modelValue?: T
45+
defaultValue?: T
4446
modelModifiers?: {
4547
string?: boolean
48+
number?: boolean
4649
trim?: boolean
4750
lazy?: boolean
4851
nullify?: boolean
@@ -67,6 +70,7 @@ export interface TextareaSlots {
6770
<script setup lang="ts" generic="T extends TextareaValue">
6871
import { ref, computed, onMounted, nextTick, watch } from 'vue'
6972
import { Primitive } from 'reka-ui'
73+
import { useVModel } from '@vueuse/core'
7074
import { useAppConfig } from '#imports'
7175
import { useComponentIcons } from '../composables/useComponentIcons'
7276
import { useFormField } from '../composables/useFormField'
@@ -77,7 +81,7 @@ import UAvatar from './Avatar.vue'
7781
7882
defineOptions({ inheritAttrs: false })
7983
80-
const props = withDefaults(defineProps<TextareaProps>(), {
84+
const props = withDefaults(defineProps<TextareaProps<T>>(), {
8185
rows: 3,
8286
maxrows: 0,
8387
autofocusDelay: 0,
@@ -86,12 +90,11 @@ const props = withDefaults(defineProps<TextareaProps>(), {
8690
const emits = defineEmits<TextareaEmits<T>>()
8791
const slots = defineSlots<TextareaSlots>()
8892
89-
// eslint-disable-next-line vue/no-dupe-keys
90-
const [modelValue, modelModifiers] = defineModel<T>()
93+
const modelValue = useVModel<TextareaProps<T>, 'modelValue', 'update:modelValue'>(props, 'modelValue', emits, { defaultValue: props.defaultValue })
9194
9295
const appConfig = useAppConfig() as Textarea['AppConfig']
9396
94-
const { emitFormFocus, emitFormBlur, emitFormInput, emitFormChange, size, color, id, name, highlight, disabled, ariaAttrs } = useFormField<TextareaProps>(props, { deferInputValidation: true })
97+
const { emitFormFocus, emitFormBlur, emitFormInput, emitFormChange, size, color, id, name, highlight, disabled, ariaAttrs } = useFormField<TextareaProps<T>>(props, { deferInputValidation: true })
9598
const { isLeading, isTrailing, leadingIconName, trailingIconName } = useComponentIcons(props)
9699
97100
const ui = computed(() => tv({ extend: tv(theme), ...(appConfig.ui?.textarea || {}) })({
@@ -109,15 +112,15 @@ const textareaRef = ref<HTMLTextAreaElement | null>(null)
109112
110113
// Custom function to handle the v-model properties
111114
function updateInput(value: string | null) {
112-
if (modelModifiers.trim) {
115+
if (props.modelModifiers?.trim) {
113116
value = value?.trim() ?? null
114117
}
115118
116-
if (modelModifiers.number) {
119+
if (props.modelModifiers?.number) {
117120
value = looseToNumber(value)
118121
}
119122
120-
if (modelModifiers.nullify) {
123+
if (props.modelModifiers?.nullify) {
121124
value ||= null
122125
}
123126
@@ -128,20 +131,20 @@ function updateInput(value: string | null) {
128131
function onInput(event: Event) {
129132
autoResize()
130133
131-
if (!modelModifiers.lazy) {
134+
if (!props.modelModifiers?.lazy) {
132135
updateInput((event.target as HTMLInputElement).value)
133136
}
134137
}
135138
136139
function onChange(event: Event) {
137140
const value = (event.target as HTMLInputElement).value
138141
139-
if (modelModifiers.lazy) {
142+
if (props.modelModifiers?.lazy) {
140143
updateInput(value)
141144
}
142145
143146
// Update trimmed textarea so that it has same behavior as native textarea https://github.com/vuejs/core/blob/5ea8a8a4fab4e19a71e123e4d27d051f5e927172/packages/runtime-dom/src/directives/vModel.ts#L63
144-
if (modelModifiers.trim) {
147+
if (props.modelModifiers?.trim) {
145148
(event.target as HTMLInputElement).value = value.trim()
146149
}
147150

0 commit comments

Comments
 (0)