From 51f151643ead33bd67568b8fcd9107bd8fcdc1a1 Mon Sep 17 00:00:00 2001 From: max Date: Tue, 24 Oct 2023 10:42:06 +0200 Subject: [PATCH 1/4] (Input/Textarea) add v-model properties --- src/runtime/components/forms/Input.vue | 41 ++++++++++++++++++++-- src/runtime/components/forms/Textarea.vue | 42 ++++++++++++++++++++--- src/runtime/utils/index.ts | 9 +++++ 3 files changed, 85 insertions(+), 7 deletions(-) diff --git a/src/runtime/components/forms/Input.vue b/src/runtime/components/forms/Input.vue index 8f77e2534b..e0a0ec739a 100644 --- a/src/runtime/components/forms/Input.vue +++ b/src/runtime/components/forms/Input.vue @@ -14,6 +14,7 @@ v-bind="attrs" @input="onInput" @blur="onBlur" + @change="onChange" > @@ -38,7 +39,7 @@ import { twMerge, twJoin } from 'tailwind-merge' import UIcon from '../elements/Icon.vue' import { useUI } from '../../composables/useUI' import { useFormGroup } from '../../composables/useFormGroup' -import { mergeConfig } from '../../utils' +import { mergeConfig, looseToNumber } from '../../utils' import type { NestedKeyOf, Strategy } from '../../types' // @ts-expect-error import appConfig from '#build/app.config' @@ -156,6 +157,10 @@ export default defineComponent({ ui: { type: Object as PropType>, default: undefined + }, + modelModifiers: { + type: Object as PropType<{ trim?: boolean, lazy?: boolean, number?: boolean }>, + default: () => ({}) } }, emits: ['update:modelValue', 'blur'], @@ -172,11 +177,40 @@ export default defineComponent({ } } - const onInput = (event: InputEvent) => { - emit('update:modelValue', (event.target as HTMLInputElement).value) + // Custom function to handle the v-model properties + const updateInput = (value: string) => { + + if (props.modelModifiers.trim) { + value = value.trim() + } + + if (props.modelModifiers.number) { + value = looseToNumber(value) + } + + emit('update:modelValue', value) emitFormInput() } + const onInput = (event: InputEvent) => { + if (!props.modelModifiers.lazy) { + updateInput((event.target as HTMLInputElement).value) + } + } + + const onChange = (event: InputEvent) => { + const value = (event.target as HTMLInputElement).value + + if (props.modelModifiers.lazy) { + updateInput(value) + } + + // Update trimmed input so that it has same behaviour as native input https://github.com/vuejs/core/blob/5ea8a8a4fab4e19a71e123e4d27d051f5e927172/packages/runtime-dom/src/directives/vModel.ts#L63 + if (props.modelModifiers.trim) { + (event.target as HTMLInputElement).value = value.trim() + } + } + const onBlur = (event: FocusEvent) => { emitFormBlur() emit('blur', event) @@ -280,6 +314,7 @@ export default defineComponent({ trailingIconClass, trailingWrapperIconClass, onInput, + onChange, onBlur } } diff --git a/src/runtime/components/forms/Textarea.vue b/src/runtime/components/forms/Textarea.vue index 54cc838b64..0bfb7b9297 100644 --- a/src/runtime/components/forms/Textarea.vue +++ b/src/runtime/components/forms/Textarea.vue @@ -14,6 +14,7 @@ v-bind="attrs" @input="onInput" @blur="onBlur" + @change="onChange" /> @@ -24,7 +25,7 @@ import type { PropType } from 'vue' import { twMerge, twJoin } from 'tailwind-merge' import { useUI } from '../../composables/useUI' import { useFormGroup } from '../../composables/useFormGroup' -import { mergeConfig } from '../../utils' +import { mergeConfig, looseToNumber } from '../../utils' import type { NestedKeyOf, Strategy } from '../../types' // @ts-expect-error import appConfig from '#build/app.config' @@ -119,6 +120,10 @@ export default defineComponent({ ui: { type: Object as PropType>, default: undefined + }, + modelModifiers: { + type: Object as PropType<{ trim?: boolean, lazy?: boolean, number?: boolean }>, + default: () => ({}) } }, emits: ['update:modelValue', 'blur'], @@ -157,13 +162,41 @@ export default defineComponent({ } } - const onInput = (event: InputEvent) => { - autoResize() + // Custom function to handle the v-model properties + const updateInput = (value: string) => { + if (props.modelModifiers.trim) { + value = value.trim() + } + + if (props.modelModifiers.number) { + value = looseToNumber(value) + } - emit('update:modelValue', (event.target as HTMLInputElement).value) + emit('update:modelValue', value) emitFormInput() } + const onInput = (event: InputEvent) => { + if (!props.modelModifiers.lazy) { + autoResize() + updateInput((event.target as HTMLInputElement).value) + } + } + + const onChange = (event: InputEvent) => { + const value = (event.target as HTMLInputElement).value + + if (props.modelModifiers.lazy) { + autoResize() + updateInput(value) + } + + // Update trimmed input so that it has same behaviour as native input + if (props.modelModifiers.trim) { + (event.target as HTMLInputElement).value = value.trim() + } + } + const onBlur = (event: FocusEvent) => { emit('blur', event) emitFormBlur() @@ -211,6 +244,7 @@ export default defineComponent({ // eslint-disable-next-line vue/no-dupe-keys textareaClass, onInput, + onChange, onBlur } } diff --git a/src/runtime/utils/index.ts b/src/runtime/utils/index.ts index 9e3e0d0746..1af846261f 100644 --- a/src/runtime/utils/index.ts +++ b/src/runtime/utils/index.ts @@ -56,4 +56,13 @@ export function getSlotsChildren (slots: any) { return children } +/** + * "123-foo" will be parsed to 123 + * This is used for the .number modifier in v-model + */ +export function looseToNumber (val: any): any { + const n = parseFloat(val) + return isNaN(n) ? val : n +} + export * from './lodash' From c6ec1418d449f40691fccd2048438add5d8c24a9 Mon Sep 17 00:00:00 2001 From: max Date: Tue, 24 Oct 2023 11:04:18 +0200 Subject: [PATCH 2/4] enable number modifier if type=number --- src/runtime/components/forms/Input.vue | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/runtime/components/forms/Input.vue b/src/runtime/components/forms/Input.vue index e0a0ec739a..4cd643e9ac 100644 --- a/src/runtime/components/forms/Input.vue +++ b/src/runtime/components/forms/Input.vue @@ -184,7 +184,7 @@ export default defineComponent({ value = value.trim() } - if (props.modelModifiers.number) { + if (props.modelModifiers.number || props.type === 'number') { value = looseToNumber(value) } From 774c30a28ff63d59b6a838465f3bd5b98de26596 Mon Sep 17 00:00:00 2001 From: max <57699643+maxsteinwand@users.noreply.github.com> Date: Sun, 29 Oct 2023 13:45:15 +0100 Subject: [PATCH 3/4] Autoresize onInput Autoresize in onInput without .lazy --- src/runtime/components/forms/Textarea.vue | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/src/runtime/components/forms/Textarea.vue b/src/runtime/components/forms/Textarea.vue index 0bfb7b9297..b5a16402ee 100644 --- a/src/runtime/components/forms/Textarea.vue +++ b/src/runtime/components/forms/Textarea.vue @@ -177,8 +177,8 @@ export default defineComponent({ } const onInput = (event: InputEvent) => { - if (!props.modelModifiers.lazy) { - autoResize() + autoResize() + if (!props.modelModifiers.lazy) { updateInput((event.target as HTMLInputElement).value) } } @@ -187,7 +187,6 @@ export default defineComponent({ const value = (event.target as HTMLInputElement).value if (props.modelModifiers.lazy) { - autoResize() updateInput(value) } From f6bbb4cecf446a6ecfa9aca3397603d8002967f8 Mon Sep 17 00:00:00 2001 From: max <57699643+maxsteinwand@users.noreply.github.com> Date: Mon, 30 Oct 2023 07:40:16 +0000 Subject: [PATCH 4/4] typcheck fixes --- src/runtime/components/forms/Input.vue | 13 ++++++++----- src/runtime/components/forms/Textarea.vue | 13 ++++++++----- 2 files changed, 16 insertions(+), 10 deletions(-) diff --git a/src/runtime/components/forms/Input.vue b/src/runtime/components/forms/Input.vue index 4cd643e9ac..94e856d4c4 100644 --- a/src/runtime/components/forms/Input.vue +++ b/src/runtime/components/forms/Input.vue @@ -37,6 +37,7 @@ import { ref, computed, toRef, onMounted, defineComponent } from 'vue' import type { PropType } from 'vue' import { twMerge, twJoin } from 'tailwind-merge' import UIcon from '../elements/Icon.vue' +import { defu } from 'defu' import { useUI } from '../../composables/useUI' import { useFormGroup } from '../../composables/useFormGroup' import { mergeConfig, looseToNumber } from '../../utils' @@ -169,6 +170,8 @@ export default defineComponent({ const { emitFormBlur, emitFormInput, size, color, inputId, name } = useFormGroup(props, config) + const modelModifiers = ref(defu({}, props.modelModifiers, { trim: false, lazy: false, number: false })) + const input = ref(null) const autoFocus = () => { @@ -180,11 +183,11 @@ export default defineComponent({ // Custom function to handle the v-model properties const updateInput = (value: string) => { - if (props.modelModifiers.trim) { + if (modelModifiers.value.trim) { value = value.trim() } - if (props.modelModifiers.number || props.type === 'number') { + if (modelModifiers.value.number || props.type === 'number') { value = looseToNumber(value) } @@ -193,7 +196,7 @@ export default defineComponent({ } const onInput = (event: InputEvent) => { - if (!props.modelModifiers.lazy) { + if (!modelModifiers.value.lazy) { updateInput((event.target as HTMLInputElement).value) } } @@ -201,12 +204,12 @@ export default defineComponent({ const onChange = (event: InputEvent) => { const value = (event.target as HTMLInputElement).value - if (props.modelModifiers.lazy) { + if (modelModifiers.value.lazy) { updateInput(value) } // Update trimmed input so that it has same behaviour as native input https://github.com/vuejs/core/blob/5ea8a8a4fab4e19a71e123e4d27d051f5e927172/packages/runtime-dom/src/directives/vModel.ts#L63 - if (props.modelModifiers.trim) { + if (modelModifiers.value.trim) { (event.target as HTMLInputElement).value = value.trim() } } diff --git a/src/runtime/components/forms/Textarea.vue b/src/runtime/components/forms/Textarea.vue index b5a16402ee..d3dff6a31e 100644 --- a/src/runtime/components/forms/Textarea.vue +++ b/src/runtime/components/forms/Textarea.vue @@ -23,6 +23,7 @@ import { ref, computed, toRef, watch, onMounted, nextTick, defineComponent } from 'vue' import type { PropType } from 'vue' import { twMerge, twJoin } from 'tailwind-merge' +import { defu } from 'defu' import { useUI } from '../../composables/useUI' import { useFormGroup } from '../../composables/useFormGroup' import { mergeConfig, looseToNumber } from '../../utils' @@ -132,6 +133,8 @@ export default defineComponent({ const { emitFormBlur, emitFormInput, inputId, color, size, name } = useFormGroup(props, config) + const modelModifiers = ref(defu({}, props.modelModifiers, { trim: false, lazy: false, number: false })) + const textarea = ref(null) const autoFocus = () => { @@ -164,11 +167,11 @@ export default defineComponent({ // Custom function to handle the v-model properties const updateInput = (value: string) => { - if (props.modelModifiers.trim) { + if (modelModifiers.value.trim) { value = value.trim() } - if (props.modelModifiers.number) { + if (modelModifiers.value.number) { value = looseToNumber(value) } @@ -178,7 +181,7 @@ export default defineComponent({ const onInput = (event: InputEvent) => { autoResize() - if (!props.modelModifiers.lazy) { + if (!modelModifiers.value.lazy) { updateInput((event.target as HTMLInputElement).value) } } @@ -186,12 +189,12 @@ export default defineComponent({ const onChange = (event: InputEvent) => { const value = (event.target as HTMLInputElement).value - if (props.modelModifiers.lazy) { + if (modelModifiers.value.lazy) { updateInput(value) } // Update trimmed input so that it has same behaviour as native input - if (props.modelModifiers.trim) { + if (modelModifiers.value.trim) { (event.target as HTMLInputElement).value = value.trim() } }