1
- import React from 'react' ;
1
+ import React , { useReducer } from 'react' ;
2
2
import PropTypes from 'prop-types' ;
3
3
import { useFormApi , FieldArray } from '@data-driven-forms/react-form-renderer' ;
4
4
@@ -8,6 +8,9 @@ import Typography from '@material-ui/core/Typography';
8
8
import FormControl from '@material-ui/core/FormControl' ;
9
9
import FormHelperText from '@material-ui/core/FormHelperText' ;
10
10
import { makeStyles } from '@material-ui/core/styles' ;
11
+ import RedoIcon from '@material-ui/icons/Redo' ;
12
+ import UndoIcon from '@material-ui/icons/Undo' ;
13
+ import IconButton from '@material-ui/core/IconButton' ;
11
14
12
15
import { useFieldApi } from '@data-driven-forms/react-form-renderer' ;
13
16
@@ -77,6 +80,38 @@ const defaultButtonLabels = {
77
80
remove : 'REMOVE'
78
81
} ;
79
82
83
+ const initialState = {
84
+ index : 0 ,
85
+ history : [ ]
86
+ } ;
87
+
88
+ export const reducer = ( state , { type, action } ) => {
89
+ switch ( type ) {
90
+ case 'redo' :
91
+ return {
92
+ ...state ,
93
+ index : state . index + 1
94
+ } ;
95
+ case 'action' :
96
+ return {
97
+ index : state . index + 1 ,
98
+ history : [ ...state . history . slice ( 0 , state . index ) , action ]
99
+ } ;
100
+ case 'undo' :
101
+ return {
102
+ ...state ,
103
+ index : state . index - 1
104
+ } ;
105
+ case 'resetHistory' :
106
+ return {
107
+ ...state ,
108
+ history : state . history . slice ( 0 , state . index )
109
+ } ;
110
+ default :
111
+ return state ;
112
+ }
113
+ } ;
114
+
80
115
const DynamicArray = ( { ...props } ) => {
81
116
const {
82
117
arrayValidator,
@@ -93,6 +128,7 @@ const DynamicArray = ({ ...props }) => {
93
128
buttonLabels,
94
129
...rest
95
130
} = useFieldApi ( props ) ;
131
+ const [ state , dispatch ] = useReducer ( reducer , initialState ) ;
96
132
97
133
const combinedButtonLabels = {
98
134
...defaultButtonLabels ,
@@ -108,51 +144,80 @@ const DynamicArray = ({ ...props }) => {
108
144
< FormFieldGrid { ...FormFieldGridProps } className = { clsx ( classes . fieldArrayGroup , FormFieldGridProps . classname ) } >
109
145
< FormControl component = "fieldset" error = { isError } { ...FormControlProps } className = { clsx ( classes . formControl , FormControlProps . className ) } >
110
146
< FieldArray key = { rest . input . name } name = { rest . input . name } validate = { arrayValidator } >
111
- { ( { fields : { map, value = [ ] , push, remove } } ) => (
112
- < Grid container spacing = { 3 } >
113
- { label && (
147
+ { ( { fields : { map, value = [ ] , push, remove } } ) => {
148
+ const pushWrapper = ( ) => {
149
+ dispatch ( { type : 'resetHistory' } ) ;
150
+ push ( defaultItem ) ;
151
+ } ;
152
+
153
+ const removeWrapper = ( index ) => {
154
+ dispatch ( { type : 'action' , action : { action : 'remove' , value : value [ index ] } } ) ;
155
+ remove ( index ) ;
156
+ } ;
157
+
158
+ const undo = ( ) => {
159
+ push ( state . history [ state . index - 1 ] . value ) ;
160
+
161
+ dispatch ( { type : 'undo' } ) ;
162
+ } ;
163
+
164
+ const redo = ( ) => {
165
+ remove ( value . length - 1 ) ;
166
+
167
+ dispatch ( { type : 'redo' } ) ;
168
+ } ;
169
+
170
+ return (
171
+ < Grid container spacing = { 3 } >
114
172
< Grid item xs = { 12 } className = { classes . header } >
115
- < Typography variant = "h6" className = { classes . label } >
116
- { label }
117
- </ Typography >
118
- < Button color = "primary" onClick = { ( ) => push ( defaultItem ) } disabled = { value . length >= maxItems } >
173
+ { label && (
174
+ < Typography variant = "h6" className = { classes . label } >
175
+ { label }
176
+ </ Typography >
177
+ ) }
178
+ < IconButton color = "primary" aria-label = "undo" component = "span" disabled = { state . index === 0 } onClick = { undo } >
179
+ < UndoIcon />
180
+ </ IconButton >
181
+ < IconButton color = "primary" aria-label = "redo" component = "span" disabled = { state . index === state . history . length } onClick = { redo } >
182
+ < RedoIcon />
183
+ </ IconButton >
184
+ < Button color = "primary" onClick = { pushWrapper } disabled = { value . length >= maxItems } >
119
185
{ combinedButtonLabels . add }
120
186
</ Button >
121
187
</ Grid >
122
- ) }
123
- { description && (
124
- < Grid item xs = { 12 } >
125
- < Typography variant = "subtitle1" > { description } </ Typography >
126
- </ Grid >
127
- ) }
128
- { value . length <= 0 && (
188
+ { description && (
189
+ < Grid item xs = { 12 } >
190
+ < Typography variant = "subtitle1" > { description } </ Typography >
191
+ </ Grid >
192
+ ) }
129
193
< Grid item xs = { 12 } >
130
- < Typography variant = "body1" gutterBottom className = { classes . centerText } >
131
- { noItemsMessage }
132
- </ Typography >
194
+ { value . length <= 0 ? (
195
+ < Typography variant = "body1" gutterBottom className = { classes . centerText } >
196
+ { noItemsMessage }
197
+ </ Typography >
198
+ ) : (
199
+ map ( ( name , index ) => (
200
+ < ArrayItem
201
+ key = { `${ name } -${ index } ` }
202
+ fields = { formFields }
203
+ name = { name }
204
+ fieldIndex = { index }
205
+ remove = { removeWrapper }
206
+ length = { value . length }
207
+ minItems = { minItems }
208
+ removeLabel = { combinedButtonLabels . remove }
209
+ />
210
+ ) )
211
+ ) }
133
212
</ Grid >
134
- ) }
135
- < Grid item xs = { 12 } >
136
- { map ( ( name , index ) => (
137
- < ArrayItem
138
- key = { `${ name } -${ index } ` }
139
- fields = { formFields }
140
- name = { name }
141
- fieldIndex = { index }
142
- remove = { remove }
143
- length = { value . length }
144
- minItems = { minItems }
145
- removeLabel = { combinedButtonLabels . remove }
146
- />
147
- ) ) }
148
213
{ isError && (
149
214
< Grid item xs = { 12 } >
150
215
< FormHelperText > { error } </ FormHelperText >
151
216
</ Grid >
152
217
) }
153
218
</ Grid >
154
- </ Grid >
155
- ) }
219
+ ) ;
220
+ } }
156
221
</ FieldArray >
157
222
</ FormControl >
158
223
</ FormFieldGrid >
0 commit comments