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