@@ -6,7 +6,7 @@ import type {
6
6
import type { AnyFieldMeta } from './FieldApi'
7
7
import type { DeepKeys } from './util-types'
8
8
9
- type ArrayFieldMode = 'insert' | 'remove' | 'swap' | 'move'
9
+ type ValueFieldMode = 'insert' | 'remove' | 'swap' | 'move'
10
10
11
11
export const defaultFieldMeta : AnyFieldMeta = {
12
12
isValidating : false ,
@@ -33,7 +33,7 @@ export function metaHelper<
33
33
TOnDynamic extends undefined | FormValidateOrFn < TFormData > ,
34
34
TOnDynamicAsync extends undefined | FormAsyncValidateOrFn < TFormData > ,
35
35
TOnServer extends undefined | FormAsyncValidateOrFn < TFormData > ,
36
- TSubmitMeta ,
36
+ TSubmitMeta = never ,
37
37
> (
38
38
formApi : FormApi <
39
39
TFormData ,
@@ -50,57 +50,176 @@ export function metaHelper<
50
50
TSubmitMeta
51
51
> ,
52
52
) {
53
- function handleArrayFieldMetaShift (
53
+ /**
54
+ * Handle the meta shift caused from moving a field from one index to another.
55
+ */
56
+ function handleArrayMove (
54
57
field : DeepKeys < TFormData > ,
55
- index : number ,
56
- mode : ArrayFieldMode ,
57
- secondIndex ?: number ,
58
+ fromIndex : number ,
59
+ toIndex : number ,
58
60
) {
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 ) )
70
67
}
71
68
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
+ } )
73
183
}
74
184
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 >
77
190
}
78
191
79
192
function getAffectedFields (
80
193
field : DeepKeys < TFormData > ,
81
194
index : number ,
82
- mode : ArrayFieldMode ,
195
+ mode : ValueFieldMode ,
83
196
secondIndex ?: number ,
84
197
) : DeepKeys < TFormData > [ ] {
85
198
const affectedFieldKeys = [ getFieldPath ( field , index ) ]
86
199
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
96
213
}
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
104
223
}
105
224
}
106
225
@@ -137,85 +256,11 @@ export function metaHelper<
137
256
138
257
const getEmptyFieldMeta = ( ) : AnyFieldMeta => defaultFieldMeta
139
258
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,
156
265
}
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 }
221
266
}
0 commit comments