1- import type { ActionFunction , GadgetRecord , GlobalActionFunction } from "@gadgetinc/api-client-core" ;
1+ import type { ActionFunction , FieldSelection , GadgetRecord , GlobalActionFunction } from "@gadgetinc/api-client-core" ;
22import { yupResolver } from "@hookform/resolvers/yup" ;
33import type { ReactNode } from "react" ;
4- import { useEffect , useMemo , useRef } from "react" ;
4+ import React , { useEffect , useMemo , useRef } from "react" ;
55import type { AnyActionWithId , RecordIdentifier , UseActionFormHookStateData } from "src/use-action-form/types.js" ;
66import type { GadgetObjectFieldConfig } from "../internal/gql/graphql.js" ;
77import type { FieldMetadata , GlobalActionMetadata , ModelWithOneActionMetadata } from "../metadata.js" ;
8- import { FieldType , filterAutoFormFieldList , isModelActionMetadata , useActionMetadata } from "../metadata.js" ;
8+ import { FieldType , buildAutoFormFieldList , isModelActionMetadata , useActionMetadata } from "../metadata.js" ;
9+ import { pathListToSelection } from "../use-table/helpers.js" ;
910import type { FieldErrors , FieldValues } from "../useActionForm.js" ;
1011import { useActionForm } from "../useActionForm.js" ;
1112import { get , getFlattenedObjectKeys , type OptionsType } from "../utils.js" ;
@@ -16,6 +17,7 @@ import {
1617 validateTriggersFromApiClient ,
1718 validateTriggersFromMetadata ,
1819} from "./AutoFormActionValidators.js" ;
20+ import { isAutoInput } from "./AutoInput.js" ;
1921
2022/** The props that any <AutoForm/> component accepts */
2123export type AutoFormProps <
@@ -89,22 +91,22 @@ export const useFormFields = (
8991 : [ ] ;
9092 const nonObjectFields = action . inputFields . filter ( ( field ) => field . configuration . __typename !== "GadgetObjectFieldConfig" ) ;
9193
92- const includedRootLevelFields = filterAutoFormFieldList ( nonObjectFields , options as any ) . map (
93- ( field ) =>
94+ const includedRootLevelFields = buildAutoFormFieldList ( nonObjectFields , options as any ) . map (
95+ ( [ path , field ] ) =>
9496 ( {
95- path : field . apiIdentifier ,
97+ path,
9698 metadata : field ,
9799 } as const )
98100 ) ;
99101
100102 const includedObjectFields = objectFields . flatMap ( ( objectField ) =>
101- filterAutoFormFieldList ( ( objectField . configuration as unknown as GadgetObjectFieldConfig ) . fields as any , {
103+ buildAutoFormFieldList ( ( objectField . configuration as unknown as GadgetObjectFieldConfig ) . fields as any , {
102104 ...( options as any ) ,
103105 isUpsertAction : true , // For upsert meta-actions, we allow IDs, and they are object fields instead of root level
104106 } ) . map (
105- ( innerField ) =>
107+ ( [ innerPath , innerField ] ) =>
106108 ( {
107- path : `${ objectField . apiIdentifier } .${ innerField . apiIdentifier } ` ,
109+ path : `${ objectField . apiIdentifier } .${ innerPath } ` ,
108110 metadata : innerField ,
109111 } as const )
110112 )
@@ -120,6 +122,19 @@ export const useFormFields = (
120122 } , [ metadata , options ] ) ;
121123} ;
122124
125+ export const useFormSelection = (
126+ modelApiIdentifier : string | undefined ,
127+ fields : readonly { path : string ; metadata : FieldMetadata } [ ]
128+ ) : FieldSelection | undefined => {
129+ if ( ! modelApiIdentifier ) return ;
130+ if ( ! fields . length ) return ;
131+
132+ const paths = fields . map ( ( f ) => f . path . replace ( new RegExp ( `^${ modelApiIdentifier } \\.` ) , "" ) ) ;
133+ const fieldMetaData = fields . map ( ( f ) => f . metadata ) ;
134+
135+ return pathListToSelection ( paths , fieldMetaData ) ;
136+ } ;
137+
123138const validateFormFieldApiIdentifierUniqueness = ( actionApiIdentifier : string , inputApiIdentifiers : string [ ] ) => {
124139 const seen = new Set < string > ( ) ;
125140
@@ -142,7 +157,15 @@ export const useAutoForm = <
142157> (
143158 props : AutoFormProps < GivenOptions , SchemaT , ActionFunc , any , any > & { findBy ?: any }
144159) => {
145- const { action, record, onSuccess, onFailure, findBy } = props ;
160+ const { action, record, onSuccess, onFailure, findBy, children } = props ;
161+
162+ let include = props . include ;
163+ let exclude = props . exclude ;
164+
165+ if ( children ) {
166+ include = extractPathsFromChildren ( children ) ;
167+ exclude = undefined ;
168+ }
146169
147170 validateNonBulkAction ( action ) ;
148171 validateTriggersFromApiClient ( action ) ;
@@ -152,12 +175,13 @@ export const useAutoForm = <
152175 validateTriggersFromMetadata ( metadata ) ;
153176
154177 // filter down the fields to render only what we want to render for this form
155- const fields = useFormFields ( metadata , props ) ;
178+ const fields = useFormFields ( metadata , { include , exclude } ) ;
156179 validateFindByObjectWithMetadata ( fields , findBy ) ;
157180 const isDeleteAction = metadata && isModelActionMetadata ( metadata ) && metadata . action . isDeleteAction ;
158181 const isGlobalAction = action . type === "globalAction" ;
159182 const operatesWithRecordId = ! ! ( metadata && isModelActionMetadata ( metadata ) && metadata . action . operatesWithRecordIdentity ) ;
160183 const modelApiIdentifier = action . type == "action" ? action . modelApiIdentifier : undefined ;
184+ const selection = useFormSelection ( modelApiIdentifier , fields ) ;
161185 const isUpsertMetaAction =
162186 metadata && isModelActionMetadata ( metadata ) && fields . some ( ( field ) => field . metadata . fieldType === FieldType . Id ) ;
163187 const isUpsertWithFindBy = isUpsertMetaAction && ! ! findBy ;
@@ -201,6 +225,8 @@ export const useAutoForm = <
201225 defaultValues : defaultValues as any ,
202226 findBy : "findBy" in props ? props . findBy : undefined ,
203227 throwOnInvalidFindByObject : false ,
228+ pause : "findBy" in props ? fetchingMetadata : undefined ,
229+ select : selection as any ,
204230 resolver : useValidationResolver ( metadata , fieldPathsToValidate ) ,
205231 send : ( ) => {
206232 const fieldsToSend = fields
@@ -282,6 +308,44 @@ export const useAutoForm = <
282308 } ;
283309} ;
284310
311+ const extractPathsFromChildren = ( children : React . ReactNode ) => {
312+ const paths = new Set < string > ( ) ;
313+
314+ React . Children . forEach ( children , ( child ) => {
315+ if ( React . isValidElement ( child ) ) {
316+ const grandChildren = child . props . children as React . ReactNode | undefined ;
317+ let childPaths : string [ ] = [ ] ;
318+
319+ if ( grandChildren ) {
320+ childPaths = extractPathsFromChildren ( grandChildren ) ;
321+ }
322+
323+ let field : string | undefined = undefined ;
324+
325+ if ( isAutoInput ( child ) ) {
326+ const props = child . props as { field : string ; selectPaths ?: string [ ] ; children ?: React . ReactNode } ;
327+ field = props . field ;
328+
329+ paths . add ( field ) ;
330+
331+ if ( props . selectPaths && Array . isArray ( props . selectPaths ) ) {
332+ props . selectPaths . forEach ( ( selectPath ) => {
333+ paths . add ( `${ field } .${ selectPath } ` ) ;
334+ } ) ;
335+ }
336+ }
337+
338+ if ( childPaths . length > 0 ) {
339+ for ( const childPath of childPaths ) {
340+ paths . add ( field ? `${ field } .${ childPath } ` : childPath ) ;
341+ }
342+ }
343+ }
344+ } ) ;
345+
346+ return Array . from ( paths ) ;
347+ } ;
348+
285349const removeIdFieldsUnlessUpsertWithoutFindBy = ( isUpsertWithFindBy ?: boolean ) => {
286350 return ( field : { metadata : FieldMetadata } ) => {
287351 return field . metadata . fieldType === FieldType . Id ? ! isUpsertWithFindBy : true ;
0 commit comments