1- import React , { Fragment , memo } from 'react' ;
1+ import React , { memo } from 'react' ;
22import isEqual from 'lodash/isEqual' ;
33import PropTypes from 'prop-types' ;
44import { useFormApi , FieldArray } from '@data-driven-forms/react-form-renderer' ;
55
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' ;
77
8- import { AddCircleOIcon , CloseIcon } from '@patternfly/react-icons' ;
8+ import { TrashIcon } from '@patternfly/react-icons' ;
99
1010import './final-form-array.css' ;
1111import { useFieldApi } from '@data-driven-forms/react-form-renderer' ;
1212
13+ const Spacer = ( ) => < span className = "ddf-final-form-spacer" /> ;
14+
1315const ArrayItem = memo (
14- ( { fields, fieldIndex, name, remove, length, minItems } ) => {
16+ ( { fields, fieldIndex, name, remove, length, minItems, buttonLabels , isLast } ) => {
1517 const { renderForm } = useFormApi ( ) ;
1618
17- const widths = {
18- label : fields [ 0 ] . label ? 5 : 0 ,
19- field : fields [ 0 ] . label ? 7 : 12 ,
20- } ;
21-
2219 const editedFields = fields . map ( ( field , index ) => {
2320 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 } ` } ;
2522 } ) ;
2623
24+ const isRemoveDisabled = length <= minItems ;
25+
2726 return (
2827 < 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" /> }
6345 </ React . Fragment >
6446 ) ;
6547 } ,
@@ -73,6 +55,10 @@ ArrayItem.propTypes = {
7355 remove : PropTypes . func . isRequired ,
7456 length : PropTypes . number ,
7557 minItems : PropTypes . number ,
58+ buttonLabels : PropTypes . shape ( {
59+ remove : PropTypes . node ,
60+ } ) ,
61+ isLast : PropTypes . bool ,
7662} ;
7763
7864const DynamicArray = ( { ...props } ) => {
@@ -86,22 +72,49 @@ const DynamicArray = ({ ...props }) => {
8672 minItems,
8773 maxItems,
8874 noItemsMessage,
75+ buttonLabels,
8976 ...rest
9077 } = useFieldApi ( props ) ;
9178 const { dirty, submitFailed, error, submitError } = meta ;
9279 const isError = ( dirty || submitFailed ) && ( error || submitError ) && ( typeof error === 'string' || typeof submitError === 'string' ) ;
9380
81+ const combinedButtonLabels = {
82+ add : 'Add item' ,
83+ removeAll : 'Delete all' ,
84+ remove : 'Remove' ,
85+ ...buttonLabels ,
86+ } ;
87+
9488 return (
9589 < 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 > }
105118 { map ( ( name , index ) => (
106119 < ArrayItem
107120 key = { `${ name } -${ index } ` }
@@ -111,6 +124,8 @@ const DynamicArray = ({ ...props }) => {
111124 remove = { remove }
112125 length = { value . length }
113126 minItems = { minItems }
127+ buttonLabels = { combinedButtonLabels }
128+ isLast = { value . length === index + 1 }
114129 />
115130 ) ) }
116131 < Grid >
@@ -121,33 +136,27 @@ const DynamicArray = ({ ...props }) => {
121136 </ FormHelperText >
122137 ) }
123138 </ 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 >
136139 </ Grid >
137- </ Fragment >
140+ </ FormFieldGroup >
138141 ) }
139142 </ FieldArray >
140143 ) ;
141144} ;
142145
143146DynamicArray . propTypes = {
147+ name : PropTypes . string ,
144148 label : PropTypes . node ,
145149 description : PropTypes . node ,
146150 fields : PropTypes . arrayOf ( PropTypes . object ) . isRequired ,
147151 defaultItem : PropTypes . any ,
148152 minItems : PropTypes . number ,
149153 maxItems : PropTypes . number ,
150154 noItemsMessage : PropTypes . node ,
155+ buttonLabels : PropTypes . shape ( {
156+ add : PropTypes . node ,
157+ remove : PropTypes . node ,
158+ removeAll : PropTypes . node ,
159+ } ) ,
151160} ;
152161
153162DynamicArray . defaultProps = {
0 commit comments