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:
Robert Soriano
2023-09-08 14:41:54 -07:00
committed by GitHub
parent 790c1aa226
commit 6935b330fa
6 changed files with 62 additions and 38 deletions

View File

@@ -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"

View File

@@ -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" />

View File

@@ -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"

View File

@@ -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}

View File

@@ -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 },
)

View File

@@ -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: {
selector?: (state: NoInfer<FormState<TFormData>>) => TSelected
}) => any
Subscribe: <TSelected = NoInfer<FormState<TFormData>>>(
props: {
selector?: (state: NoInfer<FormState<TFormData>>) => TSelected
},
context: SetupContext<
EmitsOptions,
SlotsType<{ default: NoInfer<FormState<TFormData>> }>
>,
) => any
}
}