refactor(@vben-core/form-ui): migrate to Zod 4 and TanStack Form (#8176)
* build(@vben-core/form-ui): update form validation dependencies * refactor(@vben-core/form-ui): replace vee-validate with TanStack Form * test(@vben-core/form-ui): cover TanStack Form migration * docs(@vben/docs): document Zod 4 form migration * refactor(project): update form adapters * refactor(project): migrate application form consumers * refactor(@vben/playground): migrate form examples * docs(@vben/docs): fix validate return type and required rule empty check in form docs * fix(@vben-core/form-ui): resolve oxlint and eslint errors - rewrite nested ternary expressions as if/else in form-field-array.vue and form-runtime.ts - sort @tanstack/vue-form before @vben-core/composables in package.json * chore: fix merge artifacts and formatting - move dependencies before devDependencies in root package.json - update preferences snapshot for widget positioning fields - format form-array demo README * refactor(@vben-core/form-ui): optimize form runtime and value flow - add fine-grained field selectors and atomic dependency resolution - expose raw and formatted value snapshots with focused regression coverage * fix(@vben/layouts): dispose sortable instance on unmount * docs(@vben/docs): document form runtime API changes - list added, changed, removed, and deprecated form APIs - document atomic dependencies and raw versus formatted values
This commit is contained in:
35
packages/@core/ui-kit/shadcn-ui/src/ui/form/FormField.vue
Normal file
35
packages/@core/ui-kit/shadcn-ui/src/ui/form/FormField.vue
Normal file
@@ -0,0 +1,35 @@
|
||||
<script setup lang="ts">
|
||||
import { provide, toRefs } from 'vue';
|
||||
|
||||
import { FORM_FIELD_INJECTION_KEY } from './injectionKeys';
|
||||
|
||||
const props = withDefaults(
|
||||
defineProps<{
|
||||
dirty?: boolean;
|
||||
error?: string;
|
||||
name: string;
|
||||
touched?: boolean;
|
||||
valid?: boolean;
|
||||
}>(),
|
||||
{
|
||||
dirty: false,
|
||||
error: undefined,
|
||||
touched: false,
|
||||
valid: true,
|
||||
},
|
||||
);
|
||||
|
||||
const { dirty, error, name, touched, valid } = toRefs(props);
|
||||
|
||||
provide(FORM_FIELD_INJECTION_KEY, {
|
||||
dirty,
|
||||
error,
|
||||
name,
|
||||
touched,
|
||||
valid,
|
||||
});
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<slot></slot>
|
||||
</template>
|
||||
@@ -1,27 +1,24 @@
|
||||
<script lang="ts" setup>
|
||||
import type { HTMLAttributes } from 'vue';
|
||||
|
||||
import { toValue } from 'vue';
|
||||
|
||||
import { cn } from '@vben-core/shared/utils';
|
||||
|
||||
import { ErrorMessage } from 'vee-validate';
|
||||
|
||||
import { useFormField } from './useFormField';
|
||||
|
||||
const props = defineProps<{
|
||||
class?: HTMLAttributes['class'];
|
||||
}>();
|
||||
|
||||
const { name, formMessageId } = useFormField();
|
||||
const { error, formMessageId } = useFormField();
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<ErrorMessage
|
||||
<p
|
||||
v-if="error"
|
||||
:id="formMessageId"
|
||||
data-slot="form-message"
|
||||
as="p"
|
||||
:name="toValue(name)"
|
||||
:class="cn('text-destructive text-sm', props.class)"
|
||||
/>
|
||||
>
|
||||
{{ error }}
|
||||
</p>
|
||||
</template>
|
||||
|
||||
@@ -1,11 +1,10 @@
|
||||
export { default as FormControl } from './FormControl.vue';
|
||||
export { default as FormDescription } from './FormDescription.vue';
|
||||
export { default as FormField } from './FormField.vue';
|
||||
export { default as FormItem } from './FormItem.vue';
|
||||
export { default as FormLabel } from './FormLabel.vue';
|
||||
export { default as FormMessage } from './FormMessage.vue';
|
||||
export { FORM_ITEM_INJECTION_KEY } from './injectionKeys';
|
||||
export {
|
||||
Form,
|
||||
Field as FormField,
|
||||
FieldArray as FormFieldArray,
|
||||
} from 'vee-validate';
|
||||
FORM_FIELD_INJECTION_KEY,
|
||||
FORM_ITEM_INJECTION_KEY,
|
||||
} from './injectionKeys';
|
||||
|
||||
@@ -1,3 +1,13 @@
|
||||
import type { InjectionKey } from 'vue';
|
||||
import type { InjectionKey, Ref } from 'vue';
|
||||
|
||||
export interface FormFieldContext {
|
||||
dirty: Readonly<Ref<boolean>>;
|
||||
error: Readonly<Ref<string | undefined>>;
|
||||
name: Readonly<Ref<string>>;
|
||||
touched: Readonly<Ref<boolean>>;
|
||||
valid: Readonly<Ref<boolean>>;
|
||||
}
|
||||
|
||||
export const FORM_ITEM_INJECTION_KEY = Symbol() as InjectionKey<string>;
|
||||
export const FORM_FIELD_INJECTION_KEY =
|
||||
Symbol() as InjectionKey<FormFieldContext>;
|
||||
|
||||
@@ -1,23 +1,26 @@
|
||||
import { computed, inject } from 'vue';
|
||||
|
||||
import { FieldContextKey } from 'vee-validate';
|
||||
|
||||
import { FORM_ITEM_INJECTION_KEY } from './injectionKeys';
|
||||
import {
|
||||
FORM_FIELD_INJECTION_KEY,
|
||||
FORM_ITEM_INJECTION_KEY,
|
||||
} from './injectionKeys';
|
||||
|
||||
export function useFormField() {
|
||||
const fieldContext = inject(FieldContextKey);
|
||||
const fieldContext = inject(FORM_FIELD_INJECTION_KEY);
|
||||
const fieldItemContext = inject(FORM_ITEM_INJECTION_KEY);
|
||||
|
||||
if (!fieldContext)
|
||||
throw new Error('useFormField should be used within <FormField>');
|
||||
if (!fieldItemContext)
|
||||
throw new Error('useFormField should be used within <FormItem>');
|
||||
|
||||
const { name, errorMessage: error, meta } = fieldContext;
|
||||
const { dirty, error, name, touched, valid } = fieldContext;
|
||||
const id = fieldItemContext;
|
||||
|
||||
const fieldState = {
|
||||
valid: computed(() => meta.valid),
|
||||
isDirty: computed(() => meta.dirty),
|
||||
isTouched: computed(() => meta.touched),
|
||||
valid: computed(() => valid.value),
|
||||
isDirty: computed(() => dirty.value),
|
||||
isTouched: computed(() => touched.value),
|
||||
error,
|
||||
};
|
||||
|
||||
|
||||
Reference in New Issue
Block a user