@@ -6,7 +6,7 @@ import type {
66import type { AnyFieldMeta } from './FieldApi'
77import type { DeepKeys } from './util-types'
88
9- type ArrayFieldMode = 'insert' | 'remove' | 'swap' | 'move'
9+ type ValueFieldMode = 'insert' | 'remove' | 'swap' | 'move'
1010
1111export const defaultFieldMeta : AnyFieldMeta = {
1212 isValidating : false ,
@@ -33,7 +33,7 @@ export function metaHelper<
3333 TOnDynamic extends undefined | FormValidateOrFn < TFormData > ,
3434 TOnDynamicAsync extends undefined | FormAsyncValidateOrFn < TFormData > ,
3535 TOnServer extends undefined | FormAsyncValidateOrFn < TFormData > ,
36- TSubmitMeta ,
36+ TSubmitMeta = never ,
3737> (
3838 formApi : FormApi <
3939 TFormData ,
@@ -50,57 +50,176 @@ export function metaHelper<
5050 TSubmitMeta
5151 > ,
5252) {
53- function handleArrayFieldMetaShift (
53+ /**
54+ * Handle the meta shift caused from moving a field from one index to another.
55+ */
56+ function handleArrayMove (
5457 field : DeepKeys < TFormData > ,
55- index : number ,
56- mode : ArrayFieldMode ,
57- secondIndex ?: number ,
58+ fromIndex : number ,
59+ toIndex : number ,
5860 ) {
59- const affectedFields = getAffectedFields ( field , index , mode , secondIndex )
60-
61- const handlers = {
62- insert : ( ) => handleInsertMode ( affectedFields , field , index ) ,
63- remove : ( ) => handleRemoveMode ( affectedFields ) ,
64- swap : ( ) =>
65- secondIndex !== undefined &&
66- handleSwapMode ( affectedFields , field , index , secondIndex ) ,
67- move : ( ) =>
68- secondIndex !== undefined &&
69- handleMoveMode ( affectedFields , field , index , secondIndex ) ,
61+ const affectedFields = getAffectedFields ( field , fromIndex , 'move' , toIndex )
62+
63+ const startIndex = Math . min ( fromIndex , toIndex )
64+ const endIndex = Math . max ( fromIndex , toIndex )
65+ for ( let i = startIndex ; i <= endIndex ; i ++ ) {
66+ affectedFields . push ( getFieldPath ( field , i ) )
7067 }
7168
72- handlers [ mode ] ( )
69+ // Store the original field meta that will be reapplied at the destination index
70+ const fromFields = Object . keys ( formApi . fieldInfo ) . reduce (
71+ ( fieldMap , fieldKey ) => {
72+ if ( fieldKey . startsWith ( getFieldPath ( field , fromIndex ) ) ) {
73+ fieldMap . set (
74+ fieldKey as DeepKeys < TFormData > ,
75+ formApi . getFieldMeta ( fieldKey as DeepKeys < TFormData > ) ,
76+ )
77+ }
78+ return fieldMap
79+ } ,
80+ new Map < DeepKeys < TFormData > , AnyFieldMeta | undefined > ( ) ,
81+ )
82+
83+ shiftMeta ( affectedFields , fromIndex < toIndex ? 'up' : 'down' )
84+
85+ // Reapply the stored field meta at the destination index
86+ Object . keys ( formApi . fieldInfo )
87+ . filter ( ( fieldKey ) => fieldKey . startsWith ( getFieldPath ( field , toIndex ) ) )
88+ . forEach ( ( fieldKey ) => {
89+ const fromKey = fieldKey . replace (
90+ getFieldPath ( field , toIndex ) ,
91+ getFieldPath ( field , fromIndex ) ,
92+ ) as DeepKeys < TFormData >
93+
94+ const fromMeta = fromFields . get ( fromKey )
95+ if ( fromMeta ) {
96+ formApi . setFieldMeta ( fieldKey as DeepKeys < TFormData > , fromMeta )
97+ }
98+ } )
99+ }
100+
101+ /**
102+ * Handle the meta shift from removing a field at the specified index.
103+ */
104+ function handleArrayRemove ( field : DeepKeys < TFormData > , index : number ) {
105+ const affectedFields = getAffectedFields ( field , index , 'remove' )
106+
107+ shiftMeta ( affectedFields , 'up' )
108+ }
109+
110+ /**
111+ * Handle the meta shift from swapping two fields at the specified indeces.
112+ */
113+ function handleArraySwap (
114+ field : DeepKeys < TFormData > ,
115+ index : number ,
116+ secondIndex : number ,
117+ ) {
118+ const affectedFields = getAffectedFields ( field , index , 'swap' , secondIndex )
119+
120+ affectedFields . forEach ( ( fieldKey ) => {
121+ if ( ! fieldKey . toString ( ) . startsWith ( getFieldPath ( field , index ) ) ) {
122+ return
123+ }
124+
125+ const swappedKey = fieldKey
126+ . toString ( )
127+ . replace (
128+ getFieldPath ( field , index ) ,
129+ getFieldPath ( field , secondIndex ) ,
130+ ) as DeepKeys < TFormData >
131+
132+ const [ meta1 , meta2 ] = [
133+ formApi . getFieldMeta ( fieldKey ) ,
134+ formApi . getFieldMeta ( swappedKey ) ,
135+ ]
136+
137+ if ( meta1 ) formApi . setFieldMeta ( swappedKey , meta1 )
138+ if ( meta2 ) formApi . setFieldMeta ( fieldKey , meta2 )
139+ } )
140+ }
141+
142+ /**
143+ * Handle the meta shift from inserting a field at the specified index.
144+ */
145+ function handleArrayInsert ( field : DeepKeys < TFormData > , insertIndex : number ) {
146+ const affectedFields = getAffectedFields ( field , insertIndex , 'insert' )
147+
148+ shiftMeta ( affectedFields , 'down' )
149+
150+ affectedFields . forEach ( ( fieldKey ) => {
151+ if ( fieldKey . toString ( ) . startsWith ( getFieldPath ( field , insertIndex ) ) ) {
152+ formApi . setFieldMeta ( fieldKey , getEmptyFieldMeta ( ) )
153+ }
154+ } )
155+ }
156+
157+ /**
158+ * Handle the meta shift from filtering out indeces.
159+ * @param remainingIndices An array of indeces that were NOT filtered out of the original array.
160+ */
161+ function handleArrayFilter (
162+ field : DeepKeys < TFormData > ,
163+ remainingIndices : number [ ] ,
164+ ) {
165+ if ( remainingIndices . length === 0 ) return
166+
167+ // create a map between the index and its new location
168+ remainingIndices . forEach ( ( fromIndex , toIndex ) => {
169+ if ( fromIndex === toIndex ) return
170+ // assign it the original meta
171+ const fieldKey = getFieldPath ( field , toIndex )
172+ const originalFieldKey = getFieldPath ( field , fromIndex )
173+ const originalFieldMeta = formApi . getFieldMeta ( originalFieldKey )
174+ if ( originalFieldMeta ) {
175+ formApi . setFieldMeta ( fieldKey , originalFieldMeta )
176+ } else {
177+ formApi . setFieldMeta ( fieldKey , {
178+ ...getEmptyFieldMeta ( ) ,
179+ isTouched : originalFieldKey as unknown as boolean ,
180+ } )
181+ }
182+ } )
73183 }
74184
75- function getFieldPath ( field : DeepKeys < TFormData > , index : number ) : string {
76- return `${ field } [${ index } ]`
185+ function getFieldPath (
186+ field : DeepKeys < TFormData > ,
187+ index : number ,
188+ ) : DeepKeys < TFormData > {
189+ return `${ field } [${ index } ]` as DeepKeys < TFormData >
77190 }
78191
79192 function getAffectedFields (
80193 field : DeepKeys < TFormData > ,
81194 index : number ,
82- mode : ArrayFieldMode ,
195+ mode : ValueFieldMode ,
83196 secondIndex ?: number ,
84197 ) : DeepKeys < TFormData > [ ] {
85198 const affectedFieldKeys = [ getFieldPath ( field , index ) ]
86199
87- if ( mode === 'swap' ) {
88- affectedFieldKeys . push ( getFieldPath ( field , secondIndex ! ) )
89- } else if ( mode === 'move' ) {
90- const [ startIndex , endIndex ] = [
91- Math . min ( index , secondIndex ! ) ,
92- Math . max ( index , secondIndex ! ) ,
93- ]
94- for ( let i = startIndex ; i <= endIndex ; i ++ ) {
95- affectedFieldKeys . push ( getFieldPath ( field , i ) )
200+ switch ( mode ) {
201+ case 'swap' :
202+ affectedFieldKeys . push ( getFieldPath ( field , secondIndex ! ) )
203+ break
204+ case 'move' : {
205+ const [ startIndex , endIndex ] = [
206+ Math . min ( index , secondIndex ! ) ,
207+ Math . max ( index , secondIndex ! ) ,
208+ ]
209+ for ( let i = startIndex ; i <= endIndex ; i ++ ) {
210+ affectedFieldKeys . push ( getFieldPath ( field , i ) )
211+ }
212+ break
96213 }
97- } else {
98- const currentValue = formApi . getFieldValue ( field )
99- const fieldItems = Array . isArray ( currentValue )
100- ? ( currentValue as Array < unknown > ) . length
101- : 0
102- for ( let i = index + 1 ; i < fieldItems ; i ++ ) {
103- affectedFieldKeys . push ( getFieldPath ( field , i ) )
214+ default : {
215+ const currentValue = formApi . getFieldValue ( field )
216+ const fieldItems = Array . isArray ( currentValue )
217+ ? ( currentValue as Array < unknown > ) . length
218+ : 0
219+ for ( let i = index + 1 ; i < fieldItems ; i ++ ) {
220+ affectedFieldKeys . push ( getFieldPath ( field , i ) )
221+ }
222+ break
104223 }
105224 }
106225
@@ -137,85 +256,11 @@ export function metaHelper<
137256
138257 const getEmptyFieldMeta = ( ) : AnyFieldMeta => defaultFieldMeta
139258
140- const handleInsertMode = (
141- fields : DeepKeys < TFormData > [ ] ,
142- field : DeepKeys < TFormData > ,
143- insertIndex : number ,
144- ) => {
145- shiftMeta ( fields , 'down' )
146-
147- fields . forEach ( ( fieldKey ) => {
148- if ( fieldKey . toString ( ) . startsWith ( getFieldPath ( field , insertIndex ) ) ) {
149- formApi . setFieldMeta ( fieldKey , getEmptyFieldMeta ( ) )
150- }
151- } )
152- }
153-
154- const handleRemoveMode = ( fields : DeepKeys < TFormData > [ ] ) => {
155- shiftMeta ( fields , 'up' )
259+ return {
260+ handleArrayMove,
261+ handleArrayRemove,
262+ handleArraySwap,
263+ handleArrayInsert,
264+ handleArrayFilter,
156265 }
157-
158- const handleMoveMode = (
159- fields : DeepKeys < TFormData > [ ] ,
160- field : DeepKeys < TFormData > ,
161- fromIndex : number ,
162- toIndex : number ,
163- ) => {
164- // Store the original field meta that will be reapplied at the destination index
165- const fromFields = new Map (
166- Object . keys ( formApi . fieldInfo )
167- . filter ( ( fieldKey ) =>
168- fieldKey . startsWith ( getFieldPath ( field , fromIndex ) ) ,
169- )
170- . map ( ( fieldKey ) => [
171- fieldKey as DeepKeys < TFormData > ,
172- formApi . getFieldMeta ( fieldKey as DeepKeys < TFormData > ) ,
173- ] ) ,
174- )
175-
176- shiftMeta ( fields , fromIndex < toIndex ? 'up' : 'down' )
177-
178- // Reapply the stored field meta at the destination index
179- Object . keys ( formApi . fieldInfo )
180- . filter ( ( fieldKey ) => fieldKey . startsWith ( getFieldPath ( field , toIndex ) ) )
181- . forEach ( ( fieldKey ) => {
182- const fromKey = fieldKey . replace (
183- getFieldPath ( field , toIndex ) ,
184- getFieldPath ( field , fromIndex ) ,
185- ) as DeepKeys < TFormData >
186-
187- const fromMeta = fromFields . get ( fromKey )
188- if ( fromMeta ) {
189- formApi . setFieldMeta ( fieldKey as DeepKeys < TFormData > , fromMeta )
190- }
191- } )
192- }
193-
194- const handleSwapMode = (
195- fields : DeepKeys < TFormData > [ ] ,
196- field : DeepKeys < TFormData > ,
197- index : number ,
198- secondIndex : number ,
199- ) => {
200- fields . forEach ( ( fieldKey ) => {
201- if ( ! fieldKey . toString ( ) . startsWith ( getFieldPath ( field , index ) ) ) return
202-
203- const swappedKey = fieldKey
204- . toString ( )
205- . replace (
206- getFieldPath ( field , index ) ,
207- getFieldPath ( field , secondIndex ) ,
208- ) as DeepKeys < TFormData >
209-
210- const [ meta1 , meta2 ] = [
211- formApi . getFieldMeta ( fieldKey ) ,
212- formApi . getFieldMeta ( swappedKey ) ,
213- ]
214-
215- if ( meta1 ) formApi . setFieldMeta ( swappedKey , meta1 )
216- if ( meta2 ) formApi . setFieldMeta ( fieldKey , meta2 )
217- } )
218- }
219-
220- return { handleArrayFieldMetaShift }
221266}
0 commit comments