mirror of
https://github.com/LukeHagar/form.git
synced 2025-12-06 12:27:45 +00:00
feat(vue): field is now destructured from Field. TypeScript types are much more strict now
* fix(vue): Subscribe component default scoped slot types * fix(vue): Field component default scoped slot value types * example type fixes * remove test log * docs(vue): Field component slot fix * refactor(vue): remove unused children property type from field component * chore: fix formatting --------- Co-authored-by: Corbin Crutchley <git@crutchcorn.dev>
This commit is contained in:
@@ -28,7 +28,7 @@ form.provideFormContext()
|
||||
<div>
|
||||
<div>
|
||||
<form.Field name="fullName">
|
||||
<template v-slot="field">
|
||||
<template v-slot="{ field }">
|
||||
<input
|
||||
:name="field.name"
|
||||
:value="field.state.value"
|
||||
|
||||
@@ -45,12 +45,14 @@ async function onChangeFirstName(value) {
|
||||
:onChangeAsyncDebounceMs="500"
|
||||
:onChangeAsync="onChangeFirstName"
|
||||
>
|
||||
<template v-slot="field, state">
|
||||
<template v-slot="{ field, state }">
|
||||
<label :htmlFor="field.name">First Name:</label>
|
||||
<input
|
||||
:name="field.name"
|
||||
:value="field.state.value"
|
||||
@input="(e) => field.handleChange(e.target.value)"
|
||||
@input="
|
||||
(e) => field.handleChange((e.target as HTMLInputElement).value)
|
||||
"
|
||||
@blur="field.handleBlur"
|
||||
/>
|
||||
<FieldInfo :state="state" />
|
||||
@@ -59,12 +61,14 @@ async function onChangeFirstName(value) {
|
||||
</div>
|
||||
<div>
|
||||
<form.Field name="lastName">
|
||||
<template v-slot="field, state">
|
||||
<template v-slot="{ field, state }">
|
||||
<label :htmlFor="field.name">Last Name:</label>
|
||||
<input
|
||||
:name="field.name"
|
||||
:value="field.state.value"
|
||||
@input="(e) => field.handleChange(e.target.value)"
|
||||
@input="
|
||||
(e) => field.handleChange((e.target as HTMLInputElement).value)
|
||||
"
|
||||
@blur="field.handleBlur"
|
||||
/>
|
||||
<FieldInfo :state="state" />
|
||||
|
||||
@@ -30,7 +30,7 @@ describe('useField', () => {
|
||||
|
||||
return () => (
|
||||
<form.Field name="firstName" defaultValue="FirstName">
|
||||
{(field: FieldApi<string, Person>) => (
|
||||
{({ field }: { field: FieldApi<string, Person> }) => (
|
||||
<input
|
||||
data-testid={'fieldinput'}
|
||||
value={field.state.value}
|
||||
@@ -68,7 +68,7 @@ describe('useField', () => {
|
||||
name="firstName"
|
||||
onChange={(value) => (value === 'other' ? error : undefined)}
|
||||
>
|
||||
{(field: FieldApi<string, Person>) => (
|
||||
{({ field }: { field: FieldApi<string, Person> }) => (
|
||||
<div>
|
||||
<input
|
||||
data-testid="fieldinput"
|
||||
@@ -111,7 +111,7 @@ describe('useField', () => {
|
||||
name="firstName"
|
||||
onChange={(value) => (value === 'other' ? error : undefined)}
|
||||
>
|
||||
{(field: FieldApi<string, Person>) => (
|
||||
{({ field }: { field: FieldApi<string, Person> }) => (
|
||||
<div>
|
||||
<input
|
||||
data-testid="fieldinput"
|
||||
@@ -159,7 +159,7 @@ describe('useField', () => {
|
||||
return error
|
||||
}}
|
||||
>
|
||||
{(field: FieldApi<string, Person>) => (
|
||||
{({ field }: { field: FieldApi<string, Person> }) => (
|
||||
<div>
|
||||
<input
|
||||
data-testid="fieldinput"
|
||||
@@ -211,7 +211,7 @@ describe('useField', () => {
|
||||
return error
|
||||
}}
|
||||
>
|
||||
{(field: FieldApi<string, Person>) => (
|
||||
{({ field }: { field: FieldApi<string, Person> }) => (
|
||||
<div>
|
||||
<input
|
||||
data-testid="fieldinput"
|
||||
|
||||
@@ -29,7 +29,7 @@ describe('useForm', () => {
|
||||
|
||||
return () => (
|
||||
<form.Field name="firstName" defaultValue="">
|
||||
{(field: FieldApi<string, Person>) => (
|
||||
{({ field }: { field: FieldApi<string, Person> }) => (
|
||||
<input
|
||||
data-testid={'fieldinput'}
|
||||
value={field.state.value}
|
||||
@@ -69,7 +69,9 @@ describe('useForm', () => {
|
||||
|
||||
return () => (
|
||||
<form.Field name="firstName" defaultValue="">
|
||||
{(field: FieldApi<string, Person>) => <p>{field.state.value}</p>}
|
||||
{({ field }: { field: FieldApi<string, Person> }) => (
|
||||
<p>{field.state.value}</p>
|
||||
)}
|
||||
</form.Field>
|
||||
)
|
||||
})
|
||||
@@ -96,7 +98,11 @@ describe('useForm', () => {
|
||||
return () => (
|
||||
<form.Provider>
|
||||
<form.Field name="firstName">
|
||||
{(field: FieldApi<string, { firstName: string }>) => {
|
||||
{({
|
||||
field,
|
||||
}: {
|
||||
field: FieldApi<string, { firstName: string }>
|
||||
}) => {
|
||||
return (
|
||||
<input
|
||||
value={field.state.value}
|
||||
|
||||
@@ -1,19 +1,14 @@
|
||||
import {
|
||||
type DeepKeys,
|
||||
type DeepValue,
|
||||
FieldApi,
|
||||
type FieldOptions,
|
||||
type Narrow,
|
||||
import { FieldApi } from '@tanstack/form-core'
|
||||
import type {
|
||||
FieldState,
|
||||
DeepKeys,
|
||||
DeepValue,
|
||||
FieldOptions,
|
||||
Narrow,
|
||||
} from '@tanstack/form-core'
|
||||
import { useStore } from '@tanstack/vue-store'
|
||||
import {
|
||||
type SetupContext,
|
||||
defineComponent,
|
||||
type Ref,
|
||||
onMounted,
|
||||
onUnmounted,
|
||||
watch,
|
||||
} from 'vue-demi'
|
||||
import { defineComponent, onMounted, onUnmounted, watch } from 'vue-demi'
|
||||
import type { SlotsType, SetupContext, Ref } from 'vue-demi'
|
||||
import { provideFormContext, useFormContext } from './formContext'
|
||||
|
||||
declare module '@tanstack/form-core' {
|
||||
@@ -113,11 +108,7 @@ export type FieldValue<TFormData, TField> = TFormData extends any[]
|
||||
// // ^?
|
||||
|
||||
export type FieldComponent<TParentData, TFormData> = <TField>(
|
||||
fieldOptions: {
|
||||
children?: (
|
||||
fieldApi: FieldApi<FieldValue<TParentData, TField>, TFormData>,
|
||||
) => any
|
||||
} & Omit<
|
||||
fieldOptions: Omit<
|
||||
UseFieldOptions<FieldValue<TParentData, TField>, TFormData>,
|
||||
'name' | 'index'
|
||||
> &
|
||||
@@ -130,7 +121,15 @@ export type FieldComponent<TParentData, TFormData> = <TField>(
|
||||
name: TField extends undefined ? TField : DeepKeys<TParentData>
|
||||
index?: never
|
||||
}),
|
||||
context: SetupContext,
|
||||
context: SetupContext<
|
||||
{},
|
||||
SlotsType<{
|
||||
default: {
|
||||
field: FieldApi<FieldValue<TParentData, TField>, TFormData>
|
||||
state: FieldState<any>
|
||||
}
|
||||
}>
|
||||
>,
|
||||
) => any
|
||||
|
||||
export const Field = defineComponent(
|
||||
@@ -145,7 +144,11 @@ export const Field = defineComponent(
|
||||
parentFieldName: fieldApi.api.name,
|
||||
} as never)
|
||||
|
||||
return () => context.slots.default!(fieldApi.api, fieldApi.state.value)
|
||||
return () =>
|
||||
context.slots.default!({
|
||||
field: fieldApi.api,
|
||||
state: fieldApi.state.value,
|
||||
})
|
||||
},
|
||||
{ name: 'Field', inheritAttrs: false },
|
||||
)
|
||||
|
||||
@@ -2,7 +2,12 @@ import { FormApi, type FormState, type FormOptions } from '@tanstack/form-core'
|
||||
import { useStore } from '@tanstack/vue-store'
|
||||
import { type UseField, type FieldComponent, Field, useField } from './useField'
|
||||
import { provideFormContext } from './formContext'
|
||||
import { defineComponent } from 'vue-demi'
|
||||
import {
|
||||
type EmitsOptions,
|
||||
type SlotsType,
|
||||
type SetupContext,
|
||||
defineComponent,
|
||||
} from 'vue-demi'
|
||||
import type { NoInfer } from './types'
|
||||
|
||||
declare module '@tanstack/form-core' {
|
||||
@@ -15,9 +20,15 @@ declare module '@tanstack/form-core' {
|
||||
useStore: <TSelected = NoInfer<FormState<TFormData>>>(
|
||||
selector?: (state: NoInfer<FormState<TFormData>>) => TSelected,
|
||||
) => TSelected
|
||||
Subscribe: <TSelected = NoInfer<FormState<TFormData>>>(props: {
|
||||
Subscribe: <TSelected = NoInfer<FormState<TFormData>>>(
|
||||
props: {
|
||||
selector?: (state: NoInfer<FormState<TFormData>>) => TSelected
|
||||
}) => any
|
||||
},
|
||||
context: SetupContext<
|
||||
EmitsOptions,
|
||||
SlotsType<{ default: NoInfer<FormState<TFormData>> }>
|
||||
>,
|
||||
) => any
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user