1- import React from 'react' ;
1+ import React , { useEffect , useReducer } from 'react' ;
22import PropTypes from 'prop-types' ;
33import lodashIsEmpty from 'lodash/isEmpty' ;
4- import { FormSpy } from 'react-final-form' ;
54import get from 'lodash/get' ;
5+ import isEqual from 'lodash/isEqual' ;
6+
7+ import useFormApi from '../files/use-form-api' ;
68
79const isEmptyValue = ( value ) => ( typeof value === 'number' || value === true ? false : lodashIsEmpty ( value ) ) ;
810
@@ -27,48 +29,156 @@ const fieldCondition = (value, { is, isNotEmpty, isEmpty, pattern, notMatch, fla
2729} ;
2830
2931export const parseCondition = ( condition , values ) => {
32+ let positiveResult = {
33+ visible : true ,
34+ ...condition . then ,
35+ result : true
36+ } ;
37+
38+ let negativeResult = {
39+ visible : false ,
40+ ...condition . else ,
41+ result : false
42+ } ;
43+
3044 if ( Array . isArray ( condition ) ) {
31- return ! condition . map ( ( condition ) => parseCondition ( condition , values ) ) . some ( ( result ) => result === false ) ;
45+ return ! condition . map ( ( condition ) => parseCondition ( condition , values ) ) . some ( ( { result } ) => result === false ) ? positiveResult : negativeResult ;
3246 }
3347
3448 if ( condition . and ) {
35- return condition . and . map ( ( condition ) => parseCondition ( condition , values ) ) . every ( ( result ) => result === true ) ;
49+ return ! condition . and . map ( ( condition ) => parseCondition ( condition , values ) ) . some ( ( { result } ) => result === false )
50+ ? positiveResult
51+ : negativeResult ;
52+ }
53+
54+ if ( condition . sequence ) {
55+ return condition . sequence . reduce (
56+ ( acc , curr ) => {
57+ const result = parseCondition ( curr , values ) ;
58+
59+ return {
60+ sets : [ ...acc . sets , ...( result . set ? [ result . set ] : [ ] ) ] ,
61+ visible : acc . visible || result . visible ,
62+ result : acc . result || result . result
63+ } ;
64+ } ,
65+ { ...negativeResult , sets : [ ] }
66+ ) ;
3667 }
3768
3869 if ( condition . or ) {
39- return condition . or . map ( ( condition ) => parseCondition ( condition , values ) ) . some ( ( result ) => result === true ) ;
70+ return condition . or . map ( ( condition ) => parseCondition ( condition , values ) ) . some ( ( { result } ) => result === true ) ? positiveResult : negativeResult ;
4071 }
4172
4273 if ( condition . not ) {
43- return ! parseCondition ( condition . not , values ) ;
74+ return ! parseCondition ( condition . not , values ) . result ? positiveResult : negativeResult ;
4475 }
4576
4677 if ( typeof condition . when === 'string' ) {
47- return fieldCondition ( get ( values , condition . when ) , condition ) ;
78+ return fieldCondition ( get ( values , condition . when ) , condition ) ? positiveResult : negativeResult ;
4879 }
4980
5081 if ( Array . isArray ( condition . when ) ) {
51- return ! ! condition . when . map ( ( fieldName ) => fieldCondition ( get ( values , fieldName ) , condition ) ) . find ( ( condition ) => ! ! condition ) ;
82+ return condition . when . map ( ( fieldName ) => fieldCondition ( get ( values , fieldName ) , condition ) ) . find ( ( condition ) => ! ! condition )
83+ ? positiveResult
84+ : negativeResult ;
5285 }
5386
54- return false ;
87+ return negativeResult ;
5588} ;
5689
57- const Condition = ( { condition, children } ) => < FormSpy > { ( { values } ) => ( parseCondition ( condition , values ) ? children : null ) } </ FormSpy > ;
90+ export const reducer = ( state , { type, sets } ) => {
91+ switch ( type ) {
92+ case 'formResetted' :
93+ return {
94+ ...state ,
95+ initial : true
96+ } ;
97+ case 'rememberSets' :
98+ return {
99+ ...state ,
100+ initial : false ,
101+ sets
102+ } ;
103+ default :
104+ return state ;
105+ }
106+ } ;
107+
108+ const Condition = React . memo (
109+ ( { condition, children, values } ) => {
110+ const formOptions = useFormApi ( ) ;
111+ const dirty = formOptions . getState ( ) . dirty ;
112+
113+ const [ state , dispatch ] = useReducer ( reducer , {
114+ sets : [ ] ,
115+ initial : true
116+ } ) ;
117+
118+ const conditionResult = parseCondition ( condition , values , formOptions ) ;
119+ const setters = conditionResult . set ? [ conditionResult . set ] : conditionResult . sets ;
120+
121+ useEffect ( ( ) => {
122+ if ( ! dirty ) {
123+ dispatch ( { type : 'formResetted' } ) ;
124+ }
125+ } , [ dirty ] ) ;
126+
127+ useEffect ( ( ) => {
128+ if ( setters && setters . length > 0 && ( state . initial || ! isEqual ( setters , state . sets ) ) ) {
129+ setters . forEach ( ( setter , index ) => {
130+ if ( setter && ( state . initial || ! isEqual ( setter , state . sets [ index ] ) ) ) {
131+ setTimeout ( ( ) => {
132+ formOptions . batch ( ( ) => {
133+ Object . entries ( setter ) . forEach ( ( [ name , value ] ) => {
134+ formOptions . change ( name , value ) ;
135+ } ) ;
136+ } ) ;
137+ } ) ;
138+ }
139+ } ) ;
140+ dispatch ( { type : 'rememberSets' , sets : setters } ) ;
141+ }
142+ } , [ setters , state . initial ] ) ;
143+
144+ return conditionResult . visible ? children : null ;
145+ } ,
146+ ( a , b ) => isEqual ( a . values , b . values ) && isEqual ( a . condition , b . condition )
147+ ) ;
58148
59149const conditionProps = {
60- when : PropTypes . string . isRequired ,
61- is : PropTypes . oneOfType ( [ PropTypes . array , PropTypes . string , PropTypes . object , PropTypes . number , PropTypes . bool ] ) . isRequired ,
150+ when : PropTypes . string ,
151+ is : PropTypes . oneOfType ( [ PropTypes . array , PropTypes . string , PropTypes . object , PropTypes . number , PropTypes . bool ] ) ,
62152 isNotEmpty : PropTypes . bool ,
63153 isEmpty : PropTypes . bool ,
64- children : PropTypes . oneOf ( [ PropTypes . node , PropTypes . arrayOf ( PropTypes . node ) ] ) . isRequired ,
65154 pattern : PropTypes . oneOf ( [ PropTypes . string , PropTypes . instanceOf ( RegExp ) ] ) ,
66- notMatch : PropTypes . any
155+ notMatch : PropTypes . any ,
156+ then : PropTypes . shape ( {
157+ visible : PropTypes . bool ,
158+ set : PropTypes . object
159+ } ) ,
160+ else : PropTypes . shape ( {
161+ visible : PropTypes . bool ,
162+ set : PropTypes . object
163+ } )
164+ } ;
165+
166+ const nestedConditions = {
167+ or : PropTypes . oneOfType ( [ PropTypes . shape ( conditionProps ) , PropTypes . arrayOf ( PropTypes . shape ( conditionProps ) ) ] ) ,
168+ and : PropTypes . oneOfType ( [ PropTypes . shape ( conditionProps ) , PropTypes . arrayOf ( PropTypes . shape ( conditionProps ) ) ] ) ,
169+ not : PropTypes . oneOfType ( [ PropTypes . shape ( conditionProps ) , PropTypes . arrayOf ( PropTypes . shape ( conditionProps ) ) ] ) ,
170+ sequence : PropTypes . arrayOf ( PropTypes . shape ( conditionProps ) )
171+ } ;
172+
173+ const conditionsProps = {
174+ ...conditionProps ,
175+ ...nestedConditions
67176} ;
68177
69178Condition . propTypes = {
70- condition : PropTypes . oneOfType ( [ PropTypes . shape ( conditionProps ) , PropTypes . arrayOf ( PropTypes . shape ( conditionProps ) ) ] ) ,
71- children : PropTypes . oneOf ( [ PropTypes . node , PropTypes . arrayOf ( PropTypes . node ) ] ) . isRequired
179+ condition : PropTypes . oneOfType ( [ PropTypes . shape ( conditionsProps ) , PropTypes . arrayOf ( PropTypes . shape ( conditionsProps ) ) ] ) ,
180+ children : PropTypes . oneOfType ( [ PropTypes . node , PropTypes . arrayOf ( PropTypes . node ) ] ) . isRequired ,
181+ values : PropTypes . object . isRequired
72182} ;
73183
74184export default Condition ;
0 commit comments