@flowerforce/flower-core 3.1.2-beta.0 → 3.1.2-beta.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.cjs.js CHANGED
@@ -27,254 +27,246 @@ const EMPTY_STRING_REGEXP = /^\s*$/;
27
27
  * Defines a utility object named rulesMatcherUtils, which contains various helper functions used for processing rules and data in a rule-matching context.
28
28
  */
29
29
  const rulesMatcherUtils = {
30
- isNumber: el => {
31
- const num = String(el);
32
- return !!num.match(/(^-?|^\d+\.)\d+$/);
33
- },
34
- rule: (val, data, options) => {
35
- const {
36
- prefix
37
- } = options || {};
38
- const path = Object.keys(val)[0];
39
- const valueBlock = val;
40
- const pathWithPrefix = rulesMatcherUtils.getPath(path, prefix);
41
- const valueForKey = _get(data, pathWithPrefix, undefined);
42
- const {
43
- name
44
- } = _get(valueBlock, [path], {}) || {};
45
- const {
46
- op,
47
- value,
48
- opt
49
- } = rulesMatcherUtils.getDefaultRule(valueBlock[path]);
50
- const valueRef = value && String(value).indexOf('$ref:') === 0 ? _get(data, rulesMatcherUtils.getPath(value.replace('$ref:', ''), prefix), undefined) : value;
51
- if (!operators[op]) {
52
- throw new Error(`Error missing operator:${op}`);
53
- }
54
- const valid = operators[op] && operators[op](valueForKey, valueRef, opt, data);
55
- return {
56
- valid,
57
- name: `${pathWithPrefix}___${name || op}`
58
- };
59
- },
60
- getKey: (block, keys, options) => {
61
- if (Object.prototype.hasOwnProperty.call(block, '$and')) {
62
- return block.$and.map(item => rulesMatcherUtils.getKey(item, keys, options));
63
- }
64
- if (Object.prototype.hasOwnProperty.call(block, '$or')) {
65
- return block.$or.map(item => rulesMatcherUtils.getKey(item, keys, options));
66
- }
67
- const {
68
- prefix
69
- } = options || {};
70
- const path = Object.keys(block)[0];
71
- const valueBlock = block;
72
- const res = rulesMatcherUtils.getPath(path, prefix);
73
- const {
74
- value
75
- } = rulesMatcherUtils.getDefaultRule(valueBlock[path]);
76
- if (value && String(value).indexOf('$ref:') === 0) {
77
- keys[rulesMatcherUtils.getPath(value.replace('$ref:', ''), prefix)] = true;
78
- }
79
- return keys[res] = true;
80
- },
81
- isDate: v => v instanceof Date,
82
- isDefined: v => v !== null && v !== undefined,
83
- isObject: v => v === Object(v),
84
- isFunction: v => typeof v === 'function',
85
- isString: v => typeof v === 'string',
86
- getDefaultStringValue: value => {
87
- switch (value) {
88
- case '$required':
89
- return {
90
- op: '$exists',
91
- value: true
92
- };
93
- case '$exists':
94
- return {
95
- op: '$exists',
96
- value: true
97
- };
98
- default:
99
- return {
100
- op: '$eq',
101
- value
102
- };
103
- }
104
- },
105
- getTypeOf: value => Array.isArray(value) ? 'array' : rulesMatcherUtils.isNumber(value) ? 'number' : value === null ? null : typeof value,
106
- getDefaultRule: value => {
107
- const valueType = rulesMatcherUtils.getTypeOf(value);
108
- switch (valueType) {
109
- case 'number':
110
- return {
111
- op: '$eq',
112
- value
113
- };
114
- case 'string':
115
- return rulesMatcherUtils.getDefaultStringValue(value);
116
- case 'boolean':
117
- return {
118
- op: '$exists',
119
- value
120
- };
121
- case 'array':
122
- return {
123
- op: '$in',
124
- value
125
- };
126
- case 'object':
127
- return {
128
- ...value,
129
- op: value.op || Object.keys(value)[0],
130
- value: value.value || value[Object.keys(value)[0]]
131
- };
132
- default:
133
- return {
134
- op: '$eq',
135
- value
136
- };
137
- }
138
- },
139
- isEmpty: value => {
140
- // Null and undefined are empty
141
- if (!rulesMatcherUtils.isDefined(value)) {
142
- return true;
143
- }
144
- // functions are non empty
145
- if (rulesMatcherUtils.isFunction(value)) {
146
- return false;
147
- }
148
- /* if (isBool(value)) {
149
- return false;
150
- }
151
- */
152
- // Whitespace only strings are empty
153
- if (rulesMatcherUtils.isString(value)) {
154
- return EMPTY_STRING_REGEXP.test(value);
155
- }
156
- // For arrays we use the length property
157
- if (Array.isArray(value)) {
158
- return value.length === 0;
159
- }
160
- // Dates have no attributes but aren't empty
161
- if (rulesMatcherUtils.isDate(value)) {
162
- return false;
163
- }
164
- // If we find at least one property we consider it non empty
165
- let attr;
166
- if (rulesMatcherUtils.isObject(value)) {
167
- for (attr in value) {
30
+ isNumber: (el) => {
31
+ const num = String(el);
32
+ return !!num.match(/(^-?|^\d+\.)\d+$/);
33
+ },
34
+ rule: (val, data, options) => {
35
+ const { prefix } = options || {};
36
+ const path = Object.keys(val)[0];
37
+ const valueBlock = val;
38
+ const pathWithPrefix = rulesMatcherUtils.getPath(path, prefix);
39
+ const valueForKey = _get(data, pathWithPrefix, undefined);
40
+ const { name } = _get(valueBlock, [path], {}) || {};
41
+ const { op, value, opt } = rulesMatcherUtils.getDefaultRule(valueBlock[path]);
42
+ const valueRef = value && String(value).indexOf('$ref:') === 0
43
+ ? _get(data, rulesMatcherUtils.getPath(value.replace('$ref:', ''), prefix), undefined)
44
+ : value;
45
+ if (!operators[op]) {
46
+ throw new Error(`Error missing operator:${op}`);
47
+ }
48
+ const valid = operators[op] && operators[op](valueForKey, valueRef, opt, data);
49
+ return { valid, name: `${pathWithPrefix}___${name || op}` };
50
+ },
51
+ getKey: (block, keys, options) => {
52
+ if (Object.prototype.hasOwnProperty.call(block, '$and')) {
53
+ return block.$and.map((item) => rulesMatcherUtils.getKey(item, keys, options));
54
+ }
55
+ if (Object.prototype.hasOwnProperty.call(block, '$or')) {
56
+ return block.$or.map((item) => rulesMatcherUtils.getKey(item, keys, options));
57
+ }
58
+ const { prefix } = options || {};
59
+ const path = Object.keys(block)[0];
60
+ const valueBlock = block;
61
+ const res = rulesMatcherUtils.getPath(path, prefix);
62
+ const { value } = rulesMatcherUtils.getDefaultRule(valueBlock[path]);
63
+ if (value && String(value).indexOf('$ref:') === 0) {
64
+ keys[rulesMatcherUtils.getPath(value.replace('$ref:', ''), prefix)] = true;
65
+ }
66
+ return (keys[res] = true);
67
+ },
68
+ isDate: (v) => v instanceof Date,
69
+ isDefined: (v) => v !== null && v !== undefined,
70
+ isObject: (v) => v === Object(v),
71
+ isFunction: (v) => typeof v === 'function',
72
+ isString: (v) => typeof v === 'string',
73
+ getDefaultStringValue: (value) => {
74
+ switch (value) {
75
+ case '$required':
76
+ return { op: '$exists', value: true };
77
+ case '$exists':
78
+ return { op: '$exists', value: true };
79
+ default:
80
+ return { op: '$eq', value };
81
+ }
82
+ },
83
+ getTypeOf: (value) => Array.isArray(value)
84
+ ? 'array'
85
+ : rulesMatcherUtils.isNumber(value)
86
+ ? 'number'
87
+ : value === null
88
+ ? null
89
+ : typeof value,
90
+ getDefaultRule: (value) => {
91
+ const valueType = rulesMatcherUtils.getTypeOf(value);
92
+ switch (valueType) {
93
+ case 'number':
94
+ return { op: '$eq', value };
95
+ case 'string':
96
+ return rulesMatcherUtils.getDefaultStringValue(value);
97
+ case 'boolean':
98
+ return { op: '$exists', value };
99
+ case 'array':
100
+ return { op: '$in', value };
101
+ case 'object':
102
+ return {
103
+ ...value,
104
+ op: value.op || Object.keys(value)[0],
105
+ value: value.value || value[Object.keys(value)[0]]
106
+ };
107
+ default:
108
+ return { op: '$eq', value };
109
+ }
110
+ },
111
+ isEmpty: (value) => {
112
+ // Null and undefined are empty
113
+ if (!rulesMatcherUtils.isDefined(value)) {
114
+ return true;
115
+ }
116
+ // functions are non empty
117
+ if (rulesMatcherUtils.isFunction(value)) {
118
+ return false;
119
+ }
120
+ /* if (isBool(value)) {
121
+ return false;
122
+ }
123
+ */
124
+ // Whitespace only strings are empty
125
+ if (rulesMatcherUtils.isString(value)) {
126
+ return EMPTY_STRING_REGEXP.test(value);
127
+ }
128
+ // For arrays we use the length property
129
+ if (Array.isArray(value)) {
130
+ return value.length === 0;
131
+ }
132
+ // Dates have no attributes but aren't empty
133
+ if (rulesMatcherUtils.isDate(value)) {
134
+ return false;
135
+ }
136
+ // If we find at least one property we consider it non empty
137
+ let attr;
138
+ if (rulesMatcherUtils.isObject(value)) {
139
+ for (attr in value) {
140
+ return false;
141
+ }
142
+ return true;
143
+ }
168
144
  return false;
169
- }
170
- return true;
171
- }
172
- return false;
173
- },
174
- forceArray: a => Array.isArray(a) ? a : [a],
175
- getPath: (path, prefix) => {
176
- if (path.indexOf('^') === 0) {
177
- return _trimStart(path, '^');
178
- }
179
- // da verificare se è ancora utilizzato
180
- if (path.indexOf('$') === 0) {
181
- return path;
182
- }
183
- return prefix ? `${prefix}.${path}` : path;
184
- },
185
- // TODO BUG NUMERI CON LETTERE 1asdas o solo
186
- forceNumber: el => {
187
- if (Array.isArray(el)) {
188
- return el.length;
189
- }
190
- if (rulesMatcherUtils.isNumber(String(el))) {
191
- return parseFloat(String(el));
192
- }
193
- // fix perchè un valore false < 1 è true, quindi sbagliato, mentre un valore undefined < 1 è false
194
- return 0;
195
- },
196
- checkRule: (block, data, options) => {
197
- if (!Array.isArray(block) && block && Object.prototype.hasOwnProperty.call(block, '$and')) {
198
- if (block && block['$and'] && !block['$and'].length) return true;
199
- return block['$and'].every(item => rulesMatcherUtils.checkRule(item, data, options));
200
- }
201
- if (!Array.isArray(block) && block && Object.prototype.hasOwnProperty.call(block, '$or')) {
202
- if (block && block['$or'] && !block['$or'].length) return true;
203
- return block['$or'].some(item => rulesMatcherUtils.checkRule(item, data, options));
145
+ },
146
+ forceArray: (a) => (Array.isArray(a) ? a : [a]),
147
+ getPath: (path, prefix) => {
148
+ if (path.indexOf('^') === 0) {
149
+ return _trimStart(path, '^');
150
+ }
151
+ // da verificare se è ancora utilizzato
152
+ if (path.indexOf('$') === 0) {
153
+ return path;
154
+ }
155
+ return prefix ? `${prefix}.${path}` : path;
156
+ },
157
+ // TODO BUG NUMERI CON LETTERE 1asdas o solo
158
+ forceNumber: (el) => {
159
+ if (Array.isArray(el)) {
160
+ return el.length;
161
+ }
162
+ if (rulesMatcherUtils.isNumber(String(el))) {
163
+ return parseFloat(String(el));
164
+ }
165
+ // fix perchè un valore false < 1 è true, quindi sbagliato, mentre un valore undefined < 1 è false
166
+ return 0;
167
+ },
168
+ checkRule: (block, data, options) => {
169
+ if (!Array.isArray(block) &&
170
+ block &&
171
+ Object.prototype.hasOwnProperty.call(block, '$and')) {
172
+ if (block && block['$and'] && !block['$and'].length)
173
+ return true;
174
+ return block['$and'].every((item) => rulesMatcherUtils.checkRule(item, data, options));
175
+ }
176
+ if (!Array.isArray(block) &&
177
+ block &&
178
+ Object.prototype.hasOwnProperty.call(block, '$or')) {
179
+ if (block && block['$or'] && !block['$or'].length)
180
+ return true;
181
+ return block['$or'].some((item) => rulesMatcherUtils.checkRule(item, data, options));
182
+ }
183
+ const res = rulesMatcherUtils.rule(block, data, options);
184
+ return res.valid;
185
+ },
186
+ getKeys: (rules, options) => {
187
+ if (!rules)
188
+ return null;
189
+ if (typeof rules == 'function')
190
+ return [];
191
+ if (!rulesMatcherUtils
192
+ .forceArray(rules)
193
+ .every((r) => rulesMatcherUtils.isObject(r)))
194
+ return null;
195
+ const keys = {};
196
+ const conditions = Array.isArray(rules) ? { $and: rules } : rules;
197
+ rulesMatcherUtils.getKey(conditions, keys, options ?? {});
198
+ return Object.keys(keys);
204
199
  }
205
- const res = rulesMatcherUtils.rule(block, data, options);
206
- return res.valid;
207
- },
208
- getKeys: (rules, options) => {
209
- if (!rules) return null;
210
- if (typeof rules == 'function') return [];
211
- if (!rulesMatcherUtils.forceArray(rules).every(r => rulesMatcherUtils.isObject(r))) return null;
212
- const keys = {};
213
- const conditions = Array.isArray(rules) ? {
214
- $and: rules
215
- } : rules;
216
- rulesMatcherUtils.getKey(conditions, keys, options ?? {});
217
- return Object.keys(keys);
218
- }
219
200
  };
220
201
  /**
221
202
  * Defines a set of comparison operators used for matching rules against user input.
222
203
  */
223
204
  const operators = {
224
- $exists: (a, b) => !rulesMatcherUtils.isEmpty(a) === b,
225
- $eq: (a, b) => a === b,
226
- $ne: (a, b) => a !== b,
227
- $gt: (a, b) => rulesMatcherUtils.forceNumber(a) > parseFloat(b),
228
- $gte: (a, b) => rulesMatcherUtils.forceNumber(a) >= parseFloat(b),
229
- $lt: (a, b) => rulesMatcherUtils.forceNumber(a) < parseFloat(b),
230
- $lte: (a, b) => rulesMatcherUtils.forceNumber(a) <= parseFloat(b),
231
- $strGt: (a, b) => String(a || '').length > parseFloat(b),
232
- $strGte: (a, b) => String(a || '').length >= parseFloat(b),
233
- $strLt: (a, b) => String(a || '').length < parseFloat(b),
234
- $strLte: (a, b) => String(a || '').length <= parseFloat(b),
235
- $in: (a, b) => rulesMatcherUtils.forceArray(b).some(c => _intersection(rulesMatcherUtils.forceArray(a), rulesMatcherUtils.forceArray(c)).length),
236
- $nin: (a, b) => !rulesMatcherUtils.forceArray(b).some(c => _intersection(rulesMatcherUtils.forceArray(a), rulesMatcherUtils.forceArray(c)).length),
237
- $all: (a, b) => rulesMatcherUtils.forceArray(b).every(c => _intersection(rulesMatcherUtils.forceArray(a), rulesMatcherUtils.forceArray(c)).length),
238
- $regex: (a, b, opt) => rulesMatcherUtils.forceArray(b).some(c => c instanceof RegExp ? c.test(a) : new RegExp(c, opt).test(a))
205
+ $exists: (a, b) => !rulesMatcherUtils.isEmpty(a) === b,
206
+ $eq: (a, b) => a === b,
207
+ $ne: (a, b) => a !== b,
208
+ $gt: (a, b) => rulesMatcherUtils.forceNumber(a) > parseFloat(b),
209
+ $gte: (a, b) => rulesMatcherUtils.forceNumber(a) >= parseFloat(b),
210
+ $lt: (a, b) => rulesMatcherUtils.forceNumber(a) < parseFloat(b),
211
+ $lte: (a, b) => rulesMatcherUtils.forceNumber(a) <= parseFloat(b),
212
+ $strGt: (a, b) => String(a || '').length > parseFloat(b),
213
+ $strGte: (a, b) => String(a || '').length >= parseFloat(b),
214
+ $strLt: (a, b) => String(a || '').length < parseFloat(b),
215
+ $strLte: (a, b) => String(a || '').length <= parseFloat(b),
216
+ $in: (a, b) => rulesMatcherUtils
217
+ .forceArray(b)
218
+ .some((c) => _intersection(rulesMatcherUtils.forceArray(a), rulesMatcherUtils.forceArray(c)).length),
219
+ $nin: (a, b) => !rulesMatcherUtils
220
+ .forceArray(b)
221
+ .some((c) => _intersection(rulesMatcherUtils.forceArray(a), rulesMatcherUtils.forceArray(c)).length),
222
+ $all: (a, b) => rulesMatcherUtils
223
+ .forceArray(b)
224
+ .every((c) => _intersection(rulesMatcherUtils.forceArray(a), rulesMatcherUtils.forceArray(c)).length),
225
+ $regex: (a, b, opt) => rulesMatcherUtils
226
+ .forceArray(b)
227
+ .some((c) => c instanceof RegExp ? c.test(a) : new RegExp(c, opt).test(a))
239
228
  };
240
229
 
241
230
  const rulesMatcher = (rules, formValue = {}, apply = true, options) => {
242
- if (!rules) return [apply];
243
- // if (typeof rules !== 'object' && !Array.isArray(rules)) {
244
- // throw new Error('Rules accept only array or object');
245
- // }
246
- if (typeof rules === 'function') {
247
- return [rules(formValue) === apply];
248
- }
249
- const conditions = Array.isArray(rules) ? {
250
- $and: rules
251
- } : rules;
252
- const valid = rulesMatcherUtils.checkRule(conditions, formValue, options ?? {});
253
- return [valid === apply];
231
+ if (!rules)
232
+ return [apply];
233
+ // if (typeof rules !== 'object' && !Array.isArray(rules)) {
234
+ // throw new Error('Rules accept only array or object');
235
+ // }
236
+ if (typeof rules === 'function') {
237
+ return [rules(formValue) === apply];
238
+ }
239
+ const conditions = Array.isArray(rules)
240
+ ? { $and: rules }
241
+ : rules;
242
+ const valid = rulesMatcherUtils.checkRule(conditions, formValue, options ?? {});
243
+ return [valid === apply];
254
244
  };
255
245
  const MatchRules = {
256
- rulesMatcher,
257
- utils: rulesMatcherUtils
246
+ rulesMatcher,
247
+ utils: rulesMatcherUtils
258
248
  };
259
249
 
260
250
  /* eslint-disable no-useless-escape */
261
251
  // TODO align this set of functions to selectors and reducers functions
262
- const flattenRules = ob => {
263
- const result = {};
264
- for (const i in ob) {
265
- if (ob[i] === null) {
266
- result[i] = null;
267
- }
268
- if (typeof ob[i] === 'object' && !Array.isArray(ob[i]) || Array.isArray(ob[i]) && typeof ob[i][0] === 'object') {
269
- const temp = flattenRules(ob[i]);
270
- for (const j in temp) {
271
- result[i + '*' + j] = temp[j];
272
- }
273
- } else {
274
- result[i] = ob[i];
252
+ const flattenRules = (ob) => {
253
+ const result = {};
254
+ for (const i in ob) {
255
+ if (ob[i] === null) {
256
+ result[i] = null;
257
+ }
258
+ if ((typeof ob[i] === 'object' && !Array.isArray(ob[i])) ||
259
+ (Array.isArray(ob[i]) && typeof ob[i][0] === 'object')) {
260
+ const temp = flattenRules(ob[i]);
261
+ for (const j in temp) {
262
+ result[i + '*' + j] = temp[j];
263
+ }
264
+ }
265
+ else {
266
+ result[i] = ob[i];
267
+ }
275
268
  }
276
- }
277
- return result;
269
+ return result;
278
270
  };
279
271
  // export const searchEmptyKeyRecursively = <T extends object>(
280
272
  // _key: "$and" | "$or",
@@ -297,661 +289,566 @@ const flattenRules = ob => {
297
289
  // .map((key) => searchEmptyKeyRecursively(_key, obj[key]))
298
290
  // .every((v) => v === true);
299
291
  // };
300
- const getRulesExists = rules => {
301
- return Object.keys(rules).length ? CoreUtils.mapEdge(rules) : undefined;
292
+ const getRulesExists = (rules) => {
293
+ return Object.keys(rules).length ? CoreUtils.mapEdge(rules) : undefined;
302
294
  };
303
295
  /**
304
296
  * Defines a collection of utility functions for processing rules, nodes and graph-like structures
305
297
  */
306
298
  const CoreUtils = {
307
- generateRulesName: nextRules => {
308
- const a = nextRules.reduce((acc, inc) => {
309
- const n = typeof inc.rules === 'string' ? inc.rules || '__ERROR_NAME__' : inc.rules?.name || '__ERROR_NAME__';
310
- return {
311
- ...acc,
312
- [n]: inc.nodeId
313
- };
314
- }, {});
315
- return a;
316
- },
317
- mapKeysDeepLodash: (obj, cb, isRecursive) => {
318
- /* istanbul ignore next */
319
- if (!obj && !isRecursive) {
320
- return {};
321
- }
322
- if (!isRecursive) {
323
- /* istanbul ignore next */
324
- if (typeof obj === 'string' || typeof obj === 'number' || typeof obj === 'boolean') {
325
- return {};
326
- }
327
- }
328
- if (Array.isArray(obj)) {
329
- return obj.map(item => CoreUtils.mapKeysDeepLodash(item, cb, true));
330
- }
331
- if (!isPlainObject(obj)) {
332
- return obj;
333
- }
334
- const result = mapKeys(obj, cb);
335
- return mapValues(result, value => CoreUtils.mapKeysDeepLodash(value, cb, true));
336
- },
337
- generateNodes: nodes => keyBy(nodes.map(s => omit(s, 'nextRules')), 'nodeId'),
338
- makeObjectRules: nodes => nodes.reduce((acc, inc) => ({
339
- ...acc,
340
- [inc.nodeId]: inc.nextRules
341
- }), {}),
342
- hasNode: (state, name, node) => has(state, [name, 'nodes', node]),
343
- isEmptyRules: rules => {
344
- if (isEmpty(rules)) return true;
345
- if (isEmpty(_get(rules, 'rules'))) return true;
346
- if (Object.keys(flattenRules(rules)).every(key => key.endsWith('$and') || key.endsWith('$or'))) {
347
- return true;
348
- }
349
- return false;
350
- },
351
- mapEdge: nextNode => {
352
- const res = nextNode.sort((a, b) => {
353
- const x = CoreUtils.isEmptyRules(a.rules);
354
- const y = CoreUtils.isEmptyRules(b.rules);
355
- return Number(x) - Number(y);
356
- });
357
- return res;
358
- },
359
- makeRules: rules => Object.entries(rules).reduce((acc2, [k, v]) => [...acc2, {
360
- nodeId: k,
361
- rules: v
362
- }], []),
363
- generateNodesForFlowerJson: nodes => nodes.filter(e => !!_get(e, 'props.id')).map(e => {
364
- const rules = CoreUtils.makeRules(e.props.to ?? {});
365
- const nextRules = getRulesExists(rules);
366
- const children = e.props.data?.children;
367
- return {
368
- nodeId: e.props.id,
369
- nodeType: e.type.displayName || e.props.as || 'FlowerNode',
370
- nodeTitle: _get(e.props, 'data.title'),
371
- children,
372
- nextRules,
373
- retain: e.props.retain,
374
- disabled: e.props.disabled
375
- };
376
- }),
377
- cleanPath: (name, char = '^') => _trimStart(name, char),
378
- getPath: idValue => {
379
- if (!idValue) {
380
- return {
381
- path: []
382
- };
383
- }
384
- if (idValue === '*') {
385
- return {
386
- path: '*'
387
- };
388
- }
389
- if (idValue.indexOf('^') === 0) {
390
- const [flowNameFromPath, ...rest] = CoreUtils.cleanPath(idValue).split('.');
391
- return {
392
- flowNameFromPath,
393
- path: rest
394
- };
395
- }
396
- return {
397
- path: idValue.split('.')
398
- };
399
- },
400
- allEqual: (arr, arr2) => arr.length === arr2.length && arr.every(v => arr2.includes(v)),
401
- findValidRule: (nextRules, value, prefix) => find(nextRules, rule => {
402
- // fix per evitare di entrare in un nodo senza regole, ma con un name,
403
- // invocando un next() senza paramentri
404
- if (typeof rule.rules === 'string') {
405
- return false;
406
- }
407
- if (typeof rule.rules === 'function') {
408
- return rule.rules(value);
409
- }
410
- if (rule.rules === null) {
411
- return true;
412
- }
413
- if (typeof rule.rules?.rules === 'undefined') {
414
- return false;
415
- }
416
- if (typeof rule.rules.rules === 'string') {
417
- return false;
418
- }
419
- const [valid] = MatchRules.rulesMatcher(rule.rules.rules, value, true, {
420
- prefix
421
- });
422
- return valid;
423
- })
299
+ generateRulesName: (nextRules) => {
300
+ const a = nextRules.reduce((acc, inc) => {
301
+ const n = typeof inc.rules === 'string'
302
+ ? inc.rules || '__ERROR_NAME__'
303
+ : inc.rules?.name || '__ERROR_NAME__';
304
+ return {
305
+ ...acc,
306
+ [n]: inc.nodeId
307
+ };
308
+ }, {});
309
+ return a;
310
+ },
311
+ mapKeysDeepLodash: (obj, cb, isRecursive) => {
312
+ /* istanbul ignore next */
313
+ if (!obj && !isRecursive) {
314
+ return {};
315
+ }
316
+ if (!isRecursive) {
317
+ /* istanbul ignore next */
318
+ if (typeof obj === 'string' ||
319
+ typeof obj === 'number' ||
320
+ typeof obj === 'boolean') {
321
+ return {};
322
+ }
323
+ }
324
+ if (Array.isArray(obj)) {
325
+ return obj.map((item) => CoreUtils.mapKeysDeepLodash(item, cb, true));
326
+ }
327
+ if (!isPlainObject(obj)) {
328
+ return obj;
329
+ }
330
+ const result = mapKeys(obj, cb);
331
+ return mapValues(result, (value) => CoreUtils.mapKeysDeepLodash(value, cb, true));
332
+ },
333
+ generateNodes: (nodes) => keyBy(nodes.map((s) => omit(s, 'nextRules')), 'nodeId'),
334
+ makeObjectRules: (nodes) => nodes.reduce((acc, inc) => ({ ...acc, [inc.nodeId]: inc.nextRules }), {}),
335
+ hasNode: (state, name, node) => has(state, [name, 'nodes', node]),
336
+ isEmptyRules: (rules) => {
337
+ if (isEmpty(rules))
338
+ return true;
339
+ if (isEmpty(_get(rules, 'rules')))
340
+ return true;
341
+ if (Object.keys(flattenRules(rules)).every((key) => key.endsWith('$and') || key.endsWith('$or'))) {
342
+ return true;
343
+ }
344
+ return false;
345
+ },
346
+ mapEdge: (nextNode) => {
347
+ const res = nextNode.sort((a, b) => {
348
+ const x = CoreUtils.isEmptyRules(a.rules);
349
+ const y = CoreUtils.isEmptyRules(b.rules);
350
+ return Number(x) - Number(y);
351
+ });
352
+ return res;
353
+ },
354
+ makeRules: (rules) => Object.entries(rules).reduce((acc2, [k, v]) => [...acc2, { nodeId: k, rules: v }], []),
355
+ generateNodesForFlowerJson: (nodes) => nodes
356
+ .filter((e) => !!_get(e, 'props.id'))
357
+ .map((e) => {
358
+ const rules = CoreUtils.makeRules(e.props.to ?? {});
359
+ const nextRules = getRulesExists(rules);
360
+ const children = e.props.data?.children;
361
+ return {
362
+ nodeId: e.props.id,
363
+ nodeType: e.type.displayName || e.props.as || 'FlowerNode',
364
+ nodeTitle: _get(e.props, 'data.title'),
365
+ children,
366
+ nextRules,
367
+ retain: e.props.retain,
368
+ disabled: e.props.disabled
369
+ };
370
+ }),
371
+ cleanPath: (name, char = '^') => _trimStart(name, char),
372
+ getPath: (idValue) => {
373
+ if (!idValue) {
374
+ return {
375
+ path: []
376
+ };
377
+ }
378
+ if (idValue === '*') {
379
+ return {
380
+ path: '*'
381
+ };
382
+ }
383
+ if (idValue.indexOf('^') === 0) {
384
+ const [flowNameFromPath, ...rest] = CoreUtils.cleanPath(idValue).split('.');
385
+ return {
386
+ flowNameFromPath,
387
+ path: rest
388
+ };
389
+ }
390
+ return {
391
+ path: idValue.split('.')
392
+ };
393
+ },
394
+ allEqual: (arr, arr2) => arr.length === arr2.length && arr.every((v) => arr2.includes(v)),
395
+ findValidRule: (nextRules, value, prefix) => find(nextRules, (rule) => {
396
+ // fix per evitare di entrare in un nodo senza regole, ma con un name,
397
+ // invocando un next() senza paramentri
398
+ if (typeof rule.rules === 'string') {
399
+ return false;
400
+ }
401
+ if (typeof rule.rules === 'function') {
402
+ return rule.rules(value);
403
+ }
404
+ if (rule.rules === null) {
405
+ return true;
406
+ }
407
+ if (typeof rule.rules?.rules === 'undefined') {
408
+ return false;
409
+ }
410
+ if (typeof rule.rules.rules === 'string') {
411
+ return false;
412
+ }
413
+ const [valid] = MatchRules.rulesMatcher(rule.rules.rules, value, true, {
414
+ prefix
415
+ });
416
+ return valid;
417
+ })
424
418
  };
425
419
 
426
420
  const FlowerStateUtils = {
427
- getAllData: state => state && Object.entries(state ?? {}).reduce((acc, [k, v]) => ({
428
- ...acc,
429
- [k]: v.data
430
- }), {}),
431
- selectFlowerFormNode: (name, id) => state => _get(state, [name, 'form', id]),
432
- makeSelectCurrentNextRules: name => state => {
433
- const nextRules = _get(state, [name, 'nextRules']);
434
- const currentNodeId = FlowerStateUtils.makeSelectCurrentNodeId(name)(state);
435
- return _get(nextRules, [currentNodeId]);
436
- },
437
- makeSelectCurrentNodeId: name => state => {
438
- const subState = _get(state, [name]);
439
- const startId = _get(state, ['startId']);
440
- return _get(subState, ['current']) || startId;
441
- },
442
- makeSelectNodeErrors: (name, currentNodeId) => state => {
443
- const form = FlowerStateUtils.selectFlowerFormNode(name, currentNodeId)(state);
444
- return {
445
- touched: form?.touched || false,
446
- errors: form?.errors,
447
- isValidating: form?.isValidating,
448
- isValid: form && form.errors ? Object.values(form.errors).flat().length === 0 : true
449
- };
450
- }
421
+ getAllData: (state) => state &&
422
+ Object.entries(state ?? {}).reduce((acc, [k, v]) => ({ ...acc, [k]: v.data }), {}),
423
+ selectFlowerFormNode: (name, id) => (state) => _get(state, [name, 'form', id]),
424
+ makeSelectCurrentNextRules: (name) => (state) => {
425
+ const nextRules = _get(state, [name, 'nextRules']);
426
+ const currentNodeId = FlowerStateUtils.makeSelectCurrentNodeId(name)(state);
427
+ return _get(nextRules, [currentNodeId]);
428
+ },
429
+ makeSelectCurrentNodeId: (name) => (state) => {
430
+ const subState = _get(state, [name]);
431
+ const startId = _get(state, ['startId']);
432
+ return _get(subState, ['current']) || startId;
433
+ },
434
+ makeSelectNodeErrors: (name, currentNodeId) => (state) => {
435
+ const form = FlowerStateUtils.selectFlowerFormNode(name, currentNodeId)(state);
436
+ return {
437
+ touched: form?.touched || false,
438
+ errors: form?.errors,
439
+ isValidating: form?.isValidating,
440
+ isValid: form && form.errors
441
+ ? Object.values(form.errors).flat().length === 0
442
+ : true
443
+ };
444
+ }
451
445
  };
452
446
 
453
- const {
454
- generateNodes,
455
- hasNode,
456
- makeObjectRules,
457
- generateRulesName,
458
- findValidRule
459
- } = CoreUtils;
447
+ const { generateNodes, hasNode, makeObjectRules, generateRulesName, findValidRule } = CoreUtils;
460
448
  /**
461
449
  * These functions are Redux reducers used in a Flux architecture for managing state transitions and updates in a Flower application.
462
450
  */
463
451
  const FlowerCoreReducers = {
464
- historyAdd: (state, {
465
- payload
466
- }) => {
467
- if (hasNode(state, payload.name, payload.node)) {
468
- state[payload.name].history.push(payload.node);
469
- _set(state, [payload.name, 'current'], payload.node);
470
- }
471
- return state;
472
- },
473
- historyPrevToNode: (state, {
474
- payload
475
- }) => {
476
- const history = _get(state[typeof payload === 'string' ? payload : payload.name], ['history'], []);
477
- const lastIndex = lastIndexOf(history, typeof payload === 'string' ? payload : payload.node);
478
- // se passo un nodo che non esiste
479
- if (lastIndex >= 0) {
480
- const newHistory = _slice(history, 0, lastIndex + 1);
481
- _set(state, [typeof payload === 'string' ? payload : payload.name, 'history'], newHistory);
482
- _set(state, [typeof payload === 'string' ? payload : payload.name, 'current'], typeof payload === 'string' ? payload : payload.node);
483
- }
484
- return state;
485
- },
486
- setFormTouched: (state, {
487
- payload
488
- }) => {
489
- if (!_get(state, [typeof payload === 'string' ? payload : payload.flowName, 'nodes', typeof payload === 'string' ? payload : payload.currentNode])) {
490
- return state;
491
- }
492
- _set(state, [typeof payload === 'string' ? payload : payload.flowName, 'form', typeof payload === 'string' ? payload : payload.currentNode, 'touched'], true);
493
- return state;
494
- },
495
- // TODO check internal logic and use case
496
- /* istanbul ignore next */
497
- forceAddHistory: (state, {
498
- payload
499
- }) => {
500
- const {
501
- name,
502
- flowName,
503
- history
504
- } = payload;
505
- const key = name || flowName || '' || '';
506
- const resultHistory = history.slice(1, -1);
507
- state[key].history.push(...resultHistory);
508
- return state;
509
- },
510
- historyPop: (state, {
511
- payload
512
- }) => {
513
- const container = _get(state, [payload.name]);
514
- const startId = _get(container, ['startId']);
515
- const history = _get(container, ['history']);
516
- const originHistory = [..._get(container, ['history'], [])];
517
- if (originHistory.length < 2) {
518
- _set(state, [payload.name, 'current'], startId);
519
- _set(state, [payload.name, 'history'], [startId]);
520
- return state;
521
- }
522
- // elimino lo node corrente
523
- history.pop();
524
- const total = originHistory.length - 2;
525
- // eslint-disable-next-line no-plusplus
526
- for (let index = total; index > 0; index--) {
527
- const curr = originHistory[index];
528
- const nodeType = _get(container, ['nodes', curr, 'nodeType']);
529
- const nodeDisabled = _get(container, ['nodes', curr, 'disabled']);
530
- if (nodeDisabled || nodeType === 'FlowerAction' || nodeType === 'FlowerServer' || !nodeType) {
531
- history.pop();
532
- } else {
533
- break;
534
- }
535
- }
536
- const lastId = _last(history);
537
- _set(state, [payload.name, 'current'], lastId);
538
- return state;
539
- },
540
- restoreHistory: (state, {
541
- payload
542
- }) => {
543
- const startId = _get(state, [payload.name, 'startId']);
544
- _set(state, [payload.name, 'current'], startId);
545
- _set(state, [payload.name, 'history'], [startId]);
546
- return state;
547
- },
548
- replaceNode: (state, {
549
- payload
550
- }) => {
551
- const {
552
- name,
553
- flowName,
554
- node
555
- } = payload;
556
- // non ancora implementanto nell'hook useFlower
557
- /* istanbul ignore next */
558
- if (hasNode(state, name || flowName || '', node)) {
559
- _set(state, [name || flowName || '', 'current'], node);
560
- _set(state, [name || flowName || '', 'history'], [node]);
561
- }
562
- /* istanbul ignore next */
563
- return state;
564
- },
565
- /* istanbul ignore next */
566
- initializeFromNode: (state, {
567
- payload
568
- }) => {
569
- const {
570
- name,
571
- flowName,
572
- node
573
- } = payload;
574
- if (hasNode(state, name || flowName || '', node)) {
575
- _set(state, [name || flowName || '', 'startId'], node);
576
- _set(state, [name || flowName || '', 'current'], node);
577
- _set(state, [name || flowName || '', 'history'], [node]);
578
- }
579
- return state;
580
- },
581
- /* istanbul ignore next */
582
- forceResetHistory: (state, {
583
- payload
584
- }) => {
585
- const {
586
- name,
587
- flowName
588
- } = payload;
589
- if (!name && !flowName) return state;
590
- _set(state, [name || flowName || '', 'history'], []);
591
- return state;
592
- },
593
- destroy: (state, {
594
- payload
595
- }) => {
596
- _set(state, [payload.name], {});
597
- },
598
- initNodes: (state, {
599
- payload
600
- }) => {
601
- if (payload.persist && _get(state, [payload.name, 'persist'])) return;
602
- const startId = payload.startId || _get(payload, 'nodes.0.nodeId');
603
- // TODO non verificato, controllo precendente che non lo permette
604
- /* istanbul ignore next */
605
- if (!startId) {
606
- // eslint-disable-next-line no-console
607
- console.warn('Flower is empty');
608
- return;
609
- }
610
- _set(state, payload.name, {
611
- persist: payload.persist,
612
- startId,
613
- current: startId,
614
- history: [startId],
615
- nodes: generateNodes(payload.nodes),
616
- nextRules: makeObjectRules(payload.nodes),
617
- data: payload.initialData
618
- });
619
- },
620
- // TODO usato solo da flower su vscode
621
- setCurrentNode: /* istanbul ignore next */(state, {
622
- payload
623
- }) => {
452
+ historyAdd: (state, { payload }) => {
453
+ if (hasNode(state, payload.name, payload.node)) {
454
+ state[payload.name].history.push(payload.node);
455
+ _set(state, [payload.name, 'current'], payload.node);
456
+ }
457
+ return state;
458
+ },
459
+ historyPrevToNode: (state, { payload }) => {
460
+ const history = _get(state[typeof payload === 'string' ? payload : payload.name], ['history'], []);
461
+ const lastIndex = lastIndexOf(history, typeof payload === 'string' ? payload : payload.node);
462
+ // se passo un nodo che non esiste
463
+ if (lastIndex >= 0) {
464
+ const newHistory = _slice(history, 0, lastIndex + 1);
465
+ _set(state, [typeof payload === 'string' ? payload : payload.name, 'history'], newHistory);
466
+ _set(state, [typeof payload === 'string' ? payload : payload.name, 'current'], typeof payload === 'string' ? payload : payload.node);
467
+ }
468
+ return state;
469
+ },
470
+ setFormTouched: (state, { payload }) => {
471
+ if (!_get(state, [
472
+ typeof payload === 'string' ? payload : payload.flowName,
473
+ 'nodes',
474
+ typeof payload === 'string' ? payload : payload.currentNode
475
+ ])) {
476
+ return state;
477
+ }
478
+ _set(state, [
479
+ typeof payload === 'string' ? payload : payload.flowName,
480
+ 'form',
481
+ typeof payload === 'string' ? payload : payload.currentNode,
482
+ 'touched'
483
+ ], true);
484
+ return state;
485
+ },
486
+ // TODO check internal logic and use case
624
487
  /* istanbul ignore next */
625
- if (hasNode(state, payload.name, payload.node)) {
626
- const startId = _get(state, [payload.name, 'startId']);
627
- if (payload.node === startId) {
488
+ forceAddHistory: (state, { payload }) => {
489
+ const { name, flowName, history } = payload;
490
+ const key = name || flowName || '' || '';
491
+ const resultHistory = history.slice(1, -1);
492
+ state[key].history.push(...resultHistory);
493
+ return state;
494
+ },
495
+ historyPop: (state, { payload }) => {
496
+ const container = _get(state, [payload.name]);
497
+ const startId = _get(container, ['startId']);
498
+ const history = _get(container, ['history']);
499
+ const originHistory = [..._get(container, ['history'], [])];
500
+ if (originHistory.length < 2) {
501
+ _set(state, [payload.name, 'current'], startId);
502
+ _set(state, [payload.name, 'history'], [startId]);
503
+ return state;
504
+ }
505
+ // elimino lo node corrente
506
+ history.pop();
507
+ const total = originHistory.length - 2;
508
+ // eslint-disable-next-line no-plusplus
509
+ for (let index = total; index > 0; index--) {
510
+ const curr = originHistory[index];
511
+ const nodeType = _get(container, ['nodes', curr, 'nodeType']);
512
+ const nodeDisabled = _get(container, ['nodes', curr, 'disabled']);
513
+ if (nodeDisabled ||
514
+ nodeType === 'FlowerAction' ||
515
+ nodeType === 'FlowerServer' ||
516
+ !nodeType) {
517
+ history.pop();
518
+ }
519
+ else {
520
+ break;
521
+ }
522
+ }
523
+ const lastId = _last(history);
524
+ _set(state, [payload.name, 'current'], lastId);
525
+ return state;
526
+ },
527
+ restoreHistory: (state, { payload }) => {
528
+ const startId = _get(state, [payload.name, 'startId']);
628
529
  _set(state, [payload.name, 'current'], startId);
629
530
  _set(state, [payload.name, 'history'], [startId]);
630
- } else {
631
- _set(state, [payload.name, 'current'], payload.node);
632
- }
633
- }
634
- },
635
- formAddErrors: (state, {
636
- payload
637
- }) => {
638
- _set(state, [payload.name, 'form', payload.currentNode, 'errors', payload.id], payload.errors);
639
- },
640
- formRemoveErrors: (state, {
641
- payload
642
- }) => {
643
- _unset(state, [payload.name, 'form', payload.currentNode, 'errors', payload.id]);
644
- _unset(state, [payload.name, 'form', payload.currentNode, 'isValidating']);
645
- },
646
- addData: (state, {
647
- payload
648
- }) => {
649
- const prevData = _get(state, [payload.flowName, 'data']);
650
- _set(state, [payload.flowName, 'data'], {
651
- ...prevData,
652
- ...payload.value
653
- });
654
- },
655
- addDataByPath: (state, {
656
- payload
657
- }) => {
658
- if (payload.id && payload.id.length) {
659
- _set(state, [payload.flowName, 'data', ...payload.id], payload.value);
660
- }
661
- },
662
- // TODO usato al momento solo il devtool
663
- replaceData: /* istanbul ignore next */(state, {
664
- payload
665
- }) => {
531
+ return state;
532
+ },
533
+ replaceNode: (state, { payload }) => {
534
+ const { name, flowName, node } = payload;
535
+ // non ancora implementanto nell'hook useFlower
536
+ /* istanbul ignore next */
537
+ if (hasNode(state, name || flowName || '', node)) {
538
+ _set(state, [name || flowName || '', 'current'], node);
539
+ _set(state, [name || flowName || '', 'history'], [node]);
540
+ }
541
+ /* istanbul ignore next */
542
+ return state;
543
+ },
666
544
  /* istanbul ignore next */
667
- _set(state, [payload.flowName, 'data'], payload.value);
668
- },
669
- unsetData: (state, {
670
- payload
671
- }) => {
672
- _unset(state, [payload.flowName, 'data', ...payload.id]);
673
- },
674
- setFormIsValidating: (state, {
675
- payload
676
- }) => {
677
- _set(state, [payload.name, 'form', payload.currentNode, 'isValidating'], payload.isValidating);
678
- },
679
- node: (state, {
680
- payload
681
- }) => {
682
- const {
683
- name,
684
- history
685
- } = payload;
686
- const node = payload.nodeId || payload.node || '';
687
- const flowName = name || payload.flowName || '';
688
- const startNode = _get(state, [payload.name, 'startId']);
689
- const currentNodeId = _get(state, [payload.name, 'current'], startNode);
690
- FlowerCoreReducers.setFormTouched(state, {
691
- type: 'setFormTouched',
692
- payload: {
693
- flowName,
694
- currentNode: currentNodeId
695
- }
696
- });
545
+ initializeFromNode: (state, { payload }) => {
546
+ const { name, flowName, node } = payload;
547
+ if (hasNode(state, name || flowName || '', node)) {
548
+ _set(state, [name || flowName || '', 'startId'], node);
549
+ _set(state, [name || flowName || '', 'current'], node);
550
+ _set(state, [name || flowName || '', 'history'], [node]);
551
+ }
552
+ return state;
553
+ },
697
554
  /* istanbul ignore next */
698
- // eslint-disable-next-line no-underscore-dangle
699
- if (devtoolState && _get(devtoolState, '__FLOWER_DEVTOOLS__') && history) {
700
- FlowerCoreReducers.forceAddHistory(state, {
701
- type: 'forceAddHistory',
702
- payload: {
703
- name,
704
- flowName,
705
- history
706
- }
707
- });
708
- }
709
- FlowerCoreReducers.historyAdd(state, {
710
- type: 'historyAdd',
711
- payload: {
712
- name: name || flowName || '',
713
- node
714
- }
715
- });
716
- },
717
- prevToNode: (state, {
718
- payload
719
- }) => {
720
- const {
721
- node,
722
- name,
723
- flowName
724
- } = payload;
725
- FlowerCoreReducers.historyPrevToNode(state, {
726
- type: 'historyPrevToNode',
727
- payload: {
728
- name: name || flowName || '',
729
- node
730
- }
731
- });
732
- },
733
- next: (state, {
734
- payload
735
- }) => {
736
- const {
737
- name,
738
- data = {},
739
- route
740
- } = payload;
741
- const flowName = name || payload.flowName || '';
742
- const currentNodeId = FlowerStateUtils.makeSelectCurrentNodeId(flowName)(state);
743
- const currentNextRules = FlowerStateUtils.makeSelectCurrentNextRules(flowName)(state);
744
- const form = FlowerStateUtils.makeSelectNodeErrors(flowName, currentNodeId)(state);
745
- const clonedData = _cloneDeep(FlowerStateUtils.getAllData(state));
746
- const stateWithNodeData = {
747
- $in: data,
748
- $form: form,
749
- ...clonedData
750
- };
751
- FlowerCoreReducers.setFormTouched(state, {
752
- type: 'setFormTouched',
753
- payload: {
754
- flowName,
755
- currentNode: currentNodeId
756
- }
757
- });
758
- if (!currentNextRules) {
759
- return;
760
- }
761
- if (route) {
762
- const rulesByName = generateRulesName(currentNextRules);
763
- if (!rulesByName[route]) {
764
- return;
765
- }
766
- FlowerCoreReducers.historyAdd(state, {
767
- type: 'historyAdd',
768
- payload: {
769
- name: flowName,
770
- node: rulesByName[route]
771
- }
772
- });
773
- return;
774
- }
775
- const validRule = findValidRule(currentNextRules, stateWithNodeData, flowName);
776
- const nextNumberNode = _get(validRule, 'nodeId');
777
- if (!nextNumberNode) {
778
- return;
555
+ forceResetHistory: (state, { payload }) => {
556
+ const { name, flowName } = payload;
557
+ if (!name && !flowName)
558
+ return state;
559
+ _set(state, [name || flowName || '', 'history'], []);
560
+ return state;
561
+ },
562
+ destroy: (state, { payload }) => {
563
+ _set(state, [payload.name], {});
564
+ },
565
+ initNodes: (state, { payload }) => {
566
+ if (payload.persist && _get(state, [payload.name, 'persist']))
567
+ return;
568
+ const startId = payload.startId ||
569
+ _get(payload, 'initialState.startId') ||
570
+ _get(payload, 'nodes.0.nodeId');
571
+ // TODO non verificato, controllo precendente che non lo permette
572
+ /* istanbul ignore next */
573
+ if (!startId) {
574
+ // eslint-disable-next-line no-console
575
+ console.warn('Flower is empty');
576
+ return;
577
+ }
578
+ const current = _get(payload, 'initialState.current') || startId;
579
+ const history = _get(payload, 'initialState.history') || [startId];
580
+ _set(state, payload.name, {
581
+ persist: payload.persist,
582
+ startId,
583
+ current,
584
+ history,
585
+ nodes: generateNodes(payload.nodes),
586
+ nextRules: makeObjectRules(payload.nodes),
587
+ data: payload.initialData
588
+ });
589
+ },
590
+ // TODO usato solo da flower su vscode
591
+ setCurrentNode: /* istanbul ignore next */ (state, { payload }) => {
592
+ /* istanbul ignore next */
593
+ if (hasNode(state, payload.name, payload.node)) {
594
+ const startId = _get(state, [payload.name, 'startId']);
595
+ if (payload.node === startId) {
596
+ _set(state, [payload.name, 'current'], startId);
597
+ _set(state, [payload.name, 'history'], [startId]);
598
+ }
599
+ else {
600
+ _set(state, [payload.name, 'current'], payload.node);
601
+ }
602
+ }
603
+ },
604
+ formAddErrors: (state, { payload }) => {
605
+ _set(state, [payload.name, 'form', payload.currentNode, 'errors', payload.id], payload.errors);
606
+ },
607
+ formRemoveErrors: (state, { payload }) => {
608
+ _unset(state, [
609
+ payload.name,
610
+ 'form',
611
+ payload.currentNode,
612
+ 'errors',
613
+ payload.id
614
+ ]);
615
+ _unset(state, [payload.name, 'form', payload.currentNode, 'isValidating']);
616
+ },
617
+ addData: (state, { payload }) => {
618
+ const prevData = _get(state, [payload.flowName, 'data']);
619
+ _set(state, [payload.flowName, 'data'], { ...prevData, ...payload.value });
620
+ },
621
+ addDataByPath: (state, { payload }) => {
622
+ if (payload.id && payload.id.length) {
623
+ _set(state, [payload.flowName, 'data', ...payload.id], payload.value);
624
+ }
625
+ },
626
+ // TODO usato al momento solo il devtool
627
+ replaceData: /* istanbul ignore next */ (state, { payload }) => {
628
+ /* istanbul ignore next */
629
+ _set(state, [payload.flowName, 'data'], payload.value);
630
+ },
631
+ unsetData: (state, { payload }) => {
632
+ _unset(state, [payload.flowName, 'data', ...payload.id]);
633
+ },
634
+ setFormIsValidating: (state, { payload }) => {
635
+ _set(state, [payload.name, 'form', payload.currentNode, 'isValidating'], payload.isValidating);
636
+ },
637
+ node: (state, { payload }) => {
638
+ const { name, history } = payload;
639
+ const node = payload.nodeId || payload.node || '';
640
+ const flowName = name || payload.flowName || '';
641
+ const startNode = _get(state, [payload.name, 'startId']);
642
+ const currentNodeId = _get(state, [payload.name, 'current'], startNode);
643
+ FlowerCoreReducers.setFormTouched(state, {
644
+ type: 'setFormTouched',
645
+ payload: { flowName, currentNode: currentNodeId }
646
+ });
647
+ /* istanbul ignore next */
648
+ // eslint-disable-next-line no-underscore-dangle
649
+ if (devtoolState && _get(devtoolState, '__FLOWER_DEVTOOLS__') && history) {
650
+ FlowerCoreReducers.forceAddHistory(state, {
651
+ type: 'forceAddHistory',
652
+ payload: {
653
+ name,
654
+ flowName,
655
+ history
656
+ }
657
+ });
658
+ }
659
+ FlowerCoreReducers.historyAdd(state, {
660
+ type: 'historyAdd',
661
+ payload: { name: name || flowName || '', node }
662
+ });
663
+ },
664
+ prevToNode: (state, { payload }) => {
665
+ const { node, name, flowName } = payload;
666
+ FlowerCoreReducers.historyPrevToNode(state, {
667
+ type: 'historyPrevToNode',
668
+ payload: { name: name || flowName || '', node }
669
+ });
670
+ },
671
+ next: (state, { payload }) => {
672
+ const { name, data = {}, route } = payload;
673
+ const flowName = name || payload.flowName || '';
674
+ const currentNodeId = FlowerStateUtils.makeSelectCurrentNodeId(flowName)(state);
675
+ const currentNextRules = FlowerStateUtils.makeSelectCurrentNextRules(flowName)(state);
676
+ const form = FlowerStateUtils.makeSelectNodeErrors(flowName, currentNodeId)(state);
677
+ const clonedData = _cloneDeep(FlowerStateUtils.getAllData(state));
678
+ const stateWithNodeData = {
679
+ $in: data,
680
+ $form: form,
681
+ ...clonedData
682
+ };
683
+ FlowerCoreReducers.setFormTouched(state, {
684
+ type: 'setFormTouched',
685
+ payload: { flowName, currentNode: currentNodeId }
686
+ });
687
+ if (!currentNextRules) {
688
+ return;
689
+ }
690
+ if (route) {
691
+ const rulesByName = generateRulesName(currentNextRules);
692
+ if (!rulesByName[route]) {
693
+ return;
694
+ }
695
+ FlowerCoreReducers.historyAdd(state, {
696
+ type: 'historyAdd',
697
+ payload: { name: flowName, node: rulesByName[route] }
698
+ });
699
+ return;
700
+ }
701
+ const validRule = findValidRule(currentNextRules, stateWithNodeData, flowName);
702
+ const nextNumberNode = _get(validRule, 'nodeId');
703
+ if (!nextNumberNode) {
704
+ return;
705
+ }
706
+ FlowerCoreReducers.historyAdd(state, {
707
+ type: 'historyAdd',
708
+ payload: { name: flowName, node: nextNumberNode }
709
+ });
710
+ },
711
+ prev: (state, { payload }) => {
712
+ const { name, flowName } = payload;
713
+ FlowerCoreReducers.historyPop(state, {
714
+ type: 'historyPop',
715
+ payload: { name: name || flowName || '' }
716
+ });
717
+ },
718
+ restart: (state, { payload }) => {
719
+ const { name, flowName } = payload;
720
+ FlowerCoreReducers.restoreHistory(state, {
721
+ type: 'restoreHistory',
722
+ payload: { name: name || flowName || '' }
723
+ });
724
+ },
725
+ reset: (state, { payload }) => {
726
+ const { name, flowName, initialData } = payload;
727
+ FlowerCoreReducers.restoreHistory(state, {
728
+ type: 'restoreHistory',
729
+ payload: { name: name || flowName || '' }
730
+ });
731
+ _set(state, [name || flowName || '', 'form'], {});
732
+ _set(state, [name || flowName || '', 'data'], initialData);
779
733
  }
780
- FlowerCoreReducers.historyAdd(state, {
781
- type: 'historyAdd',
782
- payload: {
783
- name: flowName,
784
- node: nextNumberNode
785
- }
786
- });
787
- },
788
- prev: (state, {
789
- payload
790
- }) => {
791
- const {
792
- name,
793
- flowName
794
- } = payload;
795
- FlowerCoreReducers.historyPop(state, {
796
- type: 'historyPop',
797
- payload: {
798
- name: name || flowName || ''
799
- }
800
- });
801
- },
802
- restart: (state, {
803
- payload
804
- }) => {
805
- const {
806
- name,
807
- flowName
808
- } = payload;
809
- FlowerCoreReducers.restoreHistory(state, {
810
- type: 'restoreHistory',
811
- payload: {
812
- name: name || flowName || ''
813
- }
814
- });
815
- },
816
- reset: (state, {
817
- payload
818
- }) => {
819
- const {
820
- name,
821
- flowName,
822
- initialData
823
- } = payload;
824
- FlowerCoreReducers.restoreHistory(state, {
825
- type: 'restoreHistory',
826
- payload: {
827
- name: name || flowName || ''
828
- }
829
- });
830
- _set(state, [name || flowName || '', 'form'], {});
831
- _set(state, [name || flowName || '', 'data'], initialData);
832
- }
833
734
  };
834
735
 
835
736
  const FlowerCoreStateSelectors = {
836
- selectGlobal: state => state && state.flower,
837
- selectFlower: name => state => _get(state, [name]),
838
- selectFlowerFormNode: id => state => _get(state, ['form', id]),
839
- selectFlowerHistory: flower => _get(flower, ['history'], []),
840
- makeSelectNodesIds: flower => _get(flower, ['nodes']),
841
- makeSelectStartNodeId: flower => _get(flower, ['startId']),
842
- getDataByFlow: flower => _get(flower, ['data']) ?? {},
843
- getDataFromState: id => data => id === '*' ? data : _get(data, id),
844
- makeSelectNodeFormTouched: form => form && form.touched,
845
- makeSelectCurrentNodeId: (flower, startNodeId) => _get(flower, ['current']) || startNodeId,
846
- makeSelectCurrentNodeDisabled: (nodes, current) => !!_get(nodes, [current, 'disabled']),
847
- makeSelectPrevNodeRetain: (nodes, history, current) => {
848
- if (!nodes) return;
849
- const prevFlowerNode = [...history].reverse().find(el => {
850
- const {
851
- nodeType,
852
- retain
853
- } = nodes[el];
854
- return nodeType === 'FlowerNode' || retain;
855
- });
856
- // eslint-disable-next-line consistent-return
857
- if (nodes[current].nodeType === 'FlowerNode' || nodes[current].retain) return;
858
- if (!prevFlowerNode) return;
859
- if (nodes[prevFlowerNode] && nodes[prevFlowerNode].disabled) return;
860
- // eslint-disable-next-line consistent-return
861
- return nodes[prevFlowerNode] && nodes[prevFlowerNode].retain && prevFlowerNode;
862
- },
863
- makeSelectNodeErrors: form => {
864
- return {
865
- touched: form?.touched || false,
866
- errors: form?.errors,
867
- isValidating: form?.isValidating,
868
- isValid: form && form.errors ? Object.values(form.errors).flat().length === 0 : true
869
- };
870
- },
871
- makeSelectFieldError: (name, id, validate) => data => {
872
- if (!validate || !data) return [];
873
- const errors = validate.filter(rule => {
874
- if (!rule) return true;
875
- if (!rule.rules) return true;
876
- const transformSelf = CoreUtils.mapKeysDeepLodash(rule.rules, (v, key) => key === '$self' ? id : key);
877
- const [hasError] = MatchRules.rulesMatcher(transformSelf, data, false, {
878
- prefix: name
879
- });
880
- return hasError;
881
- });
882
- const result = errors.map(r => r && r.message || 'error');
883
- return result.length === 0 ? [] : result;
884
- },
885
- selectorRulesDisabled: (id, rules, keys, flowName, value) => (data, form) => {
886
- const newState = {
887
- ...data,
888
- ...value,
889
- $form: form
890
- };
891
- const state = Object.assign(newState, id ? {
892
- $self: _get(newState, [flowName, ...id.split('.')])
893
- } : {});
894
- if (!rules) return false;
895
- if (typeof rules === 'function') {
896
- return !rules(state);
737
+ selectGlobal: (state) => state && state.flower,
738
+ selectFlower: (name) => (state) => _get(state, [name]),
739
+ selectFlowerFormNode: (id) => (state) => _get(state, ['form', id]),
740
+ selectFlowerHistory: (flower) => _get(flower, ['history'], []),
741
+ makeSelectNodesIds: (flower) => _get(flower, ['nodes']),
742
+ makeSelectStartNodeId: (flower) => _get(flower, ['startId']),
743
+ getDataByFlow: (flower) => _get(flower, ['data']) ?? {},
744
+ getDataFromState: (id) => (data) => (id === '*' ? data : _get(data, id)),
745
+ makeSelectNodeFormTouched: (form) => form && form.touched,
746
+ makeSelectCurrentNodeId: (flower, startNodeId) => _get(flower, ['current']) || startNodeId,
747
+ makeSelectCurrentNodeDisabled: (nodes, current) => !!_get(nodes, [current, 'disabled']),
748
+ makeSelectPrevNodeRetain: (nodes, history, current) => {
749
+ if (!nodes)
750
+ return;
751
+ const prevFlowerNode = [...history].reverse().find((el) => {
752
+ const { nodeType, retain } = nodes[el];
753
+ return nodeType === 'FlowerNode' || retain;
754
+ });
755
+ // eslint-disable-next-line consistent-return
756
+ if (nodes[current].nodeType === 'FlowerNode' || nodes[current].retain)
757
+ return;
758
+ if (!prevFlowerNode)
759
+ return;
760
+ if (nodes[prevFlowerNode] && nodes[prevFlowerNode].disabled)
761
+ return;
762
+ // eslint-disable-next-line consistent-return
763
+ return (nodes[prevFlowerNode] && nodes[prevFlowerNode].retain && prevFlowerNode);
764
+ },
765
+ makeSelectNodeErrors: (form) => {
766
+ return {
767
+ touched: form?.touched || false,
768
+ errors: form?.errors,
769
+ isValidating: form?.isValidating,
770
+ isValid: form && form.errors
771
+ ? Object.values(form.errors).flat().length === 0
772
+ : true
773
+ };
774
+ },
775
+ makeSelectFieldError: (name, id, validate) => (data) => {
776
+ if (!validate || !data)
777
+ return [];
778
+ const errors = validate.filter((rule) => {
779
+ if (!rule)
780
+ return true;
781
+ if (!rule.rules)
782
+ return true;
783
+ const transformSelf = CoreUtils.mapKeysDeepLodash(rule.rules, (v, key) => key === '$self' ? id : key);
784
+ const [hasError] = MatchRules.rulesMatcher(transformSelf, data, false, {
785
+ prefix: name
786
+ });
787
+ return hasError;
788
+ });
789
+ const result = errors.map((r) => (r && r.message) || 'error');
790
+ return result.length === 0 ? [] : result;
791
+ },
792
+ selectorRulesDisabled: (id, rules, keys, flowName, value) => (data, form) => {
793
+ const newState = { ...data, ...value, $form: form };
794
+ const state = Object.assign(newState, id ? { $self: _get(newState, [flowName, ...id.split('.')]) } : {});
795
+ if (!rules)
796
+ return false;
797
+ if (typeof rules === 'function') {
798
+ return !rules(state);
799
+ }
800
+ if (!keys)
801
+ return false;
802
+ const res = keys.reduce((acc, inc) => {
803
+ const k = inc;
804
+ return Object.assign(acc, { [k]: _get(state, k) });
805
+ }, {});
806
+ const [disabled] = MatchRules.rulesMatcher(rules, { ...flat.unflatten(res) }, false, { prefix: flowName });
807
+ return disabled;
897
808
  }
898
- if (!keys) return false;
899
- const res = keys.reduce((acc, inc) => {
900
- const k = inc;
901
- return Object.assign(acc, {
902
- [k]: _get(state, k)
903
- });
904
- }, {});
905
- const [disabled] = MatchRules.rulesMatcher(rules, {
906
- ...flat.unflatten(res)
907
- }, false, {
908
- prefix: flowName
909
- });
910
- return disabled;
911
- }
912
809
  };
913
810
 
914
811
  exports.RulesModes = void 0;
915
812
  (function (RulesModes) {
916
- RulesModes["$and"] = "$and";
917
- RulesModes["$or"] = "$or";
813
+ RulesModes["$and"] = "$and";
814
+ RulesModes["$or"] = "$or";
918
815
  })(exports.RulesModes || (exports.RulesModes = {}));
919
816
  var RulesOperators;
920
817
  (function (RulesOperators) {
921
- RulesOperators["$exist"] = "$exist";
922
- RulesOperators["$eq"] = "$eq";
923
- RulesOperators["$ne"] = "$ne";
924
- RulesOperators["$gt"] = "$gt";
925
- RulesOperators["$gte"] = "$gte";
926
- RulesOperators["$lt"] = "$lt";
927
- RulesOperators["$lte"] = "$lte";
928
- RulesOperators["$strGt"] = "$strGt";
929
- RulesOperators["$strGte"] = "$strGte";
930
- RulesOperators["$strLt"] = "$strLt";
931
- RulesOperators["$strLte"] = "$strLte";
932
- RulesOperators["$in"] = "$in";
933
- RulesOperators["$nin"] = "$nin";
934
- RulesOperators["$all"] = "$all";
935
- RulesOperators["$regex"] = "$regex";
818
+ RulesOperators["$exist"] = "$exist";
819
+ RulesOperators["$eq"] = "$eq";
820
+ RulesOperators["$ne"] = "$ne";
821
+ RulesOperators["$gt"] = "$gt";
822
+ RulesOperators["$gte"] = "$gte";
823
+ RulesOperators["$lt"] = "$lt";
824
+ RulesOperators["$lte"] = "$lte";
825
+ RulesOperators["$strGt"] = "$strGt";
826
+ RulesOperators["$strGte"] = "$strGte";
827
+ RulesOperators["$strLt"] = "$strLt";
828
+ RulesOperators["$strLte"] = "$strLte";
829
+ RulesOperators["$in"] = "$in";
830
+ RulesOperators["$nin"] = "$nin";
831
+ RulesOperators["$all"] = "$all";
832
+ RulesOperators["$regex"] = "$regex";
936
833
  })(RulesOperators || (RulesOperators = {}));
937
834
 
938
835
  exports.RulesOperators = void 0;
939
836
  (function (RulesOperators) {
940
- RulesOperators["$exist"] = "$exist";
941
- RulesOperators["$eq"] = "$eq";
942
- RulesOperators["$ne"] = "$ne";
943
- RulesOperators["$gt"] = "$gt";
944
- RulesOperators["$gte"] = "$gte";
945
- RulesOperators["$lt"] = "$lt";
946
- RulesOperators["$lte"] = "$lte";
947
- RulesOperators["$strGt"] = "$strGt";
948
- RulesOperators["$strGte"] = "$strGte";
949
- RulesOperators["$strLt"] = "$strLt";
950
- RulesOperators["$strLte"] = "$strLte";
951
- RulesOperators["$in"] = "$in";
952
- RulesOperators["$nin"] = "$nin";
953
- RulesOperators["$all"] = "$all";
954
- RulesOperators["$regex"] = "$regex";
837
+ RulesOperators["$exist"] = "$exist";
838
+ RulesOperators["$eq"] = "$eq";
839
+ RulesOperators["$ne"] = "$ne";
840
+ RulesOperators["$gt"] = "$gt";
841
+ RulesOperators["$gte"] = "$gte";
842
+ RulesOperators["$lt"] = "$lt";
843
+ RulesOperators["$lte"] = "$lte";
844
+ RulesOperators["$strGt"] = "$strGt";
845
+ RulesOperators["$strGte"] = "$strGte";
846
+ RulesOperators["$strLt"] = "$strLt";
847
+ RulesOperators["$strLte"] = "$strLte";
848
+ RulesOperators["$in"] = "$in";
849
+ RulesOperators["$nin"] = "$nin";
850
+ RulesOperators["$all"] = "$all";
851
+ RulesOperators["$regex"] = "$regex";
955
852
  })(exports.RulesOperators || (exports.RulesOperators = {}));
956
853
 
957
854
  const devtoolState = {};