replace el-form

This commit is contained in:
pa
2026-01-12 22:42:54 +09:00
committed by Natsumi
parent 82bd985142
commit c814f8f60c
34 changed files with 1419 additions and 736 deletions
+17
View File
@@ -0,0 +1,17 @@
<script setup>
import { Slot } from 'reka-ui';
import { useFormField } from './useFormField';
const { error, formItemId, formDescriptionId, formMessageId } = useFormField();
</script>
<template>
<Slot
:id="formItemId"
data-slot="form-control"
:aria-describedby="!error ? `${formDescriptionId}` : `${formDescriptionId} ${formMessageId}`"
:aria-invalid="!!error">
<slot />
</Slot>
</template>
@@ -0,0 +1,17 @@
<script setup>
import { cn } from '@/lib/utils';
import { useFormField } from './useFormField';
const props = defineProps({
class: { type: null, required: false }
});
const { formDescriptionId } = useFormField();
</script>
<template>
<p :id="formDescriptionId" data-slot="form-description" :class="cn('text-muted-foreground text-sm', props.class)">
<slot />
</p>
</template>
+20
View File
@@ -0,0 +1,20 @@
<script setup>
import { cn } from '@/lib/utils';
import { provide } from 'vue';
import { useId } from 'reka-ui';
import { FORM_ITEM_INJECTION_KEY } from './injectionKeys';
const props = defineProps({
class: { type: null, required: false }
});
const id = useId();
provide(FORM_ITEM_INJECTION_KEY, id);
</script>
<template>
<div data-slot="form-item" :class="cn('grid gap-2', props.class)">
<slot />
</div>
</template>
+25
View File
@@ -0,0 +1,25 @@
<script setup>
import { Label } from '@/components/ui/label';
import { cn } from '@/lib/utils';
import { useFormField } from './useFormField';
const props = defineProps({
for: { type: String, required: false },
asChild: { type: Boolean, required: false },
as: { type: null, required: false },
class: { type: null, required: false }
});
const { error, formItemId } = useFormField();
</script>
<template>
<Label
data-slot="form-label"
:data-error="!!error"
:class="cn('data-[error=true]:text-destructive', props.class)"
:for="formItemId">
<slot />
</Label>
</template>
+22
View File
@@ -0,0 +1,22 @@
<script setup>
import { ErrorMessage } from 'vee-validate';
import { cn } from '@/lib/utils';
import { toValue } from 'vue';
import { useFormField } from './useFormField';
const props = defineProps({
class: { type: null, required: false }
});
const { name, formMessageId } = useFormField();
</script>
<template>
<ErrorMessage
:id="formMessageId"
data-slot="form-message"
as="p"
:name="toValue(name)"
:class="cn('text-destructive text-sm', props.class)" />
</template>
+11
View File
@@ -0,0 +1,11 @@
export { default as FormControl } from './FormControl.vue';
export { default as FormDescription } from './FormDescription.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';
+1
View File
@@ -0,0 +1 @@
export const FORM_ITEM_INJECTION_KEY = Symbol();
+31
View File
@@ -0,0 +1,31 @@
import { computed, inject } from 'vue';
import { FieldContextKey } from 'vee-validate';
import { FORM_ITEM_INJECTION_KEY } from './injectionKeys';
export function useFormField() {
const fieldContext = inject(FieldContextKey);
const fieldItemContext = inject(FORM_ITEM_INJECTION_KEY);
if (!fieldContext)
throw new Error('useFormField should be used within <FormField>');
const { name, errorMessage: error, meta } = fieldContext;
const id = fieldItemContext;
const fieldState = {
valid: computed(() => meta.valid),
isDirty: computed(() => meta.dirty),
isTouched: computed(() => meta.touched),
error
};
return {
id,
name,
formItemId: `${id}-form-item`,
formDescriptionId: `${id}-form-item-description`,
formMessageId: `${id}-form-item-message`,
...fieldState
};
}