@@ -7,13 +7,16 @@ import {
7
7
ReactNode ,
8
8
useCallback ,
9
9
useMemo ,
10
+ useRef ,
10
11
} from 'react' ;
11
12
import { FormHelperText } from '@material-ui/core' ;
12
13
import classNames from 'classnames' ;
13
14
import get from 'lodash/get' ;
14
15
import PropTypes from 'prop-types' ;
15
16
import { Record , ValidationError } from 'ra-core' ;
16
17
import { FieldArrayRenderProps } from 'react-final-form-arrays' ;
18
+ import { CSSTransition , TransitionGroup } from 'react-transition-group' ;
19
+ import { CSSTransitionProps } from 'react-transition-group/CSSTransition' ;
17
20
18
21
import { ClassesOverride } from '../../types' ;
19
22
import { useArrayInput } from './useArrayInput' ;
@@ -51,16 +54,37 @@ export const SimpleFormIterator = (props: SimpleFormIteratorProps) => {
51
54
const classes = useSimpleFormIteratorStyles ( props ) ;
52
55
const { fields, meta } = useArrayInput ( props ) ;
53
56
const { error, submitFailed } = meta ;
57
+ const nodeRef = useRef ( null ) ;
58
+
59
+ // We need a unique id for each field for a proper enter/exit animation
60
+ // so we keep an internal map between the field position and an auto-increment id
61
+ const nextId = useRef (
62
+ fields && fields . length
63
+ ? fields . length
64
+ : defaultValue
65
+ ? defaultValue . length
66
+ : 0
67
+ ) ;
68
+
69
+ // We check whether we have a defaultValue (which must be an array) before checking
70
+ // the fields prop which will always be empty for a new record.
71
+ // Without it, our ids wouldn't match the default value and we would get key warnings
72
+ // on the CssTransition element inside our render method
73
+ const ids = useRef (
74
+ nextId . current > 0 ? Array . from ( Array ( nextId . current ) . keys ( ) ) : [ ]
75
+ ) ;
54
76
55
77
const removeField = useCallback (
56
78
( index : number ) => {
79
+ ids . current . splice ( index , 1 ) ;
57
80
fields . remove ( index ) ;
58
81
} ,
59
82
[ fields ]
60
83
) ;
61
84
62
85
const addField = useCallback (
63
86
( item : any = undefined ) => {
87
+ ids . current . push ( nextId . current ++ ) ;
64
88
fields . push ( item ) ;
65
89
} ,
66
90
[ fields ]
@@ -78,6 +102,9 @@ export const SimpleFormIterator = (props: SimpleFormIteratorProps) => {
78
102
79
103
const handleReorder = useCallback (
80
104
( origin : number , destination : number ) => {
105
+ const item = ids . current [ origin ] ;
106
+ ids . current [ origin ] = ids . current [ destination ] ;
107
+ ids . current [ destination ] = item ;
81
108
fields . move ( origin , destination ) ;
82
109
} ,
83
110
[ fields ]
@@ -102,32 +129,41 @@ export const SimpleFormIterator = (props: SimpleFormIteratorProps) => {
102
129
< ValidationError error = { error as string } />
103
130
</ FormHelperText >
104
131
) }
105
- { fields . map ( ( member , index ) => (
106
- < SimpleFormIteratorItem
107
- key = { member }
108
- basePath = { basePath }
109
- classes = { classes }
110
- disabled = { disabled }
111
- disableRemove = { disableRemove }
112
- disableReordering = { disableReordering }
113
- fields = { fields }
114
- getItemLabel = { getItemLabel }
115
- index = { index }
116
- margin = { margin }
117
- member = { member }
118
- meta = { meta }
119
- onRemoveField = { removeField }
120
- onReorder = { handleReorder }
121
- record = { ( records && records [ index ] ) || { } }
122
- removeButton = { removeButton }
123
- reOrderButtons = { reOrderButtons }
124
- resource = { resource }
125
- source = { source }
126
- variant = { variant }
127
- >
128
- { children }
129
- </ SimpleFormIteratorItem >
130
- ) ) }
132
+ < TransitionGroup component = { null } >
133
+ { fields . map ( ( member , index ) => (
134
+ < CSSTransition
135
+ nodeRef = { nodeRef }
136
+ key = { ids . current [ index ] }
137
+ timeout = { 500 }
138
+ classNames = "fade"
139
+ { ...TransitionProps }
140
+ >
141
+ < SimpleFormIteratorItem
142
+ basePath = { basePath }
143
+ classes = { classes }
144
+ disabled = { disabled }
145
+ disableRemove = { disableRemove }
146
+ disableReordering = { disableReordering }
147
+ fields = { fields }
148
+ getItemLabel = { getItemLabel }
149
+ index = { index }
150
+ margin = { margin }
151
+ member = { member }
152
+ meta = { meta }
153
+ onRemoveField = { removeField }
154
+ onReorder = { handleReorder }
155
+ record = { ( records && records [ index ] ) || { } }
156
+ removeButton = { removeButton }
157
+ reOrderButtons = { reOrderButtons }
158
+ resource = { resource }
159
+ source = { source }
160
+ variant = { variant }
161
+ >
162
+ { children }
163
+ </ SimpleFormIteratorItem >
164
+ </ CSSTransition >
165
+ ) ) }
166
+ </ TransitionGroup >
131
167
{ ! disabled && ! disableAdd && (
132
168
< li className = { classes . line } >
133
169
< span className = { classes . action } >
@@ -198,6 +234,7 @@ export interface SimpleFormIteratorProps
198
234
reOrderButtons ?: ReactElement ;
199
235
resource ?: string ;
200
236
source ?: string ;
237
+ TransitionProps ?: CSSTransitionProps ;
201
238
variant ?: 'standard' | 'outlined' | 'filled' ;
202
239
}
203
240
0 commit comments