From c00abf5ad767a2121eb486ca4a7f3cd665089665 Mon Sep 17 00:00:00 2001 From: mm <25961416+mlmoravek@users.noreply.github.com> Date: Thu, 4 Jul 2024 16:42:52 +0200 Subject: [PATCH] revert(defineModel): reuse `useVModel` composable for generics where the type is `T | T[]` (#998) --- .../components/autocomplete/Autocomplete.vue | 9 +-- .../src/components/dropdown/Dropdown.vue | 14 ++-- packages/oruga/src/components/input/Input.vue | 23 ++++--- .../components/select/examples/grouped.vue | 2 +- .../oruga/src/components/switch/Switch.vue | 5 +- .../components/switch/tests/switch.test.ts | 9 ++- .../src/components/taginput/Taginput.vue | 4 +- packages/oruga/src/composables/index.ts | 1 + packages/oruga/src/composables/useVModel.ts | 64 +++++++++++++++++++ 9 files changed, 105 insertions(+), 26 deletions(-) create mode 100644 packages/oruga/src/composables/useVModel.ts diff --git a/packages/oruga/src/components/autocomplete/Autocomplete.vue b/packages/oruga/src/components/autocomplete/Autocomplete.vue index 72dbcf28e..9691b7429 100644 --- a/packages/oruga/src/components/autocomplete/Autocomplete.vue +++ b/packages/oruga/src/components/autocomplete/Autocomplete.vue @@ -60,10 +60,7 @@ const props = defineProps({ default: undefined, }, /** The value of the inner input, use v-model:input to make it two-way binding */ - input: { - type: String, - default: "", - }, + input: { type: String, default: "" }, /** * Options / suggestions * @type string[]|object[] @@ -694,9 +691,9 @@ function handleBlur(event: Event): void { } /** emit input change event */ -function onInput(value: string): void { +function onInput(value: string | number): void { if (props.keepFirst && !selectedOption.value) hoverFirstOption(); - emits("input", value); + emits("input", String(value)); checkHtml5Validity(); } diff --git a/packages/oruga/src/components/dropdown/Dropdown.vue b/packages/oruga/src/components/dropdown/Dropdown.vue index f6904b670..b5402dda0 100644 --- a/packages/oruga/src/components/dropdown/Dropdown.vue +++ b/packages/oruga/src/components/dropdown/Dropdown.vue @@ -22,6 +22,7 @@ import { useMatchMedia, useEventListener, useClickOutside, + useVModel, } from "@/composables"; import type { DropdownComponent } from "./types"; @@ -156,7 +157,7 @@ const props = defineProps({ */ ariaRole: { type: String, - default: getOption("dropdown.ariaRole", "list"), + default: () => getOption("dropdown.ariaRole", "list"), validator: (value: string) => ["list", "listbox", "menu", "dialog"].indexOf(value) > -1, }, @@ -275,7 +276,8 @@ const emits = defineEmits<{ }>(); /** The selected item value */ -const vmodel = defineModel({ default: undefined }); +// const vmodel = defineModel({ default: undefined }); +const vmodel = useVModel(); /** The active state of the dropdown, use v-model:active to make it two-way binding */ const isActive = defineModel("active", { default: false }); @@ -468,21 +470,21 @@ function selectItem(value: T): void { if (vmodel.value && Array.isArray(vmodel.value)) { if (vmodel.value.indexOf(value) === -1) { // add a value - vmodel.value = [...vmodel.value, value] as T; + vmodel.value = [...vmodel.value, value]; } else { // remove a value - vmodel.value = vmodel.value.filter((val) => val !== value) as T; + vmodel.value = vmodel.value.filter((val) => val !== value); } } else { // init new value array - vmodel.value = [value] as T; + vmodel.value = [value]; } // emit change after vmodel has changed nextTick(() => emits("change", vmodel.value)); } else { if (vmodel.value !== value) { // update a single value - vmodel.value = value as T; + vmodel.value = value; // emit change after vmodel has changed nextTick(() => emits("change", vmodel.value)); } diff --git a/packages/oruga/src/components/input/Input.vue b/packages/oruga/src/components/input/Input.vue index 604ff037b..0644665da 100644 --- a/packages/oruga/src/components/input/Input.vue +++ b/packages/oruga/src/components/input/Input.vue @@ -3,7 +3,7 @@ lang="ts" generic=" IsNumber extends boolean, - ModelValue extends IsNumber extends true ? number : string + T extends IsNumber extends true ? number : string "> import { ref, @@ -19,7 +19,12 @@ import OIcon from "../icon/Icon.vue"; import { getOption } from "@/utils/config"; import { uuid } from "@/utils/helpers"; -import { defineClasses, useDebounce, useInputHandler } from "@/composables"; +import { + defineClasses, + useDebounce, + useInputHandler, + useVModel, +} from "@/composables"; import { injectField } from "../field/fieldInjection"; @@ -43,7 +48,7 @@ const props = defineProps({ * @type string | number */ modelValue: { - type: [Number, String] as unknown as PropType, + type: [Number, String] as unknown as PropType, default: undefined, }, /** @type boolean */ @@ -226,13 +231,13 @@ const emits = defineEmits<{ * modelValue prop two-way binding * @param value {string | number} updated modelValue prop */ - (e: "update:modelValue", value: ModelValue): void; + (e: "update:modelValue", value: T): void; /** * on input change event * @param value {string | number} input value * @param event {Event} native event */ - (e: "input", value: ModelValue, event: Event): void; + (e: "input", value: T, event: Event): void; /** * on input focus event * @param event {Event} native event @@ -283,7 +288,8 @@ const { // inject parent field component if used inside one const { parentField, statusVariant, statusVariantIcon } = injectField(); -const vmodel = defineModel({ default: undefined }); +// const vmodel = defineModel({ default: undefined }); +const vmodel = useVModel(); // if id is given set as `for` property on o-field wrapper if (props.id) parentField?.value?.setInputId(props.id); @@ -350,7 +356,7 @@ watch( function onInput(event: Event): void { const value = (event.target as HTMLInputElement).value; - const input = (props.number ? Number(value) : String(value)) as ModelValue; + const input = (props.number ? Number(value) : String(value)) as T; emits("input", input, event); } @@ -386,8 +392,7 @@ function iconClick(event: Event): void { function rightIconClick(event: Event): void { if (props.passwordReveal) togglePasswordVisibility(); - else if (props.clearable) - vmodel.value = (props.number ? 0 : "") as ModelValue; + else if (props.clearable) vmodel.value = (props.number ? 0 : "") as T; if (props.iconRightClickable) { emits("icon-right-click", event); nextTick(() => setFocus()); diff --git a/packages/oruga/src/components/select/examples/grouped.vue b/packages/oruga/src/components/select/examples/grouped.vue index bad6169ec..f5b9553eb 100644 --- a/packages/oruga/src/components/select/examples/grouped.vue +++ b/packages/oruga/src/components/select/examples/grouped.vue @@ -1,7 +1,7 @@