Skip to content

Commit d92fe0a

Browse files
committed
feat(Input,Textarea): add defaultValue prop
1 parent 57a5037 commit d92fe0a

File tree

2 files changed

+29
-24
lines changed

2 files changed

+29
-24
lines changed

src/runtime/components/Input.vue

Lines changed: 14 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,15 @@
11
<script lang="ts">
22
import type { InputHTMLAttributes } from 'vue'
33
import type { AppConfig } from '@nuxt/schema'
4+
import { useVModel } from '@vueuse/core'
45
import theme from '#build/ui/input'
56
import type { UseComponentIconsProps } from '../composables/useComponentIcons'
67
import type { AvatarProps } from '../types'
78
import type { AcceptableValue, ComponentConfig } from '../types/utils'
89
910
type Input = ComponentConfig<typeof theme, AppConfig, 'input'>
1011
11-
export interface InputProps extends UseComponentIconsProps {
12+
export interface InputProps<T extends AcceptableValue> extends UseComponentIconsProps {
1213
/**
1314
* The element or component this component should render as.
1415
* @defaultValue 'div'
@@ -38,6 +39,8 @@ export interface InputProps extends UseComponentIconsProps {
3839
disabled?: boolean
3940
/** Highlight the ring color like a focus state. */
4041
highlight?: boolean
42+
modelValue?: T
43+
defaultValue?: T
4144
modelModifiers?: {
4245
string?: boolean
4346
number?: boolean
@@ -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: 15 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -4,12 +4,13 @@ import theme from '#build/ui/textarea'
44
import type { UseComponentIconsProps } from '../composables/useComponentIcons'
55
import type { AvatarProps } from '../types'
66
import type { ComponentConfig } from '../types/utils'
7+
import { useVModel } from '@vueuse/core'
78
89
type Textarea = ComponentConfig<typeof theme, AppConfig, 'textarea'>
910
1011
type TextareaValue = string | number | null
1112
12-
export interface TextareaProps extends UseComponentIconsProps {
13+
export interface TextareaProps<T extends AcceptableValue> extends UseComponentIconsProps {
1314
/**
1415
* The element or component this component should render as.
1516
* @defaultValue 'div'
@@ -41,8 +42,11 @@ export interface TextareaProps extends UseComponentIconsProps {
4142
maxrows?: number
4243
/** Highlight the ring color like a focus state. */
4344
highlight?: boolean
45+
modelValue?: T
46+
defaultValue?: T
4447
modelModifiers?: {
4548
string?: boolean
49+
number?: boolean
4650
trim?: boolean
4751
lazy?: boolean
4852
nullify?: boolean
@@ -66,7 +70,7 @@ export interface TextareaSlots {
6670

6771
<script setup lang="ts" generic="T extends TextareaValue">
6872
import { ref, computed, onMounted, nextTick, watch } from 'vue'
69-
import { Primitive } from 'reka-ui'
73+
import { Primitive, type AcceptableValue } from 'reka-ui'
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)