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

View File

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

View File

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

View File

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

View File

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

View File

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