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