1+ /* eslint-disable no-plusplus */
12/* eslint-disable no-nested-ternary */
23/* eslint-disable complexity */
34/* eslint-disable @typescript-eslint/no-unused-expressions */
@@ -22,15 +23,17 @@ import type { Rule, ValidateOptions } from './validation';
2223
2324/**
2425 * Listener type definition for form state changes
25- * @type {Object }
26- * @property {Function } cb - Callback function triggered on state changes
27- * @property {ChangeMask } mask - Bit mask indicating which types of changes to listen for
2826 */
2927type Listener = {
3028 cb : ( value : StoreValue , key : string , all : Store , fired : ChangeMask ) => void ;
3129 mask : ChangeMask ;
3230} ;
3331
32+ export type ArrayField = {
33+ id : number ;
34+ keys : number [ ] ;
35+ } ;
36+
3437const matchTrigger = ( rule : Rule , trig ?: string | string [ ] ) => {
3538 const list = toArray ( rule . validateTrigger ) ;
3639
@@ -55,6 +58,13 @@ function getFlag(bucket: Set<string>, names?: NamePath[]) {
5558 return anyOn ( bucket , names ) ;
5659}
5760
61+ function move < T > ( arr : T [ ] , from : number , to : number ) : T [ ] {
62+ const clone = arr . slice ( ) ;
63+ const item = clone . splice ( from , 1 ) [ 0 ] ;
64+ clone . splice ( to , 0 , item ) ;
65+ return clone ;
66+ }
67+
5868/**
5969 * FormStore class - Core form state management implementation
6070 * Handles form state, validation, field registration, and state subscriptions
@@ -70,6 +80,8 @@ class FormStore {
7080 /** Registry of field entities */
7181 private _fieldEntities : FieldEntity [ ] = [ ] ;
7282
83+ private arrayKeyMap = new Map < string , ArrayField > ( ) ;
84+
7385 /** Form lifecycle callbacks */
7486 private _callbacks : Callbacks = { } ;
7587 /** Validation message templates */
@@ -156,32 +168,27 @@ class FormStore {
156168 // ------------------------------------------------
157169 /**
158170 * Determines if field values should be preserved based on field and global settings
159- * @param fieldPreserve - Optional field-level preserve setting
160- * @returns {boolean } Final preserve setting
161171 */
162172 private isMergedPreserve = ( fieldPreserve ?: boolean ) => {
163173 return fieldPreserve ?? this . _preserve ;
164174 } ;
165175
166176 /**
167177 * Sets form lifecycle callbacks
168- * @param c - Callback functions object
169178 */
170179 private setCallbacks = ( c : Callbacks ) => {
171180 this . _callbacks = c || { } ;
172181 } ;
173182
174183 /**
175184 * Sets validation message templates
176- * @param m - Validation message templates object
177185 */
178186 private setValidateMessages = ( m : ValidateMessages ) => {
179187 this . _validateMessages = m || { } ;
180188 } ;
181189
182190 /**
183191 * Sets global preserve flag for field values
184- * @param preserve - Whether to preserve field values after unmount
185192 */
186193 private setPreserve = ( preserve : boolean ) => {
187194 this . _preserve = preserve ;
@@ -192,7 +199,6 @@ class FormStore {
192199 // ------------------------------------------------
193200 /**
194201 * Adds a new middleware to the middleware stack
195- * @param mw - Middleware function to add
196202 */
197203 private use = ( mw : Middleware ) => {
198204 this . _middlewares . push ( mw ) ;
@@ -214,7 +220,6 @@ class FormStore {
214220 // Action Dispatch System
215221 /**
216222 * Base dispatch function that handles all form actions
217- * @param a - Action object containing type and payload
218223 */
219224 private baseDispatch = ( a : Action ) => {
220225 switch ( a . type ) {
@@ -260,7 +265,6 @@ class FormStore {
260265
261266 /**
262267 * Enhanced dispatch function that processes actions through middleware chain
263- * @param a - Action to be dispatched
264268 */
265269 private dispatch = ( a : Action ) => {
266270 const ctx = { dispatch : ( x : Action ) => this . dispatch ( x ) , getState : ( ) => this . _store } ;
@@ -274,15 +278,13 @@ class FormStore {
274278 // ------------------------------------------------
275279 /**
276280 * Updates the main form state store
277- * @param nextStore - New store state to set
278281 */
279282 private updateStore = ( nextStore : Store ) => {
280283 this . _store = nextStore ;
281284 } ;
282285
283286 /**
284287 * Sets initial form values and updates current store
285- * @param values - Initial values to set
286288 */
287289 private setInitialValues = ( values : Store ) => {
288290 this . _initial = values ;
@@ -294,7 +296,6 @@ class FormStore {
294296
295297 /**
296298 * Gets current form state including validation status
297- * @returns {Object } Current form state object
298299 */
299300 private getFormState ( ) {
300301 return {
@@ -310,8 +311,6 @@ class FormStore {
310311
311312 /**
312313 * Gets initial value for a field
313- * @param name - Field name path
314- * @returns Initial value for the field
315314 */
316315 private getInitialValue = ( name : NamePath ) => {
317316 return get ( this . _initial , keyOfName ( name ) ) ;
@@ -410,6 +409,7 @@ class FormStore {
410409 this . _fieldEntities = this . _fieldEntities . filter ( e => e . name !== name ) ;
411410 this . _initial = unset ( this . _initial , name ) ;
412411 this . _store = unset ( this . _store , name ) ;
412+ this . arrayKeyMap . delete ( name ) ;
413413 }
414414 this . _exactListeners . delete ( name ) ;
415415 this . _prefixListeners . delete ( name ) ;
@@ -980,18 +980,34 @@ class FormStore {
980980 }
981981
982982 // ===== Array Operation =====
983+
984+ private getArrayKeyManager = ( name : string ) => {
985+ let mgr = this . arrayKeyMap . get ( name ) ;
986+ if ( ! mgr ) {
987+ mgr = { id : 0 , keys : [ ] } ;
988+ this . arrayKeyMap . set ( name , mgr ) ;
989+ }
990+ return mgr ;
991+ } ;
992+
983993 private arrayOp = ( name : NamePath , op : 'insert' | 'move' | 'remove' | 'replace' | 'swap' , args : any ) => {
984994 const arr = this . getFieldValue ( name ) ;
985995 if ( ! Array . isArray ( arr ) ) return ;
986996 const next = arr . slice ( ) ;
987997 const ak = keyOfName ( name ) ;
998+
999+ const keyMgr = this . getArrayKeyManager ( ak ) ;
1000+
9881001 const affected3 = this . collectDependents ( [ ak ] ) ;
9891002 this . recomputeTargets ( affected3 ) ;
1003+
9901004 const mark = ( mask : ChangeMask = ChangeTag . Value ) => this . enqueueNotify ( [ name ] , mask ) ;
1005+
9911006 switch ( op ) {
9921007 case 'insert' : {
9931008 const { index, item } = args ;
9941009 next . splice ( index , 0 , item ) ;
1010+ keyMgr . keys . splice ( index , 0 , ( keyMgr . id += 1 ) ) ;
9951011 this . _store = set ( this . _store , name , next ) ;
9961012 this . _validated . delete ( ak ) ;
9971013 mark ( ) ;
@@ -1000,6 +1016,7 @@ class FormStore {
10001016 case 'remove' : {
10011017 const { index } = args ;
10021018 next . splice ( index , 1 ) ;
1019+ keyMgr . keys . splice ( index , 1 ) ;
10031020 this . _store = set ( this . _store , name , next ) ;
10041021 this . _validated . delete ( ak ) ;
10051022 mark ( ) ;
@@ -1009,6 +1026,7 @@ class FormStore {
10091026 const { from, to } = args ;
10101027 const [ x ] = next . splice ( from , 1 ) ;
10111028 next . splice ( to , 0 , x ) ;
1029+ keyMgr . keys = move ( keyMgr . keys , from , to ) ;
10121030 this . _store = set ( this . _store , name , next ) ;
10131031 this . _validated . delete ( ak ) ;
10141032 mark ( ) ;
@@ -1019,6 +1037,7 @@ class FormStore {
10191037 const tmp = next [ i ] ;
10201038 next [ i ] = next [ j ] ;
10211039 next [ j ] = tmp ;
1040+ [ keyMgr . keys [ i ] , keyMgr . keys [ j ] ] = [ keyMgr . keys [ j ] , keyMgr . keys [ i ] ] ;
10221041 this . _store = set ( this . _store , name , next ) ;
10231042 this . _validated . delete ( ak ) ;
10241043 mark ( ) ;
@@ -1037,6 +1056,24 @@ class FormStore {
10371056 }
10381057 } ;
10391058
1059+ private getArrayFields = ( name : NamePath , initialValue ?: StoreValue [ ] ) => {
1060+ const arr = ( this . getFieldValue ( name ) as any [ ] ) || initialValue || [ ] ;
1061+
1062+ const ak = keyOfName ( name ) ;
1063+
1064+ const keyMgr = this . getArrayKeyManager ( ak ) ;
1065+
1066+ return arr . map ( ( ___ , i ) => {
1067+ if ( keyMgr . keys [ i ] === undefined ) {
1068+ keyMgr . keys [ i ] = keyMgr . id ++ ;
1069+ }
1070+ return {
1071+ key : String ( keyMgr . keys [ i ] ) ,
1072+ name : `${ ak } .${ i } `
1073+ } ;
1074+ } ) ;
1075+ } ;
1076+
10401077 // ===== FieldChange =====
10411078 triggerOnFieldsChange = ( nameList : NamePath [ ] ) => {
10421079 if ( this . _callbacks ?. onFieldsChange ) {
@@ -1280,6 +1317,7 @@ class FormStore {
12801317 return {
12811318 destroyForm : this . destroyForm ,
12821319 dispatch : this . dispatch ,
1320+ getArrayFields : this . getArrayFields ,
12831321 getInitialValue : this . getInitialValue ,
12841322 registerComputed : this . registerComputed ,
12851323 registerField : this . registerField ,
0 commit comments