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

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