1
- import React , { Fragment , memo } from 'react' ;
1
+ import React , { memo } from 'react' ;
2
2
import isEqual from 'lodash/isEqual' ;
3
3
import PropTypes from 'prop-types' ;
4
4
import { useFormApi , FieldArray } from '@data-driven-forms/react-form-renderer' ;
5
5
6
- import { Bullseye , FormHelperText , Grid , GridItem } from '@patternfly/react-core' ;
6
+ import { Bullseye , Button , Flex , FlexItem , FormFieldGroup , FormFieldGroupHeader , FormHelperText , Grid , GridItem } from '@patternfly/react-core' ;
7
7
8
- import { AddCircleOIcon , CloseIcon } from '@patternfly/react-icons' ;
8
+ import { TrashIcon } from '@patternfly/react-icons' ;
9
9
10
10
import './final-form-array.css' ;
11
11
import { useFieldApi } from '@data-driven-forms/react-form-renderer' ;
12
12
13
+ const Spacer = ( ) => < span className = "ddf-final-form-spacer" /> ;
14
+
13
15
const ArrayItem = memo (
14
- ( { fields, fieldIndex, name, remove, length, minItems } ) => {
16
+ ( { fields, fieldIndex, name, remove, length, minItems, buttonLabels , isLast } ) => {
15
17
const { renderForm } = useFormApi ( ) ;
16
18
17
- const widths = {
18
- label : fields [ 0 ] . label ? 5 : 0 ,
19
- field : fields [ 0 ] . label ? 7 : 12 ,
20
- } ;
21
-
22
19
const editedFields = fields . map ( ( field , index ) => {
23
20
const computedName = field . name ? `${ name } .${ field . name } ` : name ;
24
- return { ...field , name : computedName , key : `${ name } -${ index } ` , hideLabel : true } ;
21
+ return { ...field , name : computedName , key : `${ name } -${ index } ` } ;
25
22
} ) ;
26
23
24
+ const isRemoveDisabled = length <= minItems ;
25
+
27
26
return (
28
27
< React . Fragment >
29
- < Grid >
30
- < GridItem sm = { 11 } >
31
- < hr className = "ddf-final-form-hr" />
32
- </ GridItem >
33
- </ Grid >
34
- < Grid >
35
- < GridItem sm = { 11 } >
36
- { editedFields . map ( ( field , index ) => (
37
- < Grid key = { `${ field . label } -${ index } ` } className = "ddf-final-form-array-grid" >
38
- { widths . label > 0 && (
39
- < GridItem sm = { widths . label } key = { `${ field . label } -${ index } ` } >
40
- < label htmlFor = { field . name } >
41
- { field . label }
42
- { field . isRequired && < span className = "pf-c-form__label-required" > *</ span > }
43
- </ label >
44
- </ GridItem >
45
- ) }
46
- < GridItem sm = { widths . field } > { renderForm ( [ field ] ) } </ GridItem >
47
- </ Grid >
48
- ) ) }
49
- </ GridItem >
50
- < GridItem sm = { 1 } >
51
- { length > minItems && (
52
- < Bullseye >
53
- < CloseIcon onClick = { ( ) => remove ( fieldIndex ) } className = "ddf-final-form-group-remove-icon" />
54
- </ Bullseye >
55
- ) }
56
- { length <= minItems && (
57
- < Bullseye >
58
- < CloseIcon className = "ddf-final-form-group-remove-icon disabled" />
59
- </ Bullseye >
60
- ) }
61
- </ GridItem >
62
- </ Grid >
28
+ < Flex >
29
+ < FlexItem className = "pf-c-form" grow = { { default : 'flex_1' } } >
30
+ { editedFields . map ( ( field ) => renderForm ( [ field ] ) ) }
31
+ </ FlexItem >
32
+ < FlexItem >
33
+ { editedFields [ 0 ] . label && < Spacer /> }
34
+ < Button
35
+ variant = "plain"
36
+ aria-label = { buttonLabels . remove }
37
+ disabled = { isRemoveDisabled }
38
+ { ...( ! isRemoveDisabled && { onClick : ( ) => remove ( fieldIndex ) } ) }
39
+ >
40
+ < TrashIcon />
41
+ </ Button >
42
+ </ FlexItem >
43
+ </ Flex >
44
+ { ! isLast && editedFields . length > 1 && < hr className = "ddf-final-form-hr" /> }
63
45
</ React . Fragment >
64
46
) ;
65
47
} ,
@@ -73,6 +55,10 @@ ArrayItem.propTypes = {
73
55
remove : PropTypes . func . isRequired ,
74
56
length : PropTypes . number ,
75
57
minItems : PropTypes . number ,
58
+ buttonLabels : PropTypes . shape ( {
59
+ remove : PropTypes . node ,
60
+ } ) ,
61
+ isLast : PropTypes . bool ,
76
62
} ;
77
63
78
64
const DynamicArray = ( { ...props } ) => {
@@ -86,22 +72,49 @@ const DynamicArray = ({ ...props }) => {
86
72
minItems,
87
73
maxItems,
88
74
noItemsMessage,
75
+ buttonLabels,
89
76
...rest
90
77
} = useFieldApi ( props ) ;
91
78
const { dirty, submitFailed, error, submitError } = meta ;
92
79
const isError = ( dirty || submitFailed ) && ( error || submitError ) && ( typeof error === 'string' || typeof submitError === 'string' ) ;
93
80
81
+ const combinedButtonLabels = {
82
+ add : 'Add item' ,
83
+ removeAll : 'Delete all' ,
84
+ remove : 'Remove' ,
85
+ ...buttonLabels ,
86
+ } ;
87
+
94
88
return (
95
89
< FieldArray key = { rest . input . name } name = { rest . input . name } validate = { arrayValidator } >
96
- { ( { fields : { map, value = [ ] , push, remove } } ) => (
97
- < Fragment >
98
- { label && < GridItem sm = { 12 } > { label } </ GridItem > }
99
- { description && < GridItem sm = { 12 } > { description } </ GridItem > }
100
- { value . length <= 0 && (
101
- < Bullseye >
102
- < GridItem sm = { 12 } > { noItemsMessage } </ GridItem >
103
- </ Bullseye >
104
- ) }
90
+ { ( { fields : { map, value = [ ] , push, remove, removeBatch } } ) => (
91
+ < FormFieldGroup
92
+ header = {
93
+ < FormFieldGroupHeader
94
+ titleText = { { text : label , id : props . name } }
95
+ titleDescription = { description }
96
+ actions = {
97
+ < React . Fragment >
98
+ < Button
99
+ variant = "link"
100
+ isDisabled = { value . length === 0 }
101
+ { ...( value . length !== 0 && { onClick : ( ) => removeBatch ( value . map ( ( _ , index ) => index ) ) } ) }
102
+ >
103
+ { combinedButtonLabels . removeAll }
104
+ </ Button >
105
+ < Button
106
+ variant = "secondary"
107
+ isDisabled = { value . length >= maxItems }
108
+ { ...( ! ( value . length >= maxItems ) && { onClick : ( ) => push ( defaultItem ) } ) }
109
+ >
110
+ { combinedButtonLabels . add }
111
+ </ Button >
112
+ </ React . Fragment >
113
+ }
114
+ />
115
+ }
116
+ >
117
+ { value . length <= 0 && < Bullseye > { noItemsMessage } </ Bullseye > }
105
118
{ map ( ( name , index ) => (
106
119
< ArrayItem
107
120
key = { `${ name } -${ index } ` }
@@ -111,6 +124,8 @@ const DynamicArray = ({ ...props }) => {
111
124
remove = { remove }
112
125
length = { value . length }
113
126
minItems = { minItems }
127
+ buttonLabels = { combinedButtonLabels }
128
+ isLast = { value . length === index + 1 }
114
129
/>
115
130
) ) }
116
131
< Grid >
@@ -121,33 +136,27 @@ const DynamicArray = ({ ...props }) => {
121
136
</ FormHelperText >
122
137
) }
123
138
</ GridItem >
124
- < GridItem sm = { 1 } className = "final-form-array-add-container" >
125
- { value . length < maxItems && (
126
- < Bullseye >
127
- < AddCircleOIcon onClick = { ( ) => push ( defaultItem ) } className = "ddf-final-form-group-add-icon" />
128
- </ Bullseye >
129
- ) }
130
- { value . length >= maxItems && (
131
- < Bullseye >
132
- < AddCircleOIcon className = "ddf-final-form-group-add-icon disabled" />
133
- </ Bullseye >
134
- ) }
135
- </ GridItem >
136
139
</ Grid >
137
- </ Fragment >
140
+ </ FormFieldGroup >
138
141
) }
139
142
</ FieldArray >
140
143
) ;
141
144
} ;
142
145
143
146
DynamicArray . propTypes = {
147
+ name : PropTypes . string ,
144
148
label : PropTypes . node ,
145
149
description : PropTypes . node ,
146
150
fields : PropTypes . arrayOf ( PropTypes . object ) . isRequired ,
147
151
defaultItem : PropTypes . any ,
148
152
minItems : PropTypes . number ,
149
153
maxItems : PropTypes . number ,
150
154
noItemsMessage : PropTypes . node ,
155
+ buttonLabels : PropTypes . shape ( {
156
+ add : PropTypes . node ,
157
+ remove : PropTypes . node ,
158
+ removeAll : PropTypes . node ,
159
+ } ) ,
151
160
} ;
152
161
153
162
DynamicArray . defaultProps = {
0 commit comments