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>
|
||||||
<div>
|
<div>
|
||||||
<form.Field name="fullName">
|
<form.Field name="fullName">
|
||||||
<template v-slot="field">
|
<template v-slot="{ field }">
|
||||||
<input
|
<input
|
||||||
:name="field.name"
|
:name="field.name"
|
||||||
:value="field.state.value"
|
:value="field.state.value"
|
||||||
|
|||||||
@@ -45,12 +45,14 @@ async function onChangeFirstName(value) {
|
|||||||
:onChangeAsyncDebounceMs="500"
|
:onChangeAsyncDebounceMs="500"
|
||||||
:onChangeAsync="onChangeFirstName"
|
:onChangeAsync="onChangeFirstName"
|
||||||
>
|
>
|
||||||
<template v-slot="field, state">
|
<template v-slot="{ field, state }">
|
||||||
<label :htmlFor="field.name">First Name:</label>
|
<label :htmlFor="field.name">First Name:</label>
|
||||||
<input
|
<input
|
||||||
:name="field.name"
|
:name="field.name"
|
||||||
:value="field.state.value"
|
:value="field.state.value"
|
||||||
@input="(e) => field.handleChange(e.target.value)"
|
@input="
|
||||||
|
(e) => field.handleChange((e.target as HTMLInputElement).value)
|
||||||
|
"
|
||||||
@blur="field.handleBlur"
|
@blur="field.handleBlur"
|
||||||
/>
|
/>
|
||||||
<FieldInfo :state="state" />
|
<FieldInfo :state="state" />
|
||||||
@@ -59,12 +61,14 @@ async function onChangeFirstName(value) {
|
|||||||
</div>
|
</div>
|
||||||
<div>
|
<div>
|
||||||
<form.Field name="lastName">
|
<form.Field name="lastName">
|
||||||
<template v-slot="field, state">
|
<template v-slot="{ field, state }">
|
||||||
<label :htmlFor="field.name">Last Name:</label>
|
<label :htmlFor="field.name">Last Name:</label>
|
||||||
<input
|
<input
|
||||||
:name="field.name"
|
:name="field.name"
|
||||||
:value="field.state.value"
|
:value="field.state.value"
|
||||||
@input="(e) => field.handleChange(e.target.value)"
|
@input="
|
||||||
|
(e) => field.handleChange((e.target as HTMLInputElement).value)
|
||||||
|
"
|
||||||
@blur="field.handleBlur"
|
@blur="field.handleBlur"
|
||||||
/>
|
/>
|
||||||
<FieldInfo :state="state" />
|
<FieldInfo :state="state" />
|
||||||
|
|||||||
@@ -30,7 +30,7 @@ describe('useField', () => {
|
|||||||
|
|
||||||
return () => (
|
return () => (
|
||||||
<form.Field name="firstName" defaultValue="FirstName">
|
<form.Field name="firstName" defaultValue="FirstName">
|
||||||
{(field: FieldApi<string, Person>) => (
|
{({ field }: { field: FieldApi<string, Person> }) => (
|
||||||
<input
|
<input
|
||||||
data-testid={'fieldinput'}
|
data-testid={'fieldinput'}
|
||||||
value={field.state.value}
|
value={field.state.value}
|
||||||
@@ -68,7 +68,7 @@ describe('useField', () => {
|
|||||||
name="firstName"
|
name="firstName"
|
||||||
onChange={(value) => (value === 'other' ? error : undefined)}
|
onChange={(value) => (value === 'other' ? error : undefined)}
|
||||||
>
|
>
|
||||||
{(field: FieldApi<string, Person>) => (
|
{({ field }: { field: FieldApi<string, Person> }) => (
|
||||||
<div>
|
<div>
|
||||||
<input
|
<input
|
||||||
data-testid="fieldinput"
|
data-testid="fieldinput"
|
||||||
@@ -111,7 +111,7 @@ describe('useField', () => {
|
|||||||
name="firstName"
|
name="firstName"
|
||||||
onChange={(value) => (value === 'other' ? error : undefined)}
|
onChange={(value) => (value === 'other' ? error : undefined)}
|
||||||
>
|
>
|
||||||
{(field: FieldApi<string, Person>) => (
|
{({ field }: { field: FieldApi<string, Person> }) => (
|
||||||
<div>
|
<div>
|
||||||
<input
|
<input
|
||||||
data-testid="fieldinput"
|
data-testid="fieldinput"
|
||||||
@@ -159,7 +159,7 @@ describe('useField', () => {
|
|||||||
return error
|
return error
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
{(field: FieldApi<string, Person>) => (
|
{({ field }: { field: FieldApi<string, Person> }) => (
|
||||||
<div>
|
<div>
|
||||||
<input
|
<input
|
||||||
data-testid="fieldinput"
|
data-testid="fieldinput"
|
||||||
@@ -211,7 +211,7 @@ describe('useField', () => {
|
|||||||
return error
|
return error
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
{(field: FieldApi<string, Person>) => (
|
{({ field }: { field: FieldApi<string, Person> }) => (
|
||||||
<div>
|
<div>
|
||||||
<input
|
<input
|
||||||
data-testid="fieldinput"
|
data-testid="fieldinput"
|
||||||
|
|||||||
@@ -29,7 +29,7 @@ describe('useForm', () => {
|
|||||||
|
|
||||||
return () => (
|
return () => (
|
||||||
<form.Field name="firstName" defaultValue="">
|
<form.Field name="firstName" defaultValue="">
|
||||||
{(field: FieldApi<string, Person>) => (
|
{({ field }: { field: FieldApi<string, Person> }) => (
|
||||||
<input
|
<input
|
||||||
data-testid={'fieldinput'}
|
data-testid={'fieldinput'}
|
||||||
value={field.state.value}
|
value={field.state.value}
|
||||||
@@ -69,7 +69,9 @@ describe('useForm', () => {
|
|||||||
|
|
||||||
return () => (
|
return () => (
|
||||||
<form.Field name="firstName" defaultValue="">
|
<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>
|
</form.Field>
|
||||||
)
|
)
|
||||||
})
|
})
|
||||||
@@ -96,7 +98,11 @@ describe('useForm', () => {
|
|||||||
return () => (
|
return () => (
|
||||||
<form.Provider>
|
<form.Provider>
|
||||||
<form.Field name="firstName">
|
<form.Field name="firstName">
|
||||||
{(field: FieldApi<string, { firstName: string }>) => {
|
{({
|
||||||
|
field,
|
||||||
|
}: {
|
||||||
|
field: FieldApi<string, { firstName: string }>
|
||||||
|
}) => {
|
||||||
return (
|
return (
|
||||||
<input
|
<input
|
||||||
value={field.state.value}
|
value={field.state.value}
|
||||||
|
|||||||
@@ -1,19 +1,14 @@
|
|||||||
import {
|
import { FieldApi } from '@tanstack/form-core'
|
||||||
type DeepKeys,
|
import type {
|
||||||
type DeepValue,
|
FieldState,
|
||||||
FieldApi,
|
DeepKeys,
|
||||||
type FieldOptions,
|
DeepValue,
|
||||||
type Narrow,
|
FieldOptions,
|
||||||
|
Narrow,
|
||||||
} from '@tanstack/form-core'
|
} from '@tanstack/form-core'
|
||||||
import { useStore } from '@tanstack/vue-store'
|
import { useStore } from '@tanstack/vue-store'
|
||||||
import {
|
import { defineComponent, onMounted, onUnmounted, watch } from 'vue-demi'
|
||||||
type SetupContext,
|
import type { SlotsType, SetupContext, Ref } from 'vue-demi'
|
||||||
defineComponent,
|
|
||||||
type Ref,
|
|
||||||
onMounted,
|
|
||||||
onUnmounted,
|
|
||||||
watch,
|
|
||||||
} from 'vue-demi'
|
|
||||||
import { provideFormContext, useFormContext } from './formContext'
|
import { provideFormContext, useFormContext } from './formContext'
|
||||||
|
|
||||||
declare module '@tanstack/form-core' {
|
declare module '@tanstack/form-core' {
|
||||||
@@ -113,11 +108,7 @@ export type FieldValue<TFormData, TField> = TFormData extends any[]
|
|||||||
// // ^?
|
// // ^?
|
||||||
|
|
||||||
export type FieldComponent<TParentData, TFormData> = <TField>(
|
export type FieldComponent<TParentData, TFormData> = <TField>(
|
||||||
fieldOptions: {
|
fieldOptions: Omit<
|
||||||
children?: (
|
|
||||||
fieldApi: FieldApi<FieldValue<TParentData, TField>, TFormData>,
|
|
||||||
) => any
|
|
||||||
} & Omit<
|
|
||||||
UseFieldOptions<FieldValue<TParentData, TField>, TFormData>,
|
UseFieldOptions<FieldValue<TParentData, TField>, TFormData>,
|
||||||
'name' | 'index'
|
'name' | 'index'
|
||||||
> &
|
> &
|
||||||
@@ -130,7 +121,15 @@ export type FieldComponent<TParentData, TFormData> = <TField>(
|
|||||||
name: TField extends undefined ? TField : DeepKeys<TParentData>
|
name: TField extends undefined ? TField : DeepKeys<TParentData>
|
||||||
index?: never
|
index?: never
|
||||||
}),
|
}),
|
||||||
context: SetupContext,
|
context: SetupContext<
|
||||||
|
{},
|
||||||
|
SlotsType<{
|
||||||
|
default: {
|
||||||
|
field: FieldApi<FieldValue<TParentData, TField>, TFormData>
|
||||||
|
state: FieldState<any>
|
||||||
|
}
|
||||||
|
}>
|
||||||
|
>,
|
||||||
) => any
|
) => any
|
||||||
|
|
||||||
export const Field = defineComponent(
|
export const Field = defineComponent(
|
||||||
@@ -145,7 +144,11 @@ export const Field = defineComponent(
|
|||||||
parentFieldName: fieldApi.api.name,
|
parentFieldName: fieldApi.api.name,
|
||||||
} as never)
|
} 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 },
|
{ 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 { useStore } from '@tanstack/vue-store'
|
||||||
import { type UseField, type FieldComponent, Field, useField } from './useField'
|
import { type UseField, type FieldComponent, Field, useField } from './useField'
|
||||||
import { provideFormContext } from './formContext'
|
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'
|
import type { NoInfer } from './types'
|
||||||
|
|
||||||
declare module '@tanstack/form-core' {
|
declare module '@tanstack/form-core' {
|
||||||
@@ -15,9 +20,15 @@ declare module '@tanstack/form-core' {
|
|||||||
useStore: <TSelected = NoInfer<FormState<TFormData>>>(
|
useStore: <TSelected = NoInfer<FormState<TFormData>>>(
|
||||||
selector?: (state: NoInfer<FormState<TFormData>>) => TSelected,
|
selector?: (state: NoInfer<FormState<TFormData>>) => TSelected,
|
||||||
) => TSelected
|
) => TSelected
|
||||||
Subscribe: <TSelected = NoInfer<FormState<TFormData>>>(props: {
|
Subscribe: <TSelected = NoInfer<FormState<TFormData>>>(
|
||||||
selector?: (state: NoInfer<FormState<TFormData>>) => TSelected
|
props: {
|
||||||
}) => any
|
selector?: (state: NoInfer<FormState<TFormData>>) => TSelected
|
||||||
|
},
|
||||||
|
context: SetupContext<
|
||||||
|
EmitsOptions,
|
||||||
|
SlotsType<{ default: NoInfer<FormState<TFormData>> }>
|
||||||
|
>,
|
||||||
|
) => any
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user